conversation_view: Move the last of the small functions to redux
This commit is contained in:
parent
86e92dda51
commit
1a68c3db62
59 changed files with 782 additions and 944 deletions
|
@ -41,6 +41,7 @@ type PropsType = {
|
|||
isMaximized: boolean;
|
||||
isFullScreen: boolean;
|
||||
menuOptions: MenuOptionsType;
|
||||
onUndoArchive: (conversationId: string) => unknown;
|
||||
openFileInFolder: (target: string) => unknown;
|
||||
hasCustomTitleBar: boolean;
|
||||
hideMenuBar: boolean;
|
||||
|
@ -73,6 +74,7 @@ export function App({
|
|||
isShowingStoriesView,
|
||||
hasCustomTitleBar,
|
||||
menuOptions,
|
||||
onUndoArchive,
|
||||
openInbox,
|
||||
openFileInFolder,
|
||||
registerSingleDevice,
|
||||
|
@ -183,6 +185,7 @@ export function App({
|
|||
<ToastManager
|
||||
hideToast={hideToast}
|
||||
i18n={i18n}
|
||||
onUndoArchive={onUndoArchive}
|
||||
openFileInFolder={openFileInFolder}
|
||||
toast={toast}
|
||||
/>
|
||||
|
|
|
@ -68,7 +68,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
// MediaEditor
|
||||
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
||||
// MediaQualitySelector
|
||||
onSelectMediaQuality: action('onSelectMediaQuality'),
|
||||
setMediaQualitySetting: action('setMediaQualitySetting'),
|
||||
shouldSendHighQualityAttachments: Boolean(
|
||||
overrideProps.shouldSendHighQualityAttachments
|
||||
),
|
||||
|
@ -116,8 +116,12 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
Boolean(overrideProps.announcementsOnly)
|
||||
),
|
||||
areWeAdmin: boolean('areWeAdmin', Boolean(overrideProps.areWeAdmin)),
|
||||
areWePendingApproval: boolean(
|
||||
'areWePendingApproval',
|
||||
Boolean(overrideProps.areWePendingApproval)
|
||||
),
|
||||
groupAdmins: [],
|
||||
onCancelJoinRequest: action('onCancelJoinRequest'),
|
||||
cancelJoinRequest: action('cancelJoinRequest'),
|
||||
showConversation: action('showConversation'),
|
||||
// SMS-only
|
||||
isSMSOnly: overrideProps.isSMSOnly || false,
|
||||
|
@ -193,6 +197,20 @@ export function Attachments(): JSX.Element {
|
|||
return <CompositionArea {...props} />;
|
||||
}
|
||||
|
||||
export function PendingApproval(): JSX.Element {
|
||||
return (
|
||||
<CompositionArea
|
||||
{...useProps({
|
||||
areWePendingApproval: true,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
AnnouncementsOnlyGroup.story = {
|
||||
name: 'Announcements Only group',
|
||||
};
|
||||
|
||||
export function AnnouncementsOnlyGroup(): JSX.Element {
|
||||
return (
|
||||
<CompositionArea
|
||||
|
|
|
@ -101,13 +101,13 @@ export type OwnProps = Readonly<{
|
|||
linkPreviewLoading: boolean;
|
||||
linkPreviewResult?: LinkPreviewType;
|
||||
messageRequestsEnabled?: boolean;
|
||||
onClearAttachments(): unknown;
|
||||
onClearAttachments(conversationId: string): unknown;
|
||||
onCloseLinkPreview(): unknown;
|
||||
processAttachments: (options: {
|
||||
conversationId: string;
|
||||
files: ReadonlyArray<File>;
|
||||
}) => unknown;
|
||||
onSelectMediaQuality(isHQ: boolean): unknown;
|
||||
setMediaQualitySetting(isHQ: boolean): unknown;
|
||||
sendStickerMessage(
|
||||
id: string,
|
||||
opts: { packId: string; stickerId: number }
|
||||
|
@ -170,7 +170,7 @@ export type Props = Pick<
|
|||
> &
|
||||
MessageRequestActionsProps &
|
||||
Pick<GroupV1DisabledActionsPropsType, 'showGV2MigrationDialog'> &
|
||||
Pick<GroupV2PendingApprovalActionsPropsType, 'onCancelJoinRequest'> & {
|
||||
Pick<GroupV2PendingApprovalActionsPropsType, 'cancelJoinRequest'> & {
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
} & OwnProps;
|
||||
|
||||
|
@ -211,7 +211,7 @@ export function CompositionArea({
|
|||
quotedMessageProps,
|
||||
scrollToMessage,
|
||||
// MediaQualitySelector
|
||||
onSelectMediaQuality,
|
||||
setMediaQualitySetting,
|
||||
shouldSendHighQualityAttachments,
|
||||
// CompositionInput
|
||||
onEditorStateChange,
|
||||
|
@ -261,7 +261,7 @@ export function CompositionArea({
|
|||
announcementsOnly,
|
||||
areWeAdmin,
|
||||
groupAdmins,
|
||||
onCancelJoinRequest,
|
||||
cancelJoinRequest,
|
||||
showConversation,
|
||||
// SMS-only contacts
|
||||
isSMSOnly,
|
||||
|
@ -393,7 +393,7 @@ export function CompositionArea({
|
|||
<MediaQualitySelector
|
||||
i18n={i18n}
|
||||
isHighQuality={shouldSendHighQualityAttachments}
|
||||
onSelectQuality={onSelectMediaQuality}
|
||||
onSelectQuality={setMediaQualitySetting}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -592,8 +592,9 @@ export function CompositionArea({
|
|||
if (areWePendingApproval) {
|
||||
return (
|
||||
<GroupV2PendingApprovalActions
|
||||
cancelJoinRequest={cancelJoinRequest}
|
||||
conversationId={conversationId}
|
||||
i18n={i18n}
|
||||
onCancelJoinRequest={onCancelJoinRequest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -683,7 +684,7 @@ export function CompositionArea({
|
|||
i18n={i18n}
|
||||
onAddAttachment={launchAttachmentPicker}
|
||||
onClickAttachment={maybeEditAttachment}
|
||||
onClose={onClearAttachments}
|
||||
onClose={() => onClearAttachments(conversationId)}
|
||||
onCloseAttachment={attachment => {
|
||||
if (attachment.path) {
|
||||
removeAttachment(conversationId, attachment.path);
|
||||
|
|
|
@ -68,7 +68,6 @@ const MESSAGE_DEFAULT_PROPS = {
|
|||
showExpiredOutgoingTapToViewToast: shouldNeverBeCalled,
|
||||
showLightbox: shouldNeverBeCalled,
|
||||
showLightboxForViewOnceMedia: shouldNeverBeCalled,
|
||||
showMessageDetail: shouldNeverBeCalled,
|
||||
startConversation: shouldNeverBeCalled,
|
||||
theme: ThemeType.dark,
|
||||
viewStory: shouldNeverBeCalled,
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ToastConversationArchived } from './ToastConversationArchived';
|
||||
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
undo: action('undo'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastConversationArchived',
|
||||
};
|
||||
|
||||
export const _ToastConversationArchived = (): JSX.Element => (
|
||||
<ToastConversationArchived {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastConversationArchived.story = {
|
||||
name: 'ToastConversationArchived',
|
||||
};
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
export type ToastPropsType = {
|
||||
undo: () => unknown;
|
||||
};
|
||||
|
||||
export type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
} & ToastPropsType;
|
||||
|
||||
export function ToastConversationArchived({
|
||||
i18n,
|
||||
onClose,
|
||||
undo,
|
||||
}: PropsType): JSX.Element {
|
||||
return (
|
||||
<Toast
|
||||
toastAction={{
|
||||
label: i18n('conversationArchivedUndo'),
|
||||
onClick: () => {
|
||||
undo();
|
||||
onClose();
|
||||
},
|
||||
}}
|
||||
onClose={onClose}
|
||||
>
|
||||
{i18n('conversationArchived')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ToastConversationMarkedUnread } from './ToastConversationMarkedUnread';
|
||||
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastConversationMarkedUnread',
|
||||
};
|
||||
|
||||
export const _ToastConversationMarkedUnread = (): JSX.Element => (
|
||||
<ToastConversationMarkedUnread {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastConversationMarkedUnread.story = {
|
||||
name: 'ToastConversationMarkedUnread',
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastConversationMarkedUnread({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return <Toast onClose={onClose}>{i18n('conversationMarkedUnread')}</Toast>;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ToastConversationUnarchived } from './ToastConversationUnarchived';
|
||||
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastConversationUnarchived',
|
||||
};
|
||||
|
||||
export const _ToastConversationUnarchived = (): JSX.Element => (
|
||||
<ToastConversationUnarchived {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastConversationUnarchived.story = {
|
||||
name: 'ToastConversationUnarchived',
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastConversationUnarchived({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return <Toast onClose={onClose}>{i18n('conversationReturnedToInbox')}</Toast>;
|
||||
}
|
|
@ -17,6 +17,8 @@ export default {
|
|||
component: ToastManager,
|
||||
argTypes: {
|
||||
hideToast: { action: true },
|
||||
openFileInFolder: { action: true },
|
||||
onUndoArchive: { action: true },
|
||||
i18n: {
|
||||
defaultValue: i18n,
|
||||
},
|
||||
|
@ -91,6 +93,30 @@ CannotStartGroupCall.args = {
|
|||
},
|
||||
};
|
||||
|
||||
export const ConversationArchived = Template.bind({});
|
||||
ConversationArchived.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationArchived,
|
||||
parameters: {
|
||||
conversationId: 'some-conversation-id',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationMarkedUnread = Template.bind({});
|
||||
ConversationMarkedUnread.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationMarkedUnread,
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversationUnarchived = Template.bind({});
|
||||
ConversationUnarchived.args = {
|
||||
toast: {
|
||||
toastType: ToastType.ConversationUnarchived,
|
||||
},
|
||||
};
|
||||
|
||||
export const CopiedUsername = Template.bind({});
|
||||
CopiedUsername.args = {
|
||||
toast: {
|
||||
|
@ -182,6 +208,13 @@ MaxAttachments.args = {
|
|||
},
|
||||
};
|
||||
|
||||
export const OriginalMessageNotFound = Template.bind({});
|
||||
OriginalMessageNotFound.args = {
|
||||
toast: {
|
||||
toastType: ToastType.OriginalMessageNotFound,
|
||||
},
|
||||
};
|
||||
|
||||
export const MessageBodyTooLong = Template.bind({});
|
||||
MessageBodyTooLong.args = {
|
||||
toast: {
|
||||
|
|
|
@ -5,7 +5,6 @@ import React from 'react';
|
|||
import type { LocalizerType, ReplacementValuesType } from '../types/Util';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { Toast } from './Toast';
|
||||
import { ToastMessageBodyTooLong } from './ToastMessageBodyTooLong';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import { ToastType } from '../types/Toast';
|
||||
|
||||
|
@ -13,6 +12,7 @@ export type PropsType = {
|
|||
hideToast: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
openFileInFolder: (target: string) => unknown;
|
||||
onUndoArchive: (conversaetionId: string) => unknown;
|
||||
toast?: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
|
@ -25,6 +25,7 @@ export function ToastManager({
|
|||
hideToast,
|
||||
i18n,
|
||||
openFileInFolder,
|
||||
onUndoArchive,
|
||||
toast,
|
||||
}: PropsType): JSX.Element | null {
|
||||
if (toast === undefined) {
|
||||
|
@ -84,6 +85,36 @@ export function ToastManager({
|
|||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.ConversationArchived) {
|
||||
return (
|
||||
<Toast
|
||||
onClose={hideToast}
|
||||
toastAction={{
|
||||
label: i18n('conversationArchivedUndo'),
|
||||
onClick: () => {
|
||||
if (toast.parameters && 'conversationId' in toast.parameters) {
|
||||
onUndoArchive(String(toast.parameters.conversationId));
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
{i18n('conversationArchived')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.ConversationMarkedUnread) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>{i18n('conversationMarkedUnread')}</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.ConversationUnarchived) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>{i18n('conversationReturnedToInbox')}</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.CopiedUsername) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={3 * SECOND}>
|
||||
|
@ -174,15 +205,11 @@ export function ToastManager({
|
|||
}
|
||||
|
||||
if (toastType === ToastType.MessageBodyTooLong) {
|
||||
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
|
||||
return <Toast onClose={hideToast}>{i18n('messageBodyTooLong')}</Toast>;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.ReportedSpamAndBlocked) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('MessageRequests--block-and-report-spam-success-toast')}
|
||||
</Toast>
|
||||
);
|
||||
if (toastType === ToastType.OriginalMessageNotFound) {
|
||||
return <Toast onClose={hideToast}>{i18n('originalMessageNotFound')}</Toast>;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.PinnedConversationsFull) {
|
||||
|
@ -193,6 +220,14 @@ export function ToastManager({
|
|||
return <Toast onClose={hideToast}>{i18n('Reactions--error')}</Toast>;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.ReportedSpamAndBlocked) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('MessageRequests--block-and-report-spam-success-toast')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryMuted) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ToastMessageBodyTooLong } from './ToastMessageBodyTooLong';
|
||||
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastMessageBodyTooLong',
|
||||
};
|
||||
|
||||
export const _ToastMessageBodyTooLong = (): JSX.Element => (
|
||||
<ToastMessageBodyTooLong {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastMessageBodyTooLong.story = {
|
||||
name: 'ToastMessageBodyTooLong',
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastMessageBodyTooLong({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return <Toast onClose={onClose}>{i18n('messageBodyTooLong')}</Toast>;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ToastOriginalMessageNotFound } from './ToastOriginalMessageNotFound';
|
||||
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const defaultProps = {
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Components/ToastOriginalMessageNotFound',
|
||||
};
|
||||
|
||||
export const _ToastOriginalMessageNotFound = (): JSX.Element => (
|
||||
<ToastOriginalMessageNotFound {...defaultProps} />
|
||||
);
|
||||
|
||||
_ToastOriginalMessageNotFound.story = {
|
||||
name: 'ToastOriginalMessageNotFound',
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastOriginalMessageNotFound({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return <Toast onClose={onClose}>{i18n('originalMessageNotFound')}</Toast>;
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
@ -15,10 +14,5 @@ export default {
|
|||
};
|
||||
|
||||
export function Default(): JSX.Element {
|
||||
return (
|
||||
<ChatSessionRefreshedNotification
|
||||
contactSupport={action('contactSupport')}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
return <ChatSessionRefreshedNotification i18n={i18n} />;
|
||||
}
|
||||
|
|
|
@ -9,21 +9,19 @@ import type { LocalizerType } from '../../types/Util';
|
|||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||
import { SystemMessage } from './SystemMessage';
|
||||
import { ChatSessionRefreshedDialog } from './ChatSessionRefreshedDialog';
|
||||
import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
||||
import { mapToSupportLocale } from '../../util/mapToSupportLocale';
|
||||
|
||||
type PropsHousekeepingType = {
|
||||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export type PropsActionsType = {
|
||||
contactSupport: () => unknown;
|
||||
};
|
||||
|
||||
export type PropsType = PropsHousekeepingType & PropsActionsType;
|
||||
export type PropsType = PropsHousekeepingType;
|
||||
|
||||
export function ChatSessionRefreshedNotification(
|
||||
props: PropsType
|
||||
): ReactElement {
|
||||
const { contactSupport, i18n } = props;
|
||||
const { i18n } = props;
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
|
||||
|
||||
const openDialog = useCallback(() => {
|
||||
|
@ -35,8 +33,15 @@ export function ChatSessionRefreshedNotification(
|
|||
|
||||
const wrappedContactSupport = useCallback(() => {
|
||||
setIsDialogOpen(false);
|
||||
contactSupport();
|
||||
}, [contactSupport, setIsDialogOpen]);
|
||||
|
||||
const baseUrl =
|
||||
'https://support.signal.org/hc/LOCALE/requests/new?desktop&chat_refreshed';
|
||||
const locale = window.getLocale();
|
||||
const supportLocale = mapToSupportLocale(locale);
|
||||
const url = baseUrl.replace('LOCALE', supportLocale);
|
||||
|
||||
openLinkInWebBrowser(url);
|
||||
}, [setIsDialogOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -22,12 +22,13 @@ const getCommonProps = () => ({
|
|||
acceptConversation: action('acceptConversation'),
|
||||
blockAndReportSpam: action('blockAndReportSpam'),
|
||||
blockConversation: action('blockConversation'),
|
||||
conversationId: 'some-conversation-id',
|
||||
deleteConversation: action('deleteConversation'),
|
||||
getPreferredBadge: () => undefined,
|
||||
groupConversationId: 'convo-id',
|
||||
i18n,
|
||||
onClose: action('onClose'),
|
||||
onShowContactModal: action('onShowContactModal'),
|
||||
showContactModal: action('showContactModal'),
|
||||
removeMember: action('removeMember'),
|
||||
theme: ThemeType.light,
|
||||
});
|
||||
|
|
|
@ -25,6 +25,7 @@ import { missingCaseError } from '../../util/missingCaseError';
|
|||
import { isInSystemContacts } from '../../util/isInSystemContacts';
|
||||
|
||||
export type PropsType = {
|
||||
conversationId: string;
|
||||
acceptConversation: (conversationId: string) => unknown;
|
||||
blockAndReportSpam: (conversationId: string) => unknown;
|
||||
blockConversation: (conversationId: string) => unknown;
|
||||
|
@ -32,8 +33,11 @@ export type PropsType = {
|
|||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
onClose: () => void;
|
||||
onShowContactModal: (contactId: string, conversationId?: string) => unknown;
|
||||
removeMember: (conversationId: string) => unknown;
|
||||
showContactModal: (contactId: string, conversationId?: string) => unknown;
|
||||
removeMember: (
|
||||
conversationId: string,
|
||||
memberConversationId: string
|
||||
) => unknown;
|
||||
theme: ThemeType;
|
||||
} & (
|
||||
| {
|
||||
|
@ -65,11 +69,12 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
|
|||
acceptConversation,
|
||||
blockAndReportSpam,
|
||||
blockConversation,
|
||||
conversationId,
|
||||
deleteConversation,
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
onClose,
|
||||
onShowContactModal,
|
||||
showContactModal,
|
||||
removeMember,
|
||||
theme,
|
||||
} = props;
|
||||
|
@ -150,7 +155,7 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
|
|||
setConfirmationState(undefined);
|
||||
}}
|
||||
onRemove={() => {
|
||||
removeMember(affectedConversation.id);
|
||||
removeMember(conversationId, affectedConversation.id);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -218,7 +223,7 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
|
|||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
onClick={() => {
|
||||
onShowContactModal(safeConversation.id);
|
||||
showContactModal(safeConversation.id);
|
||||
}}
|
||||
theme={theme}
|
||||
/>
|
||||
|
|
|
@ -39,7 +39,6 @@ const commonProps = {
|
|||
|
||||
setDisappearingMessages: action('setDisappearingMessages'),
|
||||
destroyMessages: action('destroyMessages'),
|
||||
onSearchInConversation: action('onSearchInConversation'),
|
||||
onOutgoingAudioCallInConversation: action(
|
||||
'onOutgoingAudioCallInConversation'
|
||||
),
|
||||
|
@ -47,12 +46,12 @@ const commonProps = {
|
|||
'onOutgoingVideoCallInConversation'
|
||||
),
|
||||
|
||||
onGoBack: action('onGoBack'),
|
||||
|
||||
onArchive: action('onArchive'),
|
||||
onMarkUnread: action('onMarkUnread'),
|
||||
onMoveToInbox: action('onMoveToInbox'),
|
||||
pushPanelForConversation: action('pushPanelForConversation'),
|
||||
popPanelForConversation: action('popPanelForConversation'),
|
||||
searchInConversation: action('searchInConversation'),
|
||||
setMuteExpiration: action('onSetMuteNotifications'),
|
||||
setPinned: action('setPinned'),
|
||||
viewUserStories: action('viewUserStories'),
|
||||
|
|
|
@ -20,6 +20,7 @@ import { InContactsIcon } from '../InContactsIcon';
|
|||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type {
|
||||
ConversationType,
|
||||
PopPanelForConversationActionType,
|
||||
PushPanelForConversationActionType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import type { BadgeType } from '../../badges/types';
|
||||
|
@ -85,14 +86,14 @@ export type PropsDataType = {
|
|||
|
||||
export type PropsActionsType = {
|
||||
destroyMessages: (conversationId: string) => void;
|
||||
onArchive: () => void;
|
||||
onGoBack: () => void;
|
||||
onMarkUnread: () => void;
|
||||
onMoveToInbox: () => void;
|
||||
onArchive: (conversationId: string) => void;
|
||||
onMarkUnread: (conversationId: string) => void;
|
||||
onMoveToInbox: (conversationId: string) => void;
|
||||
onOutgoingAudioCallInConversation: (conversationId: string) => void;
|
||||
onOutgoingVideoCallInConversation: (conversationId: string) => void;
|
||||
onSearchInConversation: () => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
popPanelForConversation: PopPanelForConversationActionType;
|
||||
searchInConversation: (conversationId: string) => void;
|
||||
setDisappearingMessages: (
|
||||
conversationId: string,
|
||||
seconds: DurationInSeconds
|
||||
|
@ -153,12 +154,12 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
private renderBackButton(): ReactNode {
|
||||
const { i18n, onGoBack, showBackButton } = this.props;
|
||||
const { i18n, id, popPanelForConversation, showBackButton } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onGoBack}
|
||||
onClick={() => popPanelForConversation(id)}
|
||||
className={classNames(
|
||||
'module-ConversationHeader__back-icon',
|
||||
showBackButton ? 'module-ConversationHeader__back-icon--show' : null
|
||||
|
@ -314,12 +315,12 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
}
|
||||
|
||||
private renderSearchButton(): ReactNode {
|
||||
const { i18n, onSearchInConversation, showBackButton } = this.props;
|
||||
const { i18n, id, searchInConversation, showBackButton } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSearchInConversation}
|
||||
onClick={() => searchInConversation(id)}
|
||||
className={classNames(
|
||||
'module-ConversationHeader__button',
|
||||
'module-ConversationHeader__button--search',
|
||||
|
@ -501,14 +502,18 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
</MenuItem>
|
||||
<MenuItem divider />
|
||||
{!markedUnread ? (
|
||||
<MenuItem onClick={onMarkUnread}>{i18n('markUnread')}</MenuItem>
|
||||
<MenuItem onClick={() => onMarkUnread(id)}>
|
||||
{i18n('markUnread')}
|
||||
</MenuItem>
|
||||
) : null}
|
||||
{isArchived ? (
|
||||
<MenuItem onClick={onMoveToInbox}>
|
||||
<MenuItem onClick={() => onMoveToInbox(id)}>
|
||||
{i18n('moveConversationToInbox')}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onClick={onArchive}>{i18n('archiveConversation')}</MenuItem>
|
||||
<MenuItem onClick={() => onArchive(id)}>
|
||||
{i18n('archiveConversation')}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => this.setState({ hasDeleteMessagesConfirmation: true })}
|
||||
|
|
|
@ -134,6 +134,16 @@ DirectNoGroupsNoDataNotAccepted.story = {
|
|||
name: 'Direct (No Groups, No Data, Not Accepted)',
|
||||
};
|
||||
|
||||
export const DirectNoGroupsNotAcceptedWithAvatar = Template.bind({});
|
||||
DirectNoGroupsNotAcceptedWithAvatar.args = {
|
||||
...getDefaultConversation(),
|
||||
acceptedMessageRequest: false,
|
||||
profileName: '',
|
||||
};
|
||||
DirectNoGroupsNotAcceptedWithAvatar.story = {
|
||||
name: 'Direct (No Groups, No Data, Not Accepted, With Avatar)',
|
||||
};
|
||||
|
||||
export const GroupManyMembers = Template.bind({});
|
||||
GroupManyMembers.args = {
|
||||
conversationType: 'group',
|
||||
|
|
|
@ -30,9 +30,9 @@ export type Props = {
|
|||
name?: string;
|
||||
phoneNumber?: string;
|
||||
sharedGroupNames?: Array<string>;
|
||||
unblurAvatar: () => void;
|
||||
unblurAvatar: (conversationId: string) => void;
|
||||
unblurredAvatarPath?: string;
|
||||
updateSharedGroups: () => unknown;
|
||||
updateSharedGroups: (conversationId: string) => unknown;
|
||||
theme: ThemeType;
|
||||
viewUserStories: ViewUserStoriesActionCreatorType;
|
||||
} & Omit<AvatarProps, 'onClick' | 'size' | 'noteToSelf'>;
|
||||
|
@ -133,8 +133,8 @@ export function ConversationHero({
|
|||
|
||||
useEffect(() => {
|
||||
// Kick off the expensive hydration of the current sharedGroupNames
|
||||
updateSharedGroups();
|
||||
}, [updateSharedGroups]);
|
||||
updateSharedGroups(id);
|
||||
}, [id, updateSharedGroups]);
|
||||
|
||||
let avatarBlur: AvatarBlur = AvatarBlur.NoBlur;
|
||||
let avatarOnClick: undefined | (() => void);
|
||||
|
@ -148,7 +148,7 @@ export function ConversationHero({
|
|||
})
|
||||
) {
|
||||
avatarBlur = AvatarBlur.BlurPictureWithClickToView;
|
||||
avatarOnClick = unblurAvatar;
|
||||
avatarOnClick = () => unblurAvatar(id);
|
||||
} else if (hasStories) {
|
||||
avatarOnClick = () => {
|
||||
viewUserStories({
|
||||
|
|
|
@ -22,7 +22,6 @@ export function Default(): JSX.Element {
|
|||
i18n={i18n}
|
||||
sender={sender}
|
||||
inGroup={false}
|
||||
learnMoreAboutDeliveryIssue={action('learnMoreAboutDeliveryIssue')}
|
||||
onClose={action('onClose')}
|
||||
/>
|
||||
);
|
||||
|
@ -34,7 +33,6 @@ export function InGroup(): JSX.Element {
|
|||
i18n={i18n}
|
||||
sender={sender}
|
||||
inGroup
|
||||
learnMoreAboutDeliveryIssue={action('learnMoreAboutDeliveryIssue')}
|
||||
onClose={action('onClose')}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -12,17 +12,17 @@ import { Emojify } from './Emojify';
|
|||
import { useRestoreFocus } from '../../hooks/useRestoreFocus';
|
||||
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
||||
|
||||
export type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
sender: ConversationType;
|
||||
inGroup: boolean;
|
||||
learnMoreAboutDeliveryIssue: () => unknown;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function DeliveryIssueDialog(props: PropsType): React.ReactElement {
|
||||
const { i18n, inGroup, learnMoreAboutDeliveryIssue, sender, onClose } = props;
|
||||
const { i18n, inGroup, sender, onClose } = props;
|
||||
|
||||
const key = inGroup
|
||||
? 'DeliveryIssue--summary--group'
|
||||
|
@ -34,7 +34,11 @@ export function DeliveryIssueDialog(props: PropsType): React.ReactElement {
|
|||
const footer = (
|
||||
<>
|
||||
<Button
|
||||
onClick={learnMoreAboutDeliveryIssue}
|
||||
onClick={() =>
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/4404859745690'
|
||||
)
|
||||
}
|
||||
size={ButtonSize.Medium}
|
||||
variant={ButtonVariant.Secondary}
|
||||
>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
@ -18,12 +17,7 @@ const sender = getDefaultConversation();
|
|||
|
||||
export function Default(): JSX.Element {
|
||||
return (
|
||||
<DeliveryIssueNotification
|
||||
i18n={i18n}
|
||||
inGroup={false}
|
||||
learnMoreAboutDeliveryIssue={action('learnMoreAboutDeliveryIssue')}
|
||||
sender={sender}
|
||||
/>
|
||||
<DeliveryIssueNotification i18n={i18n} inGroup={false} sender={sender} />
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -33,7 +27,6 @@ export function WithALongName(): JSX.Element {
|
|||
<DeliveryIssueNotification
|
||||
i18n={i18n}
|
||||
inGroup={false}
|
||||
learnMoreAboutDeliveryIssue={action('learnMoreAboutDeliveryIssue')}
|
||||
sender={getDefaultConversation({
|
||||
firstName: longName,
|
||||
name: longName,
|
||||
|
@ -49,12 +42,5 @@ WithALongName.story = {
|
|||
};
|
||||
|
||||
export function InGroup(): JSX.Element {
|
||||
return (
|
||||
<DeliveryIssueNotification
|
||||
i18n={i18n}
|
||||
inGroup
|
||||
learnMoreAboutDeliveryIssue={action('learnMoreAboutDeliveryIssue')}
|
||||
sender={sender}
|
||||
/>
|
||||
);
|
||||
return <DeliveryIssueNotification i18n={i18n} inGroup sender={sender} />;
|
||||
}
|
||||
|
|
|
@ -18,22 +18,16 @@ export type PropsDataType = {
|
|||
inGroup: boolean;
|
||||
};
|
||||
|
||||
export type PropsActionsType = {
|
||||
learnMoreAboutDeliveryIssue: () => unknown;
|
||||
};
|
||||
|
||||
type PropsHousekeepingType = {
|
||||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export type PropsType = PropsDataType &
|
||||
PropsActionsType &
|
||||
PropsHousekeepingType;
|
||||
export type PropsType = PropsDataType & PropsHousekeepingType;
|
||||
|
||||
export function DeliveryIssueNotification(
|
||||
props: PropsType
|
||||
): ReactElement | null {
|
||||
const { i18n, inGroup, sender, learnMoreAboutDeliveryIssue } = props;
|
||||
const { i18n, inGroup, sender } = props;
|
||||
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
|
||||
|
||||
const openDialog = useCallback(() => {
|
||||
|
@ -74,7 +68,6 @@ export function DeliveryIssueNotification(
|
|||
<DeliveryIssueDialog
|
||||
i18n={i18n}
|
||||
inGroup={inGroup}
|
||||
learnMoreAboutDeliveryIssue={learnMoreAboutDeliveryIssue}
|
||||
sender={sender}
|
||||
onClose={closeDialog}
|
||||
/>
|
||||
|
|
|
@ -55,6 +55,7 @@ const renderChange = (
|
|||
<GroupV2Change
|
||||
areWeAdmin={areWeAdmin ?? true}
|
||||
blockGroupLinkRequests={action('blockGroupLinkRequests')}
|
||||
conversationId="some-conversation-id"
|
||||
change={change}
|
||||
groupBannedMemberships={groupBannedMemberships}
|
||||
groupMemberships={groupMemberships}
|
||||
|
|
|
@ -24,6 +24,7 @@ import { ConfirmationDialog } from '../ConfirmationDialog';
|
|||
|
||||
export type PropsDataType = {
|
||||
areWeAdmin: boolean;
|
||||
conversationId: string;
|
||||
groupMemberships?: Array<{
|
||||
uuid: UUIDStringType;
|
||||
isAdmin: boolean;
|
||||
|
@ -36,7 +37,10 @@ export type PropsDataType = {
|
|||
};
|
||||
|
||||
export type PropsActionsType = {
|
||||
blockGroupLinkRequests: (uuid: UUIDStringType) => unknown;
|
||||
blockGroupLinkRequests: (
|
||||
conversationId: string,
|
||||
uuid: UUIDStringType
|
||||
) => unknown;
|
||||
};
|
||||
|
||||
export type PropsHousekeepingType = {
|
||||
|
@ -130,6 +134,7 @@ function getIcon(
|
|||
function GroupV2Detail({
|
||||
areWeAdmin,
|
||||
blockGroupLinkRequests,
|
||||
conversationId,
|
||||
detail,
|
||||
isLastText,
|
||||
fromId,
|
||||
|
@ -143,7 +148,11 @@ function GroupV2Detail({
|
|||
text,
|
||||
}: {
|
||||
areWeAdmin: boolean;
|
||||
blockGroupLinkRequests: (uuid: UUIDStringType) => unknown;
|
||||
blockGroupLinkRequests: (
|
||||
conversationId: string,
|
||||
uuid: UUIDStringType
|
||||
) => unknown;
|
||||
conversationId: string;
|
||||
detail: GroupV2ChangeDetailType;
|
||||
isLastText: boolean;
|
||||
groupMemberships?: Array<{
|
||||
|
@ -209,7 +218,7 @@ function GroupV2Detail({
|
|||
title={i18n('PendingRequests--block--title')}
|
||||
actions={[
|
||||
{
|
||||
action: () => blockGroupLinkRequests(detail.uuid),
|
||||
action: () => blockGroupLinkRequests(conversationId, detail.uuid),
|
||||
text: i18n('PendingRequests--block--confirm'),
|
||||
style: 'affirmative',
|
||||
},
|
||||
|
@ -282,6 +291,7 @@ export function GroupV2Change(props: PropsType): ReactElement {
|
|||
areWeAdmin,
|
||||
blockGroupLinkRequests,
|
||||
change,
|
||||
conversationId,
|
||||
groupBannedMemberships,
|
||||
groupMemberships,
|
||||
groupName,
|
||||
|
@ -304,6 +314,7 @@ export function GroupV2Change(props: PropsType): ReactElement {
|
|||
<GroupV2Detail
|
||||
areWeAdmin={areWeAdmin}
|
||||
blockGroupLinkRequests={blockGroupLinkRequests}
|
||||
conversationId={conversationId}
|
||||
detail={detail}
|
||||
isLastText={isLastText}
|
||||
fromId={change.from}
|
||||
|
|
|
@ -12,8 +12,9 @@ import enMessages from '../../../_locales/en/messages.json';
|
|||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const createProps = (): GroupV2PendingApprovalActionsPropsType => ({
|
||||
cancelJoinRequest: action('cancelJoinRequest'),
|
||||
conversationId: 'some-random-id',
|
||||
i18n,
|
||||
onCancelJoinRequest: action('onCancelJoinRequest'),
|
||||
});
|
||||
|
||||
export default {
|
||||
|
|
|
@ -3,16 +3,21 @@
|
|||
|
||||
import * as React from 'react';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
|
||||
export type PropsType = {
|
||||
conversationId: string;
|
||||
i18n: LocalizerType;
|
||||
onCancelJoinRequest: () => unknown;
|
||||
cancelJoinRequest: (conversationId: string) => unknown;
|
||||
};
|
||||
|
||||
export function GroupV2PendingApprovalActions({
|
||||
cancelJoinRequest,
|
||||
conversationId,
|
||||
i18n,
|
||||
onCancelJoinRequest,
|
||||
}: PropsType): JSX.Element {
|
||||
const [isConfirming, setIsConfirming] = React.useState(false);
|
||||
|
||||
return (
|
||||
<div className="module-group-v2-pending-approval-actions">
|
||||
<p className="module-group-v2-pending-approval-actions__message">
|
||||
|
@ -21,13 +26,30 @@ export function GroupV2PendingApprovalActions({
|
|||
<div className="module-group-v2-pending-approval-actions__buttons">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancelJoinRequest}
|
||||
onClick={() => setIsConfirming(true)}
|
||||
tabIndex={0}
|
||||
className="module-group-v2-pending-approval-actions__buttons__button"
|
||||
>
|
||||
{i18n('GroupV2--join--cancel-request-to-join')}
|
||||
</button>
|
||||
</div>
|
||||
{isConfirming ? (
|
||||
<ConfirmationDialog
|
||||
actions={[
|
||||
{
|
||||
text: i18n('GroupV2--join--cancel-request-to-join--yes'),
|
||||
style: 'negative',
|
||||
action: () => cancelJoinRequest(conversationId),
|
||||
},
|
||||
]}
|
||||
cancelText={i18n('GroupV2--join--cancel-request-to-join--no')}
|
||||
dialogName="GroupV2CancelRequestToJoin"
|
||||
i18n={i18n}
|
||||
onClose={() => setIsConfirming(false)}
|
||||
>
|
||||
{i18n('GroupV2--join--cancel-request-to-join--confirmation')}
|
||||
</ConfirmationDialog>
|
||||
) : undefined}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -167,7 +167,6 @@ export type AudioAttachmentProps = {
|
|||
id: string;
|
||||
conversationId: string;
|
||||
played: boolean;
|
||||
showMessageDetail: (id: string) => void;
|
||||
status?: MessageStatusType;
|
||||
textPending?: boolean;
|
||||
timestamp: number;
|
||||
|
@ -302,8 +301,6 @@ export type PropsActions = {
|
|||
messageExpanded: (id: string, displayLimit: number) => unknown;
|
||||
checkForAccount: (phoneNumber: string) => unknown;
|
||||
|
||||
showMessageDetail: (id: string) => void;
|
||||
|
||||
startConversation: (e164: string, uuid: UUIDStringType) => void;
|
||||
showConversation: ShowConversationType;
|
||||
openGiftBadge: (messageId: string) => void;
|
||||
|
@ -327,6 +324,7 @@ export type PropsActions = {
|
|||
|
||||
scrollToQuotedMessage: (options: {
|
||||
authorId: string;
|
||||
conversationId: string;
|
||||
sentAt: number;
|
||||
}) => void;
|
||||
selectMessage?: (messageId: string, conversationId: string) => unknown;
|
||||
|
@ -752,6 +750,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const {
|
||||
conversationId,
|
||||
deletedForEveryone,
|
||||
direction,
|
||||
expirationLength,
|
||||
|
@ -760,17 +759,18 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isTapToViewExpired,
|
||||
status,
|
||||
i18n,
|
||||
pushPanelForConversation,
|
||||
text,
|
||||
textAttachment,
|
||||
timestamp,
|
||||
id,
|
||||
showMessageDetail,
|
||||
} = this.props;
|
||||
|
||||
const isStickerLike = isSticker || this.canRenderStickerLikeEmoji();
|
||||
|
||||
return (
|
||||
<MessageMetadata
|
||||
conversationId={conversationId}
|
||||
deletedForEveryone={deletedForEveryone}
|
||||
direction={direction}
|
||||
expirationLength={expirationLength}
|
||||
|
@ -783,7 +783,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isSticker={isStickerLike}
|
||||
isTapToViewExpired={isTapToViewExpired}
|
||||
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
|
||||
showMessageDetail={showMessageDetail}
|
||||
pushPanelForConversation={pushPanelForConversation}
|
||||
status={status}
|
||||
textPending={textAttachment?.pending}
|
||||
timestamp={timestamp}
|
||||
|
@ -841,7 +841,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
reducedMotion,
|
||||
renderAudioAttachment,
|
||||
renderingContext,
|
||||
showMessageDetail,
|
||||
showLightbox,
|
||||
shouldCollapseAbove,
|
||||
shouldCollapseBelow,
|
||||
|
@ -967,7 +966,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
id,
|
||||
conversationId,
|
||||
played,
|
||||
showMessageDetail,
|
||||
status,
|
||||
textPending: textAttachment?.pending,
|
||||
timestamp,
|
||||
|
@ -1453,6 +1451,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
public renderQuote(): JSX.Element | null {
|
||||
const {
|
||||
conversationColor,
|
||||
conversationId,
|
||||
conversationTitle,
|
||||
customColor,
|
||||
direction,
|
||||
|
@ -1475,6 +1474,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
: () => {
|
||||
scrollToQuotedMessage({
|
||||
authorId: quote.authorId,
|
||||
conversationId,
|
||||
sentAt: quote.sentAt,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ import type { ComputePeaksResult } from '../GlobalAudioContext';
|
|||
import { MessageMetadata } from './MessageMetadata';
|
||||
import * as log from '../../logging/log';
|
||||
import type { ActiveAudioPlayerStateType } from '../../state/ducks/audioPlayer';
|
||||
import type { PushPanelForConversationActionType } from '../../state/ducks/conversations';
|
||||
|
||||
export type OwnProps = Readonly<{
|
||||
active: ActiveAudioPlayerStateType | undefined;
|
||||
|
@ -34,7 +35,6 @@ export type OwnProps = Readonly<{
|
|||
id: string;
|
||||
conversationId: string;
|
||||
played: boolean;
|
||||
showMessageDetail: (id: string) => void;
|
||||
status?: MessageStatusType;
|
||||
textPending?: boolean;
|
||||
timestamp: number;
|
||||
|
@ -51,6 +51,7 @@ export type DispatchProps = Readonly<{
|
|||
position: number,
|
||||
isConsecutive: boolean
|
||||
) => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
setCurrentTime: (currentTime: number) => void;
|
||||
setPlaybackRate: (conversationId: string, rate: number) => void;
|
||||
setIsPlaying: (value: boolean) => void;
|
||||
|
@ -263,7 +264,6 @@ export function MessageAudio(props: Props): JSX.Element {
|
|||
expirationTimestamp,
|
||||
id,
|
||||
played,
|
||||
showMessageDetail,
|
||||
status,
|
||||
textPending,
|
||||
timestamp,
|
||||
|
@ -273,6 +273,7 @@ export function MessageAudio(props: Props): JSX.Element {
|
|||
computePeaks,
|
||||
setPlaybackRate,
|
||||
loadAndPlayMessageAudio,
|
||||
pushPanelForConversation,
|
||||
setCurrentTime,
|
||||
setIsPlaying,
|
||||
} = props;
|
||||
|
@ -591,6 +592,7 @@ export function MessageAudio(props: Props): JSX.Element {
|
|||
|
||||
{!withContentBelow && !collapseMetadata && (
|
||||
<MessageMetadata
|
||||
conversationId={conversationId}
|
||||
direction={direction}
|
||||
expirationLength={expirationLength}
|
||||
expirationTimestamp={expirationTimestamp}
|
||||
|
@ -600,7 +602,7 @@ export function MessageAudio(props: Props): JSX.Element {
|
|||
isShowingImage={false}
|
||||
isSticker={false}
|
||||
isTapToViewExpired={false}
|
||||
showMessageDetail={showMessageDetail}
|
||||
pushPanelForConversation={pushPanelForConversation}
|
||||
status={status}
|
||||
textPending={textPending}
|
||||
timestamp={timestamp}
|
||||
|
|
|
@ -68,18 +68,9 @@ export type PropsData = {
|
|||
i18n: LocalizerType;
|
||||
theme: ThemeType;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
} & Pick<
|
||||
MessagePropsType,
|
||||
| 'getPreferredBadge'
|
||||
| 'interactionMode'
|
||||
| 'expirationLength'
|
||||
| 'expirationTimestamp'
|
||||
>;
|
||||
} & Pick<MessagePropsType, 'getPreferredBadge' | 'interactionMode'>;
|
||||
|
||||
export type PropsBackboneActions = Pick<
|
||||
MessagePropsType,
|
||||
'renderAudioAttachment' | 'startConversation'
|
||||
>;
|
||||
export type PropsSmartActions = Pick<MessagePropsType, 'renderAudioAttachment'>;
|
||||
|
||||
export type PropsReduxActions = Pick<
|
||||
MessagePropsType,
|
||||
|
@ -97,13 +88,13 @@ export type PropsReduxActions = Pick<
|
|||
| 'showExpiredOutgoingTapToViewToast'
|
||||
| 'showLightbox'
|
||||
| 'showLightboxForViewOnceMedia'
|
||||
| 'startConversation'
|
||||
| 'viewStory'
|
||||
> & {
|
||||
toggleSafetyNumberModal: (contactId: string) => void;
|
||||
};
|
||||
|
||||
export type ExternalProps = PropsData & PropsBackboneActions;
|
||||
export type Props = PropsData & PropsBackboneActions & PropsReduxActions;
|
||||
export type Props = PropsData & PropsSmartActions & PropsReduxActions;
|
||||
|
||||
const contactSortCollator = new Intl.Collator();
|
||||
|
||||
|
@ -280,7 +271,6 @@ export class MessageDetail extends React.Component<Props> {
|
|||
contactNameColor,
|
||||
showLightboxForViewOnceMedia,
|
||||
doubleCheckMissingQuoteReference,
|
||||
expirationTimestamp,
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
interactionMode,
|
||||
|
@ -300,8 +290,8 @@ export class MessageDetail extends React.Component<Props> {
|
|||
viewStory,
|
||||
} = this.props;
|
||||
|
||||
const timeRemaining = expirationTimestamp
|
||||
? DurationInSeconds.fromMillis(expirationTimestamp - Date.now())
|
||||
const timeRemaining = message.expirationTimestamp
|
||||
? DurationInSeconds.fromMillis(message.expirationTimestamp - Date.now())
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
|
@ -348,9 +338,6 @@ export class MessageDetail extends React.Component<Props> {
|
|||
showExpiredOutgoingTapToViewToast={
|
||||
showExpiredOutgoingTapToViewToast
|
||||
}
|
||||
showMessageDetail={() => {
|
||||
log.warn('MessageDetail: showMessageDetail called!');
|
||||
}}
|
||||
showLightbox={showLightbox}
|
||||
startConversation={startConversation}
|
||||
theme={theme}
|
||||
|
|
|
@ -11,8 +11,11 @@ import type { DirectionType, MessageStatusType } from './Message';
|
|||
import { ExpireTimer } from './ExpireTimer';
|
||||
import { MessageTimestamp } from './MessageTimestamp';
|
||||
import { Spinner } from '../Spinner';
|
||||
import type { PushPanelForConversationActionType } from '../../state/ducks/conversations';
|
||||
import { PanelType } from '../../types/Panels';
|
||||
|
||||
type PropsType = {
|
||||
conversationId: string;
|
||||
deletedForEveryone?: boolean;
|
||||
direction: DirectionType;
|
||||
expirationLength?: number;
|
||||
|
@ -25,13 +28,14 @@ type PropsType = {
|
|||
isSticker?: boolean;
|
||||
isTapToViewExpired?: boolean;
|
||||
onWidthMeasured?: (width: number) => unknown;
|
||||
showMessageDetail: (id: string) => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
status?: MessageStatusType;
|
||||
textPending?: boolean;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
export function MessageMetadata({
|
||||
conversationId,
|
||||
deletedForEveryone,
|
||||
direction,
|
||||
expirationLength,
|
||||
|
@ -44,7 +48,7 @@ export function MessageMetadata({
|
|||
isSticker,
|
||||
isTapToViewExpired,
|
||||
onWidthMeasured,
|
||||
showMessageDetail,
|
||||
pushPanelForConversation,
|
||||
status,
|
||||
textPending,
|
||||
timestamp,
|
||||
|
@ -76,7 +80,10 @@ export function MessageMetadata({
|
|||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
showMessageDetail(id);
|
||||
pushPanelForConversation(conversationId, {
|
||||
type: PanelType.MessageDetails,
|
||||
args: { messageId: id },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{deletedForEveryone
|
||||
|
|
|
@ -137,7 +137,6 @@ const defaultMessageProps: TimelineMessagesProps = {
|
|||
'showExpiredOutgoingTapToViewToast'
|
||||
),
|
||||
toggleForwardMessageModal: action('default--toggleForwardMessageModal'),
|
||||
showMessageDetail: action('default--showMessageDetail'),
|
||||
showLightbox: action('default--showLightbox'),
|
||||
startConversation: action('default--startConversation'),
|
||||
status: 'sent',
|
||||
|
|
|
@ -266,7 +266,6 @@ const actions = () => ({
|
|||
'clearInvitedUuidsForNewlyCreatedGroup'
|
||||
),
|
||||
setIsNearBottom: action('setIsNearBottom'),
|
||||
learnMoreAboutDeliveryIssue: action('learnMoreAboutDeliveryIssue'),
|
||||
loadOlderMessages: action('loadOlderMessages'),
|
||||
loadNewerMessages: action('loadNewerMessages'),
|
||||
loadNewestMessages: action('loadNewestMessages'),
|
||||
|
@ -281,7 +280,6 @@ const actions = () => ({
|
|||
retryMessageSend: action('retryMessageSend'),
|
||||
deleteMessage: action('deleteMessage'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
showMessageDetail: action('showMessageDetail'),
|
||||
saveAttachment: action('saveAttachment'),
|
||||
pushPanelForConversation: action('pushPanelForConversation'),
|
||||
showContactDetail: action('showContactDetail'),
|
||||
|
@ -310,20 +308,12 @@ const actions = () => ({
|
|||
startConversation: action('startConversation'),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
|
||||
contactSupport: action('contactSupport'),
|
||||
|
||||
closeContactSpoofingReview: action('closeContactSpoofingReview'),
|
||||
reviewGroupMemberNameCollision: action('reviewGroupMemberNameCollision'),
|
||||
reviewMessageRequestNameCollision: action(
|
||||
'reviewMessageRequestNameCollision'
|
||||
),
|
||||
|
||||
acceptConversation: action('acceptConversation'),
|
||||
blockAndReportSpam: action('blockAndReportSpam'),
|
||||
blockConversation: action('blockConversation'),
|
||||
deleteConversation: action('deleteConversation'),
|
||||
removeMember: action('removeMember'),
|
||||
|
||||
unblurAvatar: action('unblurAvatar'),
|
||||
|
||||
peekGroupCallForTheFirstTime: action('peekGroupCallForTheFirstTime'),
|
||||
|
@ -371,10 +361,23 @@ const renderItem = ({
|
|||
const renderContactSpoofingReviewDialog = (
|
||||
props: SmartContactSpoofingReviewDialogPropsType
|
||||
) => {
|
||||
const sharedProps = {
|
||||
acceptConversation: action('acceptConversation'),
|
||||
blockAndReportSpam: action('blockAndReportSpam'),
|
||||
blockConversation: action('blockConversation'),
|
||||
deleteConversation: action('deleteConversation'),
|
||||
getPreferredBadge: () => undefined,
|
||||
i18n,
|
||||
removeMember: action('removeMember'),
|
||||
showContactModal: action('showContactModal'),
|
||||
theme: ThemeType.dark,
|
||||
};
|
||||
|
||||
if (props.type === ContactSpoofingType.MultipleGroupMembersWithSameTitle) {
|
||||
return (
|
||||
<ContactSpoofingReviewDialog
|
||||
{...props}
|
||||
{...sharedProps}
|
||||
group={{
|
||||
...getDefaultConversation(),
|
||||
areWeAdmin: true,
|
||||
|
@ -383,7 +386,7 @@ const renderContactSpoofingReviewDialog = (
|
|||
);
|
||||
}
|
||||
|
||||
return <ContactSpoofingReviewDialog {...props} />;
|
||||
return <ContactSpoofingReviewDialog {...props} {...sharedProps} />;
|
||||
};
|
||||
|
||||
const getAbout = () => text('about', '👍 Free to chat');
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
// Copyright 2019-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { first, get, isNumber, last, pick, throttle } from 'lodash';
|
||||
import { first, get, isNumber, last, throttle } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactChild, ReactNode, RefObject } from 'react';
|
||||
import React from 'react';
|
||||
import { createSelector } from 'reselect';
|
||||
import Measure from 'react-measure';
|
||||
|
||||
import { ScrollDownButton } from './ScrollDownButton';
|
||||
|
||||
import type { AssertProps, LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
|
||||
import { assertDev, strictAssert } from '../../util/assert';
|
||||
|
@ -18,8 +17,6 @@ import { missingCaseError } from '../../util/missingCaseError';
|
|||
import { clearTimeoutIfNecessary } from '../../util/clearTimeoutIfNecessary';
|
||||
import { WidthBreakpoint } from '../_util';
|
||||
|
||||
import type { PropsActions as MessageActionsType } from './TimelineMessage';
|
||||
import type { PropsActionsType as ChatSessionRefreshedNotificationActionsType } from './ChatSessionRefreshedNotification';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import { Intl } from '../Intl';
|
||||
import { TimelineWarning } from './TimelineWarning';
|
||||
|
@ -44,8 +41,6 @@ import {
|
|||
} from '../../util/scrollUtil';
|
||||
import { LastSeenIndicator } from './LastSeenIndicator';
|
||||
import { MINUTE } from '../../util/durations';
|
||||
import type { PropsActionsType as DeliveryIssueNotificationActionsType } from './DeliveryIssueNotification';
|
||||
import type { PropsActionsType as GroupV2ChangeActionsType } from './GroupV2Change';
|
||||
|
||||
const AT_BOTTOM_THRESHOLD = 15;
|
||||
const AT_BOTTOM_DETECTOR_STYLE = { height: AT_BOTTOM_THRESHOLD };
|
||||
|
@ -124,7 +119,6 @@ type PropsHousekeepingType = {
|
|||
theme: ThemeType;
|
||||
|
||||
renderItem: (props: {
|
||||
actionProps: PropsActionsFromBackboneForChildrenType;
|
||||
containerElementRef: RefObject<HTMLElement>;
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
conversationId: string;
|
||||
|
@ -134,46 +128,31 @@ type PropsHousekeepingType = {
|
|||
previousMessageId: undefined | string;
|
||||
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
||||
}) => JSX.Element;
|
||||
renderHeroRow: (
|
||||
id: string,
|
||||
unblurAvatar: () => void,
|
||||
updateSharedGroups: () => unknown
|
||||
) => JSX.Element;
|
||||
renderHeroRow: (id: string) => JSX.Element;
|
||||
renderTypingBubble: (id: string) => JSX.Element;
|
||||
renderContactSpoofingReviewDialog: (
|
||||
props: SmartContactSpoofingReviewDialogPropsType
|
||||
) => JSX.Element;
|
||||
};
|
||||
|
||||
export type PropsActionsFromBackboneForChildrenType = Pick<
|
||||
MessageActionsType,
|
||||
'scrollToQuotedMessage' | 'showMessageDetail' | 'startConversation'
|
||||
> &
|
||||
ChatSessionRefreshedNotificationActionsType &
|
||||
DeliveryIssueNotificationActionsType &
|
||||
GroupV2ChangeActionsType;
|
||||
|
||||
export type PropsActionsType = {
|
||||
// From Backbone
|
||||
acknowledgeGroupMemberNameCollisions: (
|
||||
conversationId: string,
|
||||
groupNameCollisions: Readonly<GroupNameCollisionsWithIdsByTitle>
|
||||
) => void;
|
||||
loadOlderMessages: (messageId: string) => unknown;
|
||||
loadNewerMessages: (messageId: string) => unknown;
|
||||
loadNewestMessages: (messageId: string, setFocus?: boolean) => unknown;
|
||||
markMessageRead: (messageId: string) => unknown;
|
||||
removeMember: (conversationId: string) => unknown;
|
||||
unblurAvatar: () => void;
|
||||
updateSharedGroups: () => unknown;
|
||||
|
||||
// From Redux
|
||||
acceptConversation: (conversationId: string) => unknown;
|
||||
blockConversation: (conversationId: string) => unknown;
|
||||
blockAndReportSpam: (conversationId: string) => unknown;
|
||||
clearInvitedUuidsForNewlyCreatedGroup: () => void;
|
||||
clearSelectedMessage: () => unknown;
|
||||
closeContactSpoofingReview: () => void;
|
||||
deleteConversation: (conversationId: string) => unknown;
|
||||
loadOlderMessages: (conversationId: string, messageId: string) => unknown;
|
||||
loadNewerMessages: (conversationId: string, messageId: string) => unknown;
|
||||
loadNewestMessages: (
|
||||
conversationId: string,
|
||||
messageId: string,
|
||||
setFocus?: boolean
|
||||
) => unknown;
|
||||
markMessageRead: (conversationId: string, messageId: string) => unknown;
|
||||
selectMessage: (messageId: string, conversationId: string) => unknown;
|
||||
setIsNearBottom: (conversationId: string, isNearBottom: boolean) => unknown;
|
||||
peekGroupCallForTheFirstTime: (conversationId: string) => unknown;
|
||||
peekGroupCallIfItHasMembers: (conversationId: string) => unknown;
|
||||
|
@ -183,9 +162,7 @@ export type PropsActionsType = {
|
|||
safeConversationId: string;
|
||||
}>
|
||||
) => void;
|
||||
selectMessage: (messageId: string, conversationId: string) => unknown;
|
||||
showContactModal: (contactId: string, conversationId?: string) => void;
|
||||
} & PropsActionsFromBackboneForChildrenType;
|
||||
};
|
||||
|
||||
export type PropsType = PropsDataType &
|
||||
PropsHousekeepingType &
|
||||
|
@ -209,39 +186,6 @@ type SnapshotType =
|
|||
| { scrollTop: number }
|
||||
| { scrollBottom: number };
|
||||
|
||||
const getActions = createSelector(
|
||||
// It is expensive to pick so many properties out of the `props` object so we
|
||||
// use `createSelector` to memoize them by the last seen `props` object.
|
||||
(props: PropsType) => props,
|
||||
|
||||
(props: PropsType): PropsActionsFromBackboneForChildrenType => {
|
||||
// Note: Because TimelineItem is smart, we only need to include action creators here
|
||||
// which are passed in from backbone and not available via mapDispatchToProps
|
||||
const unsafe = pick(props, [
|
||||
// MessageActionsType
|
||||
'scrollToQuotedMessage',
|
||||
'showMessageDetail',
|
||||
'startConversation',
|
||||
|
||||
// ChatSessionRefreshedNotificationActionsType
|
||||
'contactSupport',
|
||||
|
||||
// DeliveryIssueNotificationActionsType
|
||||
'learnMoreAboutDeliveryIssue',
|
||||
|
||||
// GroupV2ChangeActionsType
|
||||
'blockGroupLinkRequests',
|
||||
]);
|
||||
|
||||
const safe: AssertProps<
|
||||
PropsActionsFromBackboneForChildrenType,
|
||||
typeof unsafe
|
||||
> = unsafe;
|
||||
|
||||
return safe;
|
||||
}
|
||||
);
|
||||
|
||||
export class Timeline extends React.Component<
|
||||
PropsType,
|
||||
StateType,
|
||||
|
@ -348,7 +292,7 @@ export class Timeline extends React.Component<
|
|||
} else {
|
||||
const lastId = last(items);
|
||||
if (lastId) {
|
||||
loadNewestMessages(lastId, setFocus);
|
||||
loadNewestMessages(id, lastId, setFocus);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -472,7 +416,7 @@ export class Timeline extends React.Component<
|
|||
maxRowIndex >= 0 &&
|
||||
rowIndex >= maxRowIndex - LOAD_NEWER_THRESHOLD
|
||||
) {
|
||||
loadNewerMessages(newestBottomVisibleMessageId);
|
||||
loadNewerMessages(id, newestBottomVisibleMessageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -482,7 +426,7 @@ export class Timeline extends React.Component<
|
|||
oldestPartiallyVisibleMessageId &&
|
||||
oldestPartiallyVisibleMessageId === items[0]
|
||||
) {
|
||||
loadOlderMessages(oldestPartiallyVisibleMessageId);
|
||||
loadOlderMessages(id, oldestPartiallyVisibleMessageId);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -522,10 +466,10 @@ export class Timeline extends React.Component<
|
|||
}
|
||||
|
||||
private markNewestBottomVisibleMessageRead = throttle((): void => {
|
||||
const { markMessageRead } = this.props;
|
||||
const { id, markMessageRead } = this.props;
|
||||
const { newestBottomVisibleMessageId } = this.state;
|
||||
if (newestBottomVisibleMessageId) {
|
||||
markMessageRead(newestBottomVisibleMessageId);
|
||||
markMessageRead(id, newestBottomVisibleMessageId);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
|
@ -792,14 +736,10 @@ export class Timeline extends React.Component<
|
|||
|
||||
public override render(): JSX.Element | null {
|
||||
const {
|
||||
acceptConversation,
|
||||
acknowledgeGroupMemberNameCollisions,
|
||||
blockAndReportSpam,
|
||||
blockConversation,
|
||||
clearInvitedUuidsForNewlyCreatedGroup,
|
||||
closeContactSpoofingReview,
|
||||
contactSpoofingReview,
|
||||
deleteConversation,
|
||||
getPreferredBadge,
|
||||
getTimestampForMessage,
|
||||
haveNewest,
|
||||
|
@ -813,19 +753,15 @@ export class Timeline extends React.Component<
|
|||
items,
|
||||
messageLoadingState,
|
||||
oldestUnseenIndex,
|
||||
removeMember,
|
||||
renderContactSpoofingReviewDialog,
|
||||
renderHeroRow,
|
||||
renderItem,
|
||||
renderTypingBubble,
|
||||
reviewGroupMemberNameCollision,
|
||||
reviewMessageRequestNameCollision,
|
||||
showContactModal,
|
||||
theme,
|
||||
totalUnseen,
|
||||
unblurAvatar,
|
||||
unreadCount,
|
||||
updateSharedGroups,
|
||||
} = this.props;
|
||||
const {
|
||||
hasRecentlyScrolled,
|
||||
|
@ -866,8 +802,6 @@ export class Timeline extends React.Component<
|
|||
(areUnreadBelowCurrentPosition || areSomeMessagesBelowCurrentPosition)
|
||||
);
|
||||
|
||||
const actionProps = getActions(this.props);
|
||||
|
||||
let floatingHeader: ReactNode;
|
||||
// It's possible that a message was removed from `items` but we still have its ID in
|
||||
// state. `getTimestampForMessage` might return undefined in that case.
|
||||
|
@ -938,7 +872,6 @@ export class Timeline extends React.Component<
|
|||
>
|
||||
<ErrorBoundary i18n={i18n} showDebugLog={showDebugLog}>
|
||||
{renderItem({
|
||||
actionProps,
|
||||
containerElementRef: this.containerRef,
|
||||
containerWidthBreakpoint: widthBreakpoint,
|
||||
conversationId: id,
|
||||
|
@ -1011,7 +944,7 @@ export class Timeline extends React.Component<
|
|||
/>
|
||||
);
|
||||
onClose = () => {
|
||||
acknowledgeGroupMemberNameCollisions(groupNameCollisions);
|
||||
acknowledgeGroupMemberNameCollisions(id, groupNameCollisions);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
@ -1047,16 +980,8 @@ export class Timeline extends React.Component<
|
|||
let contactSpoofingReviewDialog: ReactNode;
|
||||
if (contactSpoofingReview) {
|
||||
const commonProps = {
|
||||
acceptConversation,
|
||||
blockAndReportSpam,
|
||||
blockConversation,
|
||||
deleteConversation,
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
conversationId: id,
|
||||
onClose: closeContactSpoofingReview,
|
||||
onShowContactModal: showContactModal,
|
||||
removeMember,
|
||||
theme,
|
||||
};
|
||||
|
||||
switch (contactSpoofingReview.type) {
|
||||
|
@ -1138,7 +1063,7 @@ export class Timeline extends React.Component<
|
|||
{Timeline.getWarning(this.props, this.state) && (
|
||||
<div style={{ height: lastMeasuredWarningHeight }} />
|
||||
)}
|
||||
{renderHeroRow(id, unblurAvatar, updateSharedGroups)}
|
||||
{renderHeroRow(id)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
@ -65,7 +65,6 @@ const getDefaultProps = () => ({
|
|||
reactToMessage: action('reactToMessage'),
|
||||
checkForAccount: action('checkForAccount'),
|
||||
clearSelectedMessage: action('clearSelectedMessage'),
|
||||
contactSupport: action('contactSupport'),
|
||||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
retryMessageSend: action('retryMessageSend'),
|
||||
|
@ -73,10 +72,8 @@ const getDefaultProps = () => ({
|
|||
deleteMessage: action('deleteMessage'),
|
||||
deleteMessageForEveryone: action('deleteMessageForEveryone'),
|
||||
kickOffAttachmentDownload: action('kickOffAttachmentDownload'),
|
||||
learnMoreAboutDeliveryIssue: action('learnMoreAboutDeliveryIssue'),
|
||||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
messageExpanded: action('messageExpanded'),
|
||||
showMessageDetail: action('showMessageDetail'),
|
||||
showConversation: action('showConversation'),
|
||||
openGiftBadge: action('openGiftBadge'),
|
||||
saveAttachment: action('saveAttachment'),
|
||||
|
|
|
@ -15,12 +15,8 @@ import type {
|
|||
} from './TimelineMessage';
|
||||
import type { PropsActionsType as CallingNotificationActionsType } from './CallingNotification';
|
||||
import { CallingNotification } from './CallingNotification';
|
||||
import type { PropsActionsType as PropsChatSessionRefreshedActionsType } from './ChatSessionRefreshedNotification';
|
||||
import { ChatSessionRefreshedNotification } from './ChatSessionRefreshedNotification';
|
||||
import type {
|
||||
PropsActionsType as DeliveryIssueActionProps,
|
||||
PropsDataType as DeliveryIssueProps,
|
||||
} from './DeliveryIssueNotification';
|
||||
import type { PropsDataType as DeliveryIssueProps } from './DeliveryIssueNotification';
|
||||
import { DeliveryIssueNotification } from './DeliveryIssueNotification';
|
||||
import type { PropsData as ChangeNumberNotificationProps } from './ChangeNumberNotification';
|
||||
import { ChangeNumberNotification } from './ChangeNumberNotification';
|
||||
|
@ -171,9 +167,7 @@ type PropsLocalType = {
|
|||
|
||||
type PropsActionsType = MessageActionsType &
|
||||
CallingNotificationActionsType &
|
||||
DeliveryIssueActionProps &
|
||||
GroupV2ChangeActionsType &
|
||||
PropsChatSessionRefreshedActionsType &
|
||||
SafetyNumberActionsType;
|
||||
|
||||
export type PropsType = PropsLocalType &
|
||||
|
|
|
@ -202,15 +202,17 @@ function MessageAudioContainer({
|
|||
return (
|
||||
<MessageAudio
|
||||
{...props}
|
||||
id="storybook"
|
||||
renderingContext="storybook"
|
||||
computePeaks={computePeaks}
|
||||
conversationId="some-conversation-id"
|
||||
active={active}
|
||||
played={_played}
|
||||
computePeaks={computePeaks}
|
||||
id="storybook"
|
||||
loadAndPlayMessageAudio={loadAndPlayMessageAudio}
|
||||
played={_played}
|
||||
pushPanelForConversation={action('pushPanelForConversation')}
|
||||
renderingContext="storybook"
|
||||
setCurrentTime={setCurrentTimeAction}
|
||||
setIsPlaying={setIsPlayingAction}
|
||||
setPlaybackRate={setPlaybackRateAction}
|
||||
setCurrentTime={setCurrentTimeAction}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -315,7 +317,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
'showExpiredOutgoingTapToViewToast'
|
||||
),
|
||||
toggleForwardMessageModal: action('toggleForwardMessageModal'),
|
||||
showMessageDetail: action('showMessageDetail'),
|
||||
showLightbox: action('showLightbox'),
|
||||
startConversation: action('startConversation'),
|
||||
status: overrideProps.status || 'sent',
|
||||
|
|
|
@ -27,6 +27,8 @@ import { doesMessageBodyOverflow } from './MessageBodyReadMore';
|
|||
import type { Props as ReactionPickerProps } from './ReactionPicker';
|
||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
import { useToggleReactionPicker } from '../../hooks/useKeyboardShortcuts';
|
||||
import { PanelType } from '../../types/Panels';
|
||||
import type { PushPanelForConversationActionType } from '../../state/ducks/conversations';
|
||||
|
||||
export type PropsData = {
|
||||
canDownload: boolean;
|
||||
|
@ -45,6 +47,7 @@ export type PropsActions = {
|
|||
}) => void;
|
||||
deleteMessageForEveryone: (id: string) => void;
|
||||
toggleForwardMessageModal: (id: string) => void;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
reactToMessage: (
|
||||
id: string,
|
||||
{ emoji, remove }: { emoji: string; remove: boolean }
|
||||
|
@ -95,6 +98,7 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
isSelected,
|
||||
isSticker,
|
||||
isTapToView,
|
||||
pushPanelForConversation,
|
||||
reactToMessage,
|
||||
setQuoteByMessageId,
|
||||
renderReactionPicker,
|
||||
|
@ -103,7 +107,6 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
retryDeleteForEveryone,
|
||||
selectedReaction,
|
||||
toggleForwardMessageModal,
|
||||
showMessageDetail,
|
||||
text,
|
||||
timestamp,
|
||||
kickOffAttachmentDownload,
|
||||
|
@ -406,7 +409,12 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
onDeleteForEveryone={
|
||||
canDeleteForEveryone ? () => setHasDOEConfirmation(true) : undefined
|
||||
}
|
||||
onMoreInfo={() => showMessageDetail(id)}
|
||||
onMoreInfo={() =>
|
||||
pushPanelForConversation(conversationId, {
|
||||
type: PanelType.MessageDetails,
|
||||
args: { messageId: id },
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue