feat: favorites
This commit is contained in:
parent
d29a98bef5
commit
c369846fa8
|
@ -3,7 +3,13 @@ import './chat--leftRooms.scss';
|
|||
import React from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {favoriteChannelsSelector, recentSelector, removeRecent} from '~/common/state/user';
|
||||
import {
|
||||
favoriteChannelsSelector,
|
||||
recentSelector,
|
||||
removeRecent,
|
||||
submitAddFavorite,
|
||||
submitRemoveFavorite,
|
||||
} from '~/common/state/user';
|
||||
|
||||
import Channel from './channel';
|
||||
|
||||
|
@ -12,13 +18,6 @@ export default function ChatLeftRooms() {
|
|||
const favorites = useSelector(favoriteChannelsSelector);
|
||||
const recent = useSelector(recentSelector)
|
||||
.filter((channel) => -1 === favorites.indexOf(channel));
|
||||
const favoritesActions = [
|
||||
{
|
||||
icon: '💔',
|
||||
label: 'Unfavorite',
|
||||
onClick: () => {},
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div
|
||||
className="chat--leftRooms"
|
||||
|
@ -35,7 +34,15 @@ export default function ChatLeftRooms() {
|
|||
<>
|
||||
<h2 className="channels__chatsTitle">Favorites</h2>
|
||||
<ul className="channels__chatsList">
|
||||
{favorites.map((channel) => (
|
||||
{favorites.map((channel) => {
|
||||
const favoritesActions = [
|
||||
{
|
||||
icon: '💔',
|
||||
label: 'Unfavorite',
|
||||
onClick: () => dispatch(submitRemoveFavorite(`/r/${channel}`)),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<li
|
||||
className="channels__item"
|
||||
>
|
||||
|
@ -46,7 +53,8 @@ export default function ChatLeftRooms() {
|
|||
prefix="/r/"
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
@ -59,7 +67,7 @@ export default function ChatLeftRooms() {
|
|||
{
|
||||
icon: '❤️',
|
||||
label: 'Favorite',
|
||||
onClick: () => {},
|
||||
onClick: () => dispatch(submitAddFavorite(`/r/${channel}`)),
|
||||
},
|
||||
{
|
||||
icon: '×',
|
||||
|
|
16
src/client/store/effects.js
vendored
16
src/client/store/effects.js
vendored
|
@ -12,6 +12,14 @@ import {
|
|||
submitMessage,
|
||||
} from '~/common/state/chat';
|
||||
|
||||
import AddFavorite from '../../common/packets/add-favorite.packet';
|
||||
import RemoveFavorite from '../../common/packets/remove-favorite.packet';
|
||||
import {
|
||||
addToFavorites,
|
||||
removeFromFavorites,
|
||||
submitAddFavorite,
|
||||
submitRemoveFavorite,
|
||||
} from '../../common/state/user';
|
||||
import {
|
||||
fetchUsernames,
|
||||
} from '~/common/state/usernames';
|
||||
|
@ -44,6 +52,14 @@ const effects = {
|
|||
dispatch(confirmMessage({current, previous: payload.uuid, timestamp}));
|
||||
});
|
||||
},
|
||||
[submitAddFavorite]: ({dispatch}, {payload}) => {
|
||||
dispatch(addToFavorites(payload));
|
||||
socket.send(new AddFavorite(payload));
|
||||
},
|
||||
[submitRemoveFavorite]: ({dispatch}, {payload}) => {
|
||||
dispatch(removeFromFavorites(payload));
|
||||
socket.send(new RemoveFavorite(payload));
|
||||
},
|
||||
};
|
||||
|
||||
export const middleware = (store) => (next) => (action) => {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {Packet} from '@avocado/net';
|
||||
|
||||
export default class Blur extends Packet {
|
||||
export default class AddFavorite extends Packet {
|
||||
|
||||
static get schema() {
|
||||
return {
|
||||
...super.schema,
|
||||
data: {},
|
||||
data: 'string',
|
||||
};
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import {Packet} from '@avocado/net';
|
||||
|
||||
export default class Focus extends Packet {
|
||||
export default class RemoveFavorite extends Packet {
|
||||
|
||||
static get schema() {
|
||||
return {
|
||||
...super.schema,
|
||||
data: {},
|
||||
data: 'string',
|
||||
};
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ const slice = createSlice({
|
|||
addFriendship: (state, {payload: {friendship}}) => {
|
||||
state.friendship.push(friendship);
|
||||
},
|
||||
addToFavorites: (state, {payload: {favorite}}) => {
|
||||
addToFavorites: (state, {payload: favorite}) => {
|
||||
state.favorites.push(favorite);
|
||||
},
|
||||
blockUser: (state, {payload: {blocked}}) => {
|
||||
|
@ -101,14 +101,14 @@ const slice = createSlice({
|
|||
removeFriendship: (state, {payload: {id}}) => {
|
||||
state.friendship.splice(state.friendship.findIndex((friendship) => friendship.id === id), 1);
|
||||
},
|
||||
removeFromFavorites: (state, {payload: {type, name}}) => {
|
||||
state.favorites.splice(state.favorites.findIndex(
|
||||
(favorite) => favorite.name === name && favorite.type === type,
|
||||
), 1);
|
||||
removeFromFavorites: (state, {payload: favorite}) => {
|
||||
state.favorites.splice(state.favorites.indexOf(favorite), 1);
|
||||
},
|
||||
removeRecent: ({recent}, {payload: channel}) => {
|
||||
recent.splice(recent.indexOf(channel), 1);
|
||||
},
|
||||
submitAddFavorite: () => {},
|
||||
submitRemoveFavorite: () => {},
|
||||
updateFriendshipStatus: (state, {payload: {id, status}}) => {
|
||||
state.friendship.find((friendship) => friendship.id === id).status = status;
|
||||
},
|
||||
|
@ -135,10 +135,14 @@ const slice = createSlice({
|
|||
|
||||
export const {
|
||||
addFriend,
|
||||
addToFavorites,
|
||||
blockUser,
|
||||
focus,
|
||||
removeFriend,
|
||||
removeFromFavorites,
|
||||
removeRecent,
|
||||
submitAddFavorite,
|
||||
submitRemoveFavorite,
|
||||
} = slice.actions;
|
||||
|
||||
export default slice.reducer;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {joinChannel} from '~/common/channel';
|
||||
import {joinChannel, parseChannel} from '~/common/channel';
|
||||
|
||||
import {allModels} from './models/registrar';
|
||||
import createRedisClient, {keys} from './redis';
|
||||
|
@ -49,16 +49,22 @@ export const channelState = async (req, channel) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const channelsToHydrate = async (req) => (
|
||||
(req.channel ? [req.channel] : []).concat(req.user ? await req.user.favorites() : [])
|
||||
export const channelsToHydrate = async (req) => {
|
||||
const channels = await Promise.all(
|
||||
(req.channel ? [req.channel] : [])
|
||||
.concat(req.user ? await req.user.favorites() : []),
|
||||
);
|
||||
const channelStrings = channels.map(joinChannel);
|
||||
const uniqueChannels = (new Set(channelStrings)).values();
|
||||
return Array.from(uniqueChannels).map((channel) => parseChannel(`/chat${channel}`));
|
||||
};
|
||||
|
||||
const userState = async (req) => {
|
||||
const {channel, user} = req;
|
||||
const toHydrate = await channelsToHydrate(req);
|
||||
return user
|
||||
? {
|
||||
favorites: [],
|
||||
favorites: (await user.favorites()).map(joinChannel),
|
||||
focus: channel ? joinChannel(channel) : '',
|
||||
friends: await user.friends(),
|
||||
id: user.id,
|
||||
|
|
23
src/server/models/favorite.model.js
Normal file
23
src/server/models/favorite.model.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {DataTypes as Types} from 'sequelize';
|
||||
import BaseModel from './base';
|
||||
|
||||
class Favorite extends BaseModel {
|
||||
|
||||
static get attributes() {
|
||||
return {
|
||||
channel: Types.STRING,
|
||||
};
|
||||
}
|
||||
|
||||
static associate({User}) {
|
||||
User.hasMany(this);
|
||||
this.belongsTo(User);
|
||||
}
|
||||
|
||||
static get name() {
|
||||
return 'Favorite';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Favorite;
|
|
@ -5,6 +5,8 @@ import bcrypt from 'bcrypt';
|
|||
import {registerHooks} from 'scwp';
|
||||
import {DataTypes as Types, Op} from 'sequelize';
|
||||
|
||||
import {parseChannel} from '~/common/channel';
|
||||
|
||||
import BaseModel from './base';
|
||||
import {allModels} from './registrar';
|
||||
|
||||
|
@ -35,7 +37,9 @@ class User extends BaseModel {
|
|||
}
|
||||
|
||||
async favorites() {
|
||||
return [];
|
||||
return (await this.getFavorites())
|
||||
.map(({channel}) => channel)
|
||||
.map((channel) => parseChannel(`/chat${channel}`));
|
||||
}
|
||||
|
||||
async friends() {
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {ServerSocket, SocketServer} from '@avocado/net/server/socket';
|
||||
import socketSession from 'express-socket.io-session';
|
||||
import redisAdapter from 'socket.io-redis';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
import {ServerSocket, SocketServer} from '@avocado/net/server/socket';
|
||||
import socketSession from 'express-socket.io-session';
|
||||
|
||||
import {joinChannel, parseChannel} from '~/common/channel';
|
||||
import AddFavorite from '~/common/packets/add-favorite.packet';
|
||||
import Join from '~/common/packets/join.packet';
|
||||
import Leave from '~/common/packets/leave.packet';
|
||||
import Message from '~/common/packets/message.packet';
|
||||
import RemoveFavorite from '~/common/packets/remove-favorite.packet';
|
||||
import Usernames from '~/common/packets/usernames.packet';
|
||||
|
||||
import {
|
||||
|
@ -30,7 +31,7 @@ const subClient = createRedisClient();
|
|||
const adapter = redisAdapter({pubClient, subClient});
|
||||
|
||||
export function createSocketServer(httpServer) {
|
||||
const {User} = allModels();
|
||||
const {Favorite, User} = allModels();
|
||||
const socketServer = new SocketServer(httpServer, {
|
||||
adapter,
|
||||
});
|
||||
|
@ -76,6 +77,18 @@ export function createSocketServer(httpServer) {
|
|||
socketServer.on('connect', (socket) => {
|
||||
const {req} = socket;
|
||||
socket.on('packet', async (packet, fn) => {
|
||||
if (packet instanceof AddFavorite) {
|
||||
await req.user.createFavorite({channel: packet.data});
|
||||
}
|
||||
if (packet instanceof RemoveFavorite) {
|
||||
const favorites = await req.user.getFavorites();
|
||||
const toRemove = favorites.find(({channel}) => channel === packet.data);
|
||||
await Favorite.destroy({
|
||||
where: {
|
||||
id: toRemove.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (packet instanceof Join) {
|
||||
const {channel} = packet.data;
|
||||
await userJoin(channel, socket.socket);
|
||||
|
|
Loading…
Reference in New Issue
Block a user