Use useCallback all the time in smart components
This commit is contained in:
parent
ffb1fe2590
commit
193f344b16
13 changed files with 319 additions and 195 deletions
|
@ -11,11 +11,11 @@ import { SmartGlobalModalContainer } from './GlobalModalContainer';
|
|||
import { SmartLightbox } from './Lightbox';
|
||||
import { SmartStoryViewer } from './StoryViewer';
|
||||
import {
|
||||
getTheme,
|
||||
getIsMainWindowMaximized,
|
||||
getIsMainWindowFullScreen,
|
||||
getTheme,
|
||||
} from '../selectors/user';
|
||||
import { hasSelectedStoryData } from '../selectors/stories';
|
||||
import { hasSelectedStoryData as getHasSelectedStoryData } from '../selectors/stories';
|
||||
import { useAppActions } from '../ducks/app';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useStoriesActions } from '../ducks/stories';
|
||||
|
@ -28,53 +28,78 @@ function renderInbox(): JSX.Element {
|
|||
return <SmartInbox />;
|
||||
}
|
||||
|
||||
function renderCallManager(): JSX.Element {
|
||||
return (
|
||||
<ModalContainer className="module-calling__modal-container">
|
||||
<SmartCallManager />
|
||||
</ModalContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function renderGlobalModalContainer(): JSX.Element {
|
||||
return <SmartGlobalModalContainer />;
|
||||
}
|
||||
|
||||
function renderLightbox(): JSX.Element {
|
||||
return <SmartLightbox />;
|
||||
}
|
||||
|
||||
function renderStoryViewer(closeView: () => unknown): JSX.Element {
|
||||
return (
|
||||
<ErrorBoundary name="App/renderStoryViewer" closeView={closeView}>
|
||||
<SmartStoryViewer />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
function requestVerification(
|
||||
number: string,
|
||||
captcha: string,
|
||||
transport: VerificationTransport
|
||||
): Promise<{ sessionId: string }> {
|
||||
const { server } = window.textsecure;
|
||||
strictAssert(server !== undefined, 'WebAPI not available');
|
||||
return server.requestVerification(number, captcha, transport);
|
||||
}
|
||||
|
||||
function registerSingleDevice(
|
||||
number: string,
|
||||
code: string,
|
||||
sessionId: string
|
||||
): Promise<void> {
|
||||
return window
|
||||
.getAccountManager()
|
||||
.registerSingleDevice(number, code, sessionId);
|
||||
}
|
||||
|
||||
export const SmartApp = memo(function SmartApp() {
|
||||
const appView = useSelector(getAppView);
|
||||
const isMaximized = useSelector(getIsMainWindowMaximized);
|
||||
const isFullScreen = useSelector(getIsMainWindowFullScreen);
|
||||
const hasSelectedStoryData = useSelector(getHasSelectedStoryData);
|
||||
const theme = useSelector(getTheme);
|
||||
|
||||
const { openInbox } = useAppActions();
|
||||
const { scrollToMessage } = useConversationsActions();
|
||||
const { viewStory } = useStoriesActions();
|
||||
|
||||
const osClassName = OS.getClassName();
|
||||
|
||||
return (
|
||||
<App
|
||||
appView={appView}
|
||||
isMaximized={useSelector(getIsMainWindowMaximized)}
|
||||
isFullScreen={useSelector(getIsMainWindowFullScreen)}
|
||||
osClassName={OS.getClassName()}
|
||||
renderCallManager={() => (
|
||||
<ModalContainer className="module-calling__modal-container">
|
||||
<SmartCallManager />
|
||||
</ModalContainer>
|
||||
)}
|
||||
renderGlobalModalContainer={() => <SmartGlobalModalContainer />}
|
||||
renderLightbox={() => <SmartLightbox />}
|
||||
hasSelectedStoryData={useSelector(hasSelectedStoryData)}
|
||||
renderStoryViewer={(closeView: () => unknown) => (
|
||||
<ErrorBoundary name="App/renderStoryViewer" closeView={closeView}>
|
||||
<SmartStoryViewer />
|
||||
</ErrorBoundary>
|
||||
)}
|
||||
isMaximized={isMaximized}
|
||||
isFullScreen={isFullScreen}
|
||||
osClassName={osClassName}
|
||||
renderCallManager={renderCallManager}
|
||||
renderGlobalModalContainer={renderGlobalModalContainer}
|
||||
renderLightbox={renderLightbox}
|
||||
hasSelectedStoryData={hasSelectedStoryData}
|
||||
renderStoryViewer={renderStoryViewer}
|
||||
renderInbox={renderInbox}
|
||||
requestVerification={(
|
||||
number: string,
|
||||
captcha: string,
|
||||
transport: VerificationTransport
|
||||
): Promise<{ sessionId: string }> => {
|
||||
const { server } = window.textsecure;
|
||||
strictAssert(server !== undefined, 'WebAPI not available');
|
||||
|
||||
return server.requestVerification(number, captcha, transport);
|
||||
}}
|
||||
registerSingleDevice={(
|
||||
number: string,
|
||||
code: string,
|
||||
sessionId: string
|
||||
): Promise<void> => {
|
||||
return window
|
||||
.getAccountManager()
|
||||
.registerSingleDevice(number, code, sessionId);
|
||||
}}
|
||||
theme={useSelector(getTheme)}
|
||||
requestVerification={requestVerification}
|
||||
registerSingleDevice={registerSingleDevice}
|
||||
theme={theme}
|
||||
openInbox={openInbox}
|
||||
scrollToMessage={scrollToMessage}
|
||||
viewStory={viewStory}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { CompositionRecording } from '../../components/CompositionRecording';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
import { useAudioRecorderActions } from '../ducks/audioRecorder';
|
||||
import { useComposerActions } from '../ducks/composer';
|
||||
import { useToastActions } from '../ducks/toast';
|
||||
|
@ -21,9 +20,10 @@ export const SmartCompositionRecording = memo(
|
|||
}: SmartCompositionRecordingProps) {
|
||||
const i18n = useSelector(getIntl);
|
||||
const selectedConversationId = useSelector(getSelectedConversationId);
|
||||
const { cancelRecording, completeRecording } = useAudioRecorderActions();
|
||||
const { errorRecording, cancelRecording, completeRecording } =
|
||||
useAudioRecorderActions();
|
||||
|
||||
const { sendMultiMediaMessage } = useComposerActions();
|
||||
const { sendMultiMediaMessage, addAttachment } = useComposerActions();
|
||||
const { hideToast, showToast } = useToastActions();
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
|
@ -56,9 +56,9 @@ export const SmartCompositionRecording = memo(
|
|||
conversationId={selectedConversationId}
|
||||
onCancel={handleCancel}
|
||||
onSend={handleSend}
|
||||
errorRecording={mapDispatchToProps.errorRecording}
|
||||
addAttachment={mapDispatchToProps.addAttachment}
|
||||
completeRecording={mapDispatchToProps.completeRecording}
|
||||
errorRecording={errorRecording}
|
||||
addAttachment={addAttachment}
|
||||
completeRecording={completeRecording}
|
||||
showToast={showToast}
|
||||
hideToast={hideToast}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { ConfirmAdditionsModal } from '../../components/conversation/conversation-details/AddGroupMembersModal/ConfirmAdditionsModal';
|
||||
|
@ -27,14 +27,16 @@ export const SmartConfirmAdditionsModal = memo(
|
|||
const i18n = useSelector(getIntl);
|
||||
const conversationSelector = useSelector(getConversationByIdSelector);
|
||||
|
||||
const selectedContacts = selectedConversationIds.map(conversationId => {
|
||||
const convo = conversationSelector(conversationId);
|
||||
strictAssert(
|
||||
convo,
|
||||
'<SmartChooseGroupMemberModal> selected conversation not found'
|
||||
);
|
||||
return convo;
|
||||
});
|
||||
const selectedContacts = useMemo(() => {
|
||||
return selectedConversationIds.map(conversationId => {
|
||||
const convo = conversationSelector(conversationId);
|
||||
strictAssert(
|
||||
convo,
|
||||
'<SmartChooseGroupMemberModal> selected conversation not found'
|
||||
);
|
||||
return convo;
|
||||
});
|
||||
}, [conversationSelector, selectedConversationIds]);
|
||||
|
||||
return (
|
||||
<ConfirmAdditionsModal
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ContactName } from '../../components/conversation/ContactName';
|
||||
import { getIntl } from '../selectors/user';
|
||||
|
@ -14,26 +14,28 @@ type ExternalProps = {
|
|||
contactId: string;
|
||||
};
|
||||
|
||||
export const SmartContactName = memo(function SmartContactName(
|
||||
props: ExternalProps
|
||||
) {
|
||||
const { contactId } = props;
|
||||
export const SmartContactName = memo(function SmartContactName({
|
||||
contactId,
|
||||
}: ExternalProps) {
|
||||
const i18n = useSelector(getIntl);
|
||||
const getConversation = useSelector(getConversationSelector);
|
||||
|
||||
const contact = getConversation(contactId) || {
|
||||
title: i18n('icu:unknownContact'),
|
||||
};
|
||||
const currentConversationId = useSelector(getSelectedConversationId);
|
||||
const currentConversation = getConversation(currentConversationId);
|
||||
|
||||
const { showContactModal } = useGlobalModalActions();
|
||||
|
||||
const contact = useMemo(() => {
|
||||
return getConversation(contactId);
|
||||
}, [getConversation, contactId]);
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
showContactModal(contactId, currentConversationId);
|
||||
}, [showContactModal, contactId, currentConversationId]);
|
||||
|
||||
return (
|
||||
<ContactName
|
||||
firstName={contact.firstName}
|
||||
title={contact.title}
|
||||
onClick={() => showContactModal(contact.id, currentConversation.id)}
|
||||
title={contact.title ?? i18n('icu:unknownContact')}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -43,27 +43,41 @@ export const SmartDeleteMessagesModal = memo(
|
|||
useConversationsActions();
|
||||
const { showToast } = useToastActions();
|
||||
|
||||
const messageCount = deleteMessagesProps.messageIds.length;
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
toggleDeleteMessagesModal(undefined);
|
||||
}, [toggleDeleteMessagesModal]);
|
||||
|
||||
const handleDeleteForMe = useCallback(() => {
|
||||
deleteMessages({
|
||||
conversationId,
|
||||
messageIds,
|
||||
lastSelectedMessage,
|
||||
});
|
||||
onDelete?.();
|
||||
}, [
|
||||
conversationId,
|
||||
deleteMessages,
|
||||
lastSelectedMessage,
|
||||
messageIds,
|
||||
onDelete,
|
||||
]);
|
||||
|
||||
const handleDeleteForEveryone = useCallback(() => {
|
||||
deleteMessagesForEveryone(messageIds);
|
||||
onDelete?.();
|
||||
}, [deleteMessagesForEveryone, messageIds, onDelete]);
|
||||
|
||||
return (
|
||||
<DeleteMessagesModal
|
||||
isMe={isMe}
|
||||
canDeleteForEveryone={canDeleteForEveryone}
|
||||
i18n={i18n}
|
||||
messageCount={deleteMessagesProps.messageIds.length}
|
||||
onClose={() => {
|
||||
toggleDeleteMessagesModal(undefined);
|
||||
}}
|
||||
onDeleteForMe={() => {
|
||||
deleteMessages({
|
||||
conversationId,
|
||||
messageIds,
|
||||
lastSelectedMessage,
|
||||
});
|
||||
onDelete?.();
|
||||
}}
|
||||
onDeleteForEveryone={() => {
|
||||
deleteMessagesForEveryone(messageIds);
|
||||
onDelete?.();
|
||||
}}
|
||||
messageCount={messageCount}
|
||||
onClose={handleClose}
|
||||
onDeleteForMe={handleDeleteForMe}
|
||||
onDeleteForEveryone={handleDeleteForEveryone}
|
||||
showToast={showToast}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { forwardRef, memo } from 'react';
|
||||
import React, { useCallback, forwardRef, memo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useRecentEmojis } from '../selectors/emojis';
|
||||
import { useEmojisActions as useEmojiActions } from '../ducks/emojis';
|
||||
|
@ -25,10 +25,9 @@ export const SmartEmojiPicker = memo(
|
|||
const skinTone = useSelector(getEmojiSkinTone);
|
||||
|
||||
const recentEmojis = useRecentEmojis();
|
||||
|
||||
const { onUseEmoji } = useEmojiActions();
|
||||
|
||||
const handlePickEmoji = React.useCallback(
|
||||
const handlePickEmoji = useCallback(
|
||||
data => {
|
||||
onUseEmoji({ shortName: data.shortName });
|
||||
onPickEmoji(data);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type {
|
||||
ForwardMessagePropsType,
|
||||
|
@ -100,67 +100,83 @@ function SmartForwardMessagesModalInner({
|
|||
}
|
||||
);
|
||||
|
||||
if (!drafts.length) {
|
||||
return null;
|
||||
}
|
||||
const handleChange = useCallback(
|
||||
(
|
||||
updatedDrafts: ReadonlyArray<MessageForwardDraft>,
|
||||
caretLocation?: number
|
||||
) => {
|
||||
setDrafts(updatedDrafts);
|
||||
const isLonelyDraft = updatedDrafts.length === 1;
|
||||
const lonelyDraft = isLonelyDraft ? updatedDrafts[0] : null;
|
||||
if (lonelyDraft == null) {
|
||||
return;
|
||||
}
|
||||
const attachmentsLength = lonelyDraft.attachments?.length ?? 0;
|
||||
if (attachmentsLength === 0) {
|
||||
maybeGrabLinkPreview(
|
||||
lonelyDraft.messageBody ?? '',
|
||||
LinkPreviewSourceType.ForwardMessageModal,
|
||||
{ caretLocation }
|
||||
);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
function closeModal() {
|
||||
const closeModal = useCallback(() => {
|
||||
resetLinkPreview();
|
||||
toggleForwardMessagesModal();
|
||||
}, [toggleForwardMessagesModal]);
|
||||
|
||||
const doForwardMessages = useCallback(
|
||||
async (
|
||||
conversationIds: ReadonlyArray<string>,
|
||||
finalDrafts: ReadonlyArray<MessageForwardDraft>
|
||||
) => {
|
||||
try {
|
||||
const messages = await Promise.all(
|
||||
finalDrafts.map(async (draft): Promise<ForwardMessageData> => {
|
||||
const message = await __DEPRECATED$getMessageById(
|
||||
draft.originalMessageId
|
||||
);
|
||||
strictAssert(message, 'no message found');
|
||||
return {
|
||||
draft,
|
||||
originalMessage: message.attributes,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const didForwardSuccessfully = await maybeForwardMessages(
|
||||
messages,
|
||||
conversationIds
|
||||
);
|
||||
|
||||
if (didForwardSuccessfully) {
|
||||
closeModal();
|
||||
forwardMessagesProps?.onForward?.();
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn('doForwardMessage', Errors.toLogFormat(err));
|
||||
}
|
||||
},
|
||||
[forwardMessagesProps, closeModal]
|
||||
);
|
||||
|
||||
if (!drafts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ForwardMessagesModal
|
||||
drafts={drafts}
|
||||
candidateConversations={candidateConversations}
|
||||
doForwardMessages={async (conversationIds, finalDrafts) => {
|
||||
try {
|
||||
const messages = await Promise.all(
|
||||
finalDrafts.map(async (draft): Promise<ForwardMessageData> => {
|
||||
const message = await __DEPRECATED$getMessageById(
|
||||
draft.originalMessageId
|
||||
);
|
||||
strictAssert(message, 'no message found');
|
||||
return {
|
||||
draft,
|
||||
originalMessage: message.attributes,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const didForwardSuccessfully = await maybeForwardMessages(
|
||||
messages,
|
||||
conversationIds
|
||||
);
|
||||
|
||||
if (didForwardSuccessfully) {
|
||||
closeModal();
|
||||
forwardMessagesProps?.onForward?.();
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn('doForwardMessage', Errors.toLogFormat(err));
|
||||
}
|
||||
}}
|
||||
doForwardMessages={doForwardMessages}
|
||||
linkPreviewForSource={linkPreviewForSource}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
onClose={closeModal}
|
||||
onChange={(updatedDrafts, caretLocation) => {
|
||||
setDrafts(updatedDrafts);
|
||||
const isLonelyDraft = updatedDrafts.length === 1;
|
||||
const lonelyDraft = isLonelyDraft ? updatedDrafts[0] : null;
|
||||
if (lonelyDraft == null) {
|
||||
return;
|
||||
}
|
||||
const attachmentsLength = lonelyDraft.attachments?.length ?? 0;
|
||||
if (attachmentsLength === 0) {
|
||||
maybeGrabLinkPreview(
|
||||
lonelyDraft.messageBody ?? '',
|
||||
LinkPreviewSourceType.ForwardMessageModal,
|
||||
{ caretLocation }
|
||||
);
|
||||
}
|
||||
}}
|
||||
onChange={handleChange}
|
||||
regionCode={regionCode}
|
||||
RenderCompositionTextArea={SmartCompositionTextArea}
|
||||
removeLinkPreview={removeLinkPreview}
|
||||
|
|
|
@ -49,14 +49,14 @@ export const SmartMiniPlayer = memo(function SmartMiniPlayer({
|
|||
state = active.playing ? PlayerState.playing : PlayerState.paused;
|
||||
}
|
||||
|
||||
const title = AudioPlayerContent.isDraft(content)
|
||||
? i18n('icu:you')
|
||||
: getVoiceNoteTitle(content.current);
|
||||
|
||||
return (
|
||||
<MiniPlayer
|
||||
i18n={i18n}
|
||||
title={
|
||||
AudioPlayerContent.isDraft(content)
|
||||
? i18n('icu:you')
|
||||
: getVoiceNoteTitle(content.current)
|
||||
}
|
||||
title={title}
|
||||
onPlay={handlePlay}
|
||||
onPause={handlePause}
|
||||
onPlaybackRate={setPlaybackRate}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import * as SingleServePromise from '../../services/singleServePromise';
|
||||
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
|
||||
import {
|
||||
SafetyNumberChangeDialog,
|
||||
SafetyNumberChangeSource,
|
||||
|
@ -17,6 +18,10 @@ import { useGlobalModalActions } from '../ducks/globalModals';
|
|||
import { useStoryDistributionListsActions } from '../ducks/storyDistributionLists';
|
||||
import { getSafetyNumberChangedBlockingData } from '../selectors/globalModals';
|
||||
|
||||
function renderSafetyNumber({ contactID, onClose }: SafetyNumberProps) {
|
||||
return <SmartSafetyNumberViewer contactID={contactID} onClose={onClose} />;
|
||||
}
|
||||
|
||||
export const SmartSendAnywayDialog = memo(
|
||||
function SmartSendAnywayDialog(): JSX.Element {
|
||||
const { hideBlockingSafetyNumberChangeDialog } = useGlobalModalActions();
|
||||
|
@ -59,26 +64,36 @@ export const SmartSendAnywayDialog = memo(
|
|||
confirmText = undefined;
|
||||
}
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
cancelConversationVerification();
|
||||
explodedPromise?.resolve(false);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}, [
|
||||
cancelConversationVerification,
|
||||
explodedPromise,
|
||||
hideBlockingSafetyNumberChangeDialog,
|
||||
]);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
verifyConversationsStoppingSend();
|
||||
explodedPromise?.resolve(true);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}, [
|
||||
verifyConversationsStoppingSend,
|
||||
explodedPromise,
|
||||
hideBlockingSafetyNumberChangeDialog,
|
||||
]);
|
||||
|
||||
return (
|
||||
<SafetyNumberChangeDialog
|
||||
confirmText={confirmText}
|
||||
contacts={contacts}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
onCancel={() => {
|
||||
cancelConversationVerification();
|
||||
explodedPromise?.resolve(false);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}}
|
||||
onConfirm={() => {
|
||||
verifyConversationsStoppingSend();
|
||||
explodedPromise?.resolve(true);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
removeFromStory={removeMembersFromDistributionList}
|
||||
renderSafetyNumber={({ contactID, onClose }) => (
|
||||
<SmartSafetyNumberViewer contactID={contactID} onClose={onClose} />
|
||||
)}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import React, { memo, useCallback, useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { SmartStoryCreator } from './StoryCreator';
|
||||
import { SmartToastManager } from './ToastManager';
|
||||
|
@ -33,6 +33,7 @@ import { useItemsActions } from '../ducks/items';
|
|||
import { getHasPendingUpdate } from '../selectors/updates';
|
||||
import { getOtherTabsUnreadStats } from '../selectors/nav';
|
||||
import { getIsStoriesSettingsVisible } from '../selectors/globalModals';
|
||||
import type { StoryViewType } from '../../types/Stories';
|
||||
|
||||
function renderStoryCreator(): JSX.Element {
|
||||
return <SmartStoryCreator />;
|
||||
|
@ -92,6 +93,22 @@ export const SmartStoriesTab = memo(function SmartStoriesTab() {
|
|||
};
|
||||
}, [storiesActions]);
|
||||
|
||||
const handleForwardStory = useCallback(
|
||||
(messageId: string) => {
|
||||
toggleForwardMessagesModal([messageId]);
|
||||
},
|
||||
[toggleForwardMessagesModal]
|
||||
);
|
||||
|
||||
const handleSaveStory = useCallback(
|
||||
(story: StoryViewType) => {
|
||||
if (story.attachment) {
|
||||
saveAttachment(story.attachment, story.timestamp);
|
||||
}
|
||||
},
|
||||
[saveAttachment]
|
||||
);
|
||||
|
||||
return (
|
||||
<StoriesTab
|
||||
otherTabsUnreadStats={otherTabsUnreadStats}
|
||||
|
@ -105,14 +122,8 @@ export const SmartStoriesTab = memo(function SmartStoriesTab() {
|
|||
me={me}
|
||||
myStories={myStories}
|
||||
navTabsCollapsed={navTabsCollapsed}
|
||||
onForwardStory={messageId => {
|
||||
toggleForwardMessagesModal([messageId]);
|
||||
}}
|
||||
onSaveStory={story => {
|
||||
if (story.attachment) {
|
||||
saveAttachment(story.attachment, story.timestamp);
|
||||
}
|
||||
}}
|
||||
onForwardStory={handleForwardStory}
|
||||
onSaveStory={handleSaveStory}
|
||||
onToggleNavTabsCollapse={toggleNavTabsCollapse}
|
||||
onMediaPlaybackStart={pauseVoiceNotePlayer}
|
||||
preferredLeftPaneWidth={preferredLeftPaneWidth}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
||||
|
@ -104,6 +104,10 @@ export const SmartStoryCreator = memo(function SmartStoryCreator() {
|
|||
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
||||
const platform = useSelector(getPlatform);
|
||||
|
||||
const linkPreview = useMemo(() => {
|
||||
return linkPreviewForSource(LinkPreviewSourceType.StoryCreator);
|
||||
}, [linkPreviewForSource]);
|
||||
|
||||
return (
|
||||
<StoryCreator
|
||||
candidateConversations={candidateConversations}
|
||||
|
@ -119,7 +123,7 @@ export const SmartStoryCreator = memo(function SmartStoryCreator() {
|
|||
installedPacks={installedPacks}
|
||||
isFormattingEnabled={isFormattingEnabled}
|
||||
isSending={isSending}
|
||||
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
|
||||
linkPreview={linkPreview}
|
||||
me={me}
|
||||
mostRecentActiveStoryTimestampByGroupOrDistributionList={
|
||||
mostRecentActiveStoryTimestampByGroupOrDistributionList
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { StoryViewer } from '../../components/StoryViewer';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
|
@ -23,7 +23,7 @@ import {
|
|||
getHasAllStoriesUnmuted,
|
||||
} from '../selectors/stories';
|
||||
import { isInFullScreenCall } from '../selectors/calling';
|
||||
import { isSignalConversation } from '../../util/isSignalConversation';
|
||||
import { isSignalConversation as getIsSignalConversation } from '../../util/isSignalConversation';
|
||||
import { renderEmojiPicker } from './renderEmojiPicker';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { asyncShouldNeverBeCalled } from '../../util/shouldNeverBeCalled';
|
||||
|
@ -37,7 +37,18 @@ import { useStoriesActions } from '../ducks/stories';
|
|||
import { useIsWindowActive } from '../../hooks/useIsWindowActive';
|
||||
|
||||
export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
||||
const storiesActions = useStoriesActions();
|
||||
const {
|
||||
reactToStory,
|
||||
replyToStory,
|
||||
deleteGroupStoryReply,
|
||||
deleteGroupStoryReplyForEveryone,
|
||||
deleteStoryForEveryone,
|
||||
loadStoryReplies,
|
||||
markStoryRead,
|
||||
queueStoryDownload,
|
||||
setHasAllStoriesUnmuted,
|
||||
viewStory,
|
||||
} = useStoriesActions();
|
||||
const { onUseEmoji } = useEmojisActions();
|
||||
const {
|
||||
retryMessageSend,
|
||||
|
@ -78,66 +89,87 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
|||
selectedStoryData.messageId
|
||||
);
|
||||
|
||||
const handleGoToConversation = useCallback(
|
||||
(senderId: string) => {
|
||||
showConversation({ conversationId: senderId });
|
||||
},
|
||||
[showConversation]
|
||||
);
|
||||
|
||||
const handleReactToStory = useCallback(
|
||||
async (emoji, story) => {
|
||||
const { messageId } = story;
|
||||
reactToStory(emoji, messageId);
|
||||
},
|
||||
[reactToStory]
|
||||
);
|
||||
const handleReplyToStory = useCallback(
|
||||
(message, mentions, timestamp, story) => {
|
||||
const conversationId = storyInfo?.conversationStory?.conversationId;
|
||||
strictAssert(conversationId != null, 'conversationId is required');
|
||||
replyToStory(conversationId, message, mentions, timestamp, story);
|
||||
},
|
||||
[storyInfo, replyToStory]
|
||||
);
|
||||
const handleTextTooLong = useCallback(() => {
|
||||
showToast({ toastType: ToastType.MessageBodyTooLong });
|
||||
}, [showToast]);
|
||||
|
||||
if (!storyInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { conversationStory, distributionList, storyView } = storyInfo;
|
||||
const { group, conversationId } = conversationStory;
|
||||
|
||||
const isSignalConversation = getIsSignalConversation({
|
||||
id: conversationId,
|
||||
});
|
||||
|
||||
return (
|
||||
<StoryViewer
|
||||
currentIndex={selectedStoryData.currentIndex}
|
||||
deleteGroupStoryReply={deleteGroupStoryReply}
|
||||
deleteGroupStoryReplyForEveryone={deleteGroupStoryReplyForEveryone}
|
||||
deleteStoryForEveryone={deleteStoryForEveryone}
|
||||
distributionList={distributionList}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
group={conversationStory.group}
|
||||
group={group}
|
||||
hasActiveCall={hasActiveCall}
|
||||
hasAllStoriesUnmuted={hasAllStoriesUnmuted}
|
||||
hasViewReceiptSetting={hasViewReceiptSetting}
|
||||
i18n={i18n}
|
||||
platform={platform}
|
||||
isInternalUser={internalUser}
|
||||
isFormattingEnabled={isFormattingEnabled}
|
||||
isSignalConversation={isSignalConversation({
|
||||
id: conversationStory.conversationId,
|
||||
})}
|
||||
isInternalUser={internalUser}
|
||||
isSignalConversation={isSignalConversation}
|
||||
isWindowActive={isWindowActive}
|
||||
loadStoryReplies={loadStoryReplies}
|
||||
markStoryRead={markStoryRead}
|
||||
numStories={selectedStoryData.numStories}
|
||||
onGoToConversation={handleGoToConversation}
|
||||
onHideStory={toggleHideStories}
|
||||
onGoToConversation={senderId => {
|
||||
showConversation({ conversationId: senderId });
|
||||
}}
|
||||
onReactToStory={async (emoji, story) => {
|
||||
const { messageId } = story;
|
||||
storiesActions.reactToStory(emoji, messageId);
|
||||
}}
|
||||
onReplyToStory={(message, mentions, timestamp, story) => {
|
||||
storiesActions.replyToStory(
|
||||
conversationStory.conversationId,
|
||||
message,
|
||||
mentions,
|
||||
timestamp,
|
||||
story
|
||||
);
|
||||
}}
|
||||
onSetSkinTone={onSetSkinTone}
|
||||
onTextTooLong={() => {
|
||||
showToast({ toastType: ToastType.MessageBodyTooLong });
|
||||
}}
|
||||
onUseEmoji={onUseEmoji}
|
||||
onMediaPlaybackStart={pauseVoiceNotePlayer}
|
||||
onReactToStory={handleReactToStory}
|
||||
onReplyToStory={handleReplyToStory}
|
||||
onSetSkinTone={onSetSkinTone}
|
||||
onTextTooLong={handleTextTooLong}
|
||||
onUseEmoji={onUseEmoji}
|
||||
platform={platform}
|
||||
preferredReactionEmoji={preferredReactionEmoji}
|
||||
queueStoryDownload={queueStoryDownload}
|
||||
recentEmojis={recentEmojis}
|
||||
renderEmojiPicker={renderEmojiPicker}
|
||||
replyState={replyState}
|
||||
retryMessageSend={retryMessageSend}
|
||||
saveAttachment={internalUser ? saveAttachment : asyncShouldNeverBeCalled}
|
||||
setHasAllStoriesUnmuted={setHasAllStoriesUnmuted}
|
||||
showContactModal={showContactModal}
|
||||
showToast={showToast}
|
||||
skinTone={skinTone}
|
||||
story={storyView}
|
||||
storyViewMode={selectedStoryData.storyViewMode}
|
||||
viewStory={viewStory}
|
||||
viewTarget={selectedStoryData.viewTarget}
|
||||
{...storiesActions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -32,6 +32,10 @@ export type SmartPropsType = Readonly<{
|
|||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
}>;
|
||||
|
||||
function handleShowDebugLog() {
|
||||
window.IPC.showDebugLog();
|
||||
}
|
||||
|
||||
export const SmartToastManager = memo(function SmartToastManager({
|
||||
disableMegaphone = false,
|
||||
containerWidthBreakpoint,
|
||||
|
@ -85,7 +89,7 @@ export const SmartToastManager = memo(function SmartToastManager({
|
|||
OS={OS.getName()}
|
||||
toast={toast}
|
||||
megaphone={disableMegaphone ? undefined : megaphone}
|
||||
onShowDebugLog={() => window.IPC.showDebugLog()}
|
||||
onShowDebugLog={handleShowDebugLog}
|
||||
onUndoArchive={onUndoArchive}
|
||||
openFileInFolder={openFileInFolder}
|
||||
hideToast={hideToast}
|
||||
|
|
Loading…
Reference in a new issue