Moves startGV2Migration to redux

This commit is contained in:
Josh Perez 2022-12-08 01:41:37 -05:00 committed by GitHub
parent 452e0b7b31
commit 7ea38bb1a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 171 additions and 125 deletions

View file

@ -105,7 +105,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
), ),
title: '', title: '',
// GroupV1 Disabled Actions // GroupV1 Disabled Actions
onStartGroupMigration: action('onStartGroupMigration'), showGV2MigrationDialog: action('showGV2MigrationDialog'),
// GroupV2 // GroupV2
announcementsOnly: boolean( announcementsOnly: boolean(
'announcementsOnly', 'announcementsOnly',

View file

@ -162,7 +162,7 @@ export type Props = Pick<
| 'clearShowPickerHint' | 'clearShowPickerHint'
> & > &
MessageRequestActionsProps & MessageRequestActionsProps &
Pick<GroupV1DisabledActionsPropsType, 'onStartGroupMigration'> & Pick<GroupV1DisabledActionsPropsType, 'showGV2MigrationDialog'> &
Pick<GroupV2PendingApprovalActionsPropsType, 'onCancelJoinRequest'> & Pick<GroupV2PendingApprovalActionsPropsType, 'onCancelJoinRequest'> &
OwnProps; OwnProps;
@ -244,7 +244,7 @@ export function CompositionArea({
title, title,
// GroupV1 Disabled Actions // GroupV1 Disabled Actions
isGroupV1AndDisabled, isGroupV1AndDisabled,
onStartGroupMigration, showGV2MigrationDialog,
// GroupV2 // GroupV2
announcementsOnly, announcementsOnly,
areWeAdmin, areWeAdmin,
@ -561,8 +561,9 @@ export function CompositionArea({
if (!left && isGroupV1AndDisabled) { if (!left && isGroupV1AndDisabled) {
return ( return (
<GroupV1DisabledActions <GroupV1DisabledActions
conversationId={conversationId}
i18n={i18n} i18n={i18n}
onStartGroupMigration={onStartGroupMigration} showGV2MigrationDialog={showGV2MigrationDialog}
/> />
); );
} }

View file

@ -35,6 +35,7 @@ const contact3: ConversationType = getDefaultConversation({
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
areWeInvited: Boolean(overrideProps.areWeInvited), areWeInvited: Boolean(overrideProps.areWeInvited),
conversationId: '123',
droppedMembers: overrideProps.droppedMembers || [contact3, contact1], droppedMembers: overrideProps.droppedMembers || [contact3, contact1],
getPreferredBadge: () => undefined, getPreferredBadge: () => undefined,
hasMigrated: Boolean(overrideProps.hasMigrated), hasMigrated: Boolean(overrideProps.hasMigrated),

View file

@ -8,39 +8,62 @@ import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { GroupDialog } from './GroupDialog'; import { GroupDialog } from './GroupDialog';
import { sortByTitle } from '../util/sortByTitle'; import { sortByTitle } from '../util/sortByTitle';
type CallbackType = () => unknown;
export type DataPropsType = { export type DataPropsType = {
conversationId: string;
readonly areWeInvited: boolean; readonly areWeInvited: boolean;
readonly droppedMembers: Array<ConversationType>; readonly droppedMembers: Array<ConversationType>;
readonly hasMigrated: boolean; readonly hasMigrated: boolean;
readonly invitedMembers: Array<ConversationType>; readonly invitedMembers: Array<ConversationType>;
readonly migrate: CallbackType;
readonly onClose: CallbackType;
};
export type HousekeepingPropsType = {
readonly getPreferredBadge: PreferredBadgeSelectorType; readonly getPreferredBadge: PreferredBadgeSelectorType;
readonly i18n: LocalizerType; readonly i18n: LocalizerType;
readonly theme: ThemeType; readonly theme: ThemeType;
}; };
export type PropsType = DataPropsType & HousekeepingPropsType; type ActionsPropsType =
| {
initiateMigrationToGroupV2: (conversationId: string) => unknown;
closeGV2MigrationDialog: () => unknown;
}
| {
readonly migrate: () => unknown;
readonly onClose: () => unknown;
};
export type PropsType = DataPropsType & ActionsPropsType;
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
React.memo(function GroupV1MigrationDialogInner(props: PropsType) { React.memo(function GroupV1MigrationDialogInner(props: PropsType) {
const { const {
areWeInvited, areWeInvited,
conversationId,
droppedMembers, droppedMembers,
getPreferredBadge, getPreferredBadge,
hasMigrated, hasMigrated,
i18n, i18n,
invitedMembers, invitedMembers,
migrate,
onClose,
theme, theme,
} = props; } = props;
let migrateHandler;
if ('migrate' in props) {
migrateHandler = props.migrate;
} else if ('initiateMigrationToGroupV2' in props) {
migrateHandler = () => props.initiateMigrationToGroupV2(conversationId);
} else {
throw new Error(
'GroupV1MigrationDialog: No conversationId or migration function'
);
}
let closeHandler;
if ('onClose' in props) {
closeHandler = props.onClose;
} else if ('closeGV2MigrationDialog' in props) {
closeHandler = props.closeGV2MigrationDialog;
} else {
throw new Error('GroupV1MigrationDialog: No close function provided');
}
const title = hasMigrated const title = hasMigrated
? i18n('GroupV1--Migration--info--title') ? i18n('GroupV1--Migration--info--title')
: i18n('GroupV1--Migration--migrate--title'); : i18n('GroupV1--Migration--migrate--title');
@ -60,13 +83,13 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
}; };
if (hasMigrated) { if (hasMigrated) {
primaryButtonText = i18n('Confirmation--confirm'); primaryButtonText = i18n('Confirmation--confirm');
onClickPrimaryButton = onClose; onClickPrimaryButton = closeHandler;
} else { } else {
primaryButtonText = i18n('GroupV1--Migration--migrate'); primaryButtonText = i18n('GroupV1--Migration--migrate');
onClickPrimaryButton = migrate; onClickPrimaryButton = migrateHandler;
secondaryButtonProps = { secondaryButtonProps = {
secondaryButtonText: i18n('cancel'), secondaryButtonText: i18n('cancel'),
onClickSecondaryButton: onClose, onClickSecondaryButton: closeHandler,
}; };
} }
@ -74,7 +97,7 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
<GroupDialog <GroupDialog
i18n={i18n} i18n={i18n}
onClickPrimaryButton={onClickPrimaryButton} onClickPrimaryButton={onClickPrimaryButton}
onClose={onClose} onClose={closeHandler}
primaryButtonText={primaryButtonText} primaryButtonText={primaryButtonText}
title={title} title={title}
{...secondaryButtonProps} {...secondaryButtonProps}

View file

@ -12,8 +12,9 @@ import enMessages from '../../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
const createProps = (): GroupV1DisabledActionsPropsType => ({ const createProps = (): GroupV1DisabledActionsPropsType => ({
conversationId: '123',
i18n, i18n,
onStartGroupMigration: action('onStartGroupMigration'), showGV2MigrationDialog: action('showGV2MigrationDialog'),
}); });
export default { export default {

View file

@ -6,13 +6,15 @@ import { Intl } from '../Intl';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType } from '../../types/Util';
export type PropsType = { export type PropsType = {
conversationId: string;
i18n: LocalizerType; i18n: LocalizerType;
onStartGroupMigration: () => unknown; showGV2MigrationDialog: (id: string) => unknown;
}; };
export function GroupV1DisabledActions({ export function GroupV1DisabledActions({
conversationId,
i18n, i18n,
onStartGroupMigration, showGV2MigrationDialog,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
return ( return (
<div className="module-group-v1-disabled-actions"> <div className="module-group-v1-disabled-actions">
@ -37,7 +39,7 @@ export function GroupV1DisabledActions({
<div className="module-group-v1-disabled-actions__buttons"> <div className="module-group-v1-disabled-actions__buttons">
<button <button
type="button" type="button"
onClick={onStartGroupMigration} onClick={() => showGV2MigrationDialog(conversationId)}
tabIndex={0} tabIndex={0}
className="module-group-v1-disabled-actions__buttons__button" className="module-group-v1-disabled-actions__buttons__button"
> >

View file

@ -31,6 +31,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
'areWeInvited', 'areWeInvited',
isBoolean(overrideProps.areWeInvited) ? overrideProps.areWeInvited : false isBoolean(overrideProps.areWeInvited) ? overrideProps.areWeInvited : false
), ),
conversationId: '123',
droppedMembers: overrideProps.droppedMembers || [contact1], droppedMembers: overrideProps.droppedMembers || [contact1],
getPreferredBadge: () => undefined, getPreferredBadge: () => undefined,
i18n, i18n,

View file

@ -15,6 +15,7 @@ import * as log from '../../logging/log';
export type PropsDataType = { export type PropsDataType = {
areWeInvited: boolean; areWeInvited: boolean;
conversationId: string;
droppedMembers: Array<ConversationType>; droppedMembers: Array<ConversationType>;
invitedMembers: Array<ConversationType>; invitedMembers: Array<ConversationType>;
}; };
@ -30,6 +31,7 @@ export type PropsType = PropsDataType & PropsHousekeepingType;
export function GroupV1Migration(props: PropsType): React.ReactElement { export function GroupV1Migration(props: PropsType): React.ReactElement {
const { const {
areWeInvited, areWeInvited,
conversationId,
droppedMembers, droppedMembers,
getPreferredBadge, getPreferredBadge,
i18n, i18n,
@ -86,6 +88,7 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
{showingDialog ? ( {showingDialog ? (
<GroupV1MigrationDialog <GroupV1MigrationDialog
areWeInvited={areWeInvited} areWeInvited={areWeInvited}
conversationId={conversationId}
droppedMembers={droppedMembers} droppedMembers={droppedMembers}
getPreferredBadge={getPreferredBadge} getPreferredBadge={getPreferredBadge}
hasMigrated hasMigrated

View file

@ -28,7 +28,6 @@ import { createChatColorPicker } from './state/roots/createChatColorPicker';
import { createConversationDetails } from './state/roots/createConversationDetails'; import { createConversationDetails } from './state/roots/createConversationDetails';
import { createApp } from './state/roots/createApp'; import { createApp } from './state/roots/createApp';
import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement'; import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
import { createGroupV1MigrationModal } from './state/roots/createGroupV1MigrationModal';
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal'; import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import { createMessageDetail } from './state/roots/createMessageDetail'; import { createMessageDetail } from './state/roots/createMessageDetail';
import { createConversationNotificationsSettings } from './state/roots/createConversationNotificationsSettings'; import { createConversationNotificationsSettings } from './state/roots/createConversationNotificationsSettings';
@ -410,7 +409,6 @@ export const setup = (options: {
createChatColorPicker, createChatColorPicker,
createConversationDetails, createConversationDetails,
createGroupLinkManagement, createGroupLinkManagement,
createGroupV1MigrationModal,
createGroupV2JoinModal, createGroupV2JoinModal,
createGroupV2Permissions, createGroupV2Permissions,
createMessageDetail, createMessageDetail,

View file

@ -107,6 +107,11 @@ import { denyPendingApprovalRequest } from '../../util/denyPendingApprovalReques
import { SignalService as Proto } from '../../protobuf'; import { SignalService as Proto } from '../../protobuf';
import { addReportSpamJob } from '../../jobs/helpers/addReportSpamJob'; import { addReportSpamJob } from '../../jobs/helpers/addReportSpamJob';
import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue'; import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue';
import {
modifyGroupV2,
buildPromotePendingAdminApprovalMemberChange,
initiateMigrationToGroupV2 as doInitiateMigrationToGroupV2,
} from '../../groups';
// State // State
@ -876,6 +881,7 @@ export const actions = {
doubleCheckMissingQuoteReference, doubleCheckMissingQuoteReference,
generateNewGroupLink, generateNewGroupLink,
loadRecentMediaItems, loadRecentMediaItems,
initiateMigrationToGroupV2,
messageChanged, messageChanged,
messageDeleted, messageDeleted,
messageExpanded, messageExpanded,
@ -2142,7 +2148,7 @@ function approvePendingMembershipFromGroupV2(
isGroupV2(conversation.attributes) && isGroupV2(conversation.attributes) &&
isMemberRequestingToJoin(conversation.attributes, uuid) isMemberRequestingToJoin(conversation.attributes, uuid)
) { ) {
await window.Signal.Groups.modifyGroupV2({ await modifyGroupV2({
conversation, conversation,
usingCredentialsFrom: [pendingMember], usingCredentialsFrom: [pendingMember],
createGroupChange: async () => { createGroupChange: async () => {
@ -2157,12 +2163,10 @@ function approvePendingMembershipFromGroupV2(
return undefined; return undefined;
} }
return window.Signal.Groups.buildPromotePendingAdminApprovalMemberChange( return buildPromotePendingAdminApprovalMemberChange({
{ group: conversation.attributes,
group: conversation.attributes, uuid,
uuid, });
}
);
}, },
name: 'approvePendingMembershipFromGroupV2', name: 'approvePendingMembershipFromGroupV2',
}); });
@ -2362,6 +2366,26 @@ function deleteConversation(conversationId: string): NoopActionType {
}; };
} }
function initiateMigrationToGroupV2(conversationId: string): NoopActionType {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'deleteConversation: Expected a conversation to be found. Doing nothing'
);
}
longRunningTaskWrapper({
idForLogging: conversation.idForLogging(),
name: 'initiateMigrationToGroupV2',
task: () => doInitiateMigrationToGroupV2(conversation),
});
return {
type: 'NOOP',
payload: null,
};
}
function loadRecentMediaItems( function loadRecentMediaItems(
conversationId: string, conversationId: string,
limit: number limit: number

View file

@ -3,15 +3,19 @@
import type { ThunkAction } from 'redux-thunk'; import type { ThunkAction } from 'redux-thunk';
import type { ExplodePromiseResultType } from '../../util/explodePromise'; import type { ExplodePromiseResultType } from '../../util/explodePromise';
import type { GroupV2PendingMemberType } from '../../model-types.d';
import type { PropsForMessage } from '../selectors/message'; import type { PropsForMessage } from '../selectors/message';
import type { RecipientsByConversation } from './stories';
import type { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog'; import type { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
import type { StateType as RootStateType } from '../reducer'; import type { StateType as RootStateType } from '../reducer';
import type { UUIDStringType } from '../../types/UUID'; import type { UUIDStringType } from '../../types/UUID';
import * as SingleServePromise from '../../services/singleServePromise'; import * as SingleServePromise from '../../services/singleServePromise';
import { getMessageById } from '../../messages/getMessageById'; import { getMessageById } from '../../messages/getMessageById';
import { getMessagePropsSelector } from '../selectors/message'; import { getMessagePropsSelector } from '../selectors/message';
import { longRunningTaskWrapper } from '../../util/longRunningTaskWrapper';
import { useBoundActions } from '../../hooks/useBoundActions'; import { useBoundActions } from '../../hooks/useBoundActions';
import type { RecipientsByConversation } from './stories'; import { isGroupV1 } from '../../util/whatTypeOfConversation';
import { getGroupMigrationMembers } from '../../groups';
// State // State
@ -24,9 +28,19 @@ export type SafetyNumberChangedBlockingDataType = Readonly<{
source?: SafetyNumberChangeSource; source?: SafetyNumberChangeSource;
}>; }>;
type MigrateToGV2PropsType = {
areWeInvited: boolean;
conversationId: string;
droppedMemberIds: ReadonlyArray<string>;
hasMigrated: boolean;
invitedMemberIds: ReadonlyArray<string>;
};
export type GlobalModalsStateType = Readonly<{ export type GlobalModalsStateType = Readonly<{
addUserToAnotherGroupModalContactId?: string;
contactModalState?: ContactModalStateType; contactModalState?: ContactModalStateType;
forwardMessageProps?: ForwardMessagePropsType; forwardMessageProps?: ForwardMessagePropsType;
gv2MigrationProps?: MigrateToGV2PropsType;
isProfileEditorVisible: boolean; isProfileEditorVisible: boolean;
isSignalConnectionsVisible: boolean; isSignalConnectionsVisible: boolean;
isStoriesSettingsVisible: boolean; isStoriesSettingsVisible: boolean;
@ -34,7 +48,6 @@ export type GlobalModalsStateType = Readonly<{
profileEditorHasError: boolean; profileEditorHasError: boolean;
safetyNumberChangedBlockingData?: SafetyNumberChangedBlockingDataType; safetyNumberChangedBlockingData?: SafetyNumberChangedBlockingDataType;
safetyNumberModalContactId?: string; safetyNumberModalContactId?: string;
addUserToAnotherGroupModalContactId?: string;
userNotFoundModalState?: UserNotFoundModalStateType; userNotFoundModalState?: UserNotFoundModalStateType;
}>; }>;
@ -60,6 +73,8 @@ const TOGGLE_SIGNAL_CONNECTIONS_MODAL =
'globalModals/TOGGLE_SIGNAL_CONNECTIONS_MODAL'; 'globalModals/TOGGLE_SIGNAL_CONNECTIONS_MODAL';
export const SHOW_SEND_ANYWAY_DIALOG = 'globalModals/SHOW_SEND_ANYWAY_DIALOG'; export const SHOW_SEND_ANYWAY_DIALOG = 'globalModals/SHOW_SEND_ANYWAY_DIALOG';
const HIDE_SEND_ANYWAY_DIALOG = 'globalModals/HIDE_SEND_ANYWAY_DIALOG'; const HIDE_SEND_ANYWAY_DIALOG = 'globalModals/HIDE_SEND_ANYWAY_DIALOG';
const SHOW_GV2_MIGRATION_DIALOG = 'globalModals/SHOW_GV2_MIGRATION_DIALOG';
const CLOSE_GV2_MIGRATION_DIALOG = 'globalModals/CLOSE_GV2_MIGRATION_DIALOG';
export type ContactModalStateType = { export type ContactModalStateType = {
contactId: string; contactId: string;
@ -137,6 +152,15 @@ type HideStoriesSettingsActionType = {
type: typeof HIDE_STORIES_SETTINGS; type: typeof HIDE_STORIES_SETTINGS;
}; };
type StartMigrationToGV2ActionType = {
type: typeof SHOW_GV2_MIGRATION_DIALOG;
payload: MigrateToGV2PropsType;
};
type CloseGV2MigrationDialogActionType = {
type: typeof CLOSE_GV2_MIGRATION_DIALOG;
};
export type ShowSendAnywayDialogActionType = { export type ShowSendAnywayDialogActionType = {
type: typeof SHOW_SEND_ANYWAY_DIALOG; type: typeof SHOW_SEND_ANYWAY_DIALOG;
payload: SafetyNumberChangedBlockingDataType & { payload: SafetyNumberChangedBlockingDataType & {
@ -149,6 +173,8 @@ type HideSendAnywayDialogActiontype = {
}; };
export type GlobalModalsActionType = export type GlobalModalsActionType =
| StartMigrationToGV2ActionType
| CloseGV2MigrationDialogActionType
| HideContactModalActionType | HideContactModalActionType
| ShowContactModalActionType | ShowContactModalActionType
| HideWhatsNewModalActionType | HideWhatsNewModalActionType
@ -185,6 +211,8 @@ export const actions = {
toggleSafetyNumberModal, toggleSafetyNumberModal,
toggleAddUserToAnotherGroupModal, toggleAddUserToAnotherGroupModal,
toggleSignalConnectionsModal, toggleSignalConnectionsModal,
showGV2MigrationDialog,
closeGV2MigrationDialog,
}; };
export const useGlobalModalActions = (): typeof actions => export const useGlobalModalActions = (): typeof actions =>
@ -244,6 +272,57 @@ function showStoriesSettings(): ShowStoriesSettingsActionType {
return { type: SHOW_STORIES_SETTINGS }; return { type: SHOW_STORIES_SETTINGS };
} }
function showGV2MigrationDialog(
conversationId: string
): ThunkAction<void, RootStateType, unknown, StartMigrationToGV2ActionType> {
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'showGV2MigrationDialog: Expected a conversation to be found. Doing nothing'
);
}
const idForLogging = conversation.idForLogging();
if (!isGroupV1(conversation.attributes)) {
throw new Error(
`showGV2MigrationDialog/${idForLogging}: Cannot start, not a GroupV1 group`
);
}
// Note: this call will throw if, after generating member lists, we are no longer a
// member or are in the pending member list.
const { droppedGV2MemberIds, pendingMembersV2 } =
await longRunningTaskWrapper({
idForLogging,
name: 'getGroupMigrationMembers',
task: () => getGroupMigrationMembers(conversation),
});
const invitedMemberIds = pendingMembersV2.map(
(item: GroupV2PendingMemberType) => item.uuid
);
dispatch({
type: SHOW_GV2_MIGRATION_DIALOG,
payload: {
areWeInvited: false,
conversationId,
droppedMemberIds: droppedGV2MemberIds,
hasMigrated: false,
invitedMemberIds,
},
});
};
}
function closeGV2MigrationDialog(): CloseGV2MigrationDialogActionType {
return {
type: CLOSE_GV2_MIGRATION_DIALOG,
};
}
function toggleForwardMessageModal( function toggleForwardMessageModal(
messageId?: string messageId?: string
): ThunkAction< ): ThunkAction<

View file

@ -1,21 +0,0 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { Provider } from 'react-redux';
import type { Store } from 'redux';
import type { PropsType } from '../smart/GroupV1MigrationDialog';
import { SmartGroupV1MigrationDialog } from '../smart/GroupV1MigrationDialog';
export const createGroupV1MigrationModal = (
store: Store,
props: PropsType
): React.ReactElement => {
return (
<Provider store={store}>
<SmartGroupV1MigrationDialog {...props} />
</Provider>
);
};

View file

@ -1121,6 +1121,7 @@ function getPropsForGroupV1Migration(
return { return {
areWeInvited: false, areWeInvited: false,
conversationId: message.conversationId,
droppedMembers, droppedMembers,
invitedMembers, invitedMembers,
}; };
@ -1140,6 +1141,7 @@ function getPropsForGroupV1Migration(
return { return {
areWeInvited, areWeInvited,
conversationId: message.conversationId,
droppedMembers, droppedMembers,
invitedMembers, invitedMembers,
}; };

View file

@ -30,7 +30,6 @@ export type PropsType = {
| 'onPickSticker' | 'onPickSticker'
| 'onSelectMediaQuality' | 'onSelectMediaQuality'
| 'onSendMessage' | 'onSendMessage'
| 'onStartGroupMigration'
| 'onTextTooLong' | 'onTextTooLong'
| 'openConversation' | 'openConversation'
>; >;

View file

@ -3,7 +3,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { mapDispatchToProps } from '../actions'; import { mapDispatchToProps } from '../actions';
import type { PropsType as GroupV1MigrationDialogPropsType } from '../../components/GroupV1MigrationDialog'; import type { DataPropsType as GroupV1MigrationDialogPropsType } from '../../components/GroupV1MigrationDialog';
import { GroupV1MigrationDialog } from '../../components/GroupV1MigrationDialog'; import { GroupV1MigrationDialog } from '../../components/GroupV1MigrationDialog';
import type { ConversationType } from '../ducks/conversations'; import type { ConversationType } from '../ducks/conversations';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';

View file

@ -16,21 +16,14 @@ import * as Errors from '../types/errors';
import type { DraftBodyRangesType } from '../types/Util'; import type { DraftBodyRangesType } from '../types/Util';
import type { MIMEType } from '../types/MIME'; import type { MIMEType } from '../types/MIME';
import type { ConversationModel } from '../models/conversations'; import type { ConversationModel } from '../models/conversations';
import type { import type { MessageAttributesType } from '../model-types.d';
GroupV2PendingMemberType,
MessageAttributesType,
} from '../model-types.d';
import type { MediaItemType, MediaItemMessageType } from '../types/MediaItem'; import type { MediaItemType, MediaItemMessageType } from '../types/MediaItem';
import { getMessageById } from '../messages/getMessageById'; import { getMessageById } from '../messages/getMessageById';
import { getContactId } from '../messages/helpers'; import { getContactId } from '../messages/helpers';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
import { enqueueReactionForSend } from '../reactions/enqueueReactionForSend'; import { enqueueReactionForSend } from '../reactions/enqueueReactionForSend';
import type { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions'; import type { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions';
import { import { isDirectConversation, isGroup } from '../util/whatTypeOfConversation';
isDirectConversation,
isGroup,
isGroupV1,
} from '../util/whatTypeOfConversation';
import { findAndFormatContact } from '../util/findAndFormatContact'; import { findAndFormatContact } from '../util/findAndFormatContact';
import { getPreferredBadgeSelector } from '../state/selectors/badges'; import { getPreferredBadgeSelector } from '../state/selectors/badges';
import { import {
@ -195,7 +188,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
private contactModalView?: Backbone.View; private contactModalView?: Backbone.View;
private conversationView?: Backbone.View; private conversationView?: Backbone.View;
private lightboxView?: ReactWrapperView; private lightboxView?: ReactWrapperView;
private migrationDialog?: Backbone.View;
private stickerPreviewModalView?: Backbone.View; private stickerPreviewModalView?: Backbone.View;
// Panel support // Panel support
@ -450,7 +442,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
onTextTooLong: () => showToast(ToastMessageBodyTooLong), onTextTooLong: () => showToast(ToastMessageBodyTooLong),
getQuotedMessage: () => this.model.get('quotedMessageId'), getQuotedMessage: () => this.model.get('quotedMessageId'),
clearQuotedMessage: () => this.setQuoteMessage(undefined), clearQuotedMessage: () => this.setQuoteMessage(undefined),
onStartGroupMigration: () => this.startMigrationToGV2(),
onCancelJoinRequest: async () => { onCancelJoinRequest: async () => {
await window.showConfirmationDialog({ await window.showConfirmationDialog({
dialogName: 'GroupV2CancelRequestToJoin', dialogName: 'GroupV2CancelRequestToJoin',
@ -686,62 +677,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.model.loadAndScroll(messageId); this.model.loadAndScroll(messageId);
} }
async startMigrationToGV2(): Promise<void> {
const logId = this.model.idForLogging();
if (!isGroupV1(this.model.attributes)) {
throw new Error(
`startMigrationToGV2/${logId}: Cannot start, not a GroupV1 group`
);
}
const onClose = () => {
if (this.migrationDialog) {
this.migrationDialog.remove();
this.migrationDialog = undefined;
}
};
onClose();
const migrate = () => {
onClose();
longRunningTaskWrapper({
idForLogging: this.model.idForLogging(),
name: 'initiateMigrationToGroupV2',
task: () => window.Signal.Groups.initiateMigrationToGroupV2(this.model),
});
};
// Note: this call will throw if, after generating member lists, we are no longer a
// member or are in the pending member list.
const { droppedGV2MemberIds, pendingMembersV2 } =
await longRunningTaskWrapper({
idForLogging: this.model.idForLogging(),
name: 'getGroupMigrationMembers',
task: () => window.Signal.Groups.getGroupMigrationMembers(this.model),
});
const invitedMemberIds = pendingMembersV2.map(
(item: GroupV2PendingMemberType) => item.uuid
);
this.migrationDialog = new ReactWrapperView({
className: 'group-v1-migration-wrapper',
JSX: window.Signal.State.Roots.createGroupV1MigrationModal(
window.reduxStore,
{
areWeInvited: false,
droppedMemberIds: droppedGV2MemberIds,
hasMigrated: false,
invitedMemberIds,
migrate,
onClose,
}
),
});
}
unload(reason: string): void { unload(reason: string): void {
log.info( log.info(
'unloading conversation', 'unloading conversation',

2
ts/window.d.ts vendored
View file

@ -40,7 +40,6 @@ import type { createApp } from './state/roots/createApp';
import type { createChatColorPicker } from './state/roots/createChatColorPicker'; import type { createChatColorPicker } from './state/roots/createChatColorPicker';
import type { createConversationDetails } from './state/roots/createConversationDetails'; import type { createConversationDetails } from './state/roots/createConversationDetails';
import type { createGroupLinkManagement } from './state/roots/createGroupLinkManagement'; import type { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
import type { createGroupV1MigrationModal } from './state/roots/createGroupV1MigrationModal';
import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal'; import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import type { createGroupV2Permissions } from './state/roots/createGroupV2Permissions'; import type { createGroupV2Permissions } from './state/roots/createGroupV2Permissions';
import type { createMessageDetail } from './state/roots/createMessageDetail'; import type { createMessageDetail } from './state/roots/createMessageDetail';
@ -172,7 +171,6 @@ export type SignalCoreType = {
createChatColorPicker: typeof createChatColorPicker; createChatColorPicker: typeof createChatColorPicker;
createConversationDetails: typeof createConversationDetails; createConversationDetails: typeof createConversationDetails;
createGroupLinkManagement: typeof createGroupLinkManagement; createGroupLinkManagement: typeof createGroupLinkManagement;
createGroupV1MigrationModal: typeof createGroupV1MigrationModal;
createGroupV2JoinModal: typeof createGroupV2JoinModal; createGroupV2JoinModal: typeof createGroupV2JoinModal;
createGroupV2Permissions: typeof createGroupV2Permissions; createGroupV2Permissions: typeof createGroupV2Permissions;
createMessageDetail: typeof createMessageDetail; createMessageDetail: typeof createMessageDetail;