flow: user ops

This commit is contained in:
cha0s 2020-07-20 19:24:55 -05:00
parent 21f5feb9c0
commit 898463d305
7 changed files with 131 additions and 41 deletions

View File

@ -77,12 +77,12 @@ export default function ChatLeftFriends() {
const favoritesActions = [ const favoritesActions = [
{ {
icon: '👎', icon: '👎',
label: 'Deny', label: 'Deny friend request',
onClick: () => dispatch(submitRemoveFriend(adderId)), onClick: () => dispatch(submitRemoveFriend(adderId)),
}, },
{ {
icon: '👍', icon: '👍',
label: 'Confirm', label: 'Confirm friend request',
onClick: () => dispatch(submitAddFriend({ onClick: () => dispatch(submitAddFriend({
addeeId, addeeId,
adderId, adderId,
@ -95,7 +95,7 @@ export default function ChatLeftFriends() {
className="friends__item" className="friends__item"
> >
<Channel <Channel
key={channel} key={JSON.stringify(friendship)}
actions={favoritesActions} actions={favoritesActions}
href={`/chat/u/${channel}`} href={`/chat/u/${channel}`}
name={channel} name={channel}
@ -121,6 +121,7 @@ export default function ChatLeftFriends() {
]; ];
return ( return (
<li <li
key={channel}
className="friends__item" className="friends__item"
> >
<Channel <Channel
@ -167,6 +168,7 @@ export default function ChatLeftFriends() {
]; ];
return ( return (
<li <li
key={channel}
className="friends__item" className="friends__item"
> >
<Channel <Channel

View File

@ -29,6 +29,7 @@ export default function ChatRightBlocked(props) {
]; ];
return ( return (
<li <li
key={id}
className="blocked__item" className="blocked__item"
> >
<Channel <Channel

View File

@ -1,10 +1,23 @@
import './chat--rightUsers.scss'; import './chat--rightUsers.scss';
import React from 'react'; import React from 'react';
import {useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {channelUsersSelector} from '~/common/state/chat'; import {channelUsersSelector} from '~/common/state/chat';
import {redditUsernameSelector} from '~/common/state/user'; import {
blockedSelector,
favoriteUsersSelector,
friendshipIdIndex,
friendshipSelector,
idSelector,
redditUsernameSelector,
submitAddFavorite,
submitAddFriend,
submitBlock,
submitRemoveFavorite,
submitRemoveFriend,
submitUnblock,
} from '~/common/state/user';
import {usernamesSelector} from '~/common/state/usernames'; import {usernamesSelector} from '~/common/state/usernames';
import useChannel from '~/client/hooks/useChannel'; import useChannel from '~/client/hooks/useChannel';
@ -13,48 +26,106 @@ import Channel from './channel';
export default function ChatRightUsers() { export default function ChatRightUsers() {
const channel = useChannel(); const channel = useChannel();
const dispatch = useDispatch();
const blocked = useSelector(blockedSelector);
const favoriteUsers = useSelector(favoriteUsersSelector);
const friendship = useSelector(friendshipSelector);
const userId = useSelector(idSelector);
const ids = useSelector((state) => channelUsersSelector(state, channel)); const ids = useSelector((state) => channelUsersSelector(state, channel));
const redditUsername = useSelector(redditUsernameSelector); const redditUsername = useSelector(redditUsernameSelector);
const usernames = useSelector(usernamesSelector); const usernames = useSelector(usernamesSelector);
const list = ids.map((id) => usernames[id]).sort(); const list = ids
.map((id) => [id, usernames[id]])
.sort((l, r) => (l[1] < r[1] ? -1 : 1));
return ( return (
<div <div
className="chat--rightUsers" className="chat--rightUsers"
> >
<h2 className="users__chatsTitle">Who&apos;s here</h2> <h2 className="users__chatsTitle">Who&apos;s here</h2>
<ul className="users__chatsList"> <ul className="users__chatsList">
{list.map((username) => { {list.map(([id, username]) => {
const isLinked = ( const hasBlocked = -1 !== blocked.indexOf(id);
redditUsername !== username const isAnonymous = '/r/anonymous' === channel;
&& '/r/anonymous' !== channel const isSelf = redditUsername === username;
); const index = friendshipIdIndex(friendship, id);
const hasFriendship = -1 !== index;
const userActions = []; const userActions = [];
if (isLinked) { if (!isAnonymous && !isSelf) {
if (hasFriendship) {
if ('active' === friendship[index].status) {
userActions.push({
icon: '🚫',
label: 'Unfriend',
onClick: () => dispatch(submitRemoveFriend(id)),
});
if (-1 === favoriteUsers.indexOf(username)) {
userActions.push({
icon: '❤️',
label: 'Favorite',
onClick: () => dispatch(submitAddFavorite(`/u/${username}`)),
});
}
else {
userActions.push({
icon: '💔',
label: 'Unfavorite',
onClick: () => dispatch(submitRemoveFavorite(`/u/${username}`)),
});
}
}
else if (friendship[index].adderId !== userId) {
userActions.push({
icon: '👎',
label: 'Deny friend request',
onClick: () => dispatch(submitRemoveFriend(id)),
});
const {addeeId, adderId} = friendship[index];
userActions.push({
icon: '👍',
label: 'Confirm friend request',
onClick: () => dispatch(submitAddFriend({
adderId,
addeeId,
nameOrStatus: username,
})),
});
}
}
else if (!hasBlocked) {
userActions.push({ userActions.push({
icon: '', icon: '',
label: 'Add friend', label: 'Add friend',
onClick: () => {}, onClick: () => dispatch(submitAddFriend({nameOrStatus: username})),
}); });
}
if (hasBlocked) {
userActions.push({
icon: '😶',
label: 'Unblock',
onClick: () => dispatch(submitUnblock(id)),
});
}
else {
userActions.push({ userActions.push({
icon: '☢️', icon: '☢️',
label: 'Block', label: 'Block',
onClick: () => {}, onClick: () => dispatch(submitBlock(id)),
}); });
} }
// { }
// icon: '',
// label: 'Favorite',
// onClick: () => {},
// },
return ( return (
<li <li
className="users__item" className="users__item"
key={username} key={id}
> >
<Channel <Channel
actions={userActions} actions={userActions}
href={isLinked ? `/chat/u/${username}` : null} href={(
name={username} (hasFriendship && userId !== id)
? `/chat/u/${username}`
: null
)}
name={username || '?'}
prefix="/u/" prefix="/u/"
/> />
</li> </li>

View File

@ -20,6 +20,7 @@ import {
block, block,
confirmFriendship, confirmFriendship,
friendshipIndex, friendshipIndex,
idSelector,
pendingFriendshipSelector, pendingFriendshipSelector,
removeFromFavorites, removeFromFavorites,
removeFriendship, removeFriendship,
@ -30,6 +31,7 @@ import useSocket from './hooks/useSocket';
export default function Dispatcher({children}) { export default function Dispatcher({children}) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const id = useSelector(idSelector);
const pendingFriendship = useSelector(pendingFriendshipSelector); const pendingFriendship = useSelector(pendingFriendshipSelector);
useSocket((socket) => { useSocket((socket) => {
const onPacket = (packet) => { const onPacket = (packet) => {
@ -38,13 +40,14 @@ export default function Dispatcher({children}) {
const hasPendingFriendship = -1 !== friendshipIndex(pendingFriendship, packet.data); const hasPendingFriendship = -1 !== friendshipIndex(pendingFriendship, packet.data);
const friendship = { const friendship = {
...( ...(
hasPendingFriendship (hasPendingFriendship && id === addeeId)
? {addeeId: adderId, adderId: addeeId, status: nameOrStatus} ? {addeeId: adderId, adderId: addeeId}
: {adderId, addeeId, status: nameOrStatus} : {adderId, addeeId}
), ),
status: nameOrStatus, status: nameOrStatus,
}; };
dispatch((hasPendingFriendship ? confirmFriendship : addFriendship)(friendship)); const creator = hasPendingFriendship ? confirmFriendship : addFriendship;
dispatch(creator(friendship));
} }
if (packet instanceof Block) { if (packet instanceof Block) {
dispatch(block(packet.data)); dispatch(block(packet.data));
@ -72,7 +75,7 @@ export default function Dispatcher({children}) {
socket.on('disconnect', () => socket.off('packet', onPacket)); socket.on('disconnect', () => socket.off('packet', onPacket));
socket.on('reconnect', () => socket.on('packet', onPacket)); socket.on('reconnect', () => socket.on('packet', onPacket));
return () => socket.off('packet', onPacket); return () => socket.off('packet', onPacket);
}); }, [pendingFriendship]);
return <>{children}</>; return <>{children}</>;
} }

View File

@ -65,8 +65,9 @@ const effects = {
return; return;
} }
dispatch((hasFriendship ? confirmFriendship : addFriendship)({ dispatch((hasFriendship ? confirmFriendship : addFriendship)({
addeeId: hasFriendship ? userId : id, addeeId: id,
adderId: hasFriendship ? id : userId, adderId: userId,
...(hasFriendship ? payload : {}),
status: hasFriendship ? 'active' : 'pending', status: hasFriendship ? 'active' : 'pending',
})); }));
}); });

View File

@ -40,6 +40,11 @@ export const friendshipIndex = (friendship, test) => (
.findIndex(({addeeId, adderId}) => test.adderId === adderId && test.addeeId === addeeId) .findIndex(({addeeId, adderId}) => test.adderId === adderId && test.addeeId === addeeId)
); );
export const friendshipIdIndex = (friendship, id) => (
friendship
.findIndex(({addeeId, adderId}) => id === adderId || id === addeeId)
);
export const friendshipSelector = createSelector( export const friendshipSelector = createSelector(
userSelector, userSelector,
(user) => user.friendship, (user) => user.friendship,
@ -102,7 +107,13 @@ const slice = createSlice({
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
reducers: { reducers: {
addFriendship: ({friendship}, {payload}) => { addFriendship: ({friendship}, {payload}) => {
const index = friendshipIndex(friendship, payload);
if (-1 === index) {
friendship.push(payload); friendship.push(payload);
}
else {
friendship[index] = payload;
}
}, },
addToFavorites: ({favorites}, {payload: favorite}) => { addToFavorites: ({favorites}, {payload: favorite}) => {
favorites.push(favorite); favorites.push(favorite);
@ -114,7 +125,7 @@ const slice = createSlice({
friendship[friendshipIndex(friendship, payload)].status = 'active'; friendship[friendshipIndex(friendship, payload)].status = 'active';
}, },
removeFriendship: ({friendship}, id) => { removeFriendship: ({friendship}, id) => {
friendship.splice(friendshipIndex(friendship, {addeeId: id, adderId: id}), 1); friendship.splice(friendshipIdIndex(friendship, id), 1);
}, },
removeFromFavorites: ({favorites}, {payload: favorite}) => { removeFromFavorites: ({favorites}, {payload: favorite}) => {
favorites.splice(favorites.indexOf(favorite), 1); favorites.splice(favorites.indexOf(favorite), 1);

View File

@ -106,6 +106,7 @@ export function createSocketServer(httpServer) {
} }
const addeeId = user.id; const addeeId = user.id;
let friendship = await Friendship.findOne({where: {adderId: addeeId, addeeId: adderId}}); let friendship = await Friendship.findOne({where: {adderId: addeeId, addeeId: adderId}});
const friendshipIsActive = !!friendship;
if (friendship) { if (friendship) {
friendship.status = 'active'; friendship.status = 'active';
await friendship.save(); await friendship.save();
@ -115,8 +116,8 @@ export function createSocketServer(httpServer) {
} }
[addeeId, adderId].forEach((id) => { [addeeId, adderId].forEach((id) => {
socket.to(`/user/${id}`, new AddFriend({ socket.to(`/user/${id}`, new AddFriend({
addeeId, addeeId: friendshipIsActive ? adderId : addeeId,
adderId, adderId: friendshipIsActive ? addeeId : adderId,
nameOrStatus: friendship.status, nameOrStatus: friendship.status,
})); }));
}); });