Use sentCounter in CompositionInput to drop old draft updates
This commit is contained in:
parent
0e606c45b0
commit
4a18667ddf
8 changed files with 121 additions and 63 deletions
|
@ -35,6 +35,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
addAttachment: action('addAttachment'),
|
||||
conversationId: '123',
|
||||
focusCounter: 0,
|
||||
sendCounter: 0,
|
||||
i18n,
|
||||
isDisabled: false,
|
||||
messageCompositionId: '456',
|
||||
|
|
|
@ -162,14 +162,15 @@ export type OwnProps = Readonly<{
|
|||
|
||||
export type Props = Pick<
|
||||
CompositionInputProps,
|
||||
| 'sortedGroupMembers'
|
||||
| 'onEditorStateChange'
|
||||
| 'onTextTooLong'
|
||||
| 'clearQuotedMessage'
|
||||
| 'draftText'
|
||||
| 'draftBodyRanges'
|
||||
| 'clearQuotedMessage'
|
||||
| 'getPreferredBadge'
|
||||
| 'getQuotedMessage'
|
||||
| 'onEditorStateChange'
|
||||
| 'onTextTooLong'
|
||||
| 'sendCounter'
|
||||
| 'sortedGroupMembers'
|
||||
> &
|
||||
Pick<
|
||||
EmojiButtonProps,
|
||||
|
@ -231,13 +232,14 @@ export function CompositionArea({
|
|||
setMediaQualitySetting,
|
||||
shouldSendHighQualityAttachments,
|
||||
// CompositionInput
|
||||
onEditorStateChange,
|
||||
onTextTooLong,
|
||||
clearQuotedMessage,
|
||||
draftText,
|
||||
draftBodyRanges,
|
||||
clearQuotedMessage,
|
||||
getPreferredBadge,
|
||||
getQuotedMessage,
|
||||
onEditorStateChange,
|
||||
onTextTooLong,
|
||||
sendCounter,
|
||||
sortedGroupMembers,
|
||||
// EmojiButton
|
||||
onPickEmoji,
|
||||
|
@ -805,6 +807,7 @@ export function CompositionArea({
|
|||
onPickEmoji={onPickEmoji}
|
||||
onSubmit={handleSubmit}
|
||||
onTextTooLong={onTextTooLong}
|
||||
sendCounter={sendCounter}
|
||||
skinTone={skinTone}
|
||||
sortedGroupMembers={sortedGroupMembers}
|
||||
theme={theme}
|
||||
|
|
|
@ -33,6 +33,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
getQuotedMessage: action('getQuotedMessage'),
|
||||
onPickEmoji: action('onPickEmoji'),
|
||||
large: boolean('large', overrideProps.large || false),
|
||||
sendCounter: 0,
|
||||
sortedGroupMembers: overrideProps.sortedGroupMembers || [],
|
||||
skinTone: select(
|
||||
'skinTone',
|
||||
|
|
|
@ -79,6 +79,7 @@ export type Props = Readonly<{
|
|||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
large?: boolean;
|
||||
inputApi?: React.MutableRefObject<InputApi | undefined>;
|
||||
sendCounter: number;
|
||||
skinTone?: EmojiPickDataType['skinTone'];
|
||||
draftText?: string;
|
||||
draftBodyRanges?: DraftBodyRangesType;
|
||||
|
@ -88,12 +89,13 @@ export type Props = Readonly<{
|
|||
sortedGroupMembers?: ReadonlyArray<ConversationType>;
|
||||
scrollerRef?: React.RefObject<HTMLDivElement>;
|
||||
onDirtyChange?(dirty: boolean): unknown;
|
||||
onEditorStateChange?(
|
||||
conversationId: string | undefined,
|
||||
messageText: string,
|
||||
bodyRanges: DraftBodyRangesType,
|
||||
caretLocation?: number
|
||||
): unknown;
|
||||
onEditorStateChange?(options: {
|
||||
bodyRanges: DraftBodyRangesType;
|
||||
caretLocation?: number;
|
||||
conversationId: string | undefined;
|
||||
messageText: string;
|
||||
sendCounter: number;
|
||||
}): unknown;
|
||||
onTextTooLong(): unknown;
|
||||
onPickEmoji(o: EmojiPickDataType): unknown;
|
||||
onSubmit(
|
||||
|
@ -134,6 +136,7 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
onSubmit,
|
||||
placeholder,
|
||||
skinTone,
|
||||
sendCounter,
|
||||
sortedGroupMembers,
|
||||
theme,
|
||||
} = props;
|
||||
|
@ -458,12 +461,13 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
setTimeout(() => {
|
||||
const selection = quill.getSelection();
|
||||
|
||||
onEditorStateChange(
|
||||
onEditorStateChange({
|
||||
bodyRanges: mentions,
|
||||
caretLocation: selection ? selection.index : undefined,
|
||||
conversationId,
|
||||
text,
|
||||
mentions,
|
||||
selection ? selection.index : undefined
|
||||
);
|
||||
messageText: text,
|
||||
sendCounter,
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,12 +86,7 @@ export function CompositionTextArea({
|
|||
}, [inputApiRef]);
|
||||
|
||||
const handleChange = React.useCallback(
|
||||
(
|
||||
_conversationId: string | undefined,
|
||||
newValue: string,
|
||||
bodyRanges: DraftBodyRangesType,
|
||||
caretLocation?: number | undefined
|
||||
) => {
|
||||
({ bodyRanges, caretLocation, messageText: newValue }) => {
|
||||
const inputEl = inputApiRef.current;
|
||||
if (!inputEl) {
|
||||
return;
|
||||
|
@ -124,21 +119,22 @@ export function CompositionTextArea({
|
|||
return (
|
||||
<div className="CompositionTextArea">
|
||||
<CompositionInput
|
||||
placeholder={placeholder}
|
||||
clearQuotedMessage={shouldNeverBeCalled}
|
||||
scrollerRef={scrollerRef}
|
||||
draftText={draftText}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
getQuotedMessage={noop}
|
||||
i18n={i18n}
|
||||
inputApi={inputApiRef}
|
||||
large
|
||||
moduleClassName="CompositionTextArea__input"
|
||||
onScroll={onScroll}
|
||||
onEditorStateChange={handleChange}
|
||||
onPickEmoji={onPickEmoji}
|
||||
onScroll={onScroll}
|
||||
onSubmit={onSubmit}
|
||||
onTextTooLong={onTextTooLong}
|
||||
draftText={draftText}
|
||||
placeholder={placeholder}
|
||||
scrollerRef={scrollerRef}
|
||||
sendCounter={0}
|
||||
theme={theme}
|
||||
/>
|
||||
<div className="CompositionTextArea__emoji">
|
||||
|
|
|
@ -225,7 +225,7 @@ export function StoryViewsNRepliesModal({
|
|||
i18n={i18n}
|
||||
inputApi={inputApiRef}
|
||||
moduleClassName="StoryViewsNRepliesModal__input"
|
||||
onEditorStateChange={(_conversationId, messageText) => {
|
||||
onEditorStateChange={({ messageText }) => {
|
||||
setMessageBodyText(messageText);
|
||||
}}
|
||||
onPickEmoji={onUseEmoji}
|
||||
|
@ -242,6 +242,7 @@ export function StoryViewsNRepliesModal({
|
|||
firstName: authorTitle,
|
||||
})
|
||||
}
|
||||
sendCounter={0}
|
||||
sortedGroupMembers={sortedGroupMembers}
|
||||
theme={ThemeType.dark}
|
||||
>
|
||||
|
|
|
@ -100,6 +100,7 @@ type ComposerStateByConversationType = {
|
|||
linkPreviewResult?: LinkPreviewType;
|
||||
messageCompositionId: UUIDStringType;
|
||||
quotedMessage?: Pick<MessageAttributesType, 'conversationId' | 'quote'>;
|
||||
sendCounter: number;
|
||||
shouldSendHighQualityAttachments?: boolean;
|
||||
};
|
||||
|
||||
|
@ -121,6 +122,7 @@ function getEmptyComposerState(): ComposerStateByConversationType {
|
|||
isDisabled: false,
|
||||
linkPreviewLoading: false,
|
||||
messageCompositionId: UUID.generate().toString(),
|
||||
sendCounter: 0,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -134,6 +136,7 @@ export function getComposerStateForConversation(
|
|||
// Actions
|
||||
|
||||
const ADD_PENDING_ATTACHMENT = 'composer/ADD_PENDING_ATTACHMENT';
|
||||
const INCREMENT_SEND_COUNTER = 'composer/INCREMENT_SEND_COUNTER';
|
||||
const REPLACE_ATTACHMENTS = 'composer/REPLACE_ATTACHMENTS';
|
||||
const RESET_COMPOSER = 'composer/RESET_COMPOSER';
|
||||
const SET_FOCUS = 'composer/SET_FOCUS';
|
||||
|
@ -149,6 +152,13 @@ type AddPendingAttachmentActionType = ReadonlyDeep<{
|
|||
};
|
||||
}>;
|
||||
|
||||
export type IncrementSendActionType = ReadonlyDeep<{
|
||||
type: typeof INCREMENT_SEND_COUNTER;
|
||||
payload: {
|
||||
conversationId: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
// eslint-disable-next-line local-rules/type-alias-readonlydeep
|
||||
export type ReplaceAttachmentsActionType = {
|
||||
type: typeof REPLACE_ATTACHMENTS;
|
||||
|
@ -202,6 +212,7 @@ type ComposerActionType =
|
|||
| AddLinkPreviewActionType
|
||||
| AddPendingAttachmentActionType
|
||||
| ConversationUnloadedActionType
|
||||
| IncrementSendActionType
|
||||
| RemoveLinkPreviewActionType
|
||||
| ReplaceAttachmentsActionType
|
||||
| ResetComposerActionType
|
||||
|
@ -217,6 +228,7 @@ export const actions = {
|
|||
addAttachment,
|
||||
addPendingAttachment,
|
||||
cancelJoinRequest,
|
||||
incrementSendCounter,
|
||||
onClearAttachments,
|
||||
onCloseLinkPreview,
|
||||
onEditorStateChange,
|
||||
|
@ -236,6 +248,15 @@ export const actions = {
|
|||
setQuotedMessage,
|
||||
};
|
||||
|
||||
function incrementSendCounter(conversationId: string): IncrementSendActionType {
|
||||
return {
|
||||
type: INCREMENT_SEND_COUNTER,
|
||||
payload: {
|
||||
conversationId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const useComposerActions = (): BoundActionCreatorsMapObject<
|
||||
typeof actions
|
||||
> => useBoundActions(actions);
|
||||
|
@ -370,6 +391,7 @@ function sendMultiMediaMessage(
|
|||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
| IncrementSendActionType
|
||||
| NoopActionType
|
||||
| ResetComposerActionType
|
||||
| SetComposerDisabledStateActionType
|
||||
|
@ -495,7 +517,7 @@ function sendMultiMediaMessage(
|
|||
getState,
|
||||
undefined
|
||||
);
|
||||
dispatch(resetComposer(conversationId));
|
||||
dispatch(incrementSendCounter(conversationId));
|
||||
dispatch(setComposerDisabledState(conversationId, false));
|
||||
},
|
||||
}
|
||||
|
@ -787,44 +809,66 @@ export function setComposerFocus(
|
|||
};
|
||||
}
|
||||
|
||||
function onEditorStateChange(
|
||||
conversationId: string | undefined,
|
||||
messageText: string,
|
||||
bodyRanges: DraftBodyRangesType,
|
||||
caretLocation?: number
|
||||
): NoopActionType {
|
||||
if (!conversationId) {
|
||||
throw new Error(
|
||||
'onEditorStateChange: Got falsey conversationId, needs local override'
|
||||
);
|
||||
}
|
||||
function onEditorStateChange({
|
||||
bodyRanges,
|
||||
caretLocation,
|
||||
conversationId,
|
||||
messageText,
|
||||
sendCounter,
|
||||
}: {
|
||||
bodyRanges: DraftBodyRangesType;
|
||||
caretLocation?: number;
|
||||
conversationId: string | undefined;
|
||||
messageText: string;
|
||||
sendCounter: number;
|
||||
}): ThunkAction<void, RootStateType, unknown, NoopActionType> {
|
||||
return (dispatch, getState) => {
|
||||
if (!conversationId) {
|
||||
throw new Error(
|
||||
'onEditorStateChange: Got falsey conversationId, needs local override'
|
||||
);
|
||||
}
|
||||
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('processAttachments: Unable to find conversation');
|
||||
}
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('processAttachments: Unable to find conversation');
|
||||
}
|
||||
|
||||
if (messageText.length && conversation.throttledBumpTyping) {
|
||||
conversation.throttledBumpTyping();
|
||||
}
|
||||
const state = getState().composer.conversations[conversationId];
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
debouncedSaveDraft(conversationId, messageText, bodyRanges);
|
||||
if (state.sendCounter !== sendCounter) {
|
||||
log.warn(
|
||||
`onEditorStateChange: Got update for conversation ${conversation.idForLogging()}`,
|
||||
`but sendCounter doesnt match (old: ${state.sendCounter}, new: ${sendCounter})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have attachments, don't add link preview
|
||||
if (
|
||||
!hasDraftAttachments(conversation.attributes.draftAttachments, {
|
||||
includePending: true,
|
||||
})
|
||||
) {
|
||||
maybeGrabLinkPreview(messageText, LinkPreviewSourceType.Composer, {
|
||||
caretLocation,
|
||||
conversationId,
|
||||
if (messageText.length && conversation.throttledBumpTyping) {
|
||||
conversation.throttledBumpTyping();
|
||||
}
|
||||
|
||||
debouncedSaveDraft(conversationId, messageText, bodyRanges);
|
||||
|
||||
// If we have attachments, don't add link preview
|
||||
if (
|
||||
!hasDraftAttachments(conversation.attributes.draftAttachments, {
|
||||
includePending: true,
|
||||
})
|
||||
) {
|
||||
maybeGrabLinkPreview(messageText, LinkPreviewSourceType.Composer, {
|
||||
caretLocation,
|
||||
conversationId,
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1296,6 +1340,12 @@ export function reducer(
|
|||
}));
|
||||
}
|
||||
|
||||
if (action.type === INCREMENT_SEND_COUNTER) {
|
||||
return updateComposerState(state, action, prevState => ({
|
||||
sendCounter: prevState.sendCounter + 1,
|
||||
}));
|
||||
}
|
||||
|
||||
if (action.type === SET_FOCUS) {
|
||||
return updateComposerState(state, action, prevState => ({
|
||||
focusCounter: prevState.focusCounter + 1,
|
||||
|
|
|
@ -86,6 +86,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
linkPreviewResult,
|
||||
messageCompositionId,
|
||||
quotedMessage,
|
||||
sendCounter,
|
||||
shouldSendHighQualityAttachments,
|
||||
} = composerStateForConversationIdSelector(id);
|
||||
|
||||
|
@ -102,6 +103,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
|||
i18n: getIntl(state),
|
||||
isDisabled,
|
||||
messageCompositionId,
|
||||
sendCounter,
|
||||
theme: getTheme(state),
|
||||
|
||||
// AudioCapture
|
||||
|
|
Loading…
Add table
Reference in a new issue