feat: usernames
This commit is contained in:
parent
3ce283ff4c
commit
7c10bc807c
12
src/client/store/effects.js
vendored
12
src/client/store/effects.js
vendored
|
@ -4,7 +4,9 @@ import Message from '~/common/packets/message.packet';
|
||||||
import {
|
import {
|
||||||
addMessage,
|
addMessage,
|
||||||
confirmMessage,
|
confirmMessage,
|
||||||
|
fetchUsernames,
|
||||||
join,
|
join,
|
||||||
|
joined,
|
||||||
leave,
|
leave,
|
||||||
submitJoin,
|
submitJoin,
|
||||||
submitLeave,
|
submitLeave,
|
||||||
|
@ -14,6 +16,12 @@ import {
|
||||||
import {socket} from '~/client/hooks/useSocket';
|
import {socket} from '~/client/hooks/useSocket';
|
||||||
|
|
||||||
const effects = {
|
const effects = {
|
||||||
|
[join]: ({dispatch}, {payload: {users}}) => {
|
||||||
|
dispatch(fetchUsernames(users));
|
||||||
|
},
|
||||||
|
[joined]: ({dispatch}, {payload: {id}}) => {
|
||||||
|
dispatch(fetchUsernames([id]));
|
||||||
|
},
|
||||||
[submitJoin]: ({dispatch}, {payload}) => {
|
[submitJoin]: ({dispatch}, {payload}) => {
|
||||||
const {channel} = payload;
|
const {channel} = payload;
|
||||||
socket.send(new Join(payload), ({messages, users}) => {
|
socket.send(new Join(payload), ({messages, users}) => {
|
||||||
|
@ -22,9 +30,7 @@ const effects = {
|
||||||
},
|
},
|
||||||
[submitLeave]: ({dispatch}, {payload}) => {
|
[submitLeave]: ({dispatch}, {payload}) => {
|
||||||
const {channel} = payload;
|
const {channel} = payload;
|
||||||
socket.send(new Leave(payload), () => {
|
socket.send(new Leave(payload), () => dispatch(leave({channel})));
|
||||||
dispatch(leave({channel}));
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[submitMessage]: ({dispatch}, {payload}) => {
|
[submitMessage]: ({dispatch}, {payload}) => {
|
||||||
dispatch(addMessage(payload));
|
dispatch(addMessage(payload));
|
||||||
|
|
14
src/common/packets/usernames.packet.js
Normal file
14
src/common/packets/usernames.packet.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import {Packet} from '@avocado/net';
|
||||||
|
|
||||||
|
export default class Leave extends Packet {
|
||||||
|
|
||||||
|
static get schema() {
|
||||||
|
return {
|
||||||
|
...super.schema,
|
||||||
|
data: [
|
||||||
|
'uint32',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
import {
|
import {
|
||||||
|
createAsyncThunk,
|
||||||
createSelector,
|
createSelector,
|
||||||
createSlice,
|
createSlice,
|
||||||
} from '@reduxjs/toolkit';
|
} from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import Usernames from '~/common/packets/usernames.packet';
|
||||||
|
|
||||||
|
import {socket} from '~/client/hooks/useSocket';
|
||||||
|
|
||||||
import hydration from './hydration';
|
import hydration from './hydration';
|
||||||
|
|
||||||
export const channelsSelector = (state) => state.chat.channels;
|
export const channelsSelector = (state) => state.chat.channels;
|
||||||
|
@ -20,6 +25,20 @@ export const channelMessagesSelector = createSelector(
|
||||||
(channel, messages) => (!channel ? [] : channel.messages.map((uuid) => messages[uuid])),
|
(channel, messages) => (!channel ? [] : channel.messages.map((uuid) => messages[uuid])),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const fetchUsernames = createAsyncThunk(
|
||||||
|
'chat/fetchUsernames',
|
||||||
|
async (ids, {getState}) => {
|
||||||
|
const {chat: {users}} = getState();
|
||||||
|
const missingIds = ids.filter((id) => !users[id]);
|
||||||
|
if (0 === missingIds.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => socket.send(new Usernames(missingIds), (names) => {
|
||||||
|
resolve(missingIds.map((id, i) => [id, names[i]]));
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const slice = createSlice({
|
const slice = createSlice({
|
||||||
name: 'chat',
|
name: 'chat',
|
||||||
initialState: {
|
initialState: {
|
||||||
|
@ -93,6 +112,13 @@ const slice = createSlice({
|
||||||
submitLeave: () => {},
|
submitLeave: () => {},
|
||||||
submitMessage: () => {},
|
submitMessage: () => {},
|
||||||
},
|
},
|
||||||
|
extraReducers: {
|
||||||
|
[fetchUsernames.fulfilled]: ({users}, {payload}) => {
|
||||||
|
payload.forEach(([id, name]) => {
|
||||||
|
users[id] = name;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {promisify} from 'util';
|
||||||
|
|
||||||
import {joinChannel} from '~/common/channel';
|
import {joinChannel} from '~/common/channel';
|
||||||
|
|
||||||
|
import {allModels} from './models/registrar';
|
||||||
import createRedisClient, {keys} from './redis';
|
import createRedisClient, {keys} from './redis';
|
||||||
|
|
||||||
const redisClient = createRedisClient();
|
const redisClient = createRedisClient();
|
||||||
|
@ -41,23 +42,25 @@ export const channelState = async (req, channel) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const userState = async (req) => {
|
const userState = async (user) => (
|
||||||
const {user} = req;
|
user
|
||||||
return user
|
|
||||||
? {
|
? {
|
||||||
favorites: [],
|
favorites: [],
|
||||||
friends: await user.friends(),
|
friends: await user.friends(),
|
||||||
id: user.id,
|
id: user.id,
|
||||||
redditUsername: user.redditUsername,
|
redditUsername: user.redditUsername,
|
||||||
}
|
}
|
||||||
: null;
|
: null
|
||||||
};
|
);
|
||||||
|
|
||||||
|
export const appUserState = async (req) => userState(req.user);
|
||||||
|
|
||||||
export const channelsToHydrate = async (req) => (
|
export const channelsToHydrate = async (req) => (
|
||||||
(req.channel ? [req.channel] : []).concat(req.user ? await req.user.favorites() : [])
|
(req.channel ? [req.channel] : []).concat(req.user ? await req.user.favorites() : [])
|
||||||
);
|
);
|
||||||
|
|
||||||
export const chatState = async (req) => {
|
export const appChatState = async (req) => {
|
||||||
|
const {User} = allModels();
|
||||||
const toHydrate = await channelsToHydrate(req);
|
const toHydrate = await channelsToHydrate(req);
|
||||||
if (0 === toHydrate.length) {
|
if (0 === toHydrate.length) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -67,13 +70,16 @@ export const chatState = async (req) => {
|
||||||
focus: req.channel ? joinChannel(req.channel) : '',
|
focus: req.channel ? joinChannel(req.channel) : '',
|
||||||
messages: {},
|
messages: {},
|
||||||
recent: [],
|
recent: [],
|
||||||
|
users: {},
|
||||||
};
|
};
|
||||||
const entries = await Promise.all(
|
const entries = await Promise.all(
|
||||||
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
|
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
|
||||||
);
|
);
|
||||||
|
const chatUsers = [];
|
||||||
for (let i = 0; i < toHydrate.length; i++) {
|
for (let i = 0; i < toHydrate.length; i++) {
|
||||||
const channel = joinChannel(toHydrate[i]);
|
const channel = joinChannel(toHydrate[i]);
|
||||||
const {messages, users} = entries[i];
|
const {messages, users} = entries[i];
|
||||||
|
chatUsers.push(...users);
|
||||||
chat.channels[channel] = {
|
chat.channels[channel] = {
|
||||||
messages: messages.map((message) => message.uuid),
|
messages: messages.map((message) => message.uuid),
|
||||||
users,
|
users,
|
||||||
|
@ -83,5 +89,14 @@ export const chatState = async (req) => {
|
||||||
});
|
});
|
||||||
chat.recent.push(channel);
|
chat.recent.push(channel);
|
||||||
}
|
}
|
||||||
|
const userStates = await Promise.all(
|
||||||
|
chatUsers.map(
|
||||||
|
async (id) => userState(await User.findByPk(id)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
chat.users = userStates.reduce(
|
||||||
|
(r, userState) => ({...r, [userState.id]: userState.redditUsername}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
return chat;
|
return chat;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import session from 'express-session';
|
import session from 'express-session';
|
||||||
import {registerHooks} from 'scwp';
|
import {registerHooks} from 'scwp';
|
||||||
|
|
||||||
import {chatState, userState} from './entry';
|
import {appChatState, appUserState} from './entry';
|
||||||
import createRedisClient from './redis';
|
import createRedisClient from './redis';
|
||||||
|
|
||||||
const redisClient = createRedisClient();
|
const redisClient = createRedisClient();
|
||||||
|
@ -20,7 +20,7 @@ export default (options = {}) => (
|
||||||
|
|
||||||
registerHooks({
|
registerHooks({
|
||||||
hydration: async (req) => ({
|
hydration: async (req) => ({
|
||||||
chat: await chatState(req),
|
chat: await appChatState(req),
|
||||||
user: await userState(req),
|
user: await appUserState(req),
|
||||||
}),
|
}),
|
||||||
}, module.id);
|
}, module.id);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {joinChannel, parseChannel} from '~/common/channel';
|
||||||
import Join from '~/common/packets/join.packet';
|
import Join from '~/common/packets/join.packet';
|
||||||
import Leave from '~/common/packets/leave.packet';
|
import Leave from '~/common/packets/leave.packet';
|
||||||
import Message from '~/common/packets/message.packet';
|
import Message from '~/common/packets/message.packet';
|
||||||
|
import Usernames from '~/common/packets/usernames.packet';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
channelsToHydrate,
|
channelsToHydrate,
|
||||||
|
@ -19,8 +20,9 @@ import {
|
||||||
channelUsers,
|
channelUsers,
|
||||||
} from '~/server/entry';
|
} from '~/server/entry';
|
||||||
|
|
||||||
|
import {allModels} from './models/registrar';
|
||||||
import passport from './passport';
|
import passport from './passport';
|
||||||
import createRedisClient, {keys} from './redis';
|
import createRedisClient from './redis';
|
||||||
import session from './session';
|
import session from './session';
|
||||||
|
|
||||||
const pubClient = createRedisClient();
|
const pubClient = createRedisClient();
|
||||||
|
@ -28,6 +30,7 @@ const subClient = createRedisClient();
|
||||||
const adapter = redisAdapter({pubClient, subClient});
|
const adapter = redisAdapter({pubClient, subClient});
|
||||||
|
|
||||||
export function createSocketServer(httpServer) {
|
export function createSocketServer(httpServer) {
|
||||||
|
const {User} = allModels();
|
||||||
const socketServer = new SocketServer(httpServer, {
|
const socketServer = new SocketServer(httpServer, {
|
||||||
adapter,
|
adapter,
|
||||||
});
|
});
|
||||||
|
@ -105,6 +108,11 @@ export function createSocketServer(httpServer) {
|
||||||
.expire(key, 600)
|
.expire(key, 600)
|
||||||
.exec(() => fn([timestamp, uuid]));
|
.exec(() => fn([timestamp, uuid]));
|
||||||
}
|
}
|
||||||
|
if (packet instanceof Usernames) {
|
||||||
|
fn(await Promise.all(packet.data.map(
|
||||||
|
async (id) => (await User.findByPk(id)).redditUsername,
|
||||||
|
)));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
socket.on('disconnecting', async () => {
|
socket.on('disconnecting', async () => {
|
||||||
Object.keys(socket.socket.rooms).forEach((room) => {
|
Object.keys(socket.socket.rooms).forEach((room) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user