Removes Inbox Backbone view

This commit is contained in:
Josh Perez 2022-06-16 15:12:50 -04:00 committed by GitHub
parent 603b76c3d9
commit aa23c2def2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 496 additions and 808 deletions

View file

@ -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}
/>

View file

@ -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]}

View file

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

View file

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

View file

@ -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}

View file

@ -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({

View file

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

View file

@ -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(

View file

@ -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}

View file

@ -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}

View file

@ -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'),

View file

@ -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">

View file

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

View file

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

View file

@ -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,

View file

@ -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,

View file

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

View file

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

View file

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

View file

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