Moves ConversationDetails to react panels
This commit is contained in:
parent
ff3ef0179b
commit
d4124abb01
16 changed files with 220 additions and 199 deletions
|
@ -3,7 +3,6 @@
|
|||
|
||||
import React from 'react';
|
||||
import type { Meta, Story } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import type { Props } from './AddUserToAnotherGroupModal';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
@ -30,12 +29,8 @@ export default {
|
|||
i18n: {
|
||||
defaultValue: i18n,
|
||||
},
|
||||
addMemberToGroup: {
|
||||
defaultValue: action('addMemberToGroup'),
|
||||
},
|
||||
toggleAddUserToAnotherGroupModal: {
|
||||
defaultValue: action('toggleAddUserToAnotherGroupModal'),
|
||||
},
|
||||
addMembersToGroup: { action: true },
|
||||
toggleAddUserToAnotherGroupModal: { action: true },
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
|
|
|
@ -32,10 +32,13 @@ type OwnProps = {
|
|||
|
||||
type DispatchProps = {
|
||||
toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
|
||||
addMemberToGroup: (
|
||||
addMembersToGroup: (
|
||||
conversationId: string,
|
||||
contactId: string,
|
||||
onComplete: () => void
|
||||
contactIds: Array<string>,
|
||||
opts: {
|
||||
onSuccess?: () => unknown;
|
||||
onFailure?: () => unknown;
|
||||
}
|
||||
) => void;
|
||||
showToast: (toastType: ToastType, parameters?: ReplacementValuesType) => void;
|
||||
};
|
||||
|
@ -47,7 +50,7 @@ export function AddUserToAnotherGroupModal({
|
|||
theme,
|
||||
contact,
|
||||
toggleAddUserToAnotherGroupModal,
|
||||
addMemberToGroup,
|
||||
addMembersToGroup,
|
||||
showToast,
|
||||
candidateConversations,
|
||||
regionCode,
|
||||
|
@ -203,12 +206,13 @@ export function AddUserToAnotherGroupModal({
|
|||
showToast(ToastType.AddingUserToGroup, {
|
||||
contact: contact.title,
|
||||
});
|
||||
addMemberToGroup(selectedGroupId, contact.id, () =>
|
||||
showToast(ToastType.UserAddedToGroup, {
|
||||
contact: contact.title,
|
||||
group: selectedGroup.title,
|
||||
})
|
||||
);
|
||||
addMembersToGroup(selectedGroupId, [contact.id], {
|
||||
onSuccess: () =>
|
||||
showToast(ToastType.UserAddedToGroup, {
|
||||
contact: contact.title,
|
||||
group: selectedGroup.title,
|
||||
}),
|
||||
});
|
||||
toggleAddUserToAnotherGroupModal(undefined);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -37,7 +37,6 @@ const commonProps = {
|
|||
|
||||
i18n,
|
||||
|
||||
onShowConversationDetails: action('onShowConversationDetails'),
|
||||
setDisappearingMessages: action('setDisappearingMessages'),
|
||||
destroyMessages: action('destroyMessages'),
|
||||
onSearchInConversation: action('onSearchInConversation'),
|
||||
|
|
|
@ -93,7 +93,6 @@ export type PropsActionsType = {
|
|||
onOutgoingVideoCallInConversation: (conversationId: string) => void;
|
||||
onSearchInConversation: () => void;
|
||||
onShowAllMedia: () => void;
|
||||
onShowConversationDetails: () => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
setDisappearingMessages: (
|
||||
conversationId: string,
|
||||
|
@ -352,7 +351,6 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
onMarkUnread,
|
||||
onMoveToInbox,
|
||||
onShowAllMedia,
|
||||
onShowConversationDetails,
|
||||
pushPanelForConversation,
|
||||
setDisappearingMessages,
|
||||
setMuteExpiration,
|
||||
|
@ -475,7 +473,13 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
))}
|
||||
</SubMenu>
|
||||
{!isGroup || hasGV2AdminEnabled ? (
|
||||
<MenuItem onClick={onShowConversationDetails}>
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
pushPanelForConversation(id, {
|
||||
type: PanelType.ConversationDetails,
|
||||
})
|
||||
}
|
||||
>
|
||||
{isGroup
|
||||
? i18n('showConversationDetails')
|
||||
: i18n('showConversationDetails--direct')}
|
||||
|
@ -552,8 +556,13 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
private renderHeader(): ReactNode {
|
||||
const { conversationTitle, groupVersion, onShowConversationDetails, type } =
|
||||
this.props;
|
||||
const {
|
||||
conversationTitle,
|
||||
id,
|
||||
groupVersion,
|
||||
pushPanelForConversation,
|
||||
type,
|
||||
} = this.props;
|
||||
|
||||
if (conversationTitle !== undefined) {
|
||||
return (
|
||||
|
@ -571,14 +580,16 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
switch (type) {
|
||||
case 'direct':
|
||||
onClick = () => {
|
||||
onShowConversationDetails();
|
||||
pushPanelForConversation(id, { type: PanelType.ConversationDetails });
|
||||
};
|
||||
break;
|
||||
case 'group': {
|
||||
const hasGV2AdminEnabled = groupVersion === 2;
|
||||
onClick = hasGV2AdminEnabled
|
||||
? () => {
|
||||
onShowConversationDetails();
|
||||
pushPanelForConversation(id, {
|
||||
type: PanelType.ConversationDetails,
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
break;
|
||||
|
|
|
@ -41,8 +41,8 @@ const createProps = (
|
|||
expireTimer?: DurationInSeconds
|
||||
): Props => ({
|
||||
acceptConversation: action('acceptConversation'),
|
||||
addMembers: async () => {
|
||||
action('addMembers');
|
||||
addMembersToGroup: async () => {
|
||||
action('addMembersToGroup');
|
||||
},
|
||||
areWeASubscriber: false,
|
||||
blockConversation: action('blockConversation'),
|
||||
|
@ -57,10 +57,12 @@ const createProps = (
|
|||
hasActiveCall: false,
|
||||
hasGroupLink,
|
||||
getPreferredBadge: () => undefined,
|
||||
getProfilesForConversation: action('getProfilesForConversation'),
|
||||
groupsInCommon: [],
|
||||
i18n,
|
||||
isAdmin: false,
|
||||
isGroup: true,
|
||||
leaveGroup: action('leaveGroup'),
|
||||
loadRecentMediaItems: action('loadRecentMediaItems'),
|
||||
memberships: times(32, i => ({
|
||||
isAdmin: i === 1,
|
||||
|
@ -78,7 +80,6 @@ const createProps = (
|
|||
member: getDefaultConversation(),
|
||||
})),
|
||||
setDisappearingMessages: action('setDisappearingMessages'),
|
||||
showAllMedia: action('showAllMedia'),
|
||||
showContactModal: action('showContactModal'),
|
||||
pushPanelForConversation: action('pushPanelForConversation'),
|
||||
showConversation: action('showConversation'),
|
||||
|
@ -86,7 +87,6 @@ const createProps = (
|
|||
updateGroupAttributes: async () => {
|
||||
action('updateGroupAttributes')();
|
||||
},
|
||||
onLeave: action('onLeave'),
|
||||
deleteAvatarFromDisk: action('deleteAvatarFromDisk'),
|
||||
replaceAvatar: action('replaceAvatar'),
|
||||
saveAvatarToDisk: action('saveAvatarToDisk'),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { Button, ButtonIconType, ButtonVariant } from '../../Button';
|
||||
import { Tooltip } from '../../Tooltip';
|
||||
|
@ -63,7 +63,6 @@ enum ModalState {
|
|||
}
|
||||
|
||||
export type StateProps = {
|
||||
addMembers: (conversationIds: ReadonlyArray<string>) => Promise<void>;
|
||||
areWeASubscriber: boolean;
|
||||
badges?: ReadonlyArray<BadgeType>;
|
||||
canEditGroupInfo: boolean;
|
||||
|
@ -81,15 +80,6 @@ export type StateProps = {
|
|||
memberships: Array<GroupV2Membership>;
|
||||
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
|
||||
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
|
||||
showAllMedia: () => void;
|
||||
updateGroupAttributes: (
|
||||
_: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>
|
||||
) => Promise<void>;
|
||||
onLeave: () => void;
|
||||
theme: ThemeType;
|
||||
userAvatarData: Array<AvatarDataType>;
|
||||
renderChooseGroupMembersModal: (
|
||||
|
@ -102,8 +92,18 @@ export type StateProps = {
|
|||
|
||||
type ActionProps = {
|
||||
acceptConversation: (id: string) => void;
|
||||
addMembersToGroup: (
|
||||
conversationId: string,
|
||||
conversationIds: ReadonlyArray<string>,
|
||||
opts: {
|
||||
onSuccess?: () => unknown;
|
||||
onFailure?: () => unknown;
|
||||
}
|
||||
) => unknown;
|
||||
blockConversation: (id: string) => void;
|
||||
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
|
||||
getProfilesForConversation: (id: string) => unknown;
|
||||
leaveGroup: (conversationId: string) => void;
|
||||
loadRecentMediaItems: (id: string, limit: number) => void;
|
||||
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
|
||||
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
|
||||
|
@ -117,13 +117,25 @@ type ActionProps = {
|
|||
showConversation: ShowConversationType;
|
||||
toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
|
||||
toggleSafetyNumberModal: (conversationId: string) => unknown;
|
||||
updateGroupAttributes: (
|
||||
conversationId: string,
|
||||
_: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>,
|
||||
opts: {
|
||||
onSuccess?: () => unknown;
|
||||
onFailure?: () => unknown;
|
||||
}
|
||||
) => unknown;
|
||||
} & Pick<ConversationDetailsMediaListPropsType, 'showLightboxWithMedia'>;
|
||||
|
||||
export type Props = StateProps & ActionProps;
|
||||
|
||||
export function ConversationDetails({
|
||||
acceptConversation,
|
||||
addMembers,
|
||||
addMembersToGroup,
|
||||
areWeASubscriber,
|
||||
badges,
|
||||
blockConversation,
|
||||
|
@ -133,16 +145,17 @@ export function ConversationDetails({
|
|||
deleteAvatarFromDisk,
|
||||
hasGroupLink,
|
||||
getPreferredBadge,
|
||||
getProfilesForConversation,
|
||||
groupsInCommon,
|
||||
hasActiveCall,
|
||||
i18n,
|
||||
isAdmin,
|
||||
isGroup,
|
||||
leaveGroup,
|
||||
loadRecentMediaItems,
|
||||
memberships,
|
||||
maxGroupSize,
|
||||
maxRecommendedGroupSize,
|
||||
onLeave,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
pendingApprovalMemberships,
|
||||
|
@ -155,7 +168,6 @@ export function ConversationDetails({
|
|||
searchInConversation,
|
||||
setDisappearingMessages,
|
||||
setMuteExpiration,
|
||||
showAllMedia,
|
||||
showContactModal,
|
||||
showConversation,
|
||||
showLightboxWithMedia,
|
||||
|
@ -177,6 +189,10 @@ export function ConversationDetails({
|
|||
throw new Error('ConversationDetails rendered without a conversation');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getProfilesForConversation(conversation.id);
|
||||
}, [conversation.id, getProfilesForConversation]);
|
||||
|
||||
const invitesCount =
|
||||
pendingMemberships.length + pendingApprovalMemberships.length;
|
||||
|
||||
|
@ -214,15 +230,17 @@ export function ConversationDetails({
|
|||
) => {
|
||||
setEditGroupAttributesRequestState(RequestState.Active);
|
||||
|
||||
try {
|
||||
await updateGroupAttributes(options);
|
||||
setModalState(ModalState.NothingOpen);
|
||||
setEditGroupAttributesRequestState(RequestState.Inactive);
|
||||
} catch (err) {
|
||||
setEditGroupAttributesRequestState(
|
||||
RequestState.InactiveWithError
|
||||
);
|
||||
}
|
||||
updateGroupAttributes(conversation.id, options, {
|
||||
onSuccess: () => {
|
||||
setModalState(ModalState.NothingOpen);
|
||||
setEditGroupAttributesRequestState(RequestState.Inactive);
|
||||
},
|
||||
onFailure: () => {
|
||||
setEditGroupAttributesRequestState(
|
||||
RequestState.InactiveWithError
|
||||
);
|
||||
},
|
||||
});
|
||||
}}
|
||||
onClose={() => {
|
||||
setModalState(ModalState.NothingOpen);
|
||||
|
@ -259,13 +277,15 @@ export function ConversationDetails({
|
|||
makeRequest={async conversationIds => {
|
||||
setAddGroupMembersRequestState(RequestState.Active);
|
||||
|
||||
try {
|
||||
await addMembers(conversationIds);
|
||||
setModalState(ModalState.NothingOpen);
|
||||
setAddGroupMembersRequestState(RequestState.Inactive);
|
||||
} catch (err) {
|
||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||
}
|
||||
addMembersToGroup(conversation.id, conversationIds, {
|
||||
onSuccess: () => {
|
||||
setModalState(ModalState.NothingOpen);
|
||||
setAddGroupMembersRequestState(RequestState.Inactive);
|
||||
},
|
||||
onFailure: () => {
|
||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||
},
|
||||
});
|
||||
}}
|
||||
maxGroupSize={maxGroupSize}
|
||||
maxRecommendedGroupSize={maxRecommendedGroupSize}
|
||||
|
@ -545,7 +565,11 @@ export function ConversationDetails({
|
|||
conversation={conversation}
|
||||
i18n={i18n}
|
||||
loadRecentMediaItems={loadRecentMediaItems}
|
||||
showAllMedia={showAllMedia}
|
||||
showAllMedia={() =>
|
||||
pushPanelForConversation(conversation.id, {
|
||||
type: PanelType.AllMedia,
|
||||
})
|
||||
}
|
||||
showLightboxWithMedia={showLightboxWithMedia}
|
||||
/>
|
||||
|
||||
|
@ -570,7 +594,7 @@ export function ConversationDetails({
|
|||
isBlocked={Boolean(conversation.isBlocked)}
|
||||
isGroup={isGroup}
|
||||
left={Boolean(conversation.left)}
|
||||
onLeave={onLeave}
|
||||
onLeave={() => leaveGroup(conversation.id)}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
|
@ -2416,43 +2416,6 @@ export class ConversationModel extends window.Backbone
|
|||
});
|
||||
}
|
||||
|
||||
async addMembersV2(conversationIds: ReadonlyArray<string>): Promise<void> {
|
||||
await this.modifyGroupV2({
|
||||
name: 'addMembersV2',
|
||||
usingCredentialsFrom: conversationIds
|
||||
.map(id => window.ConversationController.get(id))
|
||||
.filter(isNotNil),
|
||||
createGroupChange: () =>
|
||||
window.Signal.Groups.buildAddMembersChange(
|
||||
this.attributes,
|
||||
conversationIds
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
async updateGroupAttributesV2(
|
||||
attributes: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>
|
||||
): Promise<void> {
|
||||
await this.modifyGroupV2({
|
||||
name: 'updateGroupAttributesV2',
|
||||
usingCredentialsFrom: [],
|
||||
createGroupChange: () =>
|
||||
window.Signal.Groups.buildUpdateAttributesChange(
|
||||
{
|
||||
id: this.id,
|
||||
publicParams: this.get('publicParams'),
|
||||
revision: this.get('revision'),
|
||||
secretParams: this.get('secretParams'),
|
||||
},
|
||||
attributes
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
async leaveGroupV2(): Promise<void> {
|
||||
if (!isGroupV2(this.attributes)) {
|
||||
return;
|
||||
|
|
|
@ -24,7 +24,6 @@ import { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
|
|||
import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
|
||||
|
||||
// State
|
||||
import { createConversationDetails } from './state/roots/createConversationDetails';
|
||||
import { createApp } from './state/roots/createApp';
|
||||
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
|
||||
import { createMessageDetail } from './state/roots/createMessageDetail';
|
||||
|
@ -395,7 +394,6 @@ export const setup = (options: {
|
|||
|
||||
const Roots = {
|
||||
createApp,
|
||||
createConversationDetails,
|
||||
createGroupV2JoinModal,
|
||||
createMessageDetail,
|
||||
createSafetyNumberViewer,
|
||||
|
|
|
@ -114,11 +114,14 @@ import { addReportSpamJob } from '../../jobs/helpers/addReportSpamJob';
|
|||
import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue';
|
||||
import {
|
||||
modifyGroupV2,
|
||||
buildAddMembersChange,
|
||||
buildPromotePendingAdminApprovalMemberChange,
|
||||
buildUpdateAttributesChange,
|
||||
initiateMigrationToGroupV2 as doInitiateMigrationToGroupV2,
|
||||
} from '../../groups';
|
||||
import { getMessageById } from '../../messages/getMessageById';
|
||||
import type { PanelRenderType } from '../../types/Panels';
|
||||
import { isNotNil } from '../../util/isNotNil';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -856,7 +859,7 @@ export type ConversationActionType =
|
|||
|
||||
export const actions = {
|
||||
acceptConversation,
|
||||
addMemberToGroup,
|
||||
addMembersToGroup,
|
||||
approvePendingMembershipFromGroupV2,
|
||||
blockAndReportSpam,
|
||||
blockConversation,
|
||||
|
@ -888,7 +891,9 @@ export const actions = {
|
|||
discardMessages,
|
||||
doubleCheckMissingQuoteReference,
|
||||
generateNewGroupLink,
|
||||
getProfilesForConversation,
|
||||
initiateMigrationToGroupV2,
|
||||
leaveGroup,
|
||||
loadRecentMediaItems,
|
||||
messageChanged,
|
||||
messageDeleted,
|
||||
|
@ -942,6 +947,7 @@ export const actions = {
|
|||
toggleGroupsForStorySend,
|
||||
toggleHideStories,
|
||||
updateConversationModelSharedGroups,
|
||||
updateGroupAttributes,
|
||||
verifyConversationsStoppingSend,
|
||||
};
|
||||
|
||||
|
@ -1911,6 +1917,20 @@ function selectMessage(
|
|||
};
|
||||
}
|
||||
|
||||
function getProfilesForConversation(conversationId: string): NoopActionType {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('getProfilesForConversation: no conversation found');
|
||||
}
|
||||
|
||||
conversation.getProfiles();
|
||||
|
||||
return {
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
function conversationStoppedByMissingVerification(payload: {
|
||||
conversationId: string;
|
||||
distributionId?: string;
|
||||
|
@ -2808,22 +2828,102 @@ function removeMemberFromGroup(
|
|||
};
|
||||
}
|
||||
|
||||
function addMemberToGroup(
|
||||
function addMembersToGroup(
|
||||
conversationId: string,
|
||||
contactId: string,
|
||||
onComplete: () => void
|
||||
contactIds: ReadonlyArray<string>,
|
||||
{
|
||||
onSuccess,
|
||||
onFailure,
|
||||
}: {
|
||||
onSuccess?: () => unknown;
|
||||
onFailure?: () => unknown;
|
||||
} = {}
|
||||
): ThunkAction<void, RootStateType, unknown, never> {
|
||||
return async () => {
|
||||
const conversationModel = window.ConversationController.get(conversationId);
|
||||
if (conversationModel) {
|
||||
const idForLogging = conversationModel.idForLogging();
|
||||
await longRunningTaskWrapper({
|
||||
name: 'addMemberToGroup',
|
||||
idForLogging,
|
||||
task: () => conversationModel.addMembersV2([contactId]),
|
||||
});
|
||||
onComplete();
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('addMembersToGroup: No conversation found');
|
||||
}
|
||||
|
||||
const idForLogging = conversation.idForLogging();
|
||||
try {
|
||||
await longRunningTaskWrapper({
|
||||
name: 'addMembersToGroup',
|
||||
idForLogging,
|
||||
task: () =>
|
||||
modifyGroupV2({
|
||||
name: 'addMembersToGroup',
|
||||
conversation,
|
||||
usingCredentialsFrom: contactIds
|
||||
.map(id => window.ConversationController.get(id))
|
||||
.filter(isNotNil),
|
||||
createGroupChange: async () =>
|
||||
buildAddMembersChange(conversation.attributes, contactIds),
|
||||
}),
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch {
|
||||
onFailure?.();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function updateGroupAttributes(
|
||||
conversationId: string,
|
||||
attributes: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
description?: string;
|
||||
title?: string;
|
||||
}>,
|
||||
{
|
||||
onSuccess,
|
||||
onFailure,
|
||||
}: {
|
||||
onSuccess?: () => unknown;
|
||||
onFailure?: () => unknown;
|
||||
} = {}
|
||||
): ThunkAction<void, RootStateType, unknown, never> {
|
||||
return async () => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('updateGroupAttributes: No conversation found');
|
||||
}
|
||||
|
||||
const { id, publicParams, revision, secretParams } =
|
||||
conversation.attributes;
|
||||
|
||||
try {
|
||||
await modifyGroupV2({
|
||||
name: 'updateGroupAttributes',
|
||||
conversation,
|
||||
usingCredentialsFrom: [],
|
||||
createGroupChange: async () =>
|
||||
buildUpdateAttributesChange(
|
||||
{ id, publicParams, revision, secretParams },
|
||||
attributes
|
||||
),
|
||||
});
|
||||
onSuccess?.();
|
||||
} catch {
|
||||
onFailure?.();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function leaveGroup(
|
||||
conversationId: string
|
||||
): ThunkAction<void, RootStateType, unknown, never> {
|
||||
return async () => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('leaveGroup: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
idForLogging: conversation.idForLogging(),
|
||||
name: 'leaveGroup',
|
||||
task: () => conversation.leaveGroupV2(),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +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 { SmartConversationDetailsProps } from '../smart/ConversationDetails';
|
||||
import { SmartConversationDetails } from '../smart/ConversationDetails';
|
||||
|
||||
export const createConversationDetails = (
|
||||
store: Store,
|
||||
props: SmartConversationDetailsProps
|
||||
): React.ReactElement => (
|
||||
<Provider store={store}>
|
||||
<SmartConversationDetails {...props} />
|
||||
</Provider>
|
||||
);
|
|
@ -35,16 +35,7 @@ import {
|
|||
} from '../../groups/limits';
|
||||
|
||||
export type SmartConversationDetailsProps = {
|
||||
addMembers: (conversationIds: ReadonlyArray<string>) => Promise<void>;
|
||||
conversationId: string;
|
||||
showAllMedia: () => void;
|
||||
updateGroupAttributes: (
|
||||
_: Readonly<{
|
||||
avatar?: undefined | Uint8Array;
|
||||
title?: string;
|
||||
}>
|
||||
) => Promise<void>;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
const ACCESS_ENUM = Proto.AccessControl.AccessRequired;
|
||||
|
|
|
@ -36,7 +36,6 @@ export type OwnProps = {
|
|||
onMoveToInbox: () => void;
|
||||
onSearchInConversation: () => void;
|
||||
onShowAllMedia: () => void;
|
||||
onShowConversationDetails: () => void;
|
||||
};
|
||||
|
||||
const getOutgoingCallButtonStyle = (
|
||||
|
|
|
@ -14,11 +14,12 @@ import { ConversationView } from '../../components/conversation/ConversationView
|
|||
import { PanelType } from '../../types/Panels';
|
||||
import { SmartChatColorPicker } from './ChatColorPicker';
|
||||
import { SmartCompositionArea } from './CompositionArea';
|
||||
import { SmartConversationNotificationsSettings } from './ConversationNotificationsSettings';
|
||||
import { SmartConversationDetails } from './ConversationDetails';
|
||||
import { SmartConversationHeader } from './ConversationHeader';
|
||||
import { SmartConversationNotificationsSettings } from './ConversationNotificationsSettings';
|
||||
import { SmartGV1Members } from './GV1Members';
|
||||
import { SmartGroupLinkManagement } from './GroupLinkManagement';
|
||||
import { SmartGroupV2Permissions } from './GroupV2Permissions';
|
||||
import { SmartGV1Members } from './GV1Members';
|
||||
import { SmartPendingInvites } from './PendingInvites';
|
||||
import { SmartStickerManager } from './StickerManager';
|
||||
import { SmartTimeline } from './Timeline';
|
||||
|
@ -102,6 +103,14 @@ export function SmartConversationView({
|
|||
);
|
||||
}
|
||||
|
||||
if (topPanel.type === PanelType.ConversationDetails) {
|
||||
return (
|
||||
<div className="panel conversation-details-pane">
|
||||
<SmartConversationDetails conversationId={conversationId} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (topPanel.type === PanelType.GroupInvites) {
|
||||
return (
|
||||
<div className="panel">
|
||||
|
|
|
@ -30,6 +30,7 @@ export type ReactPanelRenderType =
|
|||
};
|
||||
};
|
||||
}
|
||||
| { type: PanelType.ConversationDetails }
|
||||
| { type: PanelType.GroupInvites }
|
||||
| { type: PanelType.GroupLinkManagement }
|
||||
| { type: PanelType.GroupPermissions }
|
||||
|
@ -39,7 +40,6 @@ export type ReactPanelRenderType =
|
|||
|
||||
export type BackbonePanelRenderType =
|
||||
| { type: PanelType.AllMedia }
|
||||
| { type: PanelType.ConversationDetails }
|
||||
| { type: PanelType.MessageDetails; args: { messageId: string } };
|
||||
|
||||
export type PanelRenderType = ReactPanelRenderType | BackbonePanelRenderType;
|
||||
|
@ -54,6 +54,7 @@ export function isPanelHandledByReact(
|
|||
return (
|
||||
panel.type === PanelType.ChatColorEditor ||
|
||||
panel.type === PanelType.ContactDetails ||
|
||||
panel.type === PanelType.ConversationDetails ||
|
||||
panel.type === PanelType.GroupInvites ||
|
||||
panel.type === PanelType.GroupLinkManagement ||
|
||||
panel.type === PanelType.GroupPermissions ||
|
||||
|
|
|
@ -184,9 +184,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
const { searchInConversation } = window.reduxActions.search;
|
||||
searchInConversation(this.model.id);
|
||||
},
|
||||
onShowConversationDetails: () => {
|
||||
this.showConversationDetails();
|
||||
},
|
||||
onShowAllMedia: () => {
|
||||
this.showAllMedia();
|
||||
},
|
||||
|
@ -783,53 +780,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
return view;
|
||||
}
|
||||
|
||||
showConversationDetails(): void {
|
||||
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
|
||||
type: PanelType.ConversationDetails,
|
||||
});
|
||||
}
|
||||
|
||||
getConversationDetails(): Backbone.View {
|
||||
// Run a getProfiles in case member's capabilities have changed
|
||||
// Redux should cover us on the return here so no need to await this.
|
||||
if (this.model.throttledGetProfiles) {
|
||||
this.model.throttledGetProfiles();
|
||||
}
|
||||
|
||||
// these methods are used in more than one place and should probably be
|
||||
// dried up and hoisted to methods on ConversationView
|
||||
|
||||
const onLeave = () => {
|
||||
longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'onLeave',
|
||||
task: () => this.model.leaveGroupV2(),
|
||||
});
|
||||
};
|
||||
|
||||
const props = {
|
||||
addMembers: this.model.addMembersV2.bind(this.model),
|
||||
conversationId: this.model.get('id'),
|
||||
showAllMedia: this.showAllMedia.bind(this),
|
||||
updateGroupAttributes: this.model.updateGroupAttributesV2.bind(
|
||||
this.model
|
||||
),
|
||||
onLeave,
|
||||
};
|
||||
|
||||
const view = new ReactWrapperView({
|
||||
className: 'conversation-details-pane panel',
|
||||
JSX: window.Signal.State.Roots.createConversationDetails(
|
||||
window.reduxStore,
|
||||
props
|
||||
),
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
showMessageDetail(messageId: string): void {
|
||||
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
|
||||
type: PanelType.MessageDetails,
|
||||
|
@ -904,8 +854,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
let view: Backbone.View | undefined;
|
||||
if (type === PanelType.AllMedia) {
|
||||
view = this.getAllMedia();
|
||||
} else if (type === PanelType.ConversationDetails) {
|
||||
view = this.getConversationDetails();
|
||||
} else if (panel.type === PanelType.MessageDetails) {
|
||||
view = this.getMessageDetail(panel.args);
|
||||
}
|
||||
|
|
2
ts/window.d.ts
vendored
2
ts/window.d.ts
vendored
|
@ -37,7 +37,6 @@ import type { ConversationController } from './ConversationController';
|
|||
import type { ReduxActions } from './state/types';
|
||||
import type { createStore } from './state/createStore';
|
||||
import type { createApp } from './state/roots/createApp';
|
||||
import type { createConversationDetails } from './state/roots/createConversationDetails';
|
||||
import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
|
||||
import type { createMessageDetail } from './state/roots/createMessageDetail';
|
||||
import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
|
||||
|
@ -161,7 +160,6 @@ export type SignalCoreType = {
|
|||
createStore: typeof createStore;
|
||||
Roots: {
|
||||
createApp: typeof createApp;
|
||||
createConversationDetails: typeof createConversationDetails;
|
||||
createGroupV2JoinModal: typeof createGroupV2JoinModal;
|
||||
createMessageDetail: typeof createMessageDetail;
|
||||
createSafetyNumberViewer: typeof createSafetyNumberViewer;
|
||||
|
|
Loading…
Reference in a new issue