From 410bc52fd034cff4ea0b773bfaf33f087b053cef Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Mon, 13 Jun 2022 14:42:19 -0400 Subject: [PATCH] Moves group link management actions to redux --- ts/components/ConfirmationDialog.tsx | 10 +-- .../GroupLinkManagement.stories.tsx | 1 - .../GroupLinkManagement.tsx | 54 ++++++++++---- ts/state/ducks/conversations.ts | 71 +++++++++++++++++++ ts/state/smart/GroupLinkManagement.tsx | 11 ++- ts/views/conversation_view.tsx | 42 ----------- 6 files changed, 122 insertions(+), 67 deletions(-) diff --git a/ts/components/ConfirmationDialog.tsx b/ts/components/ConfirmationDialog.tsx index efff8999e..3f7e2a2fa 100644 --- a/ts/components/ConfirmationDialog.tsx +++ b/ts/components/ConfirmationDialog.tsx @@ -18,18 +18,18 @@ export type ActionSpec = { }; export type OwnProps = Readonly<{ - moduleClassName?: string; actions?: Array; + cancelButtonVariant?: ButtonVariant; cancelText?: string; children?: React.ReactNode; + hasXButton?: boolean; i18n: LocalizerType; + moduleClassName?: string; onCancel?: () => unknown; onClose: () => unknown; - title?: string | React.ReactNode; - theme?: Theme; - hasXButton?: boolean; - cancelButtonVariant?: ButtonVariant; onTopOfEverything?: boolean; + theme?: Theme; + title?: string | React.ReactNode; }>; export type Props = OwnProps; diff --git a/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx b/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx index 57ebf398f..e6e9a22f8 100644 --- a/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx +++ b/ts/components/conversation/conversation-details/GroupLinkManagement.stories.tsx @@ -47,7 +47,6 @@ const createProps = ( ): PropsType => ({ changeHasGroupLink: action('changeHasGroupLink'), conversation: conversation || getConversation(), - copyGroupLink: action('copyGroupLink'), generateNewGroupLink: action('generateNewGroupLink'), i18n, isAdmin, diff --git a/ts/components/conversation/conversation-details/GroupLinkManagement.tsx b/ts/components/conversation/conversation-details/GroupLinkManagement.tsx index fa0fceede..344267228 100644 --- a/ts/components/conversation/conversation-details/GroupLinkManagement.tsx +++ b/ts/components/conversation/conversation-details/GroupLinkManagement.tsx @@ -1,35 +1,42 @@ // Copyright 2020-2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React from 'react'; +import React, { useState } from 'react'; -import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; -import { SignalService as Proto } from '../../../protobuf'; import type { ConversationType } from '../../../state/ducks/conversations'; import type { LocalizerType } from '../../../types/Util'; + +import { ConfirmationDialog } from '../../ConfirmationDialog'; +import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; import { PanelRow } from './PanelRow'; import { PanelSection } from './PanelSection'; import { Select } from '../../Select'; +import { SignalService as Proto } from '../../../protobuf'; +import { copyGroupLink } from '../../../util/copyGroupLink'; import { useDelayedRestoreFocus } from '../../../hooks/useRestoreFocus'; import { useUniqueId } from '../../../hooks/useUniqueId'; const AccessControlEnum = Proto.AccessControl.AccessRequired; -export type PropsType = { - changeHasGroupLink: (value: boolean) => void; +export type PropsDataType = { conversation?: ConversationType; - copyGroupLink: (groupLink: string) => void; - generateNewGroupLink: () => void; i18n: LocalizerType; isAdmin: boolean; - setAccessControlAddFromInviteLinkSetting: (value: boolean) => void; +}; + +export type PropsType = PropsDataType & { + changeHasGroupLink: (conversationId: string, value: boolean) => unknown; + generateNewGroupLink: (conversationId: string) => unknown; + setAccessControlAddFromInviteLinkSetting: ( + conversationId: string, + value: boolean + ) => unknown; }; export const GroupLinkManagement: React.ComponentType = ({ changeHasGroupLink, conversation, - copyGroupLink, generateNewGroupLink, i18n, isAdmin, @@ -44,9 +51,11 @@ export const GroupLinkManagement: React.ComponentType = ({ const [focusRef] = useDelayedRestoreFocus(); - const createEventHandler = (handleEvent: (x: boolean) => void) => { + const createEventHandler = ( + handleEvent: (id: string, x: boolean) => unknown + ) => { return (value: string) => { - handleEvent(value === 'true'); + handleEvent(conversation.id, value === 'true'); }; }; @@ -60,8 +69,29 @@ export const GroupLinkManagement: React.ComponentType = ({ AccessControlEnum.UNSATISFIABLE; const groupLinkInfo = hasGroupLink ? conversation.groupLink : ''; + const [hasGenerateNewLinkDialog, setHasGenerateNewLinkDialog] = + useState(false); + return ( <> + {hasGenerateNewLinkDialog && ( + { + generateNewGroupLink(conversation.id); + }, + style: 'negative', + text: i18n('GroupLinkManagement--reset'), + }, + ]} + i18n={i18n} + onClose={() => { + setHasGenerateNewLinkDialog(false); + }} + title={i18n('GroupLinkManagement--confirm-reset')} + /> + )} = ({ /> } label={i18n('GroupLinkManagement--reset')} - onClick={generateNewGroupLink} + onClick={() => setHasGenerateNewLinkDialog(true)} /> ) : null} diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index be4d8043a..6bb554c52 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -801,6 +801,7 @@ export type ConversationActionType = export const actions = { cancelConversationVerification, + changeHasGroupLink, clearCancelledConversationVerification, clearGroupCreationError, clearInvitedUuidsForNewlyCreatedGroup, @@ -823,6 +824,7 @@ export const actions = { deleteAvatarFromDisk, discardMessages, doubleCheckMissingQuoteReference, + generateNewGroupLink, messageChanged, messageDeleted, messageExpanded, @@ -844,6 +846,7 @@ export const actions = { saveUsername, scrollToMessage, selectMessage, + setAccessControlAddFromInviteLinkSetting, setComposeGroupAvatar, setComposeGroupExpireTimer, setComposeGroupName, @@ -947,6 +950,74 @@ function deleteAvatarFromDisk( }; } +function changeHasGroupLink( + conversationId: string, + value: boolean +): ThunkAction { + return async dispatch => { + const conversation = window.ConversationController.get(conversationId); + if (!conversation) { + throw new Error('No conversation found'); + } + + await longRunningTaskWrapper({ + name: 'toggleGroupLink', + idForLogging: conversation.idForLogging(), + task: async () => conversation.toggleGroupLink(value), + }); + dispatch({ + type: 'NOOP', + payload: null, + }); + }; +} + +function generateNewGroupLink( + conversationId: string +): ThunkAction { + return async dispatch => { + const conversation = window.ConversationController.get(conversationId); + if (!conversation) { + throw new Error('No conversation found'); + } + + await longRunningTaskWrapper({ + name: 'refreshGroupLink', + idForLogging: conversation.idForLogging(), + task: async () => conversation.refreshGroupLink(), + }); + + dispatch({ + type: 'NOOP', + payload: null, + }); + }; +} + +function setAccessControlAddFromInviteLinkSetting( + conversationId: string, + value: boolean +): ThunkAction { + return async dispatch => { + const conversation = window.ConversationController.get(conversationId); + if (!conversation) { + throw new Error('No conversation found'); + } + + await longRunningTaskWrapper({ + idForLogging: conversation.idForLogging(), + name: 'updateAccessControlAddFromInviteLink', + task: async () => + conversation.updateAccessControlAddFromInviteLink(value), + }); + + dispatch({ + type: 'NOOP', + payload: null, + }); + }; +} + function discardMessages( payload: Readonly ): DiscardMessagesActionType { diff --git a/ts/state/smart/GroupLinkManagement.tsx b/ts/state/smart/GroupLinkManagement.tsx index 290ac3360..c8ce5a15a 100644 --- a/ts/state/smart/GroupLinkManagement.tsx +++ b/ts/state/smart/GroupLinkManagement.tsx @@ -3,24 +3,21 @@ import { connect } from 'react-redux'; +import type { PropsDataType } from '../../components/conversation/conversation-details/GroupLinkManagement'; import type { StateType } from '../reducer'; -import type { PropsType } from '../../components/conversation/conversation-details/GroupLinkManagement'; import { GroupLinkManagement } from '../../components/conversation/conversation-details/GroupLinkManagement'; import { getConversationSelector } from '../selectors/conversations'; import { getIntl } from '../selectors/user'; +import { mapDispatchToProps } from '../actions'; export type SmartGroupLinkManagementProps = { - changeHasGroupLink: (value: boolean) => void; conversationId: string; - copyGroupLink: (groupLink: string) => void; - generateNewGroupLink: () => void; - setAccessControlAddFromInviteLinkSetting: (value: boolean) => void; }; const mapStateToProps = ( state: StateType, props: SmartGroupLinkManagementProps -): PropsType => { +): PropsDataType => { const conversation = getConversationSelector(state)(props.conversationId); const isAdmin = Boolean(conversation?.areWeAdmin); @@ -32,6 +29,6 @@ const mapStateToProps = ( }; }; -const smart = connect(mapStateToProps); +const smart = connect(mapStateToProps, mapDispatchToProps); export const SmartGroupLinkManagement = smart(GroupLinkManagement); diff --git a/ts/views/conversation_view.tsx b/ts/views/conversation_view.tsx index d63bc4119..38897c3e2 100644 --- a/ts/views/conversation_view.tsx +++ b/ts/views/conversation_view.tsx @@ -101,7 +101,6 @@ import { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpir import { ToastUnableToLoadAttachment } from '../components/ToastUnableToLoadAttachment'; import { ToastCannotOpenGiftBadge } from '../components/ToastCannotOpenGiftBadge'; import { autoScale } from '../util/handleImageAttachment'; -import { copyGroupLink } from '../util/copyGroupLink'; import { deleteDraftAttachment } from '../util/deleteDraftAttachment'; import { markAllAsApproved } from '../util/markAllAsApproved'; import { markAllAsVerifiedDefault } from '../util/markAllAsVerifiedDefault'; @@ -2207,12 +2206,7 @@ export class ConversationView extends window.Backbone.View { JSX: window.Signal.State.Roots.createGroupLinkManagement( window.reduxStore, { - changeHasGroupLink: this.changeHasGroupLink.bind(this), conversationId: this.model.id, - copyGroupLink, - generateNewGroupLink: this.generateNewGroupLink.bind(this), - setAccessControlAddFromInviteLinkSetting: - this.setAccessControlAddFromInviteLinkSetting.bind(this), } ), }); @@ -2606,42 +2600,6 @@ export class ConversationView extends window.Backbone.View { }); } - async changeHasGroupLink(value: boolean): Promise { - const { model }: { model: ConversationModel } = this; - - await this.longRunningTaskWrapper({ - name: 'toggleGroupLink', - task: async () => model.toggleGroupLink(value), - }); - } - - async generateNewGroupLink(): Promise { - const { model }: { model: ConversationModel } = this; - - window.showConfirmationDialog({ - confirmStyle: 'negative', - message: window.i18n('GroupLinkManagement--confirm-reset'), - okText: window.i18n('GroupLinkManagement--reset'), - resolve: async () => { - await this.longRunningTaskWrapper({ - name: 'refreshGroupLink', - task: async () => model.refreshGroupLink(), - }); - }, - }); - } - - async setAccessControlAddFromInviteLinkSetting( - value: boolean - ): Promise { - const { model }: { model: ConversationModel } = this; - - await this.longRunningTaskWrapper({ - name: 'updateAccessControlAddFromInviteLink', - task: async () => model.updateAccessControlAddFromInviteLink(value), - }); - } - async setAccessControlAttributesSetting(value: number): Promise { const { model }: { model: ConversationModel } = this;