feat usernames
This commit is contained in:
parent
7c10bc807c
commit
cbd502ea7b
|
@ -4,13 +4,13 @@ import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import {useSelector} from 'react-redux';
|
||||||
|
|
||||||
|
import {usernameSelector} from '~/common/state/usernames';
|
||||||
|
|
||||||
export default function ChatMessage(props) {
|
export default function ChatMessage(props) {
|
||||||
const {message: {owner, message, timestamp}, isShort} = props;
|
const {message: {owner, message, timestamp}, isShort} = props;
|
||||||
const ownerMap = {
|
const username = useSelector((state) => usernameSelector(state, owner));
|
||||||
1: 'cha0s',
|
|
||||||
2: 'BabeHasHiccups',
|
|
||||||
};
|
|
||||||
const dtf = new Intl.DateTimeFormat(undefined, {
|
const dtf = new Intl.DateTimeFormat(undefined, {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
|
@ -31,7 +31,7 @@ export default function ChatMessage(props) {
|
||||||
{
|
{
|
||||||
!isShort && (
|
!isShort && (
|
||||||
<header>
|
<header>
|
||||||
<div className="chat--messageOwner">{ownerMap[owner]}</div>
|
<div className="chat--messageOwner">{username}</div>
|
||||||
{$messageTime}
|
{$messageTime}
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|
5
src/client/store/effects.js
vendored
5
src/client/store/effects.js
vendored
|
@ -4,7 +4,6 @@ import Message from '~/common/packets/message.packet';
|
||||||
import {
|
import {
|
||||||
addMessage,
|
addMessage,
|
||||||
confirmMessage,
|
confirmMessage,
|
||||||
fetchUsernames,
|
|
||||||
join,
|
join,
|
||||||
joined,
|
joined,
|
||||||
leave,
|
leave,
|
||||||
|
@ -13,6 +12,10 @@ import {
|
||||||
submitMessage,
|
submitMessage,
|
||||||
} from '~/common/state/chat';
|
} from '~/common/state/chat';
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchUsernames,
|
||||||
|
} from '~/common/state/usernames';
|
||||||
|
|
||||||
import {socket} from '~/client/hooks/useSocket';
|
import {socket} from '~/client/hooks/useSocket';
|
||||||
|
|
||||||
const effects = {
|
const effects = {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {combineReducers} from 'redux';
|
||||||
|
|
||||||
import chat from '~/common/state/chat';
|
import chat from '~/common/state/chat';
|
||||||
import user from '~/common/state/user';
|
import user from '~/common/state/user';
|
||||||
|
import usernames from '~/common/state/usernames';
|
||||||
import createCommonStore from '~/common/store';
|
import createCommonStore from '~/common/store';
|
||||||
|
|
||||||
import {middleware as effectsMiddleware} from './effects';
|
import {middleware as effectsMiddleware} from './effects';
|
||||||
|
@ -10,6 +11,7 @@ import {middleware as effectsMiddleware} from './effects';
|
||||||
const reducer = combineReducers({
|
const reducer = combineReducers({
|
||||||
chat,
|
chat,
|
||||||
user,
|
user,
|
||||||
|
usernames,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function createStore(options = {}) {
|
export default function createStore(options = {}) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Packet} from '@avocado/net';
|
import {Packet} from '@avocado/net';
|
||||||
|
|
||||||
export default class Leave extends Packet {
|
export default class Usernames extends Packet {
|
||||||
|
|
||||||
static get schema() {
|
static get schema() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
/* 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;
|
||||||
|
@ -25,20 +20,6 @@ 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: {
|
||||||
|
@ -47,7 +28,6 @@ const slice = createSlice({
|
||||||
messages: {},
|
messages: {},
|
||||||
recent: [],
|
recent: [],
|
||||||
unread: {},
|
unread: {},
|
||||||
users: {},
|
|
||||||
...(hydration('chat') || {}),
|
...(hydration('chat') || {}),
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
|
@ -112,13 +92,6 @@ const slice = createSlice({
|
||||||
submitLeave: () => {},
|
submitLeave: () => {},
|
||||||
submitMessage: () => {},
|
submitMessage: () => {},
|
||||||
},
|
},
|
||||||
extraReducers: {
|
|
||||||
[fetchUsernames.fulfilled]: ({users}, {payload}) => {
|
|
||||||
payload.forEach(([id, name]) => {
|
|
||||||
users[id] = name;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
|
|
56
src/common/state/usernames.js
Normal file
56
src/common/state/usernames.js
Normal 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;
|
|
@ -59,12 +59,29 @@ 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() : [])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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) => {
|
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;
|
||||||
}
|
}
|
||||||
|
const entries = await Promise.all(
|
||||||
|
toHydrate.map((favorite) => channelState(req, joinChannel(favorite))),
|
||||||
|
);
|
||||||
const chat = {
|
const chat = {
|
||||||
channels: {},
|
channels: {},
|
||||||
focus: req.channel ? joinChannel(req.channel) : '',
|
focus: req.channel ? joinChannel(req.channel) : '',
|
||||||
|
@ -72,14 +89,9 @@ export const appChatState = async (req) => {
|
||||||
recent: [],
|
recent: [],
|
||||||
users: {},
|
users: {},
|
||||||
};
|
};
|
||||||
const entries = await Promise.all(
|
|
||||||
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,
|
||||||
|
@ -89,14 +101,14 @@ export const appChatState = 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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}), {});
|
||||||
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import session from 'express-session';
|
import session from 'express-session';
|
||||||
import {registerHooks} from 'scwp';
|
import {registerHooks} from 'scwp';
|
||||||
|
|
||||||
import {appChatState, appUserState} from './entry';
|
import {appChatState, appUserState, appUsernamesState} from './entry';
|
||||||
import createRedisClient from './redis';
|
import createRedisClient from './redis';
|
||||||
|
|
||||||
const redisClient = createRedisClient();
|
const redisClient = createRedisClient();
|
||||||
|
@ -22,5 +22,6 @@ registerHooks({
|
||||||
hydration: async (req) => ({
|
hydration: async (req) => ({
|
||||||
chat: await appChatState(req),
|
chat: await appChatState(req),
|
||||||
user: await appUserState(req),
|
user: await appUserState(req),
|
||||||
|
usernames: await appUsernamesState(req),
|
||||||
}),
|
}),
|
||||||
}, module.id);
|
}, module.id);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user