flow: user ops
This commit is contained in:
parent
21f5feb9c0
commit
898463d305
|
@ -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
|
||||||
|
|
|
@ -29,6 +29,7 @@ export default function ChatRightBlocked(props) {
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
|
key={id}
|
||||||
className="blocked__item"
|
className="blocked__item"
|
||||||
>
|
>
|
||||||
<Channel
|
<Channel
|
||||||
|
|
|
@ -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's here</h2>
|
<h2 className="users__chatsTitle">Who'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) {
|
||||||
userActions.push({
|
if (hasFriendship) {
|
||||||
icon: '➕',
|
if ('active' === friendship[index].status) {
|
||||||
label: 'Add friend',
|
userActions.push({
|
||||||
onClick: () => {},
|
icon: '🚫',
|
||||||
});
|
label: 'Unfriend',
|
||||||
userActions.push({
|
onClick: () => dispatch(submitRemoveFriend(id)),
|
||||||
icon: '☢️',
|
});
|
||||||
label: 'Block',
|
if (-1 === favoriteUsers.indexOf(username)) {
|
||||||
onClick: () => {},
|
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: () => dispatch(submitAddFriend({nameOrStatus: username})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (hasBlocked) {
|
||||||
|
userActions.push({
|
||||||
|
icon: '😶',
|
||||||
|
label: 'Unblock',
|
||||||
|
onClick: () => dispatch(submitUnblock(id)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
userActions.push({
|
||||||
|
icon: '☢️',
|
||||||
|
label: 'Block',
|
||||||
|
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>
|
||||||
|
|
|
@ -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}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
src/client/store/effects.js
vendored
5
src/client/store/effects.js
vendored
|
@ -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',
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -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}) => {
|
||||||
friendship.push(payload);
|
const index = friendshipIndex(friendship, payload);
|
||||||
|
if (-1 === index) {
|
||||||
|
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);
|
||||||
|
|
|
@ -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,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user