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

View File

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

View File

@ -1,10 +1,23 @@
import './chat--rightUsers.scss';
import React from 'react';
import {useSelector} from 'react-redux';
import {useDispatch, useSelector} from 'react-redux';
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 useChannel from '~/client/hooks/useChannel';
@ -13,48 +26,106 @@ import Channel from './channel';
export default function ChatRightUsers() {
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 redditUsername = useSelector(redditUsernameSelector);
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 (
<div
className="chat--rightUsers"
>
<h2 className="users__chatsTitle">Who&apos;s here</h2>
<ul className="users__chatsList">
{list.map((username) => {
const isLinked = (
redditUsername !== username
&& '/r/anonymous' !== channel
);
{list.map(([id, username]) => {
const hasBlocked = -1 !== blocked.indexOf(id);
const isAnonymous = '/r/anonymous' === channel;
const isSelf = redditUsername === username;
const index = friendshipIdIndex(friendship, id);
const hasFriendship = -1 !== index;
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({
icon: '',
label: 'Add friend',
onClick: () => {},
onClick: () => dispatch(submitAddFriend({nameOrStatus: username})),
});
}
if (hasBlocked) {
userActions.push({
icon: '😶',
label: 'Unblock',
onClick: () => dispatch(submitUnblock(id)),
});
}
else {
userActions.push({
icon: '☢️',
label: 'Block',
onClick: () => {},
onClick: () => dispatch(submitBlock(id)),
});
}
// {
// icon: '',
// label: 'Favorite',
// onClick: () => {},
// },
}
return (
<li
className="users__item"
key={username}
key={id}
>
<Channel
actions={userActions}
href={isLinked ? `/chat/u/${username}` : null}
name={username}
href={(
(hasFriendship && userId !== id)
? `/chat/u/${username}`
: null
)}
name={username || '?'}
prefix="/u/"
/>
</li>

View File

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

View File

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

View File

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

View File

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