Removes Inbox Backbone view
This commit is contained in:
parent
603b76c3d9
commit
aa23c2def2
44 changed files with 496 additions and 808 deletions
|
@ -48,30 +48,35 @@ export const App = ({
|
|||
appView,
|
||||
cancelConversationVerification,
|
||||
conversationsStoppingSend,
|
||||
hasInitialLoadCompleted,
|
||||
executeMenuAction,
|
||||
executeMenuRole,
|
||||
getPreferredBadge,
|
||||
hasInitialLoadCompleted,
|
||||
i18n,
|
||||
isCustomizingPreferredReactions,
|
||||
isShowingStoriesView,
|
||||
isMaximized,
|
||||
isFullScreen,
|
||||
isMaximized,
|
||||
isShowingStoriesView,
|
||||
isWindows11,
|
||||
menuOptions,
|
||||
platform,
|
||||
localeMessages,
|
||||
menuOptions,
|
||||
openInbox,
|
||||
platform,
|
||||
registerSingleDevice,
|
||||
renderCallManager,
|
||||
renderCustomizingPreferredReactionsModal,
|
||||
renderGlobalModalContainer,
|
||||
renderLeftPane,
|
||||
renderSafetyNumber,
|
||||
openInbox,
|
||||
renderStories,
|
||||
requestVerification,
|
||||
registerSingleDevice,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
showConversation,
|
||||
showWhatsNewModal,
|
||||
theme,
|
||||
verifyConversationsStoppingSend,
|
||||
executeMenuAction,
|
||||
executeMenuRole,
|
||||
titleBarDoubleClick,
|
||||
verifyConversationsStoppingSend,
|
||||
}: PropsType): JSX.Element => {
|
||||
let contents;
|
||||
|
||||
|
@ -101,7 +106,12 @@ export const App = ({
|
|||
renderCustomizingPreferredReactionsModal={
|
||||
renderCustomizingPreferredReactionsModal
|
||||
}
|
||||
renderLeftPane={renderLeftPane}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
selectedConversationId={selectedConversationId}
|
||||
selectedMessage={selectedMessage}
|
||||
showConversation={showConversation}
|
||||
showWhatsNewModal={showWhatsNewModal}
|
||||
theme={theme}
|
||||
verifyConversationsStoppingSend={verifyConversationsStoppingSend}
|
||||
/>
|
||||
|
|
|
@ -79,8 +79,8 @@ const Wrapper = ({
|
|||
getPreferredBadge={() => undefined}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
openConversationInternal={action('openConversationInternal')}
|
||||
sentAt={1587358800000}
|
||||
showConversation={action('showConversation')}
|
||||
snippet="Lorem <<left>>ipsum<<right>> wow"
|
||||
theme={ThemeType.light}
|
||||
to={defaultConversations[1]}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { ScrollBehavior } from '../types/Util';
|
|||
import { getConversationListWidthBreakpoint } from './_util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { LookupConversationWithoutUuidActionsType } from '../util/lookupConversationWithoutUuid';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
|
||||
import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem';
|
||||
import { ConversationListItem } from './conversationList/ConversationListItem';
|
||||
|
@ -154,7 +155,7 @@ export type PropsType = {
|
|||
onSelectConversation: (conversationId: string, messageId?: string) => void;
|
||||
renderMessageSearchResult: (id: string) => JSX.Element;
|
||||
showChooseGroupMembers: () => void;
|
||||
showConversation: (conversationId: string) => void;
|
||||
showConversation: ShowConversationType;
|
||||
} & LookupConversationWithoutUuidActionsType;
|
||||
|
||||
const NORMAL_ROW_HEIGHT = 76;
|
||||
|
|
|
@ -2,22 +2,25 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import type * as Backbone from 'backbone';
|
||||
import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
|
||||
import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type {
|
||||
ConversationType,
|
||||
ShowConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
import type { ConversationView } from '../views/conversation_view';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
|
||||
|
||||
type InboxViewType = Backbone.View & {
|
||||
onEmpty?: () => void;
|
||||
};
|
||||
|
||||
type InboxViewOptionsType = Backbone.ViewOptions & {
|
||||
initialLoadComplete: boolean;
|
||||
window: typeof window;
|
||||
};
|
||||
import * as log from '../logging/log';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog';
|
||||
import { ToastStickerPackInstallFailed } from './ToastStickerPackInstallFailed';
|
||||
import { WhatsNewLink } from './WhatsNewLink';
|
||||
import { showToast } from '../util/showToast';
|
||||
import { strictAssert } from '../util/assert';
|
||||
|
||||
export type PropsType = {
|
||||
cancelConversationVerification: () => void;
|
||||
|
@ -27,7 +30,12 @@ export type PropsType = {
|
|||
i18n: LocalizerType;
|
||||
isCustomizingPreferredReactions: boolean;
|
||||
renderCustomizingPreferredReactionsModal: () => JSX.Element;
|
||||
renderLeftPane: () => JSX.Element;
|
||||
renderSafetyNumber: (props: SafetyNumberProps) => JSX.Element;
|
||||
selectedConversationId?: string;
|
||||
selectedMessage?: string;
|
||||
showConversation: ShowConversationType;
|
||||
showWhatsNewModal: () => unknown;
|
||||
theme: ThemeType;
|
||||
verifyConversationsStoppingSend: () => void;
|
||||
};
|
||||
|
@ -40,38 +48,182 @@ export const Inbox = ({
|
|||
i18n,
|
||||
isCustomizingPreferredReactions,
|
||||
renderCustomizingPreferredReactionsModal,
|
||||
renderLeftPane,
|
||||
renderSafetyNumber,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
showConversation,
|
||||
showWhatsNewModal,
|
||||
theme,
|
||||
verifyConversationsStoppingSend,
|
||||
}: PropsType): JSX.Element => {
|
||||
const hostRef = useRef<HTMLDivElement | null>(null);
|
||||
const viewRef = useRef<InboxViewType | undefined>(undefined);
|
||||
const [loadingMessageCount, setLoadingMessageCount] = useState(0);
|
||||
const [internalHasInitialLoadCompleted, setInternalHasInitialLoadCompleted] =
|
||||
useState(hasInitialLoadCompleted);
|
||||
|
||||
const conversationMountRef = useRef<HTMLDivElement | null>(null);
|
||||
const conversationViewRef = useRef<ConversationView | null>(null);
|
||||
|
||||
const [prevConversation, setPrevConversation] = useState<
|
||||
ConversationModel | undefined
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
const viewOptions: InboxViewOptionsType = {
|
||||
el: hostRef.current,
|
||||
initialLoadComplete: false,
|
||||
window,
|
||||
};
|
||||
const view = new window.Whisper.InboxView(viewOptions);
|
||||
if (!selectedConversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewRef.current = view;
|
||||
const conversation = window.ConversationController.get(
|
||||
selectedConversationId
|
||||
);
|
||||
strictAssert(conversation, 'Conversation must be found');
|
||||
|
||||
conversation.setMarkedUnread(false);
|
||||
|
||||
if (!prevConversation || prevConversation.id !== selectedConversationId) {
|
||||
// We create a mount point because when calling .remove() on the Backbone
|
||||
// view it'll also remove the mount point along with it.
|
||||
const viewMountNode = document.createElement('div');
|
||||
conversationMountRef.current?.appendChild(viewMountNode);
|
||||
|
||||
// Make sure to unload the previous conversation along with calling
|
||||
// Backbone's remove so that it is taken out of the DOM.
|
||||
if (prevConversation) {
|
||||
prevConversation.trigger('unload', 'opened another conversation');
|
||||
}
|
||||
conversationViewRef.current?.remove();
|
||||
|
||||
// Can't import ConversationView directly because conversation_view
|
||||
// needs access to window.Signal first.
|
||||
const view = new window.Whisper.ConversationView({
|
||||
el: viewMountNode,
|
||||
model: conversation,
|
||||
});
|
||||
conversationViewRef.current = view;
|
||||
|
||||
setPrevConversation(conversation);
|
||||
|
||||
conversation.trigger('opened', selectedMessage);
|
||||
} else if (selectedMessage) {
|
||||
conversation.trigger('scroll-to-message', selectedMessage);
|
||||
}
|
||||
|
||||
// Make sure poppers are positioned properly
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
}, [prevConversation, selectedConversationId, selectedMessage]);
|
||||
|
||||
// Whenever the selectedConversationId is cleared we should also ensure
|
||||
// that prevConversation is cleared too.
|
||||
useEffect(() => {
|
||||
if (prevConversation && !selectedConversationId) {
|
||||
setPrevConversation(undefined);
|
||||
}
|
||||
}, [prevConversation, selectedConversationId]);
|
||||
|
||||
useEffect(() => {
|
||||
function refreshConversation({
|
||||
newId,
|
||||
oldId,
|
||||
}: {
|
||||
newId: string;
|
||||
oldId: string;
|
||||
}) {
|
||||
if (prevConversation && prevConversation.get('id') === oldId) {
|
||||
showConversation({ conversationId: newId });
|
||||
}
|
||||
}
|
||||
|
||||
// Close current opened conversation to reload the group information once
|
||||
// linked.
|
||||
function unload() {
|
||||
if (!prevConversation) {
|
||||
return;
|
||||
}
|
||||
prevConversation.trigger('unload', 'force unload requested');
|
||||
}
|
||||
|
||||
function onShowConversation(id: string, messageId?: string): void {
|
||||
showConversation({ conversationId: id, messageId });
|
||||
}
|
||||
|
||||
function packInstallFailed() {
|
||||
showToast(ToastStickerPackInstallFailed);
|
||||
}
|
||||
|
||||
window.Whisper.events.on('loadingProgress', setLoadingMessageCount);
|
||||
window.Whisper.events.on('pack-install-failed', packInstallFailed);
|
||||
window.Whisper.events.on('refreshConversation', refreshConversation);
|
||||
window.Whisper.events.on('setupAsNewDevice', unload);
|
||||
window.Whisper.events.on('showConversation', onShowConversation);
|
||||
|
||||
return () => {
|
||||
// [`Backbone.View.prototype.remove`][0] removes the DOM element and stops listening
|
||||
// to event listeners. Because React will do the first, we only want to do the
|
||||
// second.
|
||||
// [0]: https://github.com/jashkenas/backbone/blob/153dc41616a1f2663e4a86b705fefd412ecb4a7a/backbone.js#L1336-L1342
|
||||
viewRef.current?.stopListening();
|
||||
viewRef.current = undefined;
|
||||
window.Whisper.events.off('loadingProgress', setLoadingMessageCount);
|
||||
window.Whisper.events.off('pack-install-failed', packInstallFailed);
|
||||
window.Whisper.events.off('refreshConversation', refreshConversation);
|
||||
window.Whisper.events.off('setupAsNewDevice', unload);
|
||||
window.Whisper.events.off('showConversation', onShowConversation);
|
||||
};
|
||||
}, []);
|
||||
}, [prevConversation, showConversation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasInitialLoadCompleted && viewRef.current && viewRef.current.onEmpty) {
|
||||
viewRef.current.onEmpty();
|
||||
if (internalHasInitialLoadCompleted) {
|
||||
return;
|
||||
}
|
||||
}, [hasInitialLoadCompleted, viewRef]);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const status = window.getSocketStatus();
|
||||
switch (status) {
|
||||
case 'CONNECTING':
|
||||
break;
|
||||
case 'OPEN':
|
||||
// if we've connected, we can wait for real empty event
|
||||
clearInterval(interval);
|
||||
break;
|
||||
case 'CLOSING':
|
||||
case 'CLOSED':
|
||||
clearInterval(interval);
|
||||
// if we failed to connect, we pretend we loaded
|
||||
setInternalHasInitialLoadCompleted(true);
|
||||
break;
|
||||
default:
|
||||
log.warn(
|
||||
`startConnectionListener: Found unexpected socket status ${status}; setting load to done manually.`
|
||||
);
|
||||
setInternalHasInitialLoadCompleted(true);
|
||||
break;
|
||||
}
|
||||
}, SECOND);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [internalHasInitialLoadCompleted]);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalHasInitialLoadCompleted(hasInitialLoadCompleted);
|
||||
}, [hasInitialLoadCompleted]);
|
||||
|
||||
if (!internalHasInitialLoadCompleted) {
|
||||
return (
|
||||
<div className="app-loading-screen">
|
||||
<div className="module-title-bar-drag-area" />
|
||||
|
||||
<div className="content">
|
||||
<div className="module-splash-screen__logo module-img--150" />
|
||||
<div className="container">
|
||||
<span className="dot" />
|
||||
<span className="dot" />
|
||||
<span className="dot" />
|
||||
</div>
|
||||
<div className="message">
|
||||
{loadingMessageCount
|
||||
? i18n('loadingMessages', [String(loadingMessageCount)])
|
||||
: i18n('loading')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let activeModal: ReactNode;
|
||||
if (conversationsStoppingSend.length) {
|
||||
|
@ -94,7 +246,28 @@ export const Inbox = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="Inbox" ref={hostRef} />
|
||||
<div className="Inbox">
|
||||
<div className="module-title-bar-drag-area" />
|
||||
|
||||
<div className="left-pane-wrapper">{renderLeftPane()}</div>
|
||||
|
||||
<div className="conversation-stack">
|
||||
<div id="toast" />
|
||||
<div className="conversation" ref={conversationMountRef} />
|
||||
{!prevConversation && (
|
||||
<div className="no-conversation-open">
|
||||
<div className="module-splash-screen__logo module-img--128 module-logo-blue" />
|
||||
<h3>{i18n('welcomeToSignal')}</h3>
|
||||
<p className="whats-new-placeholder">
|
||||
<WhatsNewLink
|
||||
i18n={i18n}
|
||||
showWhatsNewModal={showWhatsNewModal}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{activeModal}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -124,7 +124,6 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
|
|||
getPreferredBadge: () => undefined,
|
||||
i18n,
|
||||
preferredWidthFromStorage: 320,
|
||||
openConversationInternal: action('openConversationInternal'),
|
||||
regionCode: 'US',
|
||||
challengeStatus: select(
|
||||
'challengeStatus',
|
||||
|
@ -148,7 +147,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
|
|||
getPreferredBadge={() => undefined}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
openConversationInternal={action('openConversationInternal')}
|
||||
showConversation={action('showConversation')}
|
||||
sentAt={1587358800000}
|
||||
snippet="Lorem <<left>>ipsum<<right>> wow"
|
||||
theme={ThemeType.light}
|
||||
|
|
|
@ -39,7 +39,7 @@ import {
|
|||
getWidthFromPreferredWidth,
|
||||
} from '../util/leftPaneWidth';
|
||||
import type { LookupConversationWithoutUuidActionsType } from '../util/lookupConversationWithoutUuid';
|
||||
import type { OpenConversationInternalType } from '../state/ducks/conversations';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
|
||||
import { ConversationList } from './ConversationList';
|
||||
import { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox';
|
||||
|
@ -99,25 +99,25 @@ export type PropsType = {
|
|||
clearSearch: () => void;
|
||||
closeMaximumGroupSizeModal: () => void;
|
||||
closeRecommendedGroupSizeModal: () => void;
|
||||
createGroup: () => void;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
savePreferredLeftPaneWidth: (_: number) => void;
|
||||
searchInConversation: (conversationId: string) => unknown;
|
||||
setComposeSearchTerm: (composeSearchTerm: string) => void;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array) => void;
|
||||
setComposeGroupName: (_: string) => void;
|
||||
setComposeGroupExpireTimer: (_: number) => void;
|
||||
showArchivedConversations: () => void;
|
||||
showInbox: () => void;
|
||||
startComposing: () => void;
|
||||
startSearch: () => unknown;
|
||||
showChooseGroupMembers: () => void;
|
||||
startSettingGroupMetadata: () => void;
|
||||
toggleConversationInChooseMembers: (conversationId: string) => void;
|
||||
composeDeleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
|
||||
composeReplaceAvatar: ReplaceAvatarActionType;
|
||||
composeSaveAvatarToDisk: SaveAvatarToDiskActionType;
|
||||
createGroup: () => void;
|
||||
savePreferredLeftPaneWidth: (_: number) => void;
|
||||
searchInConversation: (conversationId: string) => unknown;
|
||||
setComposeGroupAvatar: (_: undefined | Uint8Array) => void;
|
||||
setComposeGroupExpireTimer: (_: number) => void;
|
||||
setComposeGroupName: (_: string) => void;
|
||||
setComposeSearchTerm: (composeSearchTerm: string) => void;
|
||||
showArchivedConversations: () => void;
|
||||
showChooseGroupMembers: () => void;
|
||||
showConversation: ShowConversationType;
|
||||
showInbox: () => void;
|
||||
startComposing: () => void;
|
||||
startSearch: () => unknown;
|
||||
startSettingGroupMetadata: () => void;
|
||||
toggleComposeEditingAvatar: () => unknown;
|
||||
toggleConversationInChooseMembers: (conversationId: string) => void;
|
||||
updateSearchTerm: (_: string) => void;
|
||||
|
||||
// Render Props
|
||||
|
@ -137,8 +137,6 @@ export type PropsType = {
|
|||
) => JSX.Element;
|
||||
renderCaptchaDialog: (props: { onSkip(): void }) => JSX.Element;
|
||||
renderCrashReportDialog: () => JSX.Element;
|
||||
|
||||
showConversation: (conversationId: string) => void;
|
||||
} & LookupConversationWithoutUuidActionsType;
|
||||
|
||||
export const LeftPane: React.FC<PropsType> = ({
|
||||
|
@ -156,7 +154,6 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
getPreferredBadge,
|
||||
i18n,
|
||||
modeSpecificProps,
|
||||
openConversationInternal,
|
||||
preferredWidthFromStorage,
|
||||
renderCaptchaDialog,
|
||||
renderCrashReportDialog,
|
||||
|
@ -363,7 +360,7 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
|
||||
if (conversationToOpen) {
|
||||
const { conversationId, messageId } = conversationToOpen;
|
||||
openConversationInternal({ conversationId, messageId });
|
||||
showConversation({ conversationId, messageId });
|
||||
if (openedByNumber) {
|
||||
clearSearch();
|
||||
}
|
||||
|
@ -383,16 +380,16 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
document.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [
|
||||
clearSearch,
|
||||
helper,
|
||||
openConversationInternal,
|
||||
searchInConversation,
|
||||
selectedConversationId,
|
||||
selectedMessageId,
|
||||
showChooseGroupMembers,
|
||||
showConversation,
|
||||
showInbox,
|
||||
startComposing,
|
||||
startSearch,
|
||||
clearSearch,
|
||||
]);
|
||||
|
||||
const requiresFullWidth = helper.requiresFullWidth();
|
||||
|
@ -488,13 +485,13 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
|
||||
const onSelectConversation = useCallback(
|
||||
(conversationId: string, messageId?: string) => {
|
||||
openConversationInternal({
|
||||
showConversation({
|
||||
conversationId,
|
||||
messageId,
|
||||
switchToAssociatedView: true,
|
||||
});
|
||||
},
|
||||
[openConversationInternal]
|
||||
[showConversation]
|
||||
);
|
||||
|
||||
const previousSelectedConversationId = usePrevious(
|
||||
|
@ -555,7 +552,7 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
setComposeSearchTerm(event.target.value);
|
||||
},
|
||||
updateSearchTerm,
|
||||
openConversationInternal,
|
||||
showConversation,
|
||||
})}
|
||||
<div className="module-left-pane__dialogs">
|
||||
{renderExpiredBuildDialog({
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import type {
|
||||
ConversationType,
|
||||
OpenConversationInternalType,
|
||||
ShowConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Avatar, AvatarSize } from './Avatar';
|
||||
|
@ -20,10 +20,10 @@ type PropsType = {
|
|||
searchTerm: string;
|
||||
startSearchCounter: number;
|
||||
updateSearchTerm: (searchTerm: string) => void;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
showConversation: ShowConversationType;
|
||||
onEnterKeyDown?: (
|
||||
clearSearch: () => void,
|
||||
openConversationInternal: OpenConversationInternalType
|
||||
showConversation: ShowConversationType
|
||||
) => void;
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,7 @@ export const LeftPaneSearchInput = ({
|
|||
searchTerm,
|
||||
startSearchCounter,
|
||||
updateSearchTerm,
|
||||
openConversationInternal,
|
||||
showConversation,
|
||||
onEnterKeyDown,
|
||||
}: PropsType): JSX.Element => {
|
||||
const inputRef = useRef<null | HTMLInputElement>(null);
|
||||
|
@ -103,7 +103,7 @@ export const LeftPaneSearchInput = ({
|
|||
}}
|
||||
onKeyDown={event => {
|
||||
if (onEnterKeyDown && event.key === 'Enter') {
|
||||
onEnterKeyDown(clearSearch, openConversationInternal);
|
||||
onEnterKeyDown(clearSearch, showConversation);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ function getAttachmentWithThumbnail(url: string): AttachmentType {
|
|||
const getDefaultProps = (): PropsType => ({
|
||||
hiddenStories: [],
|
||||
i18n,
|
||||
openConversationInternal: action('openConversationInternal'),
|
||||
preferredWidthFromStorage: 380,
|
||||
queueStoryDownload: action('queueStoryDownload'),
|
||||
renderStoryViewer: () => <div />,
|
||||
showConversation: action('showConversation'),
|
||||
stories: [
|
||||
createStory({
|
||||
attachment: getAttachmentWithThumbnail(
|
||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||
import type { ConversationStoryType } from './StoryListItem';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { PropsType as SmartStoryViewerPropsType } from '../state/smart/StoryViewer';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
import { StoriesPane } from './StoriesPane';
|
||||
import { Theme, themeClassName } from '../util/theme';
|
||||
import { getWidthFromPreferredWidth } from '../util/leftPaneWidth';
|
||||
|
@ -16,9 +17,9 @@ export type PropsType = {
|
|||
hiddenStories: Array<ConversationStoryType>;
|
||||
i18n: LocalizerType;
|
||||
preferredWidthFromStorage: number;
|
||||
openConversationInternal: (_: { conversationId: string }) => unknown;
|
||||
renderStoryViewer: (props: SmartStoryViewerPropsType) => JSX.Element;
|
||||
queueStoryDownload: (storyId: string) => unknown;
|
||||
renderStoryViewer: (props: SmartStoryViewerPropsType) => JSX.Element;
|
||||
showConversation: ShowConversationType;
|
||||
stories: Array<ConversationStoryType>;
|
||||
toggleHideStories: (conversationId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
|
@ -27,10 +28,10 @@ export type PropsType = {
|
|||
export const Stories = ({
|
||||
hiddenStories,
|
||||
i18n,
|
||||
openConversationInternal,
|
||||
preferredWidthFromStorage,
|
||||
queueStoryDownload,
|
||||
renderStoryViewer,
|
||||
showConversation,
|
||||
stories,
|
||||
toggleHideStories,
|
||||
toggleStoriesView,
|
||||
|
@ -119,8 +120,8 @@ export const Stories = ({
|
|||
});
|
||||
setConversationIdToView(clickedIdToView);
|
||||
}}
|
||||
openConversationInternal={openConversationInternal}
|
||||
queueStoryDownload={queueStoryDownload}
|
||||
showConversation={showConversation}
|
||||
stories={stories}
|
||||
toggleHideStories={toggleHideStories}
|
||||
toggleStoriesView={toggleStoriesView}
|
||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||
import { isNotNil } from '../util/isNotNil';
|
||||
import type { ConversationStoryType, StoryViewType } from './StoryListItem';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
import { SearchInput } from './SearchInput';
|
||||
import { StoryListItem } from './StoryListItem';
|
||||
|
||||
|
@ -53,8 +54,8 @@ export type PropsType = {
|
|||
hiddenStories: Array<ConversationStoryType>;
|
||||
i18n: LocalizerType;
|
||||
onStoryClicked: (conversationId: string) => unknown;
|
||||
openConversationInternal: (_: { conversationId: string }) => unknown;
|
||||
queueStoryDownload: (storyId: string) => unknown;
|
||||
showConversation: ShowConversationType;
|
||||
stories: Array<ConversationStoryType>;
|
||||
toggleHideStories: (conversationId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
|
@ -64,8 +65,8 @@ export const StoriesPane = ({
|
|||
hiddenStories,
|
||||
i18n,
|
||||
onStoryClicked,
|
||||
openConversationInternal,
|
||||
queueStoryDownload,
|
||||
showConversation,
|
||||
stories,
|
||||
toggleHideStories,
|
||||
toggleStoriesView,
|
||||
|
@ -121,7 +122,7 @@ export const StoriesPane = ({
|
|||
}}
|
||||
onHideStory={toggleHideStories}
|
||||
onGoToConversation={conversationId => {
|
||||
openConversationInternal({ conversationId });
|
||||
showConversation({ conversationId });
|
||||
toggleStoriesView();
|
||||
}}
|
||||
queueStoryDownload={queueStoryDownload}
|
||||
|
@ -150,7 +151,7 @@ export const StoriesPane = ({
|
|||
}}
|
||||
onHideStory={toggleHideStories}
|
||||
onGoToConversation={conversationId => {
|
||||
openConversationInternal({ conversationId });
|
||||
showConversation({ conversationId });
|
||||
toggleStoriesView();
|
||||
}}
|
||||
queueStoryDownload={queueStoryDownload}
|
||||
|
|
|
@ -44,8 +44,8 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
i18n,
|
||||
isAdmin: boolean('isAdmin', overrideProps.isAdmin || false),
|
||||
isMember: boolean('isMember', overrideProps.isMember || true),
|
||||
openConversationInternal: action('openConversationInternal'),
|
||||
removeMemberFromGroup: action('removeMemberFromGroup'),
|
||||
showConversation: action('showConversation'),
|
||||
theme: ThemeType.light,
|
||||
toggleSafetyNumberModal: action('toggleSafetyNumberModal'),
|
||||
toggleAdmin: action('toggleAdmin'),
|
||||
|
|
|
@ -9,7 +9,10 @@ import { missingCaseError } from '../../util/missingCaseError';
|
|||
import { About } from './About';
|
||||
import { Avatar } from '../Avatar';
|
||||
import { AvatarLightbox } from '../AvatarLightbox';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationType,
|
||||
ShowConversationType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { Modal } from '../Modal';
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import { BadgeDialog } from '../BadgeDialog';
|
||||
|
@ -32,14 +35,8 @@ export type PropsDataType = {
|
|||
|
||||
type PropsActionType = {
|
||||
hideContactModal: () => void;
|
||||
openConversationInternal: (
|
||||
options: Readonly<{
|
||||
conversationId: string;
|
||||
messageId?: string;
|
||||
switchToAssociatedView?: boolean;
|
||||
}>
|
||||
) => void;
|
||||
removeMemberFromGroup: (conversationId: string, contactId: string) => void;
|
||||
showConversation: ShowConversationType;
|
||||
toggleAdmin: (conversationId: string, contactId: string) => void;
|
||||
toggleSafetyNumberModal: (conversationId: string) => unknown;
|
||||
updateConversationModelSharedGroups: (conversationId: string) => void;
|
||||
|
@ -69,8 +66,8 @@ export const ContactModal = ({
|
|||
i18n,
|
||||
isAdmin,
|
||||
isMember,
|
||||
openConversationInternal,
|
||||
removeMemberFromGroup,
|
||||
showConversation,
|
||||
theme,
|
||||
toggleAdmin,
|
||||
toggleSafetyNumberModal,
|
||||
|
@ -205,7 +202,7 @@ export const ContactModal = ({
|
|||
className="ContactModal__button ContactModal__send-message"
|
||||
onClick={() => {
|
||||
hideContactModal();
|
||||
openConversationInternal({ conversationId: contact.id });
|
||||
showConversation({ conversationId: contact.id });
|
||||
}}
|
||||
>
|
||||
<div className="ContactModal__bubble-icon">
|
||||
|
|
|
@ -53,7 +53,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
to: overrideProps.to as PropsType['to'],
|
||||
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
|
||||
isSelected: boolean('isSelected', overrideProps.isSelected || false),
|
||||
openConversationInternal: action('openConversationInternal'),
|
||||
showConversation: action('showConversation'),
|
||||
isSearchingInConversation: boolean(
|
||||
'isSearchingInConversation',
|
||||
overrideProps.isSearchingInConversation || false
|
||||
|
|
|
@ -15,7 +15,10 @@ import type {
|
|||
ThemeType,
|
||||
} from '../../types/Util';
|
||||
import { BaseConversationListItem } from './BaseConversationListItem';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
import type {
|
||||
ConversationType,
|
||||
ShowConversationType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
|
||||
|
||||
export type PropsDataType = {
|
||||
|
@ -58,10 +61,7 @@ export type PropsDataType = {
|
|||
type PropsHousekeepingType = {
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
openConversationInternal: (_: {
|
||||
conversationId: string;
|
||||
messageId?: string;
|
||||
}) => void;
|
||||
showConversation: ShowConversationType;
|
||||
theme: ThemeType;
|
||||
};
|
||||
|
||||
|
@ -147,15 +147,15 @@ export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
|
|||
getPreferredBadge,
|
||||
i18n,
|
||||
id,
|
||||
openConversationInternal,
|
||||
sentAt,
|
||||
showConversation,
|
||||
snippet,
|
||||
theme,
|
||||
to,
|
||||
}) {
|
||||
const onClickItem = useCallback(() => {
|
||||
openConversationInternal({ conversationId, messageId: id });
|
||||
}, [openConversationInternal, conversationId, id]);
|
||||
showConversation({ conversationId, messageId: id });
|
||||
}, [showConversation, conversationId, id]);
|
||||
|
||||
if (!from || !to) {
|
||||
return <div />;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { BaseConversationListItem } from './BaseConversationListItem';
|
|||
import type { ParsedE164Type } from '../../util/libphonenumberInstance';
|
||||
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import type { ShowConversationType } from '../../state/ducks/conversations';
|
||||
import { AvatarColors } from '../../types/Colors';
|
||||
|
||||
type PropsData = {
|
||||
|
@ -20,7 +21,7 @@ type PropsData = {
|
|||
|
||||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
showConversation: (conversationId: string) => void;
|
||||
showConversation: ShowConversationType;
|
||||
} & LookupConversationWithoutUuidActionsType;
|
||||
|
||||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
@ -55,7 +56,7 @@ export const StartNewConversation: FunctionComponent<Props> = React.memo(
|
|||
});
|
||||
|
||||
if (conversationId !== undefined) {
|
||||
showConversation(conversationId);
|
||||
showConversation({ conversationId });
|
||||
}
|
||||
}, [
|
||||
showConversation,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { BaseConversationListItem } from './BaseConversationListItem';
|
|||
import type { LocalizerType } from '../../types/Util';
|
||||
import { lookupConversationWithoutUuid } from '../../util/lookupConversationWithoutUuid';
|
||||
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid';
|
||||
import type { ShowConversationType } from '../../state/ducks/conversations';
|
||||
|
||||
type PropsData = {
|
||||
username: string;
|
||||
|
@ -17,7 +18,7 @@ type PropsData = {
|
|||
|
||||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
showConversation: (conversationId: string) => void;
|
||||
showConversation: ShowConversationType;
|
||||
} & LookupConversationWithoutUuidActionsType;
|
||||
|
||||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
@ -44,7 +45,7 @@ export const UsernameSearchResultListItem: FunctionComponent<Props> = ({
|
|||
});
|
||||
|
||||
if (conversationId !== undefined) {
|
||||
showConversation(conversationId);
|
||||
showConversation({ conversationId });
|
||||
}
|
||||
}, [
|
||||
username,
|
||||
|
|
|
@ -14,7 +14,7 @@ import type { PropsData as ConversationListItemPropsType } from '../conversation
|
|||
import type { LocalizerType } from '../../types/Util';
|
||||
import type {
|
||||
ConversationType,
|
||||
OpenConversationInternalType,
|
||||
ShowConversationType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { LeftPaneSearchInput } from '../LeftPaneSearchInput';
|
||||
import type { LeftPaneSearchPropsType } from './LeftPaneSearchHelper';
|
||||
|
@ -84,13 +84,13 @@ export class LeftPaneArchiveHelper extends LeftPaneHelper<LeftPaneArchivePropsTy
|
|||
clearSearch,
|
||||
i18n,
|
||||
updateSearchTerm,
|
||||
openConversationInternal,
|
||||
showConversation,
|
||||
}: Readonly<{
|
||||
clearConversationSearch: () => unknown;
|
||||
clearSearch: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
updateSearchTerm: (searchTerm: string) => unknown;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
showConversation: ShowConversationType;
|
||||
}>): ReactChild | null {
|
||||
if (!this.searchConversation) {
|
||||
return null;
|
||||
|
@ -103,9 +103,9 @@ export class LeftPaneArchiveHelper extends LeftPaneHelper<LeftPaneArchivePropsTy
|
|||
i18n={i18n}
|
||||
searchConversation={this.searchConversation}
|
||||
searchTerm={this.searchTerm}
|
||||
showConversation={showConversation}
|
||||
startSearchCounter={this.startSearchCounter}
|
||||
updateSearchTerm={updateSearchTerm}
|
||||
openConversationInternal={openConversationInternal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import type {
|
|||
ReplaceAvatarActionType,
|
||||
SaveAvatarToDiskActionType,
|
||||
} from '../../types/Avatar';
|
||||
import type { OpenConversationInternalType } from '../../state/ducks/conversations';
|
||||
import type { ShowConversationType } from '../../state/ducks/conversations';
|
||||
|
||||
export enum FindDirection {
|
||||
Up,
|
||||
|
@ -43,7 +43,7 @@ export abstract class LeftPaneHelper<T> {
|
|||
event: ChangeEvent<HTMLInputElement>
|
||||
) => unknown;
|
||||
updateSearchTerm: (searchTerm: string) => unknown;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
showConversation: ShowConversationType;
|
||||
}>
|
||||
): null | ReactChild {
|
||||
return null;
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Intl } from '../Intl';
|
|||
import type { ToFindType } from './LeftPaneHelper';
|
||||
import type {
|
||||
ConversationType,
|
||||
OpenConversationInternalType,
|
||||
ShowConversationType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { LeftPaneHelper } from './LeftPaneHelper';
|
||||
import { getConversationInDirection } from './getConversationInDirection';
|
||||
|
@ -85,14 +85,14 @@ export class LeftPaneInboxHelper extends LeftPaneHelper<LeftPaneInboxPropsType>
|
|||
clearConversationSearch,
|
||||
clearSearch,
|
||||
i18n,
|
||||
showConversation,
|
||||
updateSearchTerm,
|
||||
openConversationInternal,
|
||||
}: Readonly<{
|
||||
clearConversationSearch: () => unknown;
|
||||
clearSearch: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
showConversation: ShowConversationType;
|
||||
updateSearchTerm: (searchTerm: string) => unknown;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
}>): ReactChild {
|
||||
return (
|
||||
<LeftPaneSearchInput
|
||||
|
@ -102,9 +102,9 @@ export class LeftPaneInboxHelper extends LeftPaneHelper<LeftPaneInboxPropsType>
|
|||
i18n={i18n}
|
||||
searchConversation={this.searchConversation}
|
||||
searchTerm={this.searchTerm}
|
||||
showConversation={showConversation}
|
||||
startSearchCounter={this.startSearchCounter}
|
||||
updateSearchTerm={updateSearchTerm}
|
||||
openConversationInternal={openConversationInternal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import type { PropsData as ConversationListItemPropsType } from '../conversation
|
|||
import { handleKeydownForSearch } from './handleKeydownForSearch';
|
||||
import type {
|
||||
ConversationType,
|
||||
OpenConversationInternalType,
|
||||
ShowConversationType,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { LeftPaneSearchInput } from '../LeftPaneSearchInput';
|
||||
|
||||
|
@ -104,14 +104,14 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
clearConversationSearch,
|
||||
clearSearch,
|
||||
i18n,
|
||||
showConversation,
|
||||
updateSearchTerm,
|
||||
openConversationInternal,
|
||||
}: Readonly<{
|
||||
clearConversationSearch: () => unknown;
|
||||
clearSearch: () => unknown;
|
||||
i18n: LocalizerType;
|
||||
showConversation: ShowConversationType;
|
||||
updateSearchTerm: (searchTerm: string) => unknown;
|
||||
openConversationInternal: OpenConversationInternalType;
|
||||
}>): ReactChild {
|
||||
return (
|
||||
<LeftPaneSearchInput
|
||||
|
@ -119,12 +119,12 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
clearSearch={clearSearch}
|
||||
disabled={this.searchDisabled}
|
||||
i18n={i18n}
|
||||
onEnterKeyDown={this.onEnterKeyDown}
|
||||
searchConversation={this.searchConversation}
|
||||
searchTerm={this.searchTerm}
|
||||
showConversation={showConversation}
|
||||
startSearchCounter={this.startSearchCounter}
|
||||
updateSearchTerm={updateSearchTerm}
|
||||
openConversationInternal={openConversationInternal}
|
||||
onEnterKeyDown={this.onEnterKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -361,13 +361,13 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
|
||||
private onEnterKeyDown(
|
||||
clearSearch: () => unknown,
|
||||
openConversationInternal: OpenConversationInternalType
|
||||
showConversation: ShowConversationType
|
||||
): void {
|
||||
const conversation = this.getConversationAndMessageAtIndex(0);
|
||||
if (!conversation) {
|
||||
return;
|
||||
}
|
||||
openConversationInternal(conversation);
|
||||
showConversation(conversation);
|
||||
clearSearch();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue