Moves ConversationDetails to react panels

This commit is contained in:
Josh Perez 2022-12-15 22:12:05 -05:00 committed by GitHub
parent ff3ef0179b
commit d4124abb01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 220 additions and 199 deletions

View file

@ -3,7 +3,6 @@
import React from 'react'; import React from 'react';
import type { Meta, Story } from '@storybook/react'; import type { Meta, Story } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import type { Props } from './AddUserToAnotherGroupModal'; import type { Props } from './AddUserToAnotherGroupModal';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
@ -30,12 +29,8 @@ export default {
i18n: { i18n: {
defaultValue: i18n, defaultValue: i18n,
}, },
addMemberToGroup: { addMembersToGroup: { action: true },
defaultValue: action('addMemberToGroup'), toggleAddUserToAnotherGroupModal: { action: true },
},
toggleAddUserToAnotherGroupModal: {
defaultValue: action('toggleAddUserToAnotherGroupModal'),
},
}, },
} as Meta; } as Meta;

View file

@ -32,10 +32,13 @@ type OwnProps = {
type DispatchProps = { type DispatchProps = {
toggleAddUserToAnotherGroupModal: (contactId?: string) => void; toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
addMemberToGroup: ( addMembersToGroup: (
conversationId: string, conversationId: string,
contactId: string, contactIds: Array<string>,
onComplete: () => void opts: {
onSuccess?: () => unknown;
onFailure?: () => unknown;
}
) => void; ) => void;
showToast: (toastType: ToastType, parameters?: ReplacementValuesType) => void; showToast: (toastType: ToastType, parameters?: ReplacementValuesType) => void;
}; };
@ -47,7 +50,7 @@ export function AddUserToAnotherGroupModal({
theme, theme,
contact, contact,
toggleAddUserToAnotherGroupModal, toggleAddUserToAnotherGroupModal,
addMemberToGroup, addMembersToGroup,
showToast, showToast,
candidateConversations, candidateConversations,
regionCode, regionCode,
@ -203,12 +206,13 @@ export function AddUserToAnotherGroupModal({
showToast(ToastType.AddingUserToGroup, { showToast(ToastType.AddingUserToGroup, {
contact: contact.title, contact: contact.title,
}); });
addMemberToGroup(selectedGroupId, contact.id, () => addMembersToGroup(selectedGroupId, [contact.id], {
showToast(ToastType.UserAddedToGroup, { onSuccess: () =>
contact: contact.title, showToast(ToastType.UserAddedToGroup, {
group: selectedGroup.title, contact: contact.title,
}) group: selectedGroup.title,
); }),
});
toggleAddUserToAnotherGroupModal(undefined); toggleAddUserToAnotherGroupModal(undefined);
}, },
}, },

View file

@ -37,7 +37,6 @@ const commonProps = {
i18n, i18n,
onShowConversationDetails: action('onShowConversationDetails'),
setDisappearingMessages: action('setDisappearingMessages'), setDisappearingMessages: action('setDisappearingMessages'),
destroyMessages: action('destroyMessages'), destroyMessages: action('destroyMessages'),
onSearchInConversation: action('onSearchInConversation'), onSearchInConversation: action('onSearchInConversation'),

View file

@ -93,7 +93,6 @@ export type PropsActionsType = {
onOutgoingVideoCallInConversation: (conversationId: string) => void; onOutgoingVideoCallInConversation: (conversationId: string) => void;
onSearchInConversation: () => void; onSearchInConversation: () => void;
onShowAllMedia: () => void; onShowAllMedia: () => void;
onShowConversationDetails: () => void;
pushPanelForConversation: PushPanelForConversationActionType; pushPanelForConversation: PushPanelForConversationActionType;
setDisappearingMessages: ( setDisappearingMessages: (
conversationId: string, conversationId: string,
@ -352,7 +351,6 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onMarkUnread, onMarkUnread,
onMoveToInbox, onMoveToInbox,
onShowAllMedia, onShowAllMedia,
onShowConversationDetails,
pushPanelForConversation, pushPanelForConversation,
setDisappearingMessages, setDisappearingMessages,
setMuteExpiration, setMuteExpiration,
@ -475,7 +473,13 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
))} ))}
</SubMenu> </SubMenu>
{!isGroup || hasGV2AdminEnabled ? ( {!isGroup || hasGV2AdminEnabled ? (
<MenuItem onClick={onShowConversationDetails}> <MenuItem
onClick={() =>
pushPanelForConversation(id, {
type: PanelType.ConversationDetails,
})
}
>
{isGroup {isGroup
? i18n('showConversationDetails') ? i18n('showConversationDetails')
: i18n('showConversationDetails--direct')} : i18n('showConversationDetails--direct')}
@ -552,8 +556,13 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
} }
private renderHeader(): ReactNode { private renderHeader(): ReactNode {
const { conversationTitle, groupVersion, onShowConversationDetails, type } = const {
this.props; conversationTitle,
id,
groupVersion,
pushPanelForConversation,
type,
} = this.props;
if (conversationTitle !== undefined) { if (conversationTitle !== undefined) {
return ( return (
@ -571,14 +580,16 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
switch (type) { switch (type) {
case 'direct': case 'direct':
onClick = () => { onClick = () => {
onShowConversationDetails(); pushPanelForConversation(id, { type: PanelType.ConversationDetails });
}; };
break; break;
case 'group': { case 'group': {
const hasGV2AdminEnabled = groupVersion === 2; const hasGV2AdminEnabled = groupVersion === 2;
onClick = hasGV2AdminEnabled onClick = hasGV2AdminEnabled
? () => { ? () => {
onShowConversationDetails(); pushPanelForConversation(id, {
type: PanelType.ConversationDetails,
});
} }
: undefined; : undefined;
break; break;

View file

@ -41,8 +41,8 @@ const createProps = (
expireTimer?: DurationInSeconds expireTimer?: DurationInSeconds
): Props => ({ ): Props => ({
acceptConversation: action('acceptConversation'), acceptConversation: action('acceptConversation'),
addMembers: async () => { addMembersToGroup: async () => {
action('addMembers'); action('addMembersToGroup');
}, },
areWeASubscriber: false, areWeASubscriber: false,
blockConversation: action('blockConversation'), blockConversation: action('blockConversation'),
@ -57,10 +57,12 @@ const createProps = (
hasActiveCall: false, hasActiveCall: false,
hasGroupLink, hasGroupLink,
getPreferredBadge: () => undefined, getPreferredBadge: () => undefined,
getProfilesForConversation: action('getProfilesForConversation'),
groupsInCommon: [], groupsInCommon: [],
i18n, i18n,
isAdmin: false, isAdmin: false,
isGroup: true, isGroup: true,
leaveGroup: action('leaveGroup'),
loadRecentMediaItems: action('loadRecentMediaItems'), loadRecentMediaItems: action('loadRecentMediaItems'),
memberships: times(32, i => ({ memberships: times(32, i => ({
isAdmin: i === 1, isAdmin: i === 1,
@ -78,7 +80,6 @@ const createProps = (
member: getDefaultConversation(), member: getDefaultConversation(),
})), })),
setDisappearingMessages: action('setDisappearingMessages'), setDisappearingMessages: action('setDisappearingMessages'),
showAllMedia: action('showAllMedia'),
showContactModal: action('showContactModal'), showContactModal: action('showContactModal'),
pushPanelForConversation: action('pushPanelForConversation'), pushPanelForConversation: action('pushPanelForConversation'),
showConversation: action('showConversation'), showConversation: action('showConversation'),
@ -86,7 +87,6 @@ const createProps = (
updateGroupAttributes: async () => { updateGroupAttributes: async () => {
action('updateGroupAttributes')(); action('updateGroupAttributes')();
}, },
onLeave: action('onLeave'),
deleteAvatarFromDisk: action('deleteAvatarFromDisk'), deleteAvatarFromDisk: action('deleteAvatarFromDisk'),
replaceAvatar: action('replaceAvatar'), replaceAvatar: action('replaceAvatar'),
saveAvatarToDisk: action('saveAvatarToDisk'), saveAvatarToDisk: action('saveAvatarToDisk'),

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Button, ButtonIconType, ButtonVariant } from '../../Button'; import { Button, ButtonIconType, ButtonVariant } from '../../Button';
import { Tooltip } from '../../Tooltip'; import { Tooltip } from '../../Tooltip';
@ -63,7 +63,6 @@ enum ModalState {
} }
export type StateProps = { export type StateProps = {
addMembers: (conversationIds: ReadonlyArray<string>) => Promise<void>;
areWeASubscriber: boolean; areWeASubscriber: boolean;
badges?: ReadonlyArray<BadgeType>; badges?: ReadonlyArray<BadgeType>;
canEditGroupInfo: boolean; canEditGroupInfo: boolean;
@ -81,15 +80,6 @@ export type StateProps = {
memberships: Array<GroupV2Membership>; memberships: Array<GroupV2Membership>;
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>; pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>; pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
showAllMedia: () => void;
updateGroupAttributes: (
_: Readonly<{
avatar?: undefined | Uint8Array;
description?: string;
title?: string;
}>
) => Promise<void>;
onLeave: () => void;
theme: ThemeType; theme: ThemeType;
userAvatarData: Array<AvatarDataType>; userAvatarData: Array<AvatarDataType>;
renderChooseGroupMembersModal: ( renderChooseGroupMembersModal: (
@ -102,8 +92,18 @@ export type StateProps = {
type ActionProps = { type ActionProps = {
acceptConversation: (id: string) => void; acceptConversation: (id: string) => void;
addMembersToGroup: (
conversationId: string,
conversationIds: ReadonlyArray<string>,
opts: {
onSuccess?: () => unknown;
onFailure?: () => unknown;
}
) => unknown;
blockConversation: (id: string) => void; blockConversation: (id: string) => void;
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType; deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
getProfilesForConversation: (id: string) => unknown;
leaveGroup: (conversationId: string) => void;
loadRecentMediaItems: (id: string, limit: number) => void; loadRecentMediaItems: (id: string, limit: number) => void;
onOutgoingAudioCallInConversation: (conversationId: string) => unknown; onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
onOutgoingVideoCallInConversation: (conversationId: string) => unknown; onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
@ -117,13 +117,25 @@ type ActionProps = {
showConversation: ShowConversationType; showConversation: ShowConversationType;
toggleAddUserToAnotherGroupModal: (contactId?: string) => void; toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
toggleSafetyNumberModal: (conversationId: string) => unknown; toggleSafetyNumberModal: (conversationId: string) => unknown;
updateGroupAttributes: (
conversationId: string,
_: Readonly<{
avatar?: undefined | Uint8Array;
description?: string;
title?: string;
}>,
opts: {
onSuccess?: () => unknown;
onFailure?: () => unknown;
}
) => unknown;
} & Pick<ConversationDetailsMediaListPropsType, 'showLightboxWithMedia'>; } & Pick<ConversationDetailsMediaListPropsType, 'showLightboxWithMedia'>;
export type Props = StateProps & ActionProps; export type Props = StateProps & ActionProps;
export function ConversationDetails({ export function ConversationDetails({
acceptConversation, acceptConversation,
addMembers, addMembersToGroup,
areWeASubscriber, areWeASubscriber,
badges, badges,
blockConversation, blockConversation,
@ -133,16 +145,17 @@ export function ConversationDetails({
deleteAvatarFromDisk, deleteAvatarFromDisk,
hasGroupLink, hasGroupLink,
getPreferredBadge, getPreferredBadge,
getProfilesForConversation,
groupsInCommon, groupsInCommon,
hasActiveCall, hasActiveCall,
i18n, i18n,
isAdmin, isAdmin,
isGroup, isGroup,
leaveGroup,
loadRecentMediaItems, loadRecentMediaItems,
memberships, memberships,
maxGroupSize, maxGroupSize,
maxRecommendedGroupSize, maxRecommendedGroupSize,
onLeave,
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
pendingApprovalMemberships, pendingApprovalMemberships,
@ -155,7 +168,6 @@ export function ConversationDetails({
searchInConversation, searchInConversation,
setDisappearingMessages, setDisappearingMessages,
setMuteExpiration, setMuteExpiration,
showAllMedia,
showContactModal, showContactModal,
showConversation, showConversation,
showLightboxWithMedia, showLightboxWithMedia,
@ -177,6 +189,10 @@ export function ConversationDetails({
throw new Error('ConversationDetails rendered without a conversation'); throw new Error('ConversationDetails rendered without a conversation');
} }
useEffect(() => {
getProfilesForConversation(conversation.id);
}, [conversation.id, getProfilesForConversation]);
const invitesCount = const invitesCount =
pendingMemberships.length + pendingApprovalMemberships.length; pendingMemberships.length + pendingApprovalMemberships.length;
@ -214,15 +230,17 @@ export function ConversationDetails({
) => { ) => {
setEditGroupAttributesRequestState(RequestState.Active); setEditGroupAttributesRequestState(RequestState.Active);
try { updateGroupAttributes(conversation.id, options, {
await updateGroupAttributes(options); onSuccess: () => {
setModalState(ModalState.NothingOpen); setModalState(ModalState.NothingOpen);
setEditGroupAttributesRequestState(RequestState.Inactive); setEditGroupAttributesRequestState(RequestState.Inactive);
} catch (err) { },
setEditGroupAttributesRequestState( onFailure: () => {
RequestState.InactiveWithError setEditGroupAttributesRequestState(
); RequestState.InactiveWithError
} );
},
});
}} }}
onClose={() => { onClose={() => {
setModalState(ModalState.NothingOpen); setModalState(ModalState.NothingOpen);
@ -259,13 +277,15 @@ export function ConversationDetails({
makeRequest={async conversationIds => { makeRequest={async conversationIds => {
setAddGroupMembersRequestState(RequestState.Active); setAddGroupMembersRequestState(RequestState.Active);
try { addMembersToGroup(conversation.id, conversationIds, {
await addMembers(conversationIds); onSuccess: () => {
setModalState(ModalState.NothingOpen); setModalState(ModalState.NothingOpen);
setAddGroupMembersRequestState(RequestState.Inactive); setAddGroupMembersRequestState(RequestState.Inactive);
} catch (err) { },
setAddGroupMembersRequestState(RequestState.InactiveWithError); onFailure: () => {
} setAddGroupMembersRequestState(RequestState.InactiveWithError);
},
});
}} }}
maxGroupSize={maxGroupSize} maxGroupSize={maxGroupSize}
maxRecommendedGroupSize={maxRecommendedGroupSize} maxRecommendedGroupSize={maxRecommendedGroupSize}
@ -545,7 +565,11 @@ export function ConversationDetails({
conversation={conversation} conversation={conversation}
i18n={i18n} i18n={i18n}
loadRecentMediaItems={loadRecentMediaItems} loadRecentMediaItems={loadRecentMediaItems}
showAllMedia={showAllMedia} showAllMedia={() =>
pushPanelForConversation(conversation.id, {
type: PanelType.AllMedia,
})
}
showLightboxWithMedia={showLightboxWithMedia} showLightboxWithMedia={showLightboxWithMedia}
/> />
@ -570,7 +594,7 @@ export function ConversationDetails({
isBlocked={Boolean(conversation.isBlocked)} isBlocked={Boolean(conversation.isBlocked)}
isGroup={isGroup} isGroup={isGroup}
left={Boolean(conversation.left)} left={Boolean(conversation.left)}
onLeave={onLeave} onLeave={() => leaveGroup(conversation.id)}
/> />
)} )}

View file

@ -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> { async leaveGroupV2(): Promise<void> {
if (!isGroupV2(this.attributes)) { if (!isGroupV2(this.attributes)) {
return; return;

View file

@ -24,7 +24,6 @@ import { StagedLinkPreview } from './components/conversation/StagedLinkPreview';
import { DisappearingTimeDialog } from './components/DisappearingTimeDialog'; import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
// State // State
import { createConversationDetails } from './state/roots/createConversationDetails';
import { createApp } from './state/roots/createApp'; import { createApp } from './state/roots/createApp';
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal'; import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import { createMessageDetail } from './state/roots/createMessageDetail'; import { createMessageDetail } from './state/roots/createMessageDetail';
@ -395,7 +394,6 @@ export const setup = (options: {
const Roots = { const Roots = {
createApp, createApp,
createConversationDetails,
createGroupV2JoinModal, createGroupV2JoinModal,
createMessageDetail, createMessageDetail,
createSafetyNumberViewer, createSafetyNumberViewer,

View file

@ -114,11 +114,14 @@ import { addReportSpamJob } from '../../jobs/helpers/addReportSpamJob';
import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue'; import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue';
import { import {
modifyGroupV2, modifyGroupV2,
buildAddMembersChange,
buildPromotePendingAdminApprovalMemberChange, buildPromotePendingAdminApprovalMemberChange,
buildUpdateAttributesChange,
initiateMigrationToGroupV2 as doInitiateMigrationToGroupV2, initiateMigrationToGroupV2 as doInitiateMigrationToGroupV2,
} from '../../groups'; } from '../../groups';
import { getMessageById } from '../../messages/getMessageById'; import { getMessageById } from '../../messages/getMessageById';
import type { PanelRenderType } from '../../types/Panels'; import type { PanelRenderType } from '../../types/Panels';
import { isNotNil } from '../../util/isNotNil';
// State // State
@ -856,7 +859,7 @@ export type ConversationActionType =
export const actions = { export const actions = {
acceptConversation, acceptConversation,
addMemberToGroup, addMembersToGroup,
approvePendingMembershipFromGroupV2, approvePendingMembershipFromGroupV2,
blockAndReportSpam, blockAndReportSpam,
blockConversation, blockConversation,
@ -888,7 +891,9 @@ export const actions = {
discardMessages, discardMessages,
doubleCheckMissingQuoteReference, doubleCheckMissingQuoteReference,
generateNewGroupLink, generateNewGroupLink,
getProfilesForConversation,
initiateMigrationToGroupV2, initiateMigrationToGroupV2,
leaveGroup,
loadRecentMediaItems, loadRecentMediaItems,
messageChanged, messageChanged,
messageDeleted, messageDeleted,
@ -942,6 +947,7 @@ export const actions = {
toggleGroupsForStorySend, toggleGroupsForStorySend,
toggleHideStories, toggleHideStories,
updateConversationModelSharedGroups, updateConversationModelSharedGroups,
updateGroupAttributes,
verifyConversationsStoppingSend, 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: { function conversationStoppedByMissingVerification(payload: {
conversationId: string; conversationId: string;
distributionId?: string; distributionId?: string;
@ -2808,22 +2828,102 @@ function removeMemberFromGroup(
}; };
} }
function addMemberToGroup( function addMembersToGroup(
conversationId: string, conversationId: string,
contactId: string, contactIds: ReadonlyArray<string>,
onComplete: () => void {
onSuccess,
onFailure,
}: {
onSuccess?: () => unknown;
onFailure?: () => unknown;
} = {}
): ThunkAction<void, RootStateType, unknown, never> { ): ThunkAction<void, RootStateType, unknown, never> {
return async () => { return async () => {
const conversationModel = window.ConversationController.get(conversationId); const conversation = window.ConversationController.get(conversationId);
if (conversationModel) { if (!conversation) {
const idForLogging = conversationModel.idForLogging(); throw new Error('addMembersToGroup: No conversation found');
await longRunningTaskWrapper({
name: 'addMemberToGroup',
idForLogging,
task: () => conversationModel.addMembersV2([contactId]),
});
onComplete();
} }
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(),
});
}; };
} }

View file

@ -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>
);

View file

@ -35,16 +35,7 @@ import {
} from '../../groups/limits'; } from '../../groups/limits';
export type SmartConversationDetailsProps = { export type SmartConversationDetailsProps = {
addMembers: (conversationIds: ReadonlyArray<string>) => Promise<void>;
conversationId: string; conversationId: string;
showAllMedia: () => void;
updateGroupAttributes: (
_: Readonly<{
avatar?: undefined | Uint8Array;
title?: string;
}>
) => Promise<void>;
onLeave: () => void;
}; };
const ACCESS_ENUM = Proto.AccessControl.AccessRequired; const ACCESS_ENUM = Proto.AccessControl.AccessRequired;

View file

@ -36,7 +36,6 @@ export type OwnProps = {
onMoveToInbox: () => void; onMoveToInbox: () => void;
onSearchInConversation: () => void; onSearchInConversation: () => void;
onShowAllMedia: () => void; onShowAllMedia: () => void;
onShowConversationDetails: () => void;
}; };
const getOutgoingCallButtonStyle = ( const getOutgoingCallButtonStyle = (

View file

@ -14,11 +14,12 @@ import { ConversationView } from '../../components/conversation/ConversationView
import { PanelType } from '../../types/Panels'; import { PanelType } from '../../types/Panels';
import { SmartChatColorPicker } from './ChatColorPicker'; import { SmartChatColorPicker } from './ChatColorPicker';
import { SmartCompositionArea } from './CompositionArea'; import { SmartCompositionArea } from './CompositionArea';
import { SmartConversationNotificationsSettings } from './ConversationNotificationsSettings'; import { SmartConversationDetails } from './ConversationDetails';
import { SmartConversationHeader } from './ConversationHeader'; import { SmartConversationHeader } from './ConversationHeader';
import { SmartConversationNotificationsSettings } from './ConversationNotificationsSettings';
import { SmartGV1Members } from './GV1Members';
import { SmartGroupLinkManagement } from './GroupLinkManagement'; import { SmartGroupLinkManagement } from './GroupLinkManagement';
import { SmartGroupV2Permissions } from './GroupV2Permissions'; import { SmartGroupV2Permissions } from './GroupV2Permissions';
import { SmartGV1Members } from './GV1Members';
import { SmartPendingInvites } from './PendingInvites'; import { SmartPendingInvites } from './PendingInvites';
import { SmartStickerManager } from './StickerManager'; import { SmartStickerManager } from './StickerManager';
import { SmartTimeline } from './Timeline'; 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) { if (topPanel.type === PanelType.GroupInvites) {
return ( return (
<div className="panel"> <div className="panel">

View file

@ -30,6 +30,7 @@ export type ReactPanelRenderType =
}; };
}; };
} }
| { type: PanelType.ConversationDetails }
| { type: PanelType.GroupInvites } | { type: PanelType.GroupInvites }
| { type: PanelType.GroupLinkManagement } | { type: PanelType.GroupLinkManagement }
| { type: PanelType.GroupPermissions } | { type: PanelType.GroupPermissions }
@ -39,7 +40,6 @@ export type ReactPanelRenderType =
export type BackbonePanelRenderType = export type BackbonePanelRenderType =
| { type: PanelType.AllMedia } | { type: PanelType.AllMedia }
| { type: PanelType.ConversationDetails }
| { type: PanelType.MessageDetails; args: { messageId: string } }; | { type: PanelType.MessageDetails; args: { messageId: string } };
export type PanelRenderType = ReactPanelRenderType | BackbonePanelRenderType; export type PanelRenderType = ReactPanelRenderType | BackbonePanelRenderType;
@ -54,6 +54,7 @@ export function isPanelHandledByReact(
return ( return (
panel.type === PanelType.ChatColorEditor || panel.type === PanelType.ChatColorEditor ||
panel.type === PanelType.ContactDetails || panel.type === PanelType.ContactDetails ||
panel.type === PanelType.ConversationDetails ||
panel.type === PanelType.GroupInvites || panel.type === PanelType.GroupInvites ||
panel.type === PanelType.GroupLinkManagement || panel.type === PanelType.GroupLinkManagement ||
panel.type === PanelType.GroupPermissions || panel.type === PanelType.GroupPermissions ||

View file

@ -184,9 +184,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
const { searchInConversation } = window.reduxActions.search; const { searchInConversation } = window.reduxActions.search;
searchInConversation(this.model.id); searchInConversation(this.model.id);
}, },
onShowConversationDetails: () => {
this.showConversationDetails();
},
onShowAllMedia: () => { onShowAllMedia: () => {
this.showAllMedia(); this.showAllMedia();
}, },
@ -783,53 +780,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
return view; 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 { showMessageDetail(messageId: string): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, { window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.MessageDetails, type: PanelType.MessageDetails,
@ -904,8 +854,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
let view: Backbone.View | undefined; let view: Backbone.View | undefined;
if (type === PanelType.AllMedia) { if (type === PanelType.AllMedia) {
view = this.getAllMedia(); view = this.getAllMedia();
} else if (type === PanelType.ConversationDetails) {
view = this.getConversationDetails();
} else if (panel.type === PanelType.MessageDetails) { } else if (panel.type === PanelType.MessageDetails) {
view = this.getMessageDetail(panel.args); view = this.getMessageDetail(panel.args);
} }

2
ts/window.d.ts vendored
View file

@ -37,7 +37,6 @@ import type { ConversationController } from './ConversationController';
import type { ReduxActions } from './state/types'; import type { ReduxActions } from './state/types';
import type { createStore } from './state/createStore'; import type { createStore } from './state/createStore';
import type { createApp } from './state/roots/createApp'; import type { createApp } from './state/roots/createApp';
import type { createConversationDetails } from './state/roots/createConversationDetails';
import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal'; import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import type { createMessageDetail } from './state/roots/createMessageDetail'; import type { createMessageDetail } from './state/roots/createMessageDetail';
import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer'; import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
@ -161,7 +160,6 @@ export type SignalCoreType = {
createStore: typeof createStore; createStore: typeof createStore;
Roots: { Roots: {
createApp: typeof createApp; createApp: typeof createApp;
createConversationDetails: typeof createConversationDetails;
createGroupV2JoinModal: typeof createGroupV2JoinModal; createGroupV2JoinModal: typeof createGroupV2JoinModal;
createMessageDetail: typeof createMessageDetail; createMessageDetail: typeof createMessageDetail;
createSafetyNumberViewer: typeof createSafetyNumberViewer; createSafetyNumberViewer: typeof createSafetyNumberViewer;