Moves various panels out to ConversationView react

This commit is contained in:
Josh Perez 2022-12-14 20:10:09 -05:00 committed by GitHub
parent 15efbde23d
commit e142cb47f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 287 additions and 473 deletions

View file

@ -92,7 +92,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
blessedPacks: [],
recentStickers: [],
clearInstalledStickerPack: action('clearInstalledStickerPack'),
onClickAddPack: action('onClickAddPack'),
pushPanelForConversation: action('pushPanelForConversation'),
sendStickerMessage: action('sendStickerMessage'),
clearShowIntroduction: action('clearShowIntroduction'),
showPickerHint: false,

View file

@ -42,6 +42,7 @@ import { AudioCapture } from './conversation/AudioCapture';
import { CompositionUpload } from './CompositionUpload';
import type {
ConversationType,
PushPanelForConversationActionType,
ShowConversationType,
} from '../state/ducks/conversations';
import type { EmojiPickDataType } from './emoji/EmojiPicker';
@ -61,6 +62,7 @@ import { MediaEditor } from './MediaEditor';
import { isImageTypeSupported } from '../util/GoogleChrome';
import * as KeyboardLayout from '../services/keyboardLayout';
import { usePrevious } from '../hooks/usePrevious';
import { PanelType } from '../types/Panels';
export type OwnProps = Readonly<{
acceptedMessageRequest?: boolean;
@ -162,15 +164,15 @@ export type Props = Pick<
| 'blessedPacks'
| 'recentStickers'
| 'clearInstalledStickerPack'
| 'onClickAddPack'
| 'clearShowIntroduction'
| 'showPickerHint'
| 'clearShowPickerHint'
> &
MessageRequestActionsProps &
Pick<GroupV1DisabledActionsPropsType, 'showGV2MigrationDialog'> &
Pick<GroupV2PendingApprovalActionsPropsType, 'onCancelJoinRequest'> &
OwnProps;
Pick<GroupV2PendingApprovalActionsPropsType, 'onCancelJoinRequest'> & {
pushPanelForConversation: PushPanelForConversationActionType;
} & OwnProps;
export function CompositionArea({
// Base props
@ -182,6 +184,7 @@ export function CompositionArea({
isDisabled,
isSignalConversation,
messageCompositionId,
pushPanelForConversation,
processAttachments,
removeAttachment,
sendMultiMediaMessage,
@ -232,7 +235,6 @@ export function CompositionArea({
blessedPacks,
recentStickers,
clearInstalledStickerPack,
onClickAddPack,
sendStickerMessage,
clearShowIntroduction,
showPickerHint,
@ -459,7 +461,11 @@ export function CompositionArea({
blessedPacks={blessedPacks}
recentStickers={recentStickers}
clearInstalledStickerPack={clearInstalledStickerPack}
onClickAddPack={onClickAddPack}
onClickAddPack={() =>
pushPanelForConversation(conversationId, {
type: PanelType.StickerManager,
})
}
onPickSticker={(packId, stickerId) =>
sendStickerMessage(conversationId, { packId, stickerId })
}

View file

@ -56,16 +56,15 @@ const MESSAGE_DEFAULT_PROPS = {
markAttachmentAsCorrupted: shouldNeverBeCalled,
markViewed: shouldNeverBeCalled,
messageExpanded: shouldNeverBeCalled,
// Called when clicking mention, but shouldn't do anything.
showConversation: noop,
openGiftBadge: shouldNeverBeCalled,
openLink: shouldNeverBeCalled,
previews: [],
pushPanelForConversation: shouldNeverBeCalled,
renderAudioAttachment: () => <div />,
saveAttachment: shouldNeverBeCalled,
scrollToQuotedMessage: shouldNeverBeCalled,
showContactDetail: shouldNeverBeCalled,
showContactModal: shouldNeverBeCalled,
showConversation: noop,
showExpiredIncomingTapToViewToast: shouldNeverBeCalled,
showExpiredOutgoingTapToViewToast: shouldNeverBeCalled,
showLightbox: shouldNeverBeCalled,

View file

@ -49,12 +49,12 @@ const commonProps = {
),
onShowAllMedia: action('onShowAllMedia'),
onShowGroupMembers: action('onShowGroupMembers'),
onGoBack: action('onGoBack'),
onArchive: action('onArchive'),
onMarkUnread: action('onMarkUnread'),
onMoveToInbox: action('onMoveToInbox'),
pushPanelForConversation: action('pushPanelForConversation'),
setMuteExpiration: action('onSetMuteNotifications'),
setPinned: action('setPinned'),
viewUserStories: action('viewUserStories'),

View file

@ -18,7 +18,10 @@ import { Avatar, AvatarSize } from '../Avatar';
import { InContactsIcon } from '../InContactsIcon';
import type { LocalizerType, ThemeType } from '../../types/Util';
import type { ConversationType } from '../../state/ducks/conversations';
import type {
ConversationType,
PushPanelForConversationActionType,
} from '../../state/ducks/conversations';
import type { BadgeType } from '../../badges/types';
import type { HasStories } from '../../types/Stories';
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
@ -34,6 +37,7 @@ import {
useStartCallShortcuts,
useKeyboardShortcuts,
} from '../../hooks/useKeyboardShortcuts';
import { PanelType } from '../../types/Panels';
export enum OutgoingCallButtonStyle {
None,
@ -90,7 +94,7 @@ export type PropsActionsType = {
onSearchInConversation: () => void;
onShowAllMedia: () => void;
onShowConversationDetails: () => void;
onShowGroupMembers: () => void;
pushPanelForConversation: PushPanelForConversationActionType;
setDisappearingMessages: (
conversationId: string,
seconds: DurationInSeconds
@ -349,7 +353,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
onMoveToInbox,
onShowAllMedia,
onShowConversationDetails,
onShowGroupMembers,
pushPanelForConversation,
setDisappearingMessages,
setMuteExpiration,
setPinned,
@ -478,7 +482,11 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
</MenuItem>
) : null}
{isGroup && !hasGV2AdminEnabled ? (
<MenuItem onClick={onShowGroupMembers}>
<MenuItem
onClick={() =>
pushPanelForConversation(id, { type: PanelType.GroupV1Members })
}
>
{i18n('showMembers')}
</MenuItem>
) : null}

View file

@ -14,6 +14,7 @@ import type {
ConversationType,
ConversationTypeType,
InteractionModeType,
PushPanelForConversationActionType,
SaveAttachmentActionCreatorType,
ShowConversationType,
} from '../../state/ducks/conversations';
@ -89,6 +90,7 @@ import { PaymentEventKind } from '../../types/Payment';
import type { AnyPaymentEvent } from '../../types/Payment';
import { Emojify } from './Emojify';
import { getPaymentEventDescription } from '../../messages/helpers';
import { PanelType } from '../../types/Panels';
const GUESS_METADATA_WIDTH_TIMESTAMP_SIZE = 10;
const GUESS_METADATA_WIDTH_EXPIRE_TIMER_SIZE = 18;
@ -302,13 +304,7 @@ export type PropsActions = {
startConversation: (e164: string, uuid: UUIDStringType) => void;
showConversation: ShowConversationType;
openGiftBadge: (messageId: string) => void;
showContactDetail: (options: {
contact: EmbeddedContactType;
signalAccount?: {
phoneNumber: string;
uuid: UUIDStringType;
};
}) => void;
pushPanelForConversation: PushPanelForConversationActionType;
showContactModal: (contactId: string, conversationId?: string) => void;
kickOffAttachmentDownload: (options: {
@ -1566,10 +1562,11 @@ export class Message extends React.PureComponent<Props, State> {
public renderEmbeddedContact(): JSX.Element | null {
const {
contact,
conversationId,
conversationType,
direction,
i18n,
showContactDetail,
pushPanelForConversation,
text,
} = this.props;
if (!contact) {
@ -1601,9 +1598,12 @@ export class Message extends React.PureComponent<Props, State> {
}
: undefined;
showContactDetail({
contact,
signalAccount,
pushPanelForConversation(conversationId, {
type: PanelType.ContactDetails,
args: {
contact,
signalAccount,
},
});
}}
withContentAbove={withContentAbove}
@ -2238,6 +2238,7 @@ export class Message extends React.PureComponent<Props, State> {
const {
attachments,
contact,
conversationId,
showLightboxForViewOnceMedia,
direction,
giftBadge,
@ -2247,7 +2248,7 @@ export class Message extends React.PureComponent<Props, State> {
kickOffAttachmentDownload,
startConversation,
openGiftBadge,
showContactDetail,
pushPanelForConversation,
showLightbox,
showExpiredIncomingTapToViewToast,
showExpiredOutgoingTapToViewToast,
@ -2374,7 +2375,13 @@ export class Message extends React.PureComponent<Props, State> {
uuid: contact.uuid,
}
: undefined;
showContactDetail({ contact, signalAccount });
pushPanelForConversation(conversationId, {
type: PanelType.ContactDetails,
args: {
contact,
signalAccount,
},
});
event.preventDefault();
event.stopPropagation();

View file

@ -83,7 +83,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
openLink: action('openLink'),
renderAudioAttachment: () => <div>*AudioAttachment*</div>,
saveAttachment: action('saveAttachment'),
showContactDetail: action('showContactDetail'),
pushPanelForConversation: action('pushPanelForConversation'),
showContactModal: action('showContactModal'),
showExpiredIncomingTapToViewToast: action(
'showExpiredIncomingTapToViewToast'

View file

@ -84,7 +84,6 @@ export type PropsBackboneActions = Pick<
| 'openGiftBadge'
| 'openLink'
| 'renderAudioAttachment'
| 'showContactDetail'
| 'showExpiredIncomingTapToViewToast'
| 'showExpiredOutgoingTapToViewToast'
| 'startConversation'
@ -95,6 +94,7 @@ export type PropsReduxActions = Pick<
| 'checkForAccount'
| 'clearSelectedMessage'
| 'doubleCheckMissingQuoteReference'
| 'pushPanelForConversation'
| 'saveAttachment'
| 'showContactModal'
| 'showConversation'
@ -292,9 +292,9 @@ export class MessageDetail extends React.Component<Props> {
markViewed,
openGiftBadge,
openLink,
pushPanelForConversation,
renderAudioAttachment,
saveAttachment,
showContactDetail,
showContactModal,
showConversation,
showExpiredIncomingTapToViewToast,
@ -339,6 +339,7 @@ export class MessageDetail extends React.Component<Props> {
showConversation={showConversation}
openGiftBadge={openGiftBadge}
openLink={openLink}
pushPanelForConversation={pushPanelForConversation}
renderAudioAttachment={renderAudioAttachment}
saveAttachment={saveAttachment}
shouldCollapseAbove={false}
@ -347,7 +348,6 @@ export class MessageDetail extends React.Component<Props> {
scrollToQuotedMessage={() => {
log.warn('MessageDetail: scrollToQuotedMessage called!');
}}
showContactDetail={showContactDetail}
showContactModal={showContactModal}
showExpiredIncomingTapToViewToast={
showExpiredIncomingTapToViewToast

View file

@ -130,7 +130,7 @@ const defaultMessageProps: TimelineMessagesProps = {
shouldCollapseAbove: false,
shouldCollapseBelow: false,
shouldHideMetadata: false,
showContactDetail: action('default--showContactDetail'),
pushPanelForConversation: action('default--pushPanelForConversation'),
showContactModal: action('default--showContactModal'),
showExpiredIncomingTapToViewToast: action(
'showExpiredIncomingTapToViewToast'

View file

@ -283,7 +283,7 @@ const actions = () => ({
deleteMessageForEveryone: action('deleteMessageForEveryone'),
showMessageDetail: action('showMessageDetail'),
saveAttachment: action('saveAttachment'),
showContactDetail: action('showContactDetail'),
pushPanelForConversation: action('pushPanelForConversation'),
showContactModal: action('showContactModal'),
showConversation: action('showConversation'),
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),

View file

@ -248,7 +248,6 @@ const getActions = createSelector(
'showMessageDetail',
'openGiftBadge',
'setQuoteByMessageId',
'showContactDetail',
'showContactModal',
'kickOffAttachmentDownload',
'markAttachmentAsCorrupted',
@ -257,6 +256,7 @@ const getActions = createSelector(
'showLightbox',
'showLightboxForViewOnceMedia',
'openLink',
'pushPanelForConversation',
'scrollToQuotedMessage',
'showExpiredIncomingTapToViewToast',
'showExpiredOutgoingTapToViewToast',

View file

@ -81,7 +81,7 @@ const getDefaultProps = () => ({
showConversation: action('showConversation'),
openGiftBadge: action('openGiftBadge'),
saveAttachment: action('saveAttachment'),
showContactDetail: action('showContactDetail'),
pushPanelForConversation: action('pushPanelForConversation'),
showContactModal: action('showContactModal'),
showLightbox: action('showLightbox'),
toggleForwardMessageModal: action('toggleForwardMessageModal'),

View file

@ -308,7 +308,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
shouldHideMetadata: isBoolean(overrideProps.shouldHideMetadata)
? overrideProps.shouldHideMetadata
: false,
showContactDetail: action('showContactDetail'),
pushPanelForConversation: action('pushPanelForConversation'),
showContactModal: action('showContactModal'),
showExpiredIncomingTapToViewToast: action(
'showExpiredIncomingTapToViewToast'

View file

@ -81,13 +81,7 @@ const createProps = (
showAllMedia: action('showAllMedia'),
showContactModal: action('showContactModal'),
pushPanelForConversation: action('pushPanelForConversation'),
showGroupLinkManagement: action('showGroupLinkManagement'),
showGroupV2Permissions: action('showGroupV2Permissions'),
showConversationNotificationsSettings: action(
'showConversationNotificationsSettings'
),
showConversation: action('showConversation'),
showPendingInvites: action('showPendingInvites'),
showLightboxWithMedia: action('showLightboxWithMedia'),
updateGroupAttributes: async () => {
action('updateGroupAttributes')();

View file

@ -82,10 +82,6 @@ export type StateProps = {
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
showAllMedia: () => void;
showGroupLinkManagement: () => void;
showGroupV2Permissions: () => void;
showPendingInvites: () => void;
showConversationNotificationsSettings: () => void;
updateGroupAttributes: (
_: Readonly<{
avatar?: undefined | Uint8Array;
@ -161,12 +157,8 @@ export function ConversationDetails({
setMuteExpiration,
showAllMedia,
showContactModal,
showConversationNotificationsSettings,
showConversation,
showGroupLinkManagement,
showGroupV2Permissions,
showLightboxWithMedia,
showPendingInvites,
theme,
toggleSafetyNumberModal,
toggleAddUserToAnotherGroupModal,
@ -451,7 +443,11 @@ export function ConversationDetails({
/>
}
label={i18n('ConversationDetails--notifications')}
onClick={showConversationNotificationsSettings}
onClick={() =>
pushPanelForConversation(conversation.id, {
type: PanelType.NotificationSettings,
})
}
right={
conversation.muteExpiresAt
? getMutedUntilText(conversation.muteExpiresAt, i18n)
@ -503,7 +499,11 @@ export function ConversationDetails({
/>
}
label={i18n('ConversationDetails--group-link')}
onClick={showGroupLinkManagement}
onClick={() =>
pushPanelForConversation(conversation.id, {
type: PanelType.GroupLinkManagement,
})
}
right={hasGroupLink ? i18n('on') : i18n('off')}
/>
) : null}
@ -515,7 +515,11 @@ export function ConversationDetails({
/>
}
label={i18n('ConversationDetails--requests-and-invites')}
onClick={showPendingInvites}
onClick={() =>
pushPanelForConversation(conversation.id, {
type: PanelType.GroupInvites,
})
}
right={invitesCount}
/>
{isAdmin ? (
@ -527,7 +531,11 @@ export function ConversationDetails({
/>
}
label={i18n('permissions')}
onClick={showGroupV2Permissions}
onClick={() =>
pushPanelForConversation(conversation.id, {
type: PanelType.GroupPermissions,
})
}
/>
) : null}
</PanelSection>

View file

@ -26,14 +26,9 @@ import { DisappearingTimeDialog } from './components/DisappearingTimeDialog';
// State
import { createConversationDetails } from './state/roots/createConversationDetails';
import { createApp } from './state/roots/createApp';
import { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
import { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import { createMessageDetail } from './state/roots/createMessageDetail';
import { createConversationNotificationsSettings } from './state/roots/createConversationNotificationsSettings';
import { createGroupV2Permissions } from './state/roots/createGroupV2Permissions';
import { createPendingInvites } from './state/roots/createPendingInvites';
import { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
import { createStickerManager } from './state/roots/createStickerManager';
import { createShortcutGuideModal } from './state/roots/createShortcutGuideModal';
import { createStore } from './state/createStore';
@ -401,15 +396,10 @@ export const setup = (options: {
const Roots = {
createApp,
createConversationDetails,
createGroupLinkManagement,
createGroupV2JoinModal,
createGroupV2Permissions,
createMessageDetail,
createConversationNotificationsSettings,
createPendingInvites,
createSafetyNumberViewer,
createShortcutGuideModal,
createStickerManager,
};
const Ducks = {

View file

@ -15,6 +15,7 @@ import type {
AttachmentDraftType,
InMemoryAttachmentDraftType,
} from '../../types/Attachment';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import type {
DraftBodyRangesType,
ReplacementValuesType,
@ -66,6 +67,7 @@ import { writeDraftAttachment } from '../../util/writeDraftAttachment';
import { getMessageById } from '../../messages/getMessageById';
import { canReply } from '../selectors/message';
import { getConversationSelector } from '../selectors/conversations';
import { useBoundActions } from '../../hooks/useBoundActions';
// State
@ -153,6 +155,10 @@ export const actions = {
setQuotedMessage,
};
export const useComposerActions = (): BoundActionCreatorsMapObject<
typeof actions
> => useBoundActions(actions);
function sendMultiMediaMessage(
conversationId: string,
options: {

View file

@ -1,19 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { Provider } from 'react-redux';
import type { Store } from 'redux';
import type { OwnProps } from '../smart/ConversationNotificationsSettings';
import { SmartConversationNotificationsSettings } from '../smart/ConversationNotificationsSettings';
export const createConversationNotificationsSettings = (
store: Store,
props: OwnProps
): React.ReactElement => (
<Provider store={store}>
<SmartConversationNotificationsSettings {...props} />
</Provider>
);

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 { SmartGroupLinkManagementProps } from '../smart/GroupLinkManagement';
import { SmartGroupLinkManagement } from '../smart/GroupLinkManagement';
export const createGroupLinkManagement = (
store: Store,
props: SmartGroupLinkManagementProps
): React.ReactElement => (
<Provider store={store}>
<SmartGroupLinkManagement {...props} />
</Provider>
);

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 { SmartGroupV2PermissionsProps } from '../smart/GroupV2Permissions';
import { SmartGroupV2Permissions } from '../smart/GroupV2Permissions';
export const createGroupV2Permissions = (
store: Store,
props: SmartGroupV2PermissionsProps
): React.ReactElement => (
<Provider store={store}>
<SmartGroupV2Permissions {...props} />
</Provider>
);

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 { SmartPendingInvitesProps } from '../smart/PendingInvites';
import { SmartPendingInvites } from '../smart/PendingInvites';
export const createPendingInvites = (
store: Store,
props: SmartPendingInvitesProps
): React.ReactElement => (
<Provider store={store}>
<SmartPendingInvites {...props} />
</Provider>
);

View file

@ -1,15 +0,0 @@
// Copyright 2019-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 { SmartStickerManager } from '../smart/StickerManager';
export const createStickerManager = (store: Store): React.ReactElement => (
<Provider store={store}>
<SmartStickerManager />
</Provider>
);

View file

@ -38,10 +38,6 @@ export type SmartConversationDetailsProps = {
addMembers: (conversationIds: ReadonlyArray<string>) => Promise<void>;
conversationId: string;
showAllMedia: () => void;
showGroupLinkManagement: () => void;
showGroupV2Permissions: () => void;
showConversationNotificationsSettings: () => void;
showPendingInvites: () => void;
updateGroupAttributes: (
_: Readonly<{
avatar?: undefined | Uint8Array;

View file

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

View file

@ -1,21 +1,31 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { connect } from 'react-redux';
import { useSelector } from 'react-redux';
import type { CompositionAreaPropsType } from './CompositionArea';
import type { OwnProps as ConversationHeaderPropsType } from './ConversationHeader';
import type { StateType } from '../reducer';
import type { ReactPanelRenderType } from '../../types/Panels';
import type { TimelinePropsType } from './Timeline';
import * as log from '../../logging/log';
import { ContactDetail } from '../../components/conversation/ContactDetail';
import { ConversationView } from '../../components/conversation/ConversationView';
import { PanelType } from '../../types/Panels';
import { SmartChatColorPicker } from './ChatColorPicker';
import { SmartCompositionArea } from './CompositionArea';
import { SmartConversationNotificationsSettings } from './ConversationNotificationsSettings';
import { SmartConversationHeader } from './ConversationHeader';
import { SmartGroupLinkManagement } from './GroupLinkManagement';
import { SmartGroupV2Permissions } from './GroupV2Permissions';
import { SmartGV1Members } from './GV1Members';
import { SmartPendingInvites } from './PendingInvites';
import { SmartStickerManager } from './StickerManager';
import { SmartTimeline } from './Timeline';
import { getIntl } from '../selectors/user';
import { getTopPanelRenderableByReact } from '../selectors/conversations';
import { mapDispatchToProps } from '../actions';
import { startConversation } from '../../util/startConversation';
import { useComposerActions } from '../ducks/composer';
export type PropsType = {
conversationId: string;
@ -24,7 +34,6 @@ export type PropsType = {
| 'id'
| 'onCancelJoinRequest'
| 'onClearAttachments'
| 'onClickAddPack'
| 'onCloseLinkPreview'
| 'onEditorStateChange'
| 'onSelectMediaQuality'
@ -34,46 +43,122 @@ export type PropsType = {
timelineProps: TimelinePropsType;
};
const mapStateToProps = (state: StateType, props: PropsType) => {
const {
compositionAreaProps,
conversationHeaderProps,
conversationId,
timelineProps,
} = props;
export function SmartConversationView({
compositionAreaProps,
conversationHeaderProps,
conversationId,
timelineProps,
}: PropsType): JSX.Element {
const topPanel = useSelector<StateType, ReactPanelRenderType | undefined>(
getTopPanelRenderableByReact
);
const topPanel = getTopPanelRenderableByReact(state);
const { processAttachments } = useComposerActions();
const i18n = useSelector(getIntl);
return {
conversationId,
renderCompositionArea: () => (
<SmartCompositionArea {...compositionAreaProps} />
),
renderConversationHeader: () => (
<SmartConversationHeader {...conversationHeaderProps} />
),
renderTimeline: () => <SmartTimeline {...timelineProps} />,
renderPanel: () => {
if (!topPanel) {
return;
}
return (
<ConversationView
conversationId={conversationId}
processAttachments={processAttachments}
renderCompositionArea={() => (
<SmartCompositionArea {...compositionAreaProps} />
)}
renderConversationHeader={() => (
<SmartConversationHeader {...conversationHeaderProps} />
)}
renderTimeline={() => <SmartTimeline {...timelineProps} />}
renderPanel={() => {
if (!topPanel) {
return;
}
if (topPanel.type === PanelType.ChatColorEditor) {
return (
<div className="panel">
<SmartChatColorPicker conversationId={conversationId} />
</div>
);
}
if (topPanel.type === PanelType.ChatColorEditor) {
return (
<div className="panel">
<SmartChatColorPicker conversationId={conversationId} />
</div>
);
}
const unknownPanelType: never = topPanel.type;
log.warn(`renderPanel: Got unexpected panel type ${unknownPanelType}`);
if (topPanel.type === PanelType.ContactDetails) {
const { contact, signalAccount } = topPanel.args;
return undefined;
},
};
};
return (
<div className="panel">
<ContactDetail
contact={contact}
hasSignalAccount={Boolean(signalAccount)}
i18n={i18n}
onSendMessage={() => {
if (signalAccount) {
startConversation(
signalAccount.phoneNumber,
signalAccount.uuid
);
}
}}
/>
</div>
);
}
const smart = connect(mapStateToProps, mapDispatchToProps);
if (topPanel.type === PanelType.GroupInvites) {
return (
<div className="panel">
<SmartPendingInvites
conversationId={conversationId}
ourUuid={window.storage.user.getCheckedUuid().toString()}
/>
</div>
);
}
export const SmartConversationView = smart(ConversationView);
if (topPanel.type === PanelType.GroupLinkManagement) {
return (
<div className="panel">
<SmartGroupLinkManagement conversationId={conversationId} />
</div>
);
}
if (topPanel.type === PanelType.GroupPermissions) {
return (
<div className="panel">
<SmartGroupV2Permissions conversationId={conversationId} />
</div>
);
}
if (topPanel.type === PanelType.GroupV1Members) {
return (
<div className="group-member-list panel">
<SmartGV1Members conversationId={conversationId} />
</div>
);
}
if (topPanel.type === PanelType.NotificationSettings) {
return (
<div className="panel">
<SmartConversationNotificationsSettings
conversationId={conversationId}
/>
</div>
);
}
if (topPanel.type === PanelType.StickerManager) {
return (
<div className="panel sticker-manager-wrapper">
<SmartStickerManager />
</div>
);
}
log.warn('renderPanel: Got unexpected panel', topPanel);
return undefined;
}}
/>
);
}

View file

@ -0,0 +1,54 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { useSelector } from 'react-redux';
import { ConversationDetailsMembershipList } from '../../components/conversation/conversation-details/ConversationDetailsMembershipList';
import { assertDev } from '../../util/assert';
import { getGroupMemberships } from '../../util/getGroupMemberships';
import {
getConversationByIdSelector,
getConversationByUuidSelector,
} from '../selectors/conversations';
import { getIntl, getTheme } from '../selectors/user';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { useGlobalModalActions } from '../ducks/globalModals';
export type PropsType = {
conversationId: string;
};
export function SmartGV1Members({ conversationId }: PropsType): JSX.Element {
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const i18n = useSelector(getIntl);
const theme = useSelector(getTheme);
const { showContactModal } = useGlobalModalActions();
const conversationSelector = useSelector(getConversationByIdSelector);
const conversationByUuidSelector = useSelector(getConversationByUuidSelector);
const conversation = conversationSelector(conversationId);
assertDev(
conversation,
'<SmartPendingInvites> expected a conversation to be found'
);
const { memberships } = getGroupMemberships(
conversation,
conversationByUuidSelector
);
return (
<ConversationDetailsMembershipList
canAddNewMembers={false}
conversationId={conversationId}
i18n={i18n}
getPreferredBadge={getPreferredBadge}
maxShownMemberCount={32}
memberships={memberships}
showContactModal={showContactModal}
theme={theme}
/>
);
}

View file

@ -44,7 +44,6 @@ const mapStateToProps = (
markAttachmentAsCorrupted,
openGiftBadge,
openLink,
showContactDetail,
showExpiredIncomingTapToViewToast,
showExpiredOutgoingTapToViewToast,
startConversation,
@ -79,7 +78,6 @@ const mapStateToProps = (
openGiftBadge,
openLink,
renderAudioAttachment,
showContactDetail,
showExpiredIncomingTapToViewToast,
showExpiredOutgoingTapToViewToast,
startConversation,

View file

@ -80,7 +80,6 @@ export type TimelinePropsType = ExternalProps &
| 'retryDeleteForEveryone'
| 'retrySend'
| 'scrollToQuotedMessage'
| 'showContactDetail'
| 'showExpiredIncomingTapToViewToast'
| 'showExpiredOutgoingTapToViewToast'
| 'showMessageDetail'

View file

@ -18,10 +18,8 @@ export enum PanelType {
StickerManager = 'StickerManager',
}
export type ReactPanelRenderType = { type: PanelType.ChatColorEditor };
export type BackbonePanelRenderType =
| { type: PanelType.AllMedia }
export type ReactPanelRenderType =
| { type: PanelType.ChatColorEditor }
| {
type: PanelType.ContactDetails;
args: {
@ -32,15 +30,18 @@ export type BackbonePanelRenderType =
};
};
}
| { type: PanelType.ConversationDetails }
| { type: PanelType.GroupInvites }
| { type: PanelType.GroupLinkManagement }
| { type: PanelType.GroupPermissions }
| { type: PanelType.GroupV1Members }
| { type: PanelType.MessageDetails; args: { messageId: string } }
| { type: PanelType.NotificationSettings }
| { type: PanelType.StickerManager };
export type BackbonePanelRenderType =
| { type: PanelType.AllMedia }
| { type: PanelType.ConversationDetails }
| { type: PanelType.MessageDetails; args: { messageId: string } };
export type PanelRenderType = ReactPanelRenderType | BackbonePanelRenderType;
export function isPanelHandledByReact(
@ -50,5 +51,14 @@ export function isPanelHandledByReact(
return false;
}
return panel.type === PanelType.ChatColorEditor;
return (
panel.type === PanelType.ChatColorEditor ||
panel.type === PanelType.ContactDetails ||
panel.type === PanelType.GroupInvites ||
panel.type === PanelType.GroupLinkManagement ||
panel.type === PanelType.GroupPermissions ||
panel.type === PanelType.GroupV1Members ||
panel.type === PanelType.NotificationSettings ||
panel.type === PanelType.StickerManager
);
}

View file

@ -21,6 +21,10 @@ export function getConversationTitleForPanelType(
return i18n('ChatColorPicker__menu-title');
}
if (panelType === PanelType.ContactDetails) {
return '';
}
if (panelType === PanelType.ConversationDetails) {
return '';
}
@ -41,11 +45,13 @@ export function getConversationTitleForPanelType(
return i18n('ConversationDetails--notifications');
}
if (panelType === PanelType.StickerManager) {
return '';
}
if (
panelType === PanelType.ContactDetails ||
panelType === PanelType.GroupV1Members ||
panelType === PanelType.MessageDetails ||
panelType === PanelType.StickerManager
panelType === PanelType.MessageDetails
) {
return undefined;
}

View file

@ -19,14 +19,10 @@ import { strictAssert } from '../util/assert';
import { enqueueReactionForSend } from '../reactions/enqueueReactionForSend';
import type { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions';
import { isGroup } from '../util/whatTypeOfConversation';
import { getPreferredBadgeSelector } from '../state/selectors/badges';
import { isIncoming } from '../state/selectors/message';
import { getActiveCallState } from '../state/selectors/calling';
import { getTheme } from '../state/selectors/user';
import { ReactWrapperView } from './ReactWrapperView';
import { ConversationDetailsMembershipList } from '../components/conversation/conversation-details/ConversationDetailsMembershipList';
import * as log from '../logging/log';
import type { EmbeddedContactType } from '../types/EmbeddedContact';
import { createConversationView } from '../state/roots/createConversationView';
import { ToastConversationArchived } from '../components/ToastConversationArchived';
import { ToastConversationMarkedUnread } from '../components/ToastConversationMarkedUnread';
@ -44,7 +40,6 @@ import { showToast } from '../util/showToast';
import { UUIDKind } from '../types/UUID';
import type { UUIDStringType } from '../types/UUID';
import { retryDeleteForEveryone } from '../util/retryDeleteForEveryone';
import { ContactDetail } from '../components/conversation/ContactDetail';
import { MediaGallery } from '../components/conversation/media-gallery/MediaGallery';
import type { ItemClickEvent } from '../components/conversation/media-gallery/types/ItemClickEvent';
import {
@ -86,13 +81,6 @@ type MessageActionsType = {
) => unknown;
retrySend: (messageId: string) => unknown;
retryDeleteForEveryone: (messageId: string) => unknown;
showContactDetail: (options: {
contact: EmbeddedContactType;
signalAccount?: {
phoneNumber: string;
uuid: UUIDStringType;
};
}) => unknown;
showExpiredIncomingTapToViewToast: () => unknown;
showExpiredOutgoingTapToViewToast: () => unknown;
showMessageDetail: (messageId: string) => unknown;
@ -202,9 +190,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
onShowAllMedia: () => {
this.showAllMedia();
},
onShowGroupMembers: () => {
this.showGV1Members();
},
onGoBack: () => {
window.reduxActions.conversations.popPanelForConversation(
this.model.id
@ -342,7 +327,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
const compositionAreaProps = {
id: this.model.id,
onClickAddPack: () => this.showStickerManager(),
onTextTooLong: () => showToast(ToastMessageBodyTooLong),
onCancelJoinRequest: async () => {
await window.showConfirmationDialog({
@ -411,15 +395,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
const showMessageDetail = (messageId: string) => {
this.showMessageDetail(messageId);
};
const showContactDetail = (options: {
contact: EmbeddedContactType;
signalAccount?: {
phoneNumber: string;
uuid: UUIDStringType;
};
}) => {
this.showContactDetail(options);
};
const kickOffAttachmentDownload = async (
options: Readonly<{ messageId: string }>
) => {
@ -474,7 +449,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
reactToMessage,
retrySend,
retryDeleteForEveryone,
showContactDetail,
showExpiredIncomingTapToViewToast,
showExpiredOutgoingTapToViewToast,
showMessageDetail,
@ -809,139 +783,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
return view;
}
showGV1Members(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.GroupV1Members,
});
}
getGV1Members(): Backbone.View {
const { contactCollection, id } = this.model;
const memberships =
contactCollection?.map((conversation: ConversationModel) => {
return {
isAdmin: false,
member: conversation.format(),
};
}) || [];
const reduxState = window.reduxStore.getState();
const getPreferredBadge = getPreferredBadgeSelector(reduxState);
const theme = getTheme(reduxState);
const view = new ReactWrapperView({
className: 'group-member-list panel',
JSX: (
<ConversationDetailsMembershipList
canAddNewMembers={false}
conversationId={id}
i18n={window.i18n}
getPreferredBadge={getPreferredBadge}
maxShownMemberCount={32}
memberships={memberships}
showContactModal={contactId => {
window.reduxActions.globalModals.showContactModal(
contactId,
this.model.id
);
}}
theme={theme}
/>
),
});
view.render();
return view;
}
showGroupLinkManagement(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.GroupLinkManagement,
});
}
getGroupLinkManagement(): Backbone.View {
const view = new ReactWrapperView({
className: 'panel',
JSX: window.Signal.State.Roots.createGroupLinkManagement(
window.reduxStore,
{
conversationId: this.model.id,
}
),
});
view.render();
return view;
}
showGroupV2Permissions(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.GroupPermissions,
});
}
getGroupV2Permissions(): Backbone.View {
const view = new ReactWrapperView({
className: 'panel',
JSX: window.Signal.State.Roots.createGroupV2Permissions(
window.reduxStore,
{
conversationId: this.model.id,
}
),
});
view.render();
return view;
}
showPendingInvites(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.GroupInvites,
});
}
getPendingInvites(): Backbone.View {
const view = new ReactWrapperView({
className: 'panel',
JSX: window.Signal.State.Roots.createPendingInvites(window.reduxStore, {
conversationId: this.model.id,
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
}),
});
view.render();
return view;
}
showConversationNotificationsSettings(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.NotificationSettings,
});
}
getConversationNotificationsSettings(): Backbone.View {
const view = new ReactWrapperView({
className: 'panel',
JSX: window.Signal.State.Roots.createConversationNotificationsSettings(
window.reduxStore,
{
conversationId: this.model.id,
}
),
});
view.render();
return view;
}
showConversationDetails(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.ConversationDetails,
@ -970,11 +811,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
addMembers: this.model.addMembersV2.bind(this.model),
conversationId: this.model.get('id'),
showAllMedia: this.showAllMedia.bind(this),
showGroupLinkManagement: this.showGroupLinkManagement.bind(this),
showGroupV2Permissions: this.showGroupV2Permissions.bind(this),
showConversationNotificationsSettings:
this.showConversationNotificationsSettings.bind(this),
showPendingInvites: this.showPendingInvites.bind(this),
updateGroupAttributes: this.model.updateGroupAttributesV2.bind(
this.model
),
@ -1052,78 +888,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
return view;
}
showStickerManager(): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.StickerManager,
});
}
getStickerManager(): Backbone.View {
const view = new ReactWrapperView({
className: ['sticker-manager-wrapper', 'panel'].join(' '),
JSX: window.Signal.State.Roots.createStickerManager(window.reduxStore),
onClose: () => {
window.reduxActions.conversations.popPanelForConversation(
this.model.id
);
},
});
view.render();
return view;
}
showContactDetail({
contact,
signalAccount,
}: {
contact: EmbeddedContactType;
signalAccount?: {
phoneNumber: string;
uuid: UUIDStringType;
};
}): void {
window.reduxActions.conversations.pushPanelForConversation(this.model.id, {
type: PanelType.ContactDetails,
args: { contact, signalAccount },
});
}
getContactDetail({
contact,
signalAccount,
}: {
contact: EmbeddedContactType;
signalAccount?: {
phoneNumber: string;
uuid: UUIDStringType;
};
}): Backbone.View {
const view = new ReactWrapperView({
className: 'contact-detail-pane panel',
JSX: (
<ContactDetail
i18n={window.i18n}
contact={contact}
hasSignalAccount={Boolean(signalAccount)}
onSendMessage={() => {
if (signalAccount) {
startConversation(signalAccount.phoneNumber, signalAccount.uuid);
}
}}
/>
),
onClose: () => {
window.reduxActions.conversations.popPanelForConversation(
this.model.id
);
},
});
return view;
}
pushPanel(panel: PanelRenderType): void {
if (isPanelHandledByReact(panel)) {
return;
@ -1140,24 +904,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
let view: Backbone.View | undefined;
if (type === PanelType.AllMedia) {
view = this.getAllMedia();
} else if (panel.type === PanelType.ContactDetails) {
view = this.getContactDetail(panel.args);
} else if (type === PanelType.ConversationDetails) {
view = this.getConversationDetails();
} else if (type === PanelType.GroupInvites) {
view = this.getPendingInvites();
} else if (type === PanelType.GroupLinkManagement) {
view = this.getGroupLinkManagement();
} else if (type === PanelType.GroupPermissions) {
view = this.getGroupV2Permissions();
} else if (type === PanelType.GroupV1Members) {
view = this.getGV1Members();
} else if (type === PanelType.NotificationSettings) {
view = this.getConversationNotificationsSettings();
} else if (panel.type === PanelType.MessageDetails) {
view = this.getMessageDetail(panel.args);
} else if (type === PanelType.StickerManager) {
view = this.getStickerManager();
}
if (!view) {

10
ts/window.d.ts vendored
View file

@ -38,15 +38,10 @@ 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 { createGroupLinkManagement } from './state/roots/createGroupLinkManagement';
import type { createGroupV2JoinModal } from './state/roots/createGroupV2JoinModal';
import type { createGroupV2Permissions } from './state/roots/createGroupV2Permissions';
import type { createMessageDetail } from './state/roots/createMessageDetail';
import type { createConversationNotificationsSettings } from './state/roots/createConversationNotificationsSettings';
import type { createPendingInvites } from './state/roots/createPendingInvites';
import type { createSafetyNumberViewer } from './state/roots/createSafetyNumberViewer';
import type { createShortcutGuideModal } from './state/roots/createShortcutGuideModal';
import type { createStickerManager } from './state/roots/createStickerManager';
import type * as appDuck from './state/ducks/app';
import type * as callingDuck from './state/ducks/calling';
import type * as conversationsDuck from './state/ducks/conversations';
@ -167,15 +162,10 @@ export type SignalCoreType = {
Roots: {
createApp: typeof createApp;
createConversationDetails: typeof createConversationDetails;
createGroupLinkManagement: typeof createGroupLinkManagement;
createGroupV2JoinModal: typeof createGroupV2JoinModal;
createGroupV2Permissions: typeof createGroupV2Permissions;
createMessageDetail: typeof createMessageDetail;
createConversationNotificationsSettings: typeof createConversationNotificationsSettings;
createPendingInvites: typeof createPendingInvites;
createSafetyNumberViewer: typeof createSafetyNumberViewer;
createShortcutGuideModal: typeof createShortcutGuideModal;
createStickerManager: typeof createStickerManager;
};
Ducks: {
app: typeof appDuck;