Scroll to bottom of conversation on message send
This commit is contained in:
parent
254c87a1ac
commit
5bd7eda124
13 changed files with 107 additions and 21 deletions
|
@ -63,6 +63,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
draftText: overrideProps.draftText || undefined,
|
draftText: overrideProps.draftText || undefined,
|
||||||
clearQuotedMessage: action('clearQuotedMessage'),
|
clearQuotedMessage: action('clearQuotedMessage'),
|
||||||
getQuotedMessage: action('getQuotedMessage'),
|
getQuotedMessage: action('getQuotedMessage'),
|
||||||
|
scrollToBottom: action('scrollToBottom'),
|
||||||
sortedGroupMembers: [],
|
sortedGroupMembers: [],
|
||||||
// EmojiButton
|
// EmojiButton
|
||||||
onPickEmoji: action('onPickEmoji'),
|
onPickEmoji: action('onPickEmoji'),
|
||||||
|
|
|
@ -118,6 +118,7 @@ export type OwnProps = Readonly<{
|
||||||
setQuotedMessage(message: undefined): unknown;
|
setQuotedMessage(message: undefined): unknown;
|
||||||
shouldSendHighQualityAttachments: boolean;
|
shouldSendHighQualityAttachments: boolean;
|
||||||
startRecording: () => unknown;
|
startRecording: () => unknown;
|
||||||
|
scrollToBottom: (converstionId: string) => unknown;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
@ -196,6 +197,7 @@ export const CompositionArea = ({
|
||||||
draftBodyRanges,
|
draftBodyRanges,
|
||||||
clearQuotedMessage,
|
clearQuotedMessage,
|
||||||
getQuotedMessage,
|
getQuotedMessage,
|
||||||
|
scrollToBottom,
|
||||||
sortedGroupMembers,
|
sortedGroupMembers,
|
||||||
// EmojiButton
|
// EmojiButton
|
||||||
onPickEmoji,
|
onPickEmoji,
|
||||||
|
@ -622,19 +624,21 @@ export const CompositionArea = ({
|
||||||
<div className="CompositionArea__input">
|
<div className="CompositionArea__input">
|
||||||
<CompositionInput
|
<CompositionInput
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
conversationId={conversationId}
|
||||||
|
clearQuotedMessage={clearQuotedMessage}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
large={large}
|
draftBodyRanges={draftBodyRanges}
|
||||||
|
draftText={draftText}
|
||||||
|
getQuotedMessage={getQuotedMessage}
|
||||||
inputApi={inputApiRef}
|
inputApi={inputApiRef}
|
||||||
|
large={large}
|
||||||
|
onDirtyChange={setDirty}
|
||||||
|
onEditorStateChange={onEditorStateChange}
|
||||||
onPickEmoji={onPickEmoji}
|
onPickEmoji={onPickEmoji}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onEditorStateChange={onEditorStateChange}
|
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
onDirtyChange={setDirty}
|
scrollToBottom={scrollToBottom}
|
||||||
skinTone={skinTone}
|
skinTone={skinTone}
|
||||||
draftText={draftText}
|
|
||||||
draftBodyRanges={draftBodyRanges}
|
|
||||||
clearQuotedMessage={clearQuotedMessage}
|
|
||||||
getQuotedMessage={getQuotedMessage}
|
|
||||||
sortedGroupMembers={sortedGroupMembers}
|
sortedGroupMembers={sortedGroupMembers}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,6 +20,7 @@ const story = storiesOf('Components/CompositionInput', module);
|
||||||
|
|
||||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
i18n,
|
i18n,
|
||||||
|
conversationId: 'conversation-id',
|
||||||
disabled: boolean('disabled', overrideProps.disabled || false),
|
disabled: boolean('disabled', overrideProps.disabled || false),
|
||||||
onSubmit: action('onSubmit'),
|
onSubmit: action('onSubmit'),
|
||||||
onEditorStateChange: action('onEditorStateChange'),
|
onEditorStateChange: action('onEditorStateChange'),
|
||||||
|
@ -30,6 +31,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
getQuotedMessage: action('getQuotedMessage'),
|
getQuotedMessage: action('getQuotedMessage'),
|
||||||
onPickEmoji: action('onPickEmoji'),
|
onPickEmoji: action('onPickEmoji'),
|
||||||
large: boolean('large', overrideProps.large || false),
|
large: boolean('large', overrideProps.large || false),
|
||||||
|
scrollToBottom: action('scrollToBottom'),
|
||||||
sortedGroupMembers: overrideProps.sortedGroupMembers || [],
|
sortedGroupMembers: overrideProps.sortedGroupMembers || [],
|
||||||
skinTone: select(
|
skinTone: select(
|
||||||
'skinTone',
|
'skinTone',
|
||||||
|
|
|
@ -61,6 +61,7 @@ export type InputApi = {
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
readonly i18n: LocalizerType;
|
readonly i18n: LocalizerType;
|
||||||
|
readonly conversationId: string;
|
||||||
readonly disabled?: boolean;
|
readonly disabled?: boolean;
|
||||||
readonly large?: boolean;
|
readonly large?: boolean;
|
||||||
readonly inputApi?: React.MutableRefObject<InputApi | undefined>;
|
readonly inputApi?: React.MutableRefObject<InputApi | undefined>;
|
||||||
|
@ -84,6 +85,7 @@ export type Props = {
|
||||||
): unknown;
|
): unknown;
|
||||||
getQuotedMessage(): unknown;
|
getQuotedMessage(): unknown;
|
||||||
clearQuotedMessage(): unknown;
|
clearQuotedMessage(): unknown;
|
||||||
|
scrollToBottom: (converstionId: string) => unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_LENGTH = 64 * 1024;
|
const MAX_LENGTH = 64 * 1024;
|
||||||
|
@ -92,6 +94,7 @@ const BASE_CLASS_NAME = 'module-composition-input';
|
||||||
export function CompositionInput(props: Props): React.ReactElement {
|
export function CompositionInput(props: Props): React.ReactElement {
|
||||||
const {
|
const {
|
||||||
i18n,
|
i18n,
|
||||||
|
conversationId,
|
||||||
disabled,
|
disabled,
|
||||||
large,
|
large,
|
||||||
inputApi,
|
inputApi,
|
||||||
|
@ -103,6 +106,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
||||||
draftBodyRanges,
|
draftBodyRanges,
|
||||||
getQuotedMessage,
|
getQuotedMessage,
|
||||||
clearQuotedMessage,
|
clearQuotedMessage,
|
||||||
|
scrollToBottom,
|
||||||
sortedGroupMembers,
|
sortedGroupMembers,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -238,6 +242,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
||||||
`CompositionInput: Submitting message ${timestamp} with ${mentions.length} mentions`
|
`CompositionInput: Submitting message ${timestamp} with ${mentions.length} mentions`
|
||||||
);
|
);
|
||||||
onSubmit(text, mentions, timestamp);
|
onSubmit(text, mentions, timestamp);
|
||||||
|
scrollToBottom(conversationId);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inputApi) {
|
if (inputApi) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ const candidateConversations = Array.from(Array(100), () =>
|
||||||
|
|
||||||
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
attachments: overrideProps.attachments,
|
attachments: overrideProps.attachments,
|
||||||
|
conversationId: 'conversation-id',
|
||||||
candidateConversations,
|
candidateConversations,
|
||||||
doForwardMessage: action('doForwardMessage'),
|
doForwardMessage: action('doForwardMessage'),
|
||||||
i18n,
|
i18n,
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { useAnimated } from '../hooks/useAnimated';
|
||||||
export type DataPropsType = {
|
export type DataPropsType = {
|
||||||
attachments?: Array<AttachmentType>;
|
attachments?: Array<AttachmentType>;
|
||||||
candidateConversations: ReadonlyArray<ConversationType>;
|
candidateConversations: ReadonlyArray<ConversationType>;
|
||||||
|
conversationId: string;
|
||||||
doForwardMessage: (
|
doForwardMessage: (
|
||||||
selectedContacts: Array<string>,
|
selectedContacts: Array<string>,
|
||||||
messageBody?: string,
|
messageBody?: string,
|
||||||
|
@ -74,6 +75,7 @@ const MAX_FORWARD = 5;
|
||||||
export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
attachments,
|
attachments,
|
||||||
candidateConversations,
|
candidateConversations,
|
||||||
|
conversationId,
|
||||||
doForwardMessage,
|
doForwardMessage,
|
||||||
i18n,
|
i18n,
|
||||||
isSticker,
|
isSticker,
|
||||||
|
@ -181,10 +183,10 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
}, [candidateConversations]);
|
}, [candidateConversations]);
|
||||||
|
|
||||||
const toggleSelectedConversation = useCallback(
|
const toggleSelectedConversation = useCallback(
|
||||||
(conversationId: string) => {
|
(selectedConversationId: string) => {
|
||||||
let removeContact = false;
|
let removeContact = false;
|
||||||
const nextSelectedContacts = selectedContacts.filter(contact => {
|
const nextSelectedContacts = selectedContacts.filter(contact => {
|
||||||
if (contact.id === conversationId) {
|
if (contact.id === selectedConversationId) {
|
||||||
removeContact = true;
|
removeContact = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -194,7 +196,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
setSelectedContacts(nextSelectedContacts);
|
setSelectedContacts(nextSelectedContacts);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selectedContact = contactLookup.get(conversationId);
|
const selectedContact = contactLookup.get(selectedConversationId);
|
||||||
if (selectedContact) {
|
if (selectedContact) {
|
||||||
if (selectedContact.announcementsOnly && !selectedContact.areWeAdmin) {
|
if (selectedContact.announcementsOnly && !selectedContact.areWeAdmin) {
|
||||||
setCannotMessage(true);
|
setCannotMessage(true);
|
||||||
|
@ -330,6 +332,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
) : null}
|
) : null}
|
||||||
<div className="module-ForwardMessageModal__text-edit-area">
|
<div className="module-ForwardMessageModal__text-edit-area">
|
||||||
<CompositionInput
|
<CompositionInput
|
||||||
|
conversationId={conversationId}
|
||||||
clearQuotedMessage={shouldNeverBeCalled}
|
clearQuotedMessage={shouldNeverBeCalled}
|
||||||
draftText={messageBodyText}
|
draftText={messageBodyText}
|
||||||
getQuotedMessage={noop}
|
getQuotedMessage={noop}
|
||||||
|
@ -337,6 +340,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
inputApi={inputApiRef}
|
inputApi={inputApiRef}
|
||||||
large
|
large
|
||||||
moduleClassName="module-ForwardMessageModal__input"
|
moduleClassName="module-ForwardMessageModal__input"
|
||||||
|
scrollToBottom={noop}
|
||||||
onEditorStateChange={(
|
onEditorStateChange={(
|
||||||
messageText,
|
messageText,
|
||||||
bodyRanges,
|
bodyRanges,
|
||||||
|
@ -391,7 +395,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClickArchiveButton={shouldNeverBeCalled}
|
onClickArchiveButton={shouldNeverBeCalled}
|
||||||
onClickContactCheckbox={(
|
onClickContactCheckbox={(
|
||||||
conversationId: string,
|
selectedConversationId: string,
|
||||||
disabledReason:
|
disabledReason:
|
||||||
| undefined
|
| undefined
|
||||||
| ContactCheckboxDisabledReason
|
| ContactCheckboxDisabledReason
|
||||||
|
@ -400,7 +404,9 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
disabledReason !==
|
disabledReason !==
|
||||||
ContactCheckboxDisabledReason.MaximumContactsSelected
|
ContactCheckboxDisabledReason.MaximumContactsSelected
|
||||||
) {
|
) {
|
||||||
toggleSelectedConversation(conversationId);
|
toggleSelectedConversation(
|
||||||
|
selectedConversationId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSelectConversation={shouldNeverBeCalled}
|
onSelectConversation={shouldNeverBeCalled}
|
||||||
|
|
|
@ -466,7 +466,10 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
overrideProps.isLoadingMessages === false
|
overrideProps.isLoadingMessages === false
|
||||||
),
|
),
|
||||||
items: overrideProps.items || Object.keys(items),
|
items: overrideProps.items || Object.keys(items),
|
||||||
|
loadCountdownStart: undefined,
|
||||||
|
messageHeightChangeIndex: undefined,
|
||||||
resetCounter: 0,
|
resetCounter: 0,
|
||||||
|
scrollToBottomCounter: 0,
|
||||||
scrollToIndex: overrideProps.scrollToIndex,
|
scrollToIndex: overrideProps.scrollToIndex,
|
||||||
scrollToIndexCounter: 0,
|
scrollToIndexCounter: 0,
|
||||||
totalUnread: number('totalUnread', overrideProps.totalUnread || 0),
|
totalUnread: number('totalUnread', overrideProps.totalUnread || 0),
|
||||||
|
@ -478,6 +481,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
warning: overrideProps.warning,
|
warning: overrideProps.warning,
|
||||||
|
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
isNearBottom: false,
|
||||||
renderItem,
|
renderItem,
|
||||||
renderLastSeenIndicator,
|
renderLastSeenIndicator,
|
||||||
renderHeroRow,
|
renderHeroRow,
|
||||||
|
|
|
@ -77,13 +77,14 @@ export type PropsDataType = {
|
||||||
haveNewest: boolean;
|
haveNewest: boolean;
|
||||||
haveOldest: boolean;
|
haveOldest: boolean;
|
||||||
isLoadingMessages: boolean;
|
isLoadingMessages: boolean;
|
||||||
isNearBottom?: boolean;
|
isNearBottom: boolean;
|
||||||
items: ReadonlyArray<string>;
|
items: ReadonlyArray<string>;
|
||||||
loadCountdownStart?: number;
|
loadCountdownStart: number | undefined;
|
||||||
messageHeightChangeIndex?: number;
|
messageHeightChangeIndex: number | undefined;
|
||||||
oldestUnreadIndex?: number;
|
oldestUnreadIndex: number | undefined;
|
||||||
resetCounter: number;
|
resetCounter: number;
|
||||||
scrollToIndex?: number;
|
scrollToBottomCounter: number;
|
||||||
|
scrollToIndex: number | undefined;
|
||||||
scrollToIndexCounter: number;
|
scrollToIndexCounter: number;
|
||||||
totalUnread: number;
|
totalUnread: number;
|
||||||
};
|
};
|
||||||
|
@ -959,7 +960,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
this.scrollDown(false);
|
this.scrollDown(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
public scrollDown = (setFocus?: boolean): void => {
|
public scrollDown = (setFocus?: boolean, forceScrollDown?: boolean): void => {
|
||||||
const {
|
const {
|
||||||
haveNewest,
|
haveNewest,
|
||||||
id,
|
id,
|
||||||
|
@ -976,7 +977,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
const lastId = items[items.length - 1];
|
const lastId = items[items.length - 1];
|
||||||
const lastSeenIndicatorRow = this.getLastSeenIndicatorRow();
|
const lastSeenIndicatorRow = this.getLastSeenIndicatorRow();
|
||||||
|
|
||||||
if (!this.visibleRows) {
|
if (!this.visibleRows || forceScrollDown) {
|
||||||
if (haveNewest) {
|
if (haveNewest) {
|
||||||
this.scrollToBottom(setFocus);
|
this.scrollToBottom(setFocus);
|
||||||
} else if (!isLoadingMessages) {
|
} else if (!isLoadingMessages) {
|
||||||
|
@ -1033,6 +1034,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
messageHeightChangeIndex,
|
messageHeightChangeIndex,
|
||||||
oldestUnreadIndex,
|
oldestUnreadIndex,
|
||||||
resetCounter,
|
resetCounter,
|
||||||
|
scrollToBottomCounter,
|
||||||
scrollToIndex,
|
scrollToIndex,
|
||||||
typingContact,
|
typingContact,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -1050,6 +1052,10 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
|
||||||
this.resizeHeroRow();
|
this.resizeHeroRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scrollToBottomCounter !== prevProps.scrollToBottomCounter) {
|
||||||
|
this.scrollDown(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
// There are a number of situations which can necessitate that we forget about row
|
// There are a number of situations which can necessitate that we forget about row
|
||||||
// heights previously calculated. We reset the minimum number of rows to minimize
|
// heights previously calculated. We reset the minimum number of rows to minimize
|
||||||
// unexpected changes to the scroll position. Those changes happen because
|
// unexpected changes to the scroll position. Those changes happen because
|
||||||
|
|
|
@ -245,6 +245,7 @@ export type ConversationMessageType = {
|
||||||
messageIds: Array<string>;
|
messageIds: Array<string>;
|
||||||
metrics: MessageMetricsType;
|
metrics: MessageMetricsType;
|
||||||
resetCounter: number;
|
resetCounter: number;
|
||||||
|
scrollToBottomCounter: number;
|
||||||
scrollToMessageId?: string;
|
scrollToMessageId?: string;
|
||||||
scrollToMessageCounter: number;
|
scrollToMessageCounter: number;
|
||||||
};
|
};
|
||||||
|
@ -592,6 +593,12 @@ export type SetSelectedConversationPanelDepthActionType = {
|
||||||
type: 'SET_SELECTED_CONVERSATION_PANEL_DEPTH';
|
type: 'SET_SELECTED_CONVERSATION_PANEL_DEPTH';
|
||||||
payload: { panelDepth: number };
|
payload: { panelDepth: number };
|
||||||
};
|
};
|
||||||
|
export type ScrollToBpttomActionType = {
|
||||||
|
type: 'SCROLL_TO_BOTTOM';
|
||||||
|
payload: {
|
||||||
|
conversationId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
export type ScrollToMessageActionType = {
|
export type ScrollToMessageActionType = {
|
||||||
type: 'SCROLL_TO_MESSAGE';
|
type: 'SCROLL_TO_MESSAGE';
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -741,6 +748,7 @@ export type ConversationActionType =
|
||||||
| ReplaceAvatarsActionType
|
| ReplaceAvatarsActionType
|
||||||
| ReviewGroupMemberNameCollisionActionType
|
| ReviewGroupMemberNameCollisionActionType
|
||||||
| ReviewMessageRequestNameCollisionActionType
|
| ReviewMessageRequestNameCollisionActionType
|
||||||
|
| ScrollToBpttomActionType
|
||||||
| ScrollToMessageActionType
|
| ScrollToMessageActionType
|
||||||
| SelectedConversationChangedActionType
|
| SelectedConversationChangedActionType
|
||||||
| SetComposeGroupAvatarActionType
|
| SetComposeGroupAvatarActionType
|
||||||
|
@ -810,6 +818,7 @@ export const actions = {
|
||||||
reviewMessageRequestNameCollision,
|
reviewMessageRequestNameCollision,
|
||||||
saveAvatarToDisk,
|
saveAvatarToDisk,
|
||||||
saveUsername,
|
saveUsername,
|
||||||
|
scrollToBottom,
|
||||||
scrollToMessage,
|
scrollToMessage,
|
||||||
selectMessage,
|
selectMessage,
|
||||||
setComposeGroupAvatar,
|
setComposeGroupAvatar,
|
||||||
|
@ -1685,6 +1694,15 @@ function closeMaximumGroupSizeModal(): CloseMaximumGroupSizeModalActionType {
|
||||||
function closeRecommendedGroupSizeModal(): CloseRecommendedGroupSizeModalActionType {
|
function closeRecommendedGroupSizeModal(): CloseRecommendedGroupSizeModalActionType {
|
||||||
return { type: 'CLOSE_RECOMMENDED_GROUP_SIZE_MODAL' };
|
return { type: 'CLOSE_RECOMMENDED_GROUP_SIZE_MODAL' };
|
||||||
}
|
}
|
||||||
|
function scrollToBottom(conversationId: string): ScrollToBpttomActionType {
|
||||||
|
return {
|
||||||
|
type: 'SCROLL_TO_BOTTOM',
|
||||||
|
payload: {
|
||||||
|
conversationId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function scrollToMessage(
|
function scrollToMessage(
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
messageId: string
|
messageId: string
|
||||||
|
@ -2454,6 +2472,9 @@ export function reducer(
|
||||||
scrollToMessageCounter: existingConversation
|
scrollToMessageCounter: existingConversation
|
||||||
? existingConversation.scrollToMessageCounter + 1
|
? existingConversation.scrollToMessageCounter + 1
|
||||||
: 0,
|
: 0,
|
||||||
|
scrollToBottomCounter: existingConversation
|
||||||
|
? existingConversation.scrollToBottomCounter + 1
|
||||||
|
: 0,
|
||||||
messageIds,
|
messageIds,
|
||||||
metrics: {
|
metrics: {
|
||||||
...metrics,
|
...metrics,
|
||||||
|
@ -2533,6 +2554,28 @@ export function reducer(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (action.type === 'SCROLL_TO_BOTTOM') {
|
||||||
|
const { payload } = action;
|
||||||
|
const { conversationId } = payload;
|
||||||
|
const { messagesByConversation } = state;
|
||||||
|
const existingConversation = messagesByConversation[conversationId];
|
||||||
|
|
||||||
|
if (!existingConversation) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messagesByConversation: {
|
||||||
|
...messagesByConversation,
|
||||||
|
[conversationId]: {
|
||||||
|
...existingConversation,
|
||||||
|
scrollToBottomCounter: existingConversation.scrollToBottomCounter + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (action.type === 'SCROLL_TO_MESSAGE') {
|
if (action.type === 'SCROLL_TO_MESSAGE') {
|
||||||
const { payload } = action;
|
const { payload } = action;
|
||||||
const { conversationId, messageId } = payload;
|
const { conversationId, messageId } = payload;
|
||||||
|
|
|
@ -824,6 +824,7 @@ export function _conversationMessagesSelector(
|
||||||
messageIds,
|
messageIds,
|
||||||
metrics,
|
metrics,
|
||||||
resetCounter,
|
resetCounter,
|
||||||
|
scrollToBottomCounter,
|
||||||
scrollToMessageId,
|
scrollToMessageId,
|
||||||
scrollToMessageCounter,
|
scrollToMessageCounter,
|
||||||
} = conversation;
|
} = conversation;
|
||||||
|
@ -862,7 +863,7 @@ export function _conversationMessagesSelector(
|
||||||
isLoadingMessages,
|
isLoadingMessages,
|
||||||
loadCountdownStart,
|
loadCountdownStart,
|
||||||
items,
|
items,
|
||||||
isNearBottom,
|
isNearBottom: isNearBottom || false,
|
||||||
messageHeightChangeIndex:
|
messageHeightChangeIndex:
|
||||||
isNumber(messageHeightChangeIndex) && messageHeightChangeIndex >= 0
|
isNumber(messageHeightChangeIndex) && messageHeightChangeIndex >= 0
|
||||||
? messageHeightChangeIndex
|
? messageHeightChangeIndex
|
||||||
|
@ -872,6 +873,7 @@ export function _conversationMessagesSelector(
|
||||||
? oldestUnreadIndex
|
? oldestUnreadIndex
|
||||||
: undefined,
|
: undefined,
|
||||||
resetCounter,
|
resetCounter,
|
||||||
|
scrollToBottomCounter,
|
||||||
scrollToIndex:
|
scrollToIndex:
|
||||||
isNumber(scrollToIndex) && scrollToIndex >= 0 ? scrollToIndex : undefined,
|
isNumber(scrollToIndex) && scrollToIndex >= 0 ? scrollToIndex : undefined,
|
||||||
scrollToIndexCounter: scrollToMessageCounter,
|
scrollToIndexCounter: scrollToMessageCounter,
|
||||||
|
@ -907,10 +909,16 @@ export const getConversationMessagesSelector = createSelector(
|
||||||
haveNewest: false,
|
haveNewest: false,
|
||||||
haveOldest: false,
|
haveOldest: false,
|
||||||
isLoadingMessages: false,
|
isLoadingMessages: false,
|
||||||
|
isNearBottom: false,
|
||||||
|
items: [],
|
||||||
|
loadCountdownStart: undefined,
|
||||||
|
messageHeightChangeIndex: undefined,
|
||||||
|
oldestUnreadIndex: undefined,
|
||||||
resetCounter: 0,
|
resetCounter: 0,
|
||||||
|
scrollToBottomCounter: 0,
|
||||||
|
scrollToIndex: undefined,
|
||||||
scrollToIndexCounter: 0,
|
scrollToIndexCounter: 0,
|
||||||
totalUnread: 0,
|
totalUnread: 0,
|
||||||
items: [],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import type { AttachmentType } from '../../types/Attachment';
|
||||||
|
|
||||||
export type SmartForwardMessageModalProps = {
|
export type SmartForwardMessageModalProps = {
|
||||||
attachments?: Array<AttachmentType>;
|
attachments?: Array<AttachmentType>;
|
||||||
|
conversationId: string;
|
||||||
doForwardMessage: (
|
doForwardMessage: (
|
||||||
selectedContacts: Array<string>,
|
selectedContacts: Array<string>,
|
||||||
messageBody?: string,
|
messageBody?: string,
|
||||||
|
@ -40,6 +41,7 @@ const mapStateToProps = (
|
||||||
): DataPropsType => {
|
): DataPropsType => {
|
||||||
const {
|
const {
|
||||||
attachments,
|
attachments,
|
||||||
|
conversationId,
|
||||||
doForwardMessage,
|
doForwardMessage,
|
||||||
isSticker,
|
isSticker,
|
||||||
messageBody,
|
messageBody,
|
||||||
|
@ -56,6 +58,7 @@ const mapStateToProps = (
|
||||||
return {
|
return {
|
||||||
attachments,
|
attachments,
|
||||||
candidateConversations,
|
candidateConversations,
|
||||||
|
conversationId,
|
||||||
doForwardMessage,
|
doForwardMessage,
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
isSticker,
|
isSticker,
|
||||||
|
|
|
@ -337,6 +337,7 @@ describe('both/state/ducks/conversations', () => {
|
||||||
totalUnread: 0,
|
totalUnread: 0,
|
||||||
},
|
},
|
||||||
resetCounter: 0,
|
resetCounter: 0,
|
||||||
|
scrollToBottomCounter: 0,
|
||||||
scrollToMessageCounter: 0,
|
scrollToMessageCounter: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -839,6 +840,7 @@ describe('both/state/ducks/conversations', () => {
|
||||||
messageIds: [messageId],
|
messageIds: [messageId],
|
||||||
metrics: { totalUnread: 0 },
|
metrics: { totalUnread: 0 },
|
||||||
resetCounter: 0,
|
resetCounter: 0,
|
||||||
|
scrollToBottomCounter: 0,
|
||||||
scrollToMessageCounter: 0,
|
scrollToMessageCounter: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1654,6 +1654,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
||||||
window.reduxStore,
|
window.reduxStore,
|
||||||
{
|
{
|
||||||
attachments,
|
attachments,
|
||||||
|
conversationId: this.model.id,
|
||||||
doForwardMessage: async (
|
doForwardMessage: async (
|
||||||
conversationIds: Array<string>,
|
conversationIds: Array<string>,
|
||||||
messageBody?: string,
|
messageBody?: string,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue