fix: distinction

This commit is contained in:
cha0s 2020-12-13 15:04:34 -06:00
parent 2ef9155ca3
commit 8b0f1b109a
9 changed files with 121 additions and 5 deletions

View File

@ -2,10 +2,11 @@ import './index.scss';
import {useSocket} from '@latus/socket/client';
import {channelIsAnonymous, renderChannel} from '@reddichat/core/client';
import {ADMIN, MOD, submitMessageDistinction} from '@reddichat/chat/client';
import {idSelector, isAdminSelector, isModOfSelector} from '@reddichat/user/client';
import PropTypes from 'prop-types';
import React from 'react';
import {useSelector} from 'react-redux';
import {useDispatch, useSelector} from 'react-redux';
import Actions from 'components/actions';
@ -17,6 +18,7 @@ export default function Moderation(props) {
uuid,
} = props;
const channel = useChannel();
const dispatch = useDispatch();
const id = useSelector(idSelector);
const isAnonymous = channelIsAnonymous(channel);
const isAdmin = useSelector(isAdminSelector);
@ -24,13 +26,13 @@ export default function Moderation(props) {
const socket = useSocket();
const actions = [];
if (isAdmin && (id === owner || 0 === owner)) {
actions.push(['👑', 'Admin distinction', () => {
socket.send(['MessageAdminDistinction', uuid]);
actions.push(['👑', 'Toggle admin distinction', () => {
dispatch(submitMessageDistinction({uuid, distinction: ADMIN}));
}]);
}
if (isMod && id === owner) {
actions.push(['🎩', 'Moderator distinction', () => {
socket.send(['MessageModeratorDistinction', uuid]);
actions.push(['🎩', 'Toggle moderator distinction', () => {
dispatch(submitMessageDistinction({uuid, distinction: MOD}));
}]);
}
if (isAdmin) {

View File

@ -19,6 +19,8 @@ import {
submitJoin,
submitLeave,
submitMessage,
submitMessageDistinction,
toggleMessageDistinction,
} from './state';
export default ({config: {'%socket': socket}}) => {
@ -101,5 +103,9 @@ export default ({config: {'%socket': socket}}) => {
reject(Math.round(Math.max(0, error.msBeforeNext) / 1000) || 1);
}
},
[submitMessageDistinction]: async ({dispatch}, {payload}) => {
dispatch(toggleMessageDistinction(payload));
await socket.send(['MessageDistinction', payload]);
},
});
};

View File

@ -3,6 +3,7 @@ import Join from '../packets/join';
import Leave from '../packets/leave';
import Message from '../packets/message';
import MessageSiteBan from '../packets/message-site-ban';
import MessageDistinction from '../packets/message-distinction';
import chat from './state';
@ -17,6 +18,7 @@ export default {
Leave: Leave(latus),
Message: Message(latus),
MessageSiteBan: MessageSiteBan(latus),
MessageDistinction: MessageDistinction(latus),
}),
// eslint-disable-next-line global-require
'@reddichat/state/effects': (latus) => require('./effects').default(latus),

View File

@ -120,6 +120,15 @@ const slice = createSlice({
submitJoin: () => {},
submitLeave: () => {},
submitMessage: () => {},
submitMessageDistinction: () => {},
toggleMessageDistinction: ({messages}, {payload: {distinction, uuid}}) => {
const message = messages[uuid];
/* eslint-disable no-bitwise */
message.distinction = message.distinction & distinction
? message.distinction & ~distinction
: message.distinction | distinction;
/* eslint-enable no-bitwise */
},
},
/* eslint-enable no-param-reassign */
});
@ -138,6 +147,8 @@ export const {
submitJoin,
submitLeave,
submitMessage,
submitMessageDistinction,
toggleMessageDistinction,
} = slice.actions;
slice.reducer.subscription = slice.reducer;

View File

@ -13,6 +13,7 @@ import Activity from './packets/activity.server';
import Join from './packets/join.server';
import Leave from './packets/leave.server';
import Message from './packets/message.server';
import MessageDistinction from './packets/message-distinction.server';
import MessageSiteBan from './packets/message-site-ban.server';
import ensureCanonical from './ensure-canonical';
@ -66,6 +67,7 @@ export default {
Join: Join(latus),
Leave: Leave(latus),
Message: Message(latus),
MessageDistinction: MessageDistinction(latus),
MessageSiteBan: MessageSiteBan(latus),
}),
'@latus/socket/connect': async (socket, latus) => {

View File

@ -0,0 +1,9 @@
import {createClient, keys} from '@latus/redis';
import {parseChannel} from '@reddichat/core';
const messageChannel = async (latus, uuid) => {
const key = (await keys(createClient(latus), `*:messages:${uuid}`)).pop();
return parseChannel(key.split(':')[0]);
};
export default messageChannel;

View File

@ -0,0 +1,22 @@
import {Packet, ValidationError} from '@latus/socket/packets';
import {validate} from 'uuid';
export default () => class MessageDistinction extends Packet {
static get data() {
return {
distinction: 'uint8',
uuid: 'string',
};
}
static async validate({data: {uuid}}, {req: {user}}) {
if (!validate(uuid)) {
throw new ValidationError({code: 400, reason: 'malformed'});
}
if (!user) {
throw new ValidationError({code: 403, reason: 'unauthorized'});
}
}
};

View File

@ -0,0 +1,46 @@
import {ValidationError} from '@latus/socket/packets';
import {renderChannel} from '@reddichat/core';
import {ADMIN, MOD} from '../distinction';
import messageChannel from '../message-channel';
import replaceMessage from '../replace-message';
import MessageDistinction from './message-distinction';
export default (latus) => class MessageDistinctionServer extends MessageDistinction() {
static async respond({data: {distinction, uuid}}, socket) {
const {req} = socket;
const message = await replaceMessage(
req,
uuid,
(msg) => ({
...msg,
/* eslint-disable no-bitwise */
distinction: msg.distinction & distinction
? msg.distinction & ~distinction
: msg.distinction | distinction,
/* eslint-enable no-bitwise */
}),
);
const channel = await messageChannel(latus, uuid);
socket
.to(renderChannel(channel))
.send(['MessageDistinction', {uuid, distinction: message.distinction}]);
}
static async validate(packet, socket) {
super.validate(packet, socket);
const {data: {distinction, uuid}} = packet;
const {req} = socket;
const {user} = req;
// eslint-disable-next-line no-bitwise
if ((distinction & ADMIN) && !user.isAdmin) {
throw new ValidationError({code: 400, reason: 'unauthorized'});
}
// eslint-disable-next-line no-bitwise
if ((distinction & MOD) && !user.isModOf(await messageChannel(latus, uuid))) {
throw new ValidationError({code: 400, reason: 'unauthorized'});
}
}
};

View File

@ -0,0 +1,16 @@
import {promisify} from 'util';
import {keys} from '@latus/redis';
const replaceMessage = async (req, uuid, fn) => {
const {pubClient} = req.adapter;
const get = promisify(pubClient.get.bind(pubClient));
const key = (await keys(pubClient, `*:messages:${uuid}`)).pop();
const message = fn(JSON.parse(await get(key)));
return new Promise((resolve, reject) => pubClient
.multi()
.set(key, JSON.stringify(message))
.exec((error) => (error ? reject(error) : resolve(message))));
};
export default replaceMessage;