Edit message: Don't allow send unless message contents changed

This commit is contained in:
Scott Nonnenberg 2024-07-16 19:16:57 -07:00 committed by GitHub
parent 5987350dbe
commit f53e956810
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 70 additions and 8 deletions

View file

@ -82,6 +82,9 @@
&::before { &::before {
@include color-svg('../images/icons/v3/check/check.svg', $color-white); @include color-svg('../images/icons/v3/check/check.svg', $color-white);
} }
&:disabled {
opacity: 0.5;
}
} }
} }

View file

@ -359,7 +359,15 @@ export const CompositionArea = memo(function CompositionArea({
const editedMessageId = draftEditMessage?.targetMessageId; const editedMessageId = draftEditMessage?.targetMessageId;
const handleSubmit = useCallback( const handleSubmit = useCallback(
(message: string, bodyRanges: DraftBodyRanges, timestamp: number) => { (
message: string,
bodyRanges: DraftBodyRanges,
timestamp: number
): boolean => {
if (!dirty) {
return false;
}
emojiButtonRef.current?.close(); emojiButtonRef.current?.close();
if (editedMessageId) { if (editedMessageId) {
@ -380,9 +388,12 @@ export const CompositionArea = memo(function CompositionArea({
}); });
} }
setLarge(false); setLarge(false);
return true;
}, },
[ [
conversationId, conversationId,
dirty,
draftAttachments, draftAttachments,
editedMessageId, editedMessageId,
quotedMessageSentAt, quotedMessageSentAt,
@ -592,6 +603,7 @@ export const CompositionArea = memo(function CompositionArea({
<button <button
aria-label={i18n('icu:CompositionArea__edit-action--send')} aria-label={i18n('icu:CompositionArea__edit-action--send')}
className="CompositionArea__edit-button CompositionArea__edit-button--accept" className="CompositionArea__edit-button CompositionArea__edit-button--accept"
disabled={!dirty}
onClick={() => inputApiRef.current?.submit()} onClick={() => inputApiRef.current?.submit()}
type="button" type="button"
/> />

View file

@ -22,7 +22,12 @@ import type {
HydratedBodyRangesType, HydratedBodyRangesType,
RangeNode, RangeNode,
} from '../types/BodyRange'; } from '../types/BodyRange';
import { BodyRange, collapseRangeTree, insertRange } from '../types/BodyRange'; import {
BodyRange,
areBodyRangesEqual,
collapseRangeTree,
insertRange,
} from '../types/BodyRange';
import type { LocalizerType, ThemeType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
@ -359,7 +364,11 @@ export function CompositionInput(props: Props): React.ReactElement {
`CompositionInput: Submitting message ${timestamp} with ${bodyRanges.length} ranges` `CompositionInput: Submitting message ${timestamp} with ${bodyRanges.length} ranges`
); );
canSendRef.current = false; canSendRef.current = false;
onSubmit(text, bodyRanges, timestamp); const didSend = onSubmit(text, bodyRanges, timestamp);
if (!didSend) {
canSendRef.current = true;
}
}; };
if (inputApi) { if (inputApi) {
@ -579,7 +588,19 @@ export function CompositionInput(props: Props): React.ReactElement {
} }
if (propsRef.current.onDirtyChange) { if (propsRef.current.onDirtyChange) {
propsRef.current.onDirtyChange(text.length > 0); let isDirty: boolean = false;
if (!draftEditMessage) {
isDirty = text.length > 0;
} else if (text.trimEnd() !== draftEditMessage.body.trimEnd()) {
isDirty = true;
} else if (bodyRanges.length !== draftEditMessage.bodyRanges?.length) {
isDirty = true;
} else if (!areBodyRangesEqual(bodyRanges, draftEditMessage.bodyRanges)) {
isDirty = true;
}
propsRef.current.onDirtyChange(isDirty);
} }
}; };

View file

@ -1913,18 +1913,20 @@ function setMessageToEdit(
: undefined; : undefined;
} }
const draftBodyRanges = processBodyRanges(message, {
conversationSelector: getConversationSelector(getState()),
});
conversation.set({ conversation.set({
draftEditMessage: { draftEditMessage: {
body: message.body, body: message.body,
bodyRanges: draftBodyRanges,
editHistoryLength: message.editHistory?.length ?? 0, editHistoryLength: message.editHistory?.length ?? 0,
attachmentThumbnail, attachmentThumbnail,
preview: message.preview ? message.preview[0] : undefined, preview: message.preview ? message.preview[0] : undefined,
targetMessageId: messageId, targetMessageId: messageId,
quote: message.quote, quote: message.quote,
}, },
draftBodyRanges: processBodyRanges(message, { draftBodyRanges,
conversationSelector: getConversationSelector(getState()),
}),
}); });
dispatch({ dispatch({

View file

@ -3,7 +3,7 @@
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
import { isNumber, omit, partition } from 'lodash'; import { isEqual, isNumber, omit, orderBy, partition } from 'lodash';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log'; import * as log from '../logging/log';
@ -849,3 +849,27 @@ export function applyRangesToText(
return state; return state;
} }
// For ease of working with draft mentions in Quill, a conversationID field is present.
function normalizeBodyRanges(bodyRanges: DraftBodyRanges) {
return orderBy(bodyRanges, ['start', 'length']).map(item => {
if (BodyRange.isMention(item)) {
return { ...item, conversationID: undefined };
}
return item;
});
}
export function areBodyRangesEqual(
left: DraftBodyRanges,
right: DraftBodyRanges
): boolean {
const normalizedLeft = normalizeBodyRanges(left);
const sortedRight = normalizeBodyRanges(right);
if (normalizedLeft.length !== sortedRight.length) {
return false;
}
return isEqual(normalizedLeft, sortedRight);
}