feat usernames

This commit is contained in:
cha0s 2020-07-18 05:24:52 -05:00
parent 7c10bc807c
commit cbd502ea7b
8 changed files with 97 additions and 50 deletions

View File

@ -4,13 +4,13 @@ import classnames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import ReactMarkdown from 'react-markdown';
import {useSelector} from 'react-redux';
import {usernameSelector} from '~/common/state/usernames';
export default function ChatMessage(props) {
const {message: {owner, message, timestamp}, isShort} = props;
const ownerMap = {
1: 'cha0s',
2: 'BabeHasHiccups',
};
const username = useSelector((state) => usernameSelector(state, owner));
const dtf = new Intl.DateTimeFormat(undefined, {
hour: '2-digit',
minute: '2-digit',
@ -31,7 +31,7 @@ export default function ChatMessage(props) {
{
!isShort && (
<header>
<div className="chat--messageOwner">{ownerMap[owner]}</div>
<div className="chat--messageOwner">{username}</div>
{$messageTime}
</header>
)

View File

@ -4,7 +4,6 @@ import Message from '~/common/packets/message.packet';
import {
addMessage,
confirmMessage,
fetchUsernames,
join,
joined,
leave,
@ -13,6 +12,10 @@ import {
submitMessage,
} from '~/common/state/chat';
import {
fetchUsernames,
} from '~/common/state/usernames';
import {socket} from '~/client/hooks/useSocket';
const effects = {

View File

@ -3,6 +3,7 @@ import {combineReducers} from 'redux';
import chat from '~/common/state/chat';
import user from '~/common/state/user';
import usernames from '~/common/state/usernames';
import createCommonStore from '~/common/store';
import {middleware as effectsMiddleware} from './effects';
@ -10,6 +11,7 @@ import {middleware as effectsMiddleware} from './effects';
const reducer = combineReducers({
chat,
user,
usernames,
});
export default function createStore(options = {}) {

View File

@ -1,6 +1,6 @@
import {Packet} from '@avocado/net';
export default class Leave extends Packet {
export default class Usernames extends Packet {
static get schema() {
return {

View File

@ -1,14 +1,9 @@
/* eslint-disable no-param-reassign */
import {
createAsyncThunk,
createSelector,
createSlice,
} from '@reduxjs/toolkit';
import Usernames from '~/common/packets/usernames.packet';
import {socket} from '~/client/hooks/useSocket';
import hydration from './hydration';
export const channelsSelector = (state) => state.chat.channels;
@ -25,20 +20,6 @@ export const channelMessagesSelector = createSelector(
(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({
name: 'chat',
initialState: {
@ -47,7 +28,6 @@ const slice = createSlice({
messages: {},
recent: [],
unread: {},
users: {},
...(hydration('chat') || {}),
},
reducers: {
@ -112,13 +92,6 @@ const slice = createSlice({
submitLeave: () => {},
submitMessage: () => {},
},
extraReducers: {
[fetchUsernames.fulfilled]: ({users}, {payload}) => {
payload.forEach(([id, name]) => {
users[id] = name;
});
},
},
});
export const {

View File

@ -0,0 +1,56 @@
import {
createAsyncThunk,
createSelector,
createSlice,
} from '@reduxjs/toolkit';
import Usernames from '~/common/packets/usernames.packet';
import {socket} from '~/client/hooks/useSocket';
import hydration from './hydration';
export const usernamesSelector = (state) => state.usernames;
export const usernameSelector = createSelector(
[usernamesSelector, (_, id) => id],
(usernames, id) => usernames[id],
);
export const fetchUsernames = createAsyncThunk(
'usernames/fetchUsernames',
async (ids, {getState}) => {
const usernames = usernamesSelector(getState());
const missingIds = ids.filter((id) => !usernames[id]);
if (0 === missingIds.length) {
return [];
}
return new Promise((resolve) => (
socket.send(new Usernames(missingIds), (usernames) => {
resolve(missingIds.map((id, i) => [id, usernames[i]]));
})
));
},
);
/* eslint-disable no-param-reassign */
const slice = createSlice({
name: 'usernames',
initialState: hydration('usernames') || {},
extraReducers: {
[fetchUsernames.fulfilled]: (state, {payload}) => {
payload.forEach(([id, name]) => {
state[id] = name;
});
},
},
});
/* eslint-enable no-param-reassign */
export const {
addFriend,
blockUser,
removeFriend,
} = slice.actions;
export default slice.reducer;

View File

@ -59,12 +59,29 @@ export const channelsToHydrate = async (req) => (
(req.channel ? [req.channel] : []).concat(req.user ? await req.user.favorites() : [])
);
const chatUsers = async (req) => {
const toHydrate = await channelsToHydrate(req);
if (0 === toHydrate.length) {
return [];
}
const entries = await Promise.all(
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
);
const chatUsers = [];
for (let i = 0; i < toHydrate.length; i++) {
chatUsers.push(...entries[i].users);
}
return chatUsers;
};
export const appChatState = async (req) => {
const {User} = allModels();
const toHydrate = await channelsToHydrate(req);
if (0 === toHydrate.length) {
return null;
}
const entries = await Promise.all(
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
);
const chat = {
channels: {},
focus: req.channel ? joinChannel(req.channel) : '',
@ -72,14 +89,9 @@ export const appChatState = async (req) => {
recent: [],
users: {},
};
const entries = await Promise.all(
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
);
const chatUsers = [];
for (let i = 0; i < toHydrate.length; i++) {
const channel = joinChannel(toHydrate[i]);
const {messages, users} = entries[i];
chatUsers.push(...users);
chat.channels[channel] = {
messages: messages.map((message) => message.uuid),
users,
@ -89,14 +101,14 @@ export const appChatState = async (req) => {
});
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;
};
export const appUsernamesState = async (req) => {
const {User} = allModels();
const usernames = await Promise.all(
(await chatUsers(req))
.map(async (id) => [id, (await User.findByPk(id)).redditUsername]),
);
return usernames.reduce((r, [id, username]) => ({...r, [id]: username}), {});
};

View File

@ -1,7 +1,7 @@
import session from 'express-session';
import {registerHooks} from 'scwp';
import {appChatState, appUserState} from './entry';
import {appChatState, appUserState, appUsernamesState} from './entry';
import createRedisClient from './redis';
const redisClient = createRedisClient();
@ -22,5 +22,6 @@ registerHooks({
hydration: async (req) => ({
chat: await appChatState(req),
user: await appUserState(req),
usernames: await appUsernamesState(req),
}),
}, module.id);