ConversationView: Move setQuotedMessage/scrollToMessage to redux
This commit is contained in:
parent
7c68f9ef1a
commit
07f7fa93d6
18 changed files with 183 additions and 159 deletions
|
@ -1631,7 +1631,16 @@ export async function startApp(): Promise<void> {
|
|||
) {
|
||||
const { selectedMessage } = state.conversations;
|
||||
|
||||
conversation.trigger('toggle-reply', selectedMessage);
|
||||
const composerState = window.reduxStore
|
||||
? window.reduxStore.getState().composer
|
||||
: undefined;
|
||||
const quote = composerState?.quotedMessage?.quote;
|
||||
|
||||
window.reduxActions.composer.setQuoteByMessageId(
|
||||
conversation.id,
|
||||
quote ? undefined : selectedMessage
|
||||
);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
|
|
|
@ -45,12 +45,13 @@ type PropsType = {
|
|||
|
||||
executeMenuRole: ExecuteMenuRoleType;
|
||||
executeMenuAction: (action: MenuActionType) => void;
|
||||
hideToast: () => unknown;
|
||||
titleBarDoubleClick: () => void;
|
||||
toast?: {
|
||||
toastType: ToastType;
|
||||
parameters?: ReplacementValuesType;
|
||||
};
|
||||
hideToast: () => unknown;
|
||||
scrollToMessage: (conversationId: string, messageId: string) => unknown;
|
||||
toggleStoriesView: () => unknown;
|
||||
viewStory: ViewStoryActionCreatorType;
|
||||
} & ComponentProps<typeof Inbox>;
|
||||
|
@ -79,6 +80,7 @@ export function App({
|
|||
renderStories,
|
||||
renderStoryViewer,
|
||||
requestVerification,
|
||||
scrollToMessage,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
selectedMessageSource,
|
||||
|
@ -116,6 +118,7 @@ export function App({
|
|||
renderCustomizingPreferredReactionsModal
|
||||
}
|
||||
renderLeftPane={renderLeftPane}
|
||||
scrollToMessage={scrollToMessage}
|
||||
selectedConversationId={selectedConversationId}
|
||||
selectedMessage={selectedMessage}
|
||||
selectedMessageSource={selectedMessageSource}
|
||||
|
|
|
@ -43,6 +43,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
removeAttachment: action('removeAttachment'),
|
||||
theme: React.useContext(StorybookThemeContext),
|
||||
setComposerFocus: action('setComposerFocus'),
|
||||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
|
||||
// AttachmentList
|
||||
draftAttachments: overrideProps.draftAttachments || [],
|
||||
|
@ -63,8 +64,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
onCloseLinkPreview: action('onCloseLinkPreview'),
|
||||
// Quote
|
||||
quotedMessageProps: overrideProps.quotedMessageProps,
|
||||
onClickQuotedMessage: action('onClickQuotedMessage'),
|
||||
setQuotedMessage: action('setQuotedMessage'),
|
||||
scrollToMessage: action('scrollToMessage'),
|
||||
// MediaEditor
|
||||
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
||||
// MediaQualitySelector
|
||||
|
|
|
@ -97,7 +97,6 @@ export type OwnProps = Readonly<{
|
|||
linkPreviewResult?: LinkPreviewType;
|
||||
messageRequestsEnabled?: boolean;
|
||||
onClearAttachments(): unknown;
|
||||
onClickQuotedMessage(): unknown;
|
||||
onCloseLinkPreview(): unknown;
|
||||
processAttachments: (options: {
|
||||
conversationId: string;
|
||||
|
@ -119,13 +118,18 @@ export type OwnProps = Readonly<{
|
|||
}
|
||||
): unknown;
|
||||
openConversation(conversationId: string): unknown;
|
||||
quotedMessageId?: string;
|
||||
quotedMessageProps?: Omit<
|
||||
QuoteProps,
|
||||
'i18n' | 'onClick' | 'onClose' | 'withContentAbove'
|
||||
>;
|
||||
removeAttachment: (conversationId: string, filePath: string) => unknown;
|
||||
scrollToMessage: (conversationId: string, messageId: string) => unknown;
|
||||
setComposerFocus: (conversationId: string) => unknown;
|
||||
setQuotedMessage(message: undefined): unknown;
|
||||
setQuoteByMessageId(
|
||||
conversationId: string,
|
||||
messageId: string | undefined
|
||||
): unknown;
|
||||
shouldSendHighQualityAttachments: boolean;
|
||||
startRecording: () => unknown;
|
||||
theme: ThemeType;
|
||||
|
@ -179,6 +183,7 @@ export function CompositionArea({
|
|||
removeAttachment,
|
||||
sendMultiMediaMessage,
|
||||
setComposerFocus,
|
||||
setQuoteByMessageId,
|
||||
theme,
|
||||
|
||||
// AttachmentList
|
||||
|
@ -196,9 +201,9 @@ export function CompositionArea({
|
|||
linkPreviewResult,
|
||||
onCloseLinkPreview,
|
||||
// Quote
|
||||
quotedMessageId,
|
||||
quotedMessageProps,
|
||||
onClickQuotedMessage,
|
||||
setQuotedMessage,
|
||||
scrollToMessage,
|
||||
// MediaQualitySelector
|
||||
onSelectMediaQuality,
|
||||
shouldSendHighQualityAttachments,
|
||||
|
@ -639,18 +644,15 @@ export function CompositionArea({
|
|||
'CompositionArea__row--column'
|
||||
)}
|
||||
>
|
||||
{quotedMessageProps && (
|
||||
{quotedMessageId && quotedMessageProps && (
|
||||
<div className="quote-wrapper">
|
||||
<Quote
|
||||
isCompose
|
||||
{...quotedMessageProps}
|
||||
i18n={i18n}
|
||||
onClick={onClickQuotedMessage}
|
||||
onClick={() => scrollToMessage(conversationId, quotedMessageId)}
|
||||
onClose={() => {
|
||||
// This one is for redux...
|
||||
setQuotedMessage(undefined);
|
||||
// and this is for conversation_view.
|
||||
clearQuotedMessage?.();
|
||||
setQuoteByMessageId(conversationId, undefined);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -23,6 +23,7 @@ export type PropsType = {
|
|||
isCustomizingPreferredReactions: boolean;
|
||||
renderCustomizingPreferredReactionsModal: () => JSX.Element;
|
||||
renderLeftPane: () => JSX.Element;
|
||||
scrollToMessage: (conversationId: string, messageId: string) => unknown;
|
||||
selectedConversationId?: string;
|
||||
selectedMessage?: string;
|
||||
selectedMessageSource?: SelectedMessageSource;
|
||||
|
@ -36,6 +37,7 @@ export function Inbox({
|
|||
isCustomizingPreferredReactions,
|
||||
renderCustomizingPreferredReactionsModal,
|
||||
renderLeftPane,
|
||||
scrollToMessage,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
selectedMessageSource,
|
||||
|
@ -93,10 +95,11 @@ export function Inbox({
|
|||
selectedMessage &&
|
||||
selectedMessageSource !== SelectedMessageSource.Focus
|
||||
) {
|
||||
conversation.trigger('scroll-to-message', selectedMessage);
|
||||
scrollToMessage(conversation.id, selectedMessage);
|
||||
}
|
||||
}, [
|
||||
prevConversation,
|
||||
scrollToMessage,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
selectedMessageSource,
|
||||
|
|
|
@ -122,7 +122,7 @@ const defaultMessageProps: TimelineMessagesProps = {
|
|||
renderEmojiPicker: () => <div />,
|
||||
renderReactionPicker: () => <div />,
|
||||
renderAudioAttachment: () => <div>*AudioAttachment*</div>,
|
||||
replyToMessage: action('default--replyToMessage'),
|
||||
setQuoteByMessageId: action('default--setQuoteByMessageId'),
|
||||
retrySend: action('default--retrySend'),
|
||||
retryDeleteForEveryone: action('default--retryDeleteForEveryone'),
|
||||
scrollToQuotedMessage: action('default--scrollToQuotedMessage'),
|
||||
|
|
|
@ -276,7 +276,7 @@ const actions = () => ({
|
|||
updateSharedGroups: action('updateSharedGroups'),
|
||||
|
||||
reactToMessage: action('reactToMessage'),
|
||||
replyToMessage: action('replyToMessage'),
|
||||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
retrySend: action('retrySend'),
|
||||
deleteMessage: action('deleteMessage'),
|
||||
|
|
|
@ -239,7 +239,6 @@ const getActions = createSelector(
|
|||
'doubleCheckMissingQuoteReference',
|
||||
'checkForAccount',
|
||||
'reactToMessage',
|
||||
'replyToMessage',
|
||||
'retryDeleteForEveryone',
|
||||
'retrySend',
|
||||
'toggleForwardMessageModal',
|
||||
|
@ -248,6 +247,7 @@ const getActions = createSelector(
|
|||
'showMessageDetail',
|
||||
'openConversation',
|
||||
'openGiftBadge',
|
||||
'setQuoteByMessageId',
|
||||
'showContactDetail',
|
||||
'showContactModal',
|
||||
'kickOffAttachmentDownload',
|
||||
|
|
|
@ -66,7 +66,7 @@ const getDefaultProps = () => ({
|
|||
checkForAccount: action('checkForAccount'),
|
||||
clearSelectedMessage: action('clearSelectedMessage'),
|
||||
contactSupport: action('contactSupport'),
|
||||
replyToMessage: action('replyToMessage'),
|
||||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
retrySend: action('retrySend'),
|
||||
blockGroupLinkRequests: action('blockGroupLinkRequests'),
|
||||
|
|
|
@ -294,7 +294,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
renderEmojiPicker,
|
||||
renderReactionPicker,
|
||||
renderAudioAttachment,
|
||||
replyToMessage: action('replyToMessage'),
|
||||
setQuoteByMessageId: action('setQuoteByMessageId'),
|
||||
retrySend: action('retrySend'),
|
||||
retryDeleteForEveryone: action('retryDeleteForEveryone'),
|
||||
scrollToQuotedMessage: action('scrollToQuotedMessage'),
|
||||
|
|
|
@ -49,8 +49,7 @@ export type PropsActions = {
|
|||
) => void;
|
||||
retrySend: (id: string) => void;
|
||||
retryDeleteForEveryone: (id: string) => void;
|
||||
|
||||
replyToMessage: (id: string) => void;
|
||||
setQuoteByMessageId: (conversationId: string, messageId: string) => void;
|
||||
} & MessagePropsActions;
|
||||
|
||||
export type Props = PropsData &
|
||||
|
@ -83,6 +82,7 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
canRetryDeleteForEveryone,
|
||||
contact,
|
||||
payment,
|
||||
conversationId,
|
||||
containerElementRef,
|
||||
containerWidthBreakpoint,
|
||||
deletedForEveryone,
|
||||
|
@ -94,7 +94,7 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
isSticker,
|
||||
isTapToView,
|
||||
reactToMessage,
|
||||
replyToMessage,
|
||||
setQuoteByMessageId,
|
||||
renderReactionPicker,
|
||||
renderEmojiPicker,
|
||||
retrySend,
|
||||
|
@ -234,7 +234,9 @@ export function TimelineMessage(props: Props): JSX.Element {
|
|||
? openGenericAttachment
|
||||
: undefined;
|
||||
|
||||
const handleReplyToMessage = canReply ? () => replyToMessage(id) : undefined;
|
||||
const handleReplyToMessage = canReply
|
||||
? () => setQuoteByMessageId(conversationId, id)
|
||||
: undefined;
|
||||
|
||||
const handleReact = canReact ? () => toggleReactionPicker() : undefined;
|
||||
|
||||
|
|
|
@ -61,6 +61,9 @@ import { resolveAttachmentDraftData } from '../../util/resolveAttachmentDraftDat
|
|||
import { resolveDraftAttachmentOnDisk } from '../../util/resolveDraftAttachmentOnDisk';
|
||||
import { shouldShowInvalidMessageToast } from '../../util/shouldShowInvalidMessageToast';
|
||||
import { writeDraftAttachment } from '../../util/writeDraftAttachment';
|
||||
import { getMessageById } from '../../messages/getMessageById';
|
||||
import { canReply } from '../selectors/message';
|
||||
import { getConversationSelector } from '../selectors/conversations';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -143,6 +146,7 @@ export const actions = {
|
|||
sendStickerMessage,
|
||||
setComposerDisabledState,
|
||||
setComposerFocus,
|
||||
setQuoteByMessageId,
|
||||
setMediaQualitySetting,
|
||||
setQuotedMessage,
|
||||
};
|
||||
|
@ -267,7 +271,11 @@ function sendMultiMediaMessage(
|
|||
conversation.setMarkedUnread(false);
|
||||
resetLinkPreview();
|
||||
clearConversationDraftAttachments(conversationId, draftAttachments);
|
||||
dispatch(setQuotedMessage(undefined));
|
||||
setQuoteByMessageId(conversationId, undefined)(
|
||||
dispatch,
|
||||
getState,
|
||||
undefined
|
||||
);
|
||||
dispatch(resetComposer());
|
||||
},
|
||||
}
|
||||
|
@ -349,6 +357,77 @@ function getAttachmentsFromConversationModel(
|
|||
return conversation?.get('draftAttachments') || [];
|
||||
}
|
||||
|
||||
function setQuoteByMessageId(
|
||||
conversationId: string,
|
||||
messageId: string | undefined
|
||||
): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
SetComposerDisabledStateActionType | SetQuotedMessageActionType
|
||||
> {
|
||||
return async (dispatch, getState) => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('sendStickerMessage: No conversation found');
|
||||
}
|
||||
|
||||
const message = messageId ? await getMessageById(messageId) : undefined;
|
||||
const state = getState();
|
||||
|
||||
if (
|
||||
message &&
|
||||
!canReply(
|
||||
message.attributes,
|
||||
window.ConversationController.getOurConversationIdOrThrow(),
|
||||
getConversationSelector(state)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message && !message.isNormalBubble()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = conversation.get('quotedMessageId');
|
||||
if (existing !== messageId) {
|
||||
const now = Date.now();
|
||||
let activeAt = conversation.get('active_at');
|
||||
let timestamp = conversation.get('timestamp');
|
||||
|
||||
if (!activeAt && messageId) {
|
||||
activeAt = now;
|
||||
timestamp = now;
|
||||
}
|
||||
|
||||
conversation.set({
|
||||
active_at: activeAt,
|
||||
draftChanged: true,
|
||||
quotedMessageId: messageId,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
window.Signal.Data.updateConversation(conversation.attributes);
|
||||
}
|
||||
|
||||
if (message) {
|
||||
const quote = await conversation.makeQuote(message);
|
||||
dispatch(
|
||||
setQuotedMessage({
|
||||
conversationId,
|
||||
quote,
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(setComposerFocus(conversation.id));
|
||||
dispatch(setComposerDisabledState(false));
|
||||
} else {
|
||||
dispatch(setQuotedMessage(undefined));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function addAttachment(
|
||||
conversationId: string,
|
||||
attachment: InMemoryAttachmentDraftType
|
||||
|
|
|
@ -70,6 +70,7 @@ import {
|
|||
getConversationUuidsStoppingSend,
|
||||
getConversationIdsStoppedForVerification,
|
||||
getMe,
|
||||
getMessagesByConversation,
|
||||
} from '../selectors/conversations';
|
||||
import type { AvatarDataType, AvatarUpdateType } from '../../types/Avatar';
|
||||
import { getDefaultAvatars } from '../../types/Avatar';
|
||||
|
@ -113,6 +114,7 @@ import {
|
|||
buildPromotePendingAdminApprovalMemberChange,
|
||||
initiateMigrationToGroupV2 as doInitiateMigrationToGroupV2,
|
||||
} from '../../groups';
|
||||
import { getMessageById } from '../../messages/getMessageById';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -2480,16 +2482,55 @@ function closeMaximumGroupSizeModal(): CloseMaximumGroupSizeModalActionType {
|
|||
function closeRecommendedGroupSizeModal(): CloseRecommendedGroupSizeModalActionType {
|
||||
return { type: 'CLOSE_RECOMMENDED_GROUP_SIZE_MODAL' };
|
||||
}
|
||||
|
||||
function scrollToMessage(
|
||||
conversationId: string,
|
||||
messageId: string
|
||||
): ScrollToMessageActionType {
|
||||
return {
|
||||
type: 'SCROLL_TO_MESSAGE',
|
||||
payload: {
|
||||
conversationId,
|
||||
messageId,
|
||||
},
|
||||
): ThunkAction<void, RootStateType, unknown, ScrollToMessageActionType> {
|
||||
return async (dispatch, getState) => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('scrollToMessage: No conversation found');
|
||||
}
|
||||
|
||||
const message = await getMessageById(messageId);
|
||||
if (!message) {
|
||||
throw new Error(`scrollToMessage: failed to load message ${messageId}`);
|
||||
}
|
||||
if (message.get('conversationId') !== conversationId) {
|
||||
throw new Error(
|
||||
`scrollToMessage: ${messageId} didn't have conversationId ${conversationId}`
|
||||
);
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
|
||||
let isInMemory = true;
|
||||
|
||||
if (!window.MessageController.getById(messageId)) {
|
||||
isInMemory = false;
|
||||
}
|
||||
|
||||
// Message might be in memory, but not in the redux anymore because
|
||||
// we call `messageReset()` in `loadAndScroll()`.
|
||||
const messagesByConversation =
|
||||
getMessagesByConversation(state)[conversationId];
|
||||
if (!messagesByConversation?.messageIds.includes(messageId)) {
|
||||
isInMemory = false;
|
||||
}
|
||||
|
||||
if (isInMemory) {
|
||||
dispatch({
|
||||
type: 'SCROLL_TO_MESSAGE',
|
||||
payload: {
|
||||
conversationId,
|
||||
messageId,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
conversation.loadAndScroll(messageId);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,13 +33,12 @@ import { isSignalConversation } from '../../util/isSignalConversation';
|
|||
|
||||
type ExternalProps = {
|
||||
id: string;
|
||||
handleClickQuotedMessage: (id: string) => unknown;
|
||||
};
|
||||
|
||||
export type CompositionAreaPropsType = ExternalProps & ComponentPropsType;
|
||||
|
||||
const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||
const { id, handleClickQuotedMessage } = props;
|
||||
const { id } = props;
|
||||
|
||||
const conversationSelector = getConversationSelector(state);
|
||||
const conversation = conversationSelector(id);
|
||||
|
@ -108,18 +107,13 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
linkPreviewLoading,
|
||||
linkPreviewResult,
|
||||
// Quote
|
||||
quotedMessageId: quotedMessage?.quote?.messageId,
|
||||
quotedMessageProps: quotedMessage
|
||||
? getPropsForQuote(quotedMessage, {
|
||||
conversationSelector,
|
||||
ourConversationId: getUserConversationId(state),
|
||||
})
|
||||
: undefined,
|
||||
onClickQuotedMessage: () => {
|
||||
const messageId = quotedMessage?.quote?.messageId;
|
||||
if (messageId) {
|
||||
handleClickQuotedMessage(messageId);
|
||||
}
|
||||
},
|
||||
// Emojis
|
||||
recentEmojis,
|
||||
skinTone: getEmojiSkinTone(state),
|
||||
|
|
|
@ -17,9 +17,6 @@ export type PropsType = {
|
|||
conversationId: string;
|
||||
compositionAreaProps: Pick<
|
||||
CompositionAreaPropsType,
|
||||
| 'clearQuotedMessage'
|
||||
| 'getQuotedMessage'
|
||||
| 'handleClickQuotedMessage'
|
||||
| 'id'
|
||||
| 'onCancelJoinRequest'
|
||||
| 'onClearAttachments'
|
||||
|
|
|
@ -81,7 +81,6 @@ export type TimelinePropsType = ExternalProps &
|
|||
| 'openLink'
|
||||
| 'reactToMessage'
|
||||
| 'removeMember'
|
||||
| 'replyToMessage'
|
||||
| 'retryDeleteForEveryone'
|
||||
| 'retrySend'
|
||||
| 'scrollToQuotedMessage'
|
||||
|
|
|
@ -15,6 +15,7 @@ import { fakeDraftAttachment } from '../../helpers/fakeAttachment';
|
|||
describe('both/state/ducks/composer', () => {
|
||||
const QUOTED_MESSAGE = {
|
||||
conversationId: '123',
|
||||
id: 'quoted-message-id',
|
||||
quote: {
|
||||
attachments: [],
|
||||
id: 456,
|
||||
|
|
|
@ -21,18 +21,13 @@ import { strictAssert } from '../util/assert';
|
|||
import { enqueueReactionForSend } from '../reactions/enqueueReactionForSend';
|
||||
import type { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions';
|
||||
import { isGroup } from '../util/whatTypeOfConversation';
|
||||
import { findAndFormatContact } from '../util/findAndFormatContact';
|
||||
import { getPreferredBadgeSelector } from '../state/selectors/badges';
|
||||
import {
|
||||
canReply,
|
||||
isIncoming,
|
||||
isOutgoing,
|
||||
isTapToView,
|
||||
} from '../state/selectors/message';
|
||||
import {
|
||||
getConversationSelector,
|
||||
getMessagesByConversation,
|
||||
} from '../state/selectors/conversations';
|
||||
import { getConversationSelector } from '../state/selectors/conversations';
|
||||
import { getActiveCallState } from '../state/selectors/calling';
|
||||
import { getTheme } from '../state/selectors/user';
|
||||
import { ReactWrapperView } from './ReactWrapperView';
|
||||
|
@ -113,7 +108,6 @@ type MessageActionsType = {
|
|||
messageId: string,
|
||||
reaction: { emoji: string; remove: boolean }
|
||||
) => unknown;
|
||||
replyToMessage: (messageId: string) => unknown;
|
||||
retrySend: (messageId: string) => unknown;
|
||||
retryDeleteForEveryone: (messageId: string) => unknown;
|
||||
showContactDetail: (options: {
|
||||
|
@ -171,7 +165,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
|
||||
// These are triggered by InboxView
|
||||
this.listenTo(this.model, 'opened', this.onOpened);
|
||||
this.listenTo(this.model, 'scroll-to-message', this.scrollToMessage);
|
||||
this.listenTo(this.model, 'unload', (reason: string) =>
|
||||
this.unload(`model trigger - ${reason}`)
|
||||
);
|
||||
|
@ -180,18 +173,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
this.listenTo(this.model, 'open-all-media', this.showAllMedia);
|
||||
this.listenTo(this.model, 'escape-pressed', this.resetPanel);
|
||||
this.listenTo(this.model, 'show-message-details', this.showMessageDetail);
|
||||
this.listenTo(
|
||||
this.model,
|
||||
'toggle-reply',
|
||||
(messageId: string | undefined) => {
|
||||
const composerState = window.reduxStore
|
||||
? window.reduxStore.getState().composer
|
||||
: undefined;
|
||||
const quote = composerState?.quotedMessage?.quote;
|
||||
|
||||
this.setQuoteMessage(quote ? undefined : messageId);
|
||||
}
|
||||
);
|
||||
this.listenTo(
|
||||
this.model,
|
||||
'save-attachment',
|
||||
|
@ -332,7 +313,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
return;
|
||||
}
|
||||
|
||||
this.scrollToMessage(message.id);
|
||||
window.reduxActions.conversations.scrollToMessage(
|
||||
conversationId,
|
||||
message.id
|
||||
);
|
||||
};
|
||||
|
||||
const markMessageRead = async (messageId: string) => {
|
||||
|
@ -396,8 +380,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
id: this.model.id,
|
||||
onClickAddPack: () => this.showStickerManager(),
|
||||
onTextTooLong: () => showToast(ToastMessageBodyTooLong),
|
||||
getQuotedMessage: () => this.model.get('quotedMessageId'),
|
||||
clearQuotedMessage: () => this.setQuoteMessage(undefined),
|
||||
onCancelJoinRequest: async () => {
|
||||
await window.showConfirmationDialog({
|
||||
dialogName: 'GroupV2CancelRequestToJoin',
|
||||
|
@ -421,8 +403,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
window.reduxActions.composer.setMediaQualitySetting(isHQ);
|
||||
},
|
||||
|
||||
handleClickQuotedMessage: (id: string) => this.scrollToMessage(id),
|
||||
|
||||
onCloseLinkPreview: () => {
|
||||
suspendLinkPreviews();
|
||||
removeLinkPreview();
|
||||
|
@ -461,9 +441,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
showToast(ToastReactionFailed);
|
||||
}
|
||||
};
|
||||
const replyToMessage = (messageId: string) => {
|
||||
this.setQuoteMessage(messageId);
|
||||
};
|
||||
const retrySend = retryMessageSend;
|
||||
const deleteMessage = (messageId: string) => {
|
||||
this.deleteMessage(messageId);
|
||||
|
@ -555,7 +532,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
openGiftBadge,
|
||||
openLink,
|
||||
reactToMessage,
|
||||
replyToMessage,
|
||||
retrySend,
|
||||
retryDeleteForEveryone,
|
||||
showContactDetail,
|
||||
|
@ -567,37 +543,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
};
|
||||
}
|
||||
|
||||
async scrollToMessage(messageId: string): Promise<void> {
|
||||
const message = await getMessageById(messageId);
|
||||
if (!message) {
|
||||
throw new Error(`scrollToMessage: failed to load message ${messageId}`);
|
||||
}
|
||||
|
||||
const state = window.reduxStore.getState();
|
||||
|
||||
let isInMemory = true;
|
||||
|
||||
if (!window.MessageController.getById(messageId)) {
|
||||
isInMemory = false;
|
||||
}
|
||||
|
||||
// Message might be in memory, but not in the redux anymore because
|
||||
// we call `messageReset()` in `loadAndScroll()`.
|
||||
const messagesByConversation =
|
||||
getMessagesByConversation(state)[this.model.id];
|
||||
if (!messagesByConversation?.messageIds.includes(messageId)) {
|
||||
isInMemory = false;
|
||||
}
|
||||
|
||||
if (isInMemory) {
|
||||
const { scrollToMessage } = window.reduxActions.conversations;
|
||||
scrollToMessage(this.model.id, messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.loadAndScroll(messageId);
|
||||
}
|
||||
|
||||
unload(reason: string): void {
|
||||
log.info(
|
||||
'unloading conversation',
|
||||
|
@ -697,7 +642,10 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
|
||||
const quotedMessageId = this.model.get('quotedMessageId');
|
||||
if (quotedMessageId) {
|
||||
this.setQuoteMessage(quotedMessageId);
|
||||
window.reduxActions.composer.setQuoteByMessageId(
|
||||
this.model.id,
|
||||
quotedMessageId
|
||||
);
|
||||
}
|
||||
|
||||
this.model.fetchLatestGroupV2Data();
|
||||
|
@ -1532,60 +1480,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
);
|
||||
}
|
||||
|
||||
async setQuoteMessage(messageId: string | undefined): Promise<void> {
|
||||
const { model } = this;
|
||||
const message = messageId ? await getMessageById(messageId) : undefined;
|
||||
|
||||
if (
|
||||
message &&
|
||||
!canReply(
|
||||
message.attributes,
|
||||
window.ConversationController.getOurConversationIdOrThrow(),
|
||||
findAndFormatContact
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message && !message.isNormalBubble()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = model.get('quotedMessageId');
|
||||
if (existing !== messageId) {
|
||||
const now = Date.now();
|
||||
let active_at = this.model.get('active_at');
|
||||
let timestamp = this.model.get('timestamp');
|
||||
|
||||
if (!active_at && messageId) {
|
||||
active_at = now;
|
||||
timestamp = now;
|
||||
}
|
||||
|
||||
this.model.set({
|
||||
active_at,
|
||||
draftChanged: true,
|
||||
quotedMessageId: messageId,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
await this.saveModel();
|
||||
}
|
||||
|
||||
if (message) {
|
||||
const quote = await model.makeQuote(message);
|
||||
window.reduxActions.composer.setQuotedMessage({
|
||||
conversationId: model.id,
|
||||
quote,
|
||||
});
|
||||
|
||||
window.reduxActions.composer.setComposerFocus(this.model.id);
|
||||
window.reduxActions.composer.setComposerDisabledState(false);
|
||||
} else {
|
||||
window.reduxActions.composer.setQuotedMessage(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async clearAttachments(): Promise<void> {
|
||||
const draftAttachments = this.model.get('draftAttachments') || [];
|
||||
this.model.set({
|
||||
|
|
Loading…
Add table
Reference in a new issue