From 5916ce5cbe1a9067738f10c5903a3ad3b503ec93 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 27 Apr 2021 12:06:13 +1000 Subject: [PATCH] add the ban/unban logic for opengroupv2 --- _locales/en/messages.json | 16 +++++++ ts/components/conversation/Message.tsx | 5 ++ .../conversation/ModeratorsAddDialog.tsx | 17 +++++-- ts/interactions/message.ts | 47 ++++++++++++++++++- ts/models/conversation.ts | 6 ++- ts/models/message.ts | 12 +++-- ts/models/messageType.ts | 3 ++ ts/opengroup/opengroupV2/OpenGroupAPIV2.ts | 11 +++-- .../opengroupV2/OpenGroupServerPoller.ts | 5 +- ts/session/snode_api/onions.ts | 6 +-- ts/session/utils/Toast.tsx | 8 ++++ 11 files changed, 115 insertions(+), 21 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 93139c59f..e90f773aa 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1683,6 +1683,22 @@ "description": "Shown in the settings page as the heading for the blocked user settings", "androidKey": "preferences_app_protection__blocked_contacts" }, + "unbanUser": { + "message": "Unban User", + "description": "Unban user from open group by public key." + }, + "unbanUserConfirm": { + "message": "Are you sure you want to unban user?", + "description": "Message shown when confirming user unban." + }, + "userUnbanned": { + "message": "User unbanned successfully", + "description": "Toast on succesful user unban." + }, + "userUnbanFailed": { + "message": "Unban failed!", + "description": "Toast on unsuccesful user unban." + }, "banUser": { "message": "Ban User", "description": "Ban user from open group by public key." diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 6ee0c105e..9d31b7e80 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -559,9 +559,11 @@ class MessageInner extends React.PureComponent { onRetrySend, onShowDetail, isPublic, + isOpenGroupV2, weAreAdmin, isAdmin, onBanUser, + onUnbanUser, } = this.props; const showRetry = status === 'error' && direction === 'outgoing'; @@ -625,6 +627,9 @@ class MessageInner extends React.PureComponent { ) : null} {weAreAdmin && isPublic ? {window.i18n('banUser')} : null} + {weAreAdmin && isOpenGroupV2 ? ( + {window.i18n('unbanUser')} + ) : null} {weAreAdmin && isPublic && !isAdmin ? ( {window.i18n('addAsModerator')} ) : null} diff --git a/ts/components/conversation/ModeratorsAddDialog.tsx b/ts/components/conversation/ModeratorsAddDialog.tsx index 97bfeacd3..8bfa11c68 100644 --- a/ts/components/conversation/ModeratorsAddDialog.tsx +++ b/ts/components/conversation/ModeratorsAddDialog.tsx @@ -36,7 +36,9 @@ export class AddModeratorsDialog extends React.Component { } public async componentDidMount() { - this.channelAPI = await this.props.convo.getPublicSendData(); + if (this.props.convo.isOpenGroupV1()) { + this.channelAPI = await this.props.convo.getPublicSendData(); + } this.setState({ firstLoading: false }); } @@ -56,9 +58,16 @@ export class AddModeratorsDialog extends React.Component { this.setState({ addingInProgress: true, }); - const res = await this.channelAPI.serverAPI.addModerator([pubkey.key]); - if (!res) { - window.log.warn('failed to add moderators:', res); + let isAdded: any; + if (this.props.convo.isOpenGroupV1()) { + isAdded = await this.channelAPI.serverAPI.addModerator([pubkey.key]); + } else { + // this is a v2 opengroup + // FIXME audric addModerators + throw new Error('TODO'); + } + if (!isAdded) { + window.log.warn('failed to add moderators:', isAdded); ToastUtils.pushUserNeedsToHaveJoined(); } else { diff --git a/ts/interactions/message.ts b/ts/interactions/message.ts index b11a78691..350cfbcaa 100644 --- a/ts/interactions/message.ts +++ b/ts/interactions/message.ts @@ -29,7 +29,7 @@ export function banUser(userToBan: string, conversation?: ConversationModel) { if (!roomInfos) { window.log.warn('banUser room not found'); } else { - await ApiV2.banUser(pubKeyToBan, _.pick(roomInfos, 'serverUrl', 'roomId')); + success = await ApiV2.banUser(pubKeyToBan, _.pick(roomInfos, 'serverUrl', 'roomId')); } } else { const channelAPI = await conversation.getPublicSendData(); @@ -48,6 +48,51 @@ export function banUser(userToBan: string, conversation?: ConversationModel) { }); } +/** + * There is no way to unban on an opengroupv1 server. + * This function only works for opengroupv2 server + */ +export function unbanUser(userToUnBan: string, conversation?: ConversationModel) { + let pubKeyToUnban: PubKey; + try { + pubKeyToUnban = PubKey.cast(userToUnBan); + } catch (e) { + window.log.warn(e); + ToastUtils.pushUserBanFailure(); + return; + } + if (!isOpenGroupV2(conversation?.id || '')) { + window.log.warn('no way to unban on a opengroupv1'); + ToastUtils.pushUserBanFailure(); + return; + } + window.confirmationDialog({ + title: window.i18n('unbanUser'), + message: window.i18n('unbanUserConfirm'), + resolve: async () => { + if (!conversation) { + // double check here. the convo might have been removed since the dialog was opened + window.log.info('cannot unban user, the corresponding conversation was not found.'); + return; + } + let success = false; + if (isOpenGroupV2(conversation.id)) { + const roomInfos = await getV2OpenGroupRoom(conversation.id); + if (!roomInfos) { + window.log.warn('unbanUser room not found'); + } else { + success = await ApiV2.unbanUser(pubKeyToUnban, _.pick(roomInfos, 'serverUrl', 'roomId')); + } + } + if (success) { + ToastUtils.pushUserUnbanSuccess(); + } else { + ToastUtils.pushUserUnbanFailure(); + } + }, + }); +} + export function copyBodyToClipboard(body?: string) { window.clipboard.writeText(body); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 05aa92400..2b51b5493 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -375,7 +375,11 @@ export class ConversationModel extends Backbone.Model { } public getGroupAdmins() { - return this.get('groupAdmins') || this.get('moderators'); + const groupAdmins = this.get('groupAdmins'); + if (groupAdmins?.length) { + return groupAdmins; + } + return this.get('moderators'); } public getProps(): ReduxConversationType { const groupAdmins = this.getGroupAdmins(); diff --git a/ts/models/message.ts b/ts/models/message.ts index 27e57efcb..c6f69983d 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -3,13 +3,13 @@ import Backbone from 'backbone'; import filesize from 'filesize'; import _ from 'lodash'; import { SignalService } from '../../ts/protobuf'; -import { getMessageQueue, Types, Utils } from '../../ts/session'; +import { getMessageQueue, Utils } from '../../ts/session'; import { ConversationController } from '../../ts/session/conversations'; import { MessageController } from '../../ts/session/messages'; import { DataMessage, OpenGroupMessage } from '../../ts/session/messages/outgoing'; import { ClosedGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { PubKey } from '../../ts/session/types'; -import { ToastUtils, UserUtils } from '../../ts/session/utils'; +import { UserUtils } from '../../ts/session/utils'; import { fillMessageAttributesWithDefaults, MessageAttributes, @@ -24,8 +24,6 @@ import { actions as conversationActions } from '../state/ducks/conversations'; import { VisibleMessage } from '../session/messages/outgoing/visibleMessage/VisibleMessage'; import { buildSyncMessage } from '../session/utils/syncUtils'; import { isOpenGroupV2 } from '../opengroup/utils/OpenGroupUtils'; -import { banUser } from '../opengroup/opengroupV2/OpenGroupAPIV2'; -import { getV2OpenGroupRoom } from '../data/opengroups'; import { MessageInteraction } from '../interactions'; import { uploadAttachmentsV2, @@ -468,6 +466,7 @@ export class MessageModel extends Backbone.Model { const convoId = conversation ? conversation.id : undefined; const isGroup = !!conversation && !conversation.isPrivate(); const isPublic = !!this.get('isPublic'); + const isPublicOpenGroupV2 = isOpenGroupV2(this.getConversation()?.id || ''); const attachments = this.get('attachments') || []; @@ -493,11 +492,13 @@ export class MessageModel extends Backbone.Model { expirationLength, expirationTimestamp, isPublic, + isOpenGroupV2: isPublicOpenGroupV2, isKickedFromGroup: conversation && conversation.get('isKickedFromGroup'), onCopyText: this.copyText, onCopyPubKey: this.copyPubKey, onBanUser: this.banUser, + onUnbanUser: this.unbanUser, onRetrySend: this.retrySend, markRead: this.markRead, @@ -709,6 +710,9 @@ export class MessageModel extends Backbone.Model { public banUser() { MessageInteraction.banUser(this.get('source'), this.getConversation()); } + public unbanUser() { + MessageInteraction.unbanUser(this.get('source'), this.getConversation()); + } public copyText() { MessageInteraction.copyBodyToClipboard(this.get('body')); diff --git a/ts/models/messageType.ts b/ts/models/messageType.ts index c17589704..2c1a6d469 100644 --- a/ts/models/messageType.ts +++ b/ts/models/messageType.ts @@ -207,6 +207,7 @@ export interface MessageRegularProps { expirationTimestamp?: number; convoId: string; isPublic?: boolean; + isOpenGroupV2?: boolean; selected: boolean; isKickedFromGroup: boolean; // whether or not to show check boxes @@ -225,6 +226,8 @@ export interface MessageRegularProps { onDeleteMessage: (messageId: string) => void; onCopyPubKey?: () => void; onBanUser?: () => void; + onUnbanUser?: () => void; + onShowDetail: () => void; onShowUserDetails: (userPubKey: string) => void; markRead: (readAt: number) => Promise; diff --git a/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts b/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts index b5d89d456..79d699079 100644 --- a/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts +++ b/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts @@ -417,7 +417,7 @@ export const isUserModerator = ( export const banUser = async ( userToBan: PubKey, roomInfos: OpenGroupRequestCommonType -): Promise => { +): Promise => { const queryParams = { public_key: userToBan.key }; const request: OpenGroupV2Request = { method: 'POST', @@ -428,13 +428,15 @@ export const banUser = async ( endpoint: 'block_list', }; const banResult = await sendOpenGroupV2Request(request); + const isOk = parseStatusCodeFromOnionRequest(banResult) === 200; console.warn('banResult', banResult); + return isOk; }; export const unbanUser = async ( userToBan: PubKey, roomInfos: OpenGroupRequestCommonType -): Promise => { +): Promise => { const request: OpenGroupV2Request = { method: 'DELETE', room: roomInfos.roomId, @@ -442,7 +444,10 @@ export const unbanUser = async ( isAuthRequired: true, endpoint: `block_list/${userToBan.key}`, }; - await sendOpenGroupV2Request(request); + const unbanResult = await sendOpenGroupV2Request(request); + const isOk = parseStatusCodeFromOnionRequest(unbanResult) === 200; + console.warn('unbanResult', unbanResult); + return isOk; }; export const getAllRoomInfos = async (roomInfos: OpenGroupRequestCommonType) => { diff --git a/ts/opengroup/opengroupV2/OpenGroupServerPoller.ts b/ts/opengroup/opengroupV2/OpenGroupServerPoller.ts index b36374cfc..f41cada5b 100644 --- a/ts/opengroup/opengroupV2/OpenGroupServerPoller.ts +++ b/ts/opengroup/opengroupV2/OpenGroupServerPoller.ts @@ -10,7 +10,7 @@ import { getV2OpenGroupRoom, saveV2OpenGroupRoom } from '../../data/opengroups'; import { OpenGroupMessageV2 } from './OpenGroupMessageV2'; import { handleOpenGroupV2Message } from '../../receiver/receiver'; -const pollForEverythingInterval = 4 * 1000; +const pollForEverythingInterval = 6 * 1000; /** * An OpenGroupServerPollerV2 polls for everything for a particular server. We should @@ -256,8 +256,7 @@ const handleCompactPollResults = async ( } const existingModerators = convo.get('moderators') || []; let changeOnConvo = false; - // res.moderators is already sorted - if (!_.isEqual(existingModerators.sort(), res.moderators)) { + if (!_.isEqual(existingModerators.sort(), res.moderators.sort())) { convo.set({ moderators: res.moderators }); changeOnConvo = true; } diff --git a/ts/session/snode_api/onions.ts b/ts/session/snode_api/onions.ts index b41d270d4..aa7b8f9c7 100644 --- a/ts/session/snode_api/onions.ts +++ b/ts/session/snode_api/onions.ts @@ -341,11 +341,7 @@ const processOnionResponse = async ( return jsonRes; } catch (e) { log.error( - `(${reqIdx}) [path] lokiRpc::processOnionResponse - parse error outer json`, - e.code, - e.message, - 'json:', - plaintext + `(${reqIdx}) [path] lokiRpc::processOnionResponse - parse error outer json ${e.code} ${e.message} json: '${plaintext}'` ); return RequestError.OTHER; } diff --git a/ts/session/utils/Toast.tsx b/ts/session/utils/Toast.tsx index 15f616fb1..14cfcadd7 100644 --- a/ts/session/utils/Toast.tsx +++ b/ts/session/utils/Toast.tsx @@ -118,6 +118,14 @@ export function pushUserBanFailure() { pushToastError('userBanFailed', window.i18n('userBanFailed')); } +export function pushUserUnbanSuccess() { + pushToastSuccess('userUnbanned', window.i18n('userUnbanned')); +} + +export function pushUserUnbanFailure() { + pushToastError('userUnbanFailed', window.i18n('userUnbanFailed')); +} + export function pushMessageDeleteForbidden() { pushToastError('messageDeletionForbidden', window.i18n('messageDeletionForbidden')); }