mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-02 08:13:37 +01:00
Use binary proto fields in staging
This commit is contained in:
@@ -252,8 +252,6 @@ export const CompositionArea = memo(function CompositionArea({
|
||||
theme,
|
||||
setMuteExpiration,
|
||||
|
||||
// MediaEditor
|
||||
conversationSelector,
|
||||
// AttachmentList
|
||||
draftAttachments,
|
||||
onClearAttachments,
|
||||
@@ -967,7 +965,9 @@ export const CompositionArea = memo(function CompositionArea({
|
||||
isCreatingStory={false}
|
||||
isFormattingEnabled={isFormattingEnabled}
|
||||
isSending={false}
|
||||
conversationSelector={conversationSelector}
|
||||
convertDraftBodyRangesIntoHydrated={
|
||||
convertDraftBodyRangesIntoHydrated
|
||||
}
|
||||
onClose={() => setAttachmentToEdit(undefined)}
|
||||
onDone={({
|
||||
caption,
|
||||
|
||||
@@ -5,10 +5,9 @@ import React, { useRef, useCallback, useState } from 'react';
|
||||
import type { LocalizerType } from '../types/I18N.std.js';
|
||||
import type { InputApi } from './CompositionInput.dom.js';
|
||||
import { CompositionInput } from './CompositionInput.dom.js';
|
||||
import {
|
||||
hydrateRanges,
|
||||
type DraftBodyRanges,
|
||||
type HydratedBodyRangesType,
|
||||
import type {
|
||||
DraftBodyRanges,
|
||||
HydratedBodyRangesType,
|
||||
} from '../types/BodyRange.std.js';
|
||||
import type { ThemeType } from '../types/Util.std.js';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges.preload.js';
|
||||
@@ -17,7 +16,6 @@ import { FunEmojiPicker } from './fun/FunEmojiPicker.dom.js';
|
||||
import type { FunEmojiSelection } from './fun/panels/FunPanelEmojis.dom.js';
|
||||
import type { EmojiSkinTone } from './fun/data/emojis.std.js';
|
||||
import { FunEmojiPickerButton } from './fun/FunButton.dom.js';
|
||||
import type { GetConversationByIdType } from '../state/selectors/conversations.dom.js';
|
||||
|
||||
export type CompositionTextAreaProps = {
|
||||
bodyRanges: HydratedBodyRangesType | null;
|
||||
@@ -47,7 +45,9 @@ export type CompositionTextAreaProps = {
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
draftText: string;
|
||||
theme: ThemeType;
|
||||
conversationSelector: GetConversationByIdType;
|
||||
convertDraftBodyRangesIntoHydrated: (
|
||||
bodyRanges: DraftBodyRanges | undefined
|
||||
) => HydratedBodyRangesType | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -76,7 +76,7 @@ export function CompositionTextArea({
|
||||
emojiSkinToneDefault,
|
||||
theme,
|
||||
whenToShowRemainingCount = Infinity,
|
||||
conversationSelector,
|
||||
convertDraftBodyRangesIntoHydrated,
|
||||
}: CompositionTextAreaProps): JSX.Element {
|
||||
const inputApiRef = useRef<InputApi | undefined>();
|
||||
const [characterCount, setCharacterCount] = useState(
|
||||
@@ -132,7 +132,7 @@ export function CompositionTextArea({
|
||||
);
|
||||
|
||||
const hydratedBodyRanges =
|
||||
hydrateRanges(updatedBodyRanges, conversationSelector) ?? [];
|
||||
convertDraftBodyRangesIntoHydrated(updatedBodyRanges) ?? [];
|
||||
|
||||
if (maxLength !== undefined) {
|
||||
// if we had to truncate
|
||||
@@ -150,7 +150,7 @@ export function CompositionTextArea({
|
||||
setCharacterCount(newCharacterCount);
|
||||
onChange(newValue, hydratedBodyRanges, caretLocation);
|
||||
},
|
||||
[maxLength, onChange, conversationSelector]
|
||||
[maxLength, onChange, convertDraftBodyRangesIntoHydrated]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,6 @@ import { EmojiSkinTone } from './fun/data/emojis.std.js';
|
||||
import { LoadingState } from '../util/loadable.std.js';
|
||||
import { VIDEO_MP4 } from '../types/MIME.std.js';
|
||||
import { drop } from '../util/drop.std.js';
|
||||
import { getDefaultConversation } from '../test-helpers/getDefaultConversation.std.js';
|
||||
|
||||
const { i18n } = window.SignalContext;
|
||||
|
||||
@@ -39,7 +38,7 @@ function RenderCompositionTextArea(props: SmartCompositionTextAreaProps) {
|
||||
ourConversationId="me"
|
||||
platform="darwin"
|
||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||
conversationSelector={() => getDefaultConversation()}
|
||||
convertDraftBodyRangesIntoHydrated={() => []}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
ourConversationId="me"
|
||||
platform="darwin"
|
||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||
conversationSelector={() => getDefaultConversation()}
|
||||
convertDraftBodyRangesIntoHydrated={() => []}
|
||||
/>
|
||||
),
|
||||
showToast: action('showToast'),
|
||||
|
||||
@@ -31,6 +31,7 @@ export default {
|
||||
onTextTooLong: action('onTextTooLong'),
|
||||
platform: 'darwin',
|
||||
emojiSkinToneDefault: EmojiSkinTone.None,
|
||||
convertDraftBodyRangesIntoHydrated: () => undefined,
|
||||
},
|
||||
} satisfies Meta<PropsType>;
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ import classNames from 'classnames';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { fabric } from 'fabric';
|
||||
import lodash from 'lodash';
|
||||
import type { DraftBodyRanges } from '../types/BodyRange.std.js';
|
||||
import type {
|
||||
DraftBodyRanges,
|
||||
HydratedBodyRangesType,
|
||||
} from '../types/BodyRange.std.js';
|
||||
import type { ImageStateType } from '../mediaEditor/ImageStateType.std.js';
|
||||
import type {
|
||||
InputApi,
|
||||
@@ -47,7 +50,6 @@ import { ThemeType } from '../types/Util.std.js';
|
||||
import { arrow } from '../util/keyboard.dom.js';
|
||||
import { canvasToBytes } from '../util/canvasToBytes.std.js';
|
||||
import { loadImage } from '../util/loadImage.std.js';
|
||||
import { hydrateRanges } from '../types/BodyRange.std.js';
|
||||
import { useConfirmDiscard } from '../hooks/useConfirmDiscard.dom.js';
|
||||
import { useFabricHistory } from '../mediaEditor/useFabricHistory.dom.js';
|
||||
import { usePortal } from '../hooks/usePortal.dom.js';
|
||||
@@ -62,7 +64,6 @@ import type { FunStickerSelection } from './fun/panels/FunPanelStickers.dom.js';
|
||||
import { drop } from '../util/drop.std.js';
|
||||
import type { FunTimeStickerStyle } from './fun/constants.dom.js';
|
||||
import * as Errors from '../types/errors.std.js';
|
||||
import type { GetConversationByIdType } from '../state/selectors/conversations.dom.js';
|
||||
|
||||
const { get, has, noop } = lodash;
|
||||
|
||||
@@ -85,7 +86,9 @@ export type PropsType = {
|
||||
imageToBlurHash: typeof imageToBlurHash;
|
||||
onClose: () => unknown;
|
||||
onDone: (result: MediaEditorResultType) => unknown;
|
||||
conversationSelector: GetConversationByIdType;
|
||||
convertDraftBodyRangesIntoHydrated: (
|
||||
bodyRanges: DraftBodyRanges | undefined
|
||||
) => HydratedBodyRangesType | undefined;
|
||||
} & Pick<
|
||||
CompositionInputProps,
|
||||
| 'draftText'
|
||||
@@ -168,7 +171,7 @@ export function MediaEditor({
|
||||
ourConversationId,
|
||||
platform,
|
||||
sortedGroupMembers,
|
||||
conversationSelector,
|
||||
convertDraftBodyRangesIntoHydrated,
|
||||
imageToBlurHash,
|
||||
}: PropsType): JSX.Element | null {
|
||||
const [fabricCanvas, setFabricCanvas] = useState<fabric.Canvas | undefined>();
|
||||
@@ -181,8 +184,8 @@ export function MediaEditor({
|
||||
useState<DraftBodyRanges | null>(draftBodyRanges);
|
||||
|
||||
const hydratedBodyRanges = useMemo(
|
||||
() => hydrateRanges(captionBodyRanges ?? undefined, conversationSelector),
|
||||
[captionBodyRanges, conversationSelector]
|
||||
() => convertDraftBodyRangesIntoHydrated(captionBodyRanges ?? undefined),
|
||||
[captionBodyRanges, convertDraftBodyRangesIntoHydrated]
|
||||
);
|
||||
|
||||
const inputApiRef = useRef<InputApi | undefined>();
|
||||
|
||||
@@ -93,12 +93,12 @@ export type PropsType = {
|
||||
| 'onTextTooLong'
|
||||
| 'platform'
|
||||
| 'sortedGroupMembers'
|
||||
| 'conversationSelector'
|
||||
| 'convertDraftBodyRangesIntoHydrated'
|
||||
>;
|
||||
|
||||
export function StoryCreator({
|
||||
candidateConversations,
|
||||
conversationSelector,
|
||||
convertDraftBodyRangesIntoHydrated,
|
||||
debouncedMaybeGrabLinkPreview,
|
||||
distributionLists,
|
||||
file,
|
||||
@@ -265,7 +265,9 @@ export function StoryCreator({
|
||||
isCreatingStory
|
||||
isFormattingEnabled={isFormattingEnabled}
|
||||
isSending={isSending}
|
||||
conversationSelector={conversationSelector}
|
||||
convertDraftBodyRangesIntoHydrated={
|
||||
convertDraftBodyRangesIntoHydrated
|
||||
}
|
||||
onClose={onClose}
|
||||
onDone={({
|
||||
contentType,
|
||||
|
||||
@@ -107,10 +107,8 @@ import {
|
||||
convertBackupMessageAttachmentToAttachment,
|
||||
convertFilePointerToAttachment,
|
||||
} from './util/filePointers.preload.js';
|
||||
import {
|
||||
filterAndClean,
|
||||
trimMessageWhitespace,
|
||||
} from '../../types/BodyRange.std.js';
|
||||
import { trimMessageWhitespace } from '../../types/BodyRange.std.js';
|
||||
import { filterAndClean } from '../../util/BodyRange.node.js';
|
||||
import {
|
||||
APPLICATION_OCTET_STREAM,
|
||||
stringToMIMEType,
|
||||
|
||||
@@ -96,7 +96,7 @@ import {
|
||||
fromAciUuidBytesOrString,
|
||||
fromPniUuidBytesOrUntaggedString,
|
||||
} from '../util/ServiceId.node.js';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.std.js';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.dom.js';
|
||||
import {
|
||||
getLinkPreviewSetting,
|
||||
getReadReceiptSetting,
|
||||
|
||||
@@ -44,7 +44,7 @@ import { isPermanentlyUndownloadable } from '../../jobs/AttachmentDownloadManage
|
||||
import type { ButtonVariant } from '../../components/Button.dom.js';
|
||||
import type { MessageRequestState } from '../../components/conversation/MessageRequestActionsConfirmation.dom.js';
|
||||
import type { MessageForwardDraft } from '../../types/ForwardDraft.std.js';
|
||||
import { hydrateRanges } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import {
|
||||
getConversationSelector,
|
||||
type GetConversationByIdType,
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
getIsActivelySearching,
|
||||
getQuery,
|
||||
getSearchConversation,
|
||||
} from '../selectors/search.dom.js';
|
||||
} from '../selectors/search.preload.js';
|
||||
import { getAllConversations } from '../selectors/conversations.dom.js';
|
||||
import {
|
||||
getIntl,
|
||||
|
||||
@@ -57,7 +57,7 @@ import type {
|
||||
import type { EmbeddedContactForUIType } from '../../types/EmbeddedContact.std.js';
|
||||
import { embeddedContactSelector } from '../../types/EmbeddedContact.std.js';
|
||||
import type { HydratedBodyRangesType } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import type { AssertProps } from '../../types/Util.std.js';
|
||||
import type { LinkPreviewForUIType } from '../../types/message/LinkPreviews.std.js';
|
||||
import { getMentionsRegex } from '../../types/Message.std.js';
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
getSelectedConversationId,
|
||||
} from './conversations.dom.js';
|
||||
|
||||
import { hydrateRanges } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import { createLogger } from '../../logging/log.std.js';
|
||||
import { getOwn } from '../../util/getOwn.std.js';
|
||||
|
||||
@@ -43,7 +43,8 @@ import {
|
||||
reduceStorySendStatus,
|
||||
resolveStorySendStatus,
|
||||
} from '../../util/resolveStorySendStatus.std.js';
|
||||
import { BodyRange, hydrateRanges } from '../../types/BodyRange.std.js';
|
||||
import { BodyRange } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import { getStoriesEnabled } from './items.dom.js';
|
||||
|
||||
const { pick } = lodash;
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
DraftBodyRanges,
|
||||
HydratedBodyRangesType,
|
||||
} from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import { strictAssert } from '../../util/assert.std.js';
|
||||
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.js';
|
||||
import { AutoSubstituteAsciiEmojis } from '../../quill/auto-substitute-ascii-emojis/index.dom.js';
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
// 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 type { CompositionTextAreaProps } from '../../components/CompositionTextArea.dom.js';
|
||||
import { CompositionTextArea } from '../../components/CompositionTextArea.dom.js';
|
||||
import type {
|
||||
DraftBodyRanges,
|
||||
HydratedBodyRangesType,
|
||||
} from '../../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import {
|
||||
getIntl,
|
||||
getPlatform,
|
||||
@@ -46,6 +51,15 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
||||
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
||||
const conversationSelector = useSelector(getConversationSelector);
|
||||
|
||||
const convertDraftBodyRangesIntoHydrated = useCallback(
|
||||
(
|
||||
bodyRanges: DraftBodyRanges | undefined
|
||||
): HydratedBodyRangesType | undefined => {
|
||||
return hydrateRanges(bodyRanges, conversationSelector);
|
||||
},
|
||||
[conversationSelector]
|
||||
);
|
||||
|
||||
return (
|
||||
<CompositionTextArea
|
||||
{...props}
|
||||
@@ -58,7 +72,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
||||
onTextTooLong={onTextTooLong}
|
||||
platform={platform}
|
||||
ourConversationId={ourConversationId}
|
||||
conversationSelector={conversationSelector}
|
||||
convertDraftBodyRangesIntoHydrated={convertDraftBodyRangesIntoHydrated}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ import {
|
||||
getSearchConversation,
|
||||
getSearchResults,
|
||||
getStartSearchCounter,
|
||||
} from '../selectors/search.dom.js';
|
||||
} from '../selectors/search.preload.js';
|
||||
import {
|
||||
isUpdateDownloaded as getIsUpdateDownloaded,
|
||||
isOSUnsupported,
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useNavActions } from '../ducks/nav.std.js';
|
||||
import { NavTab, SettingsPage } from '../../types/Nav.std.js';
|
||||
import type { ChatFolderParams } from '../../types/ChatFolder.std.js';
|
||||
import { getSelectedLocation } from '../selectors/nav.preload.js';
|
||||
import { getIsActivelySearching } from '../selectors/search.dom.js';
|
||||
import { getIsActivelySearching } from '../selectors/search.preload.js';
|
||||
|
||||
export const SmartLeftPaneConversationListItemContextMenu: FC<RenderConversationListItemContextMenuProps> =
|
||||
memo(function SmartLeftPaneConversationListItemContextMenu(props) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useSelector } from 'react-redux';
|
||||
import { MessageSearchResult } from '../../components/conversationList/MessageSearchResult.dom.js';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges.preload.js';
|
||||
import { getIntl, getTheme } from '../selectors/user.std.js';
|
||||
import { getMessageSearchResultSelector } from '../selectors/search.dom.js';
|
||||
import { getMessageSearchResultSelector } from '../selectors/search.preload.js';
|
||||
import { createLogger } from '../../logging/log.std.js';
|
||||
import { useConversationsActions } from '../ducks/conversations.preload.js';
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import React, { memo, useMemo, useCallback } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ThemeType } from '../../types/Util.std.js';
|
||||
import { LinkPreviewSourceType } from '../../types/LinkPreview.std.js';
|
||||
import type {
|
||||
DraftBodyRanges,
|
||||
HydratedBodyRangesType,
|
||||
} from '../../types/BodyRange.std.js';
|
||||
import { StoryCreator } from '../../components/StoryCreator.dom.js';
|
||||
import {
|
||||
getCandidateContactsForNewGroup,
|
||||
@@ -31,6 +35,7 @@ import {
|
||||
} from '../selectors/items.dom.js';
|
||||
import { imageToBlurHash } from '../../util/imageToBlurHash.dom.js';
|
||||
import { processAttachment } from '../../util/processAttachment.preload.js';
|
||||
import { hydrateRanges } from '../../util/BodyRange.node.js';
|
||||
import { useEmojisActions } from '../ducks/emojis.preload.js';
|
||||
import { useAudioPlayerActions } from '../ducks/audioPlayer.preload.js';
|
||||
import { useComposerActions } from '../ducks/composer.preload.js';
|
||||
@@ -103,10 +108,19 @@ export const SmartStoryCreator = memo(function SmartStoryCreator() {
|
||||
return linkPreviewForSource(LinkPreviewSourceType.StoryCreator);
|
||||
}, [linkPreviewForSource]);
|
||||
|
||||
const convertDraftBodyRangesIntoHydrated = useCallback(
|
||||
(
|
||||
bodyRanges: DraftBodyRanges | undefined
|
||||
): HydratedBodyRangesType | undefined => {
|
||||
return hydrateRanges(bodyRanges, conversationSelector);
|
||||
},
|
||||
[conversationSelector]
|
||||
);
|
||||
|
||||
return (
|
||||
<StoryCreator
|
||||
candidateConversations={candidateConversations}
|
||||
conversationSelector={conversationSelector}
|
||||
convertDraftBodyRangesIntoHydrated={convertDraftBodyRangesIntoHydrated}
|
||||
debouncedMaybeGrabLinkPreview={debouncedMaybeGrabLinkPreview}
|
||||
distributionLists={distributionLists}
|
||||
file={file}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
getIsSearchingInAConversation,
|
||||
getMessageSearchResultSelector,
|
||||
getSearchResults,
|
||||
} from '../../../state/selectors/search.dom.js';
|
||||
} from '../../../state/selectors/search.preload.js';
|
||||
import { makeLookup } from '../../../util/makeLookup.std.js';
|
||||
import { generateAci } from '../../../types/ServiceId.std.js';
|
||||
import {
|
||||
|
||||
@@ -153,7 +153,7 @@ import { isNotNil } from '../util/isNotNil.std.js';
|
||||
import { chunk } from '../util/iterables.std.js';
|
||||
import { inspectUnknownFieldTags } from '../util/inspectProtobufs.std.js';
|
||||
import { incrementMessageCounter } from '../util/incrementMessageCounter.preload.js';
|
||||
import { filterAndClean } from '../types/BodyRange.std.js';
|
||||
import { filterAndClean } from '../util/BodyRange.node.js';
|
||||
import {
|
||||
getCallEventForProto,
|
||||
getCallLogEventForProto,
|
||||
|
||||
@@ -99,7 +99,7 @@ import {
|
||||
getProtoForCallHistory,
|
||||
} from '../util/callDisposition.preload.js';
|
||||
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types.std.js';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.std.js';
|
||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.dom.js';
|
||||
import type { GroupSendToken } from '../types/GroupSendEndorsements.std.js';
|
||||
import type { OutgoingPollVote, PollCreateType } from '../types/Polls.dom.js';
|
||||
import { itemStorage } from './Storage.preload.js';
|
||||
@@ -433,11 +433,12 @@ class Message {
|
||||
proto.reaction = new Proto.DataMessage.Reaction();
|
||||
proto.reaction.emoji = this.reaction.emoji || null;
|
||||
proto.reaction.remove = this.reaction.remove || false;
|
||||
proto.reaction.targetAuthorAci = this.reaction.targetAuthorAci || null;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
proto.reaction.targetAuthorAciBinary = this.reaction.targetAuthorAci
|
||||
? toAciObject(this.reaction.targetAuthorAci).getRawUuidBytes()
|
||||
: null;
|
||||
} else {
|
||||
proto.reaction.targetAuthorAci = this.reaction.targetAuthorAci || null;
|
||||
}
|
||||
proto.reaction.targetSentTimestamp =
|
||||
this.reaction.targetTimestamp === undefined
|
||||
@@ -543,11 +544,12 @@ class Message {
|
||||
|
||||
quote.id =
|
||||
this.quote.id === undefined ? null : Long.fromNumber(this.quote.id);
|
||||
quote.authorAci = this.quote.authorAci || null;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
quote.authorAciBinary = this.quote.authorAci
|
||||
? toAciObject(this.quote.authorAci).getRawUuidBytes()
|
||||
: null;
|
||||
} else {
|
||||
quote.authorAci = this.quote.authorAci || null;
|
||||
}
|
||||
quote.text = this.quote.text || null;
|
||||
quote.attachments = this.quote.attachments.slice() || [];
|
||||
@@ -557,11 +559,12 @@ class Message {
|
||||
bodyRange.start = range.start;
|
||||
bodyRange.length = range.length;
|
||||
if (BodyRange.isMention(range)) {
|
||||
bodyRange.mentionAci = range.mentionAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
bodyRange.mentionAciBinary = toAciObject(
|
||||
range.mentionAci
|
||||
).getRawUuidBytes();
|
||||
} else {
|
||||
bodyRange.mentionAci = range.mentionAci;
|
||||
}
|
||||
} else if (BodyRange.isFormatting(range)) {
|
||||
bodyRange.style = range.style;
|
||||
@@ -601,6 +604,15 @@ class Message {
|
||||
const { start, length } = bodyRange;
|
||||
|
||||
if (BodyRange.isMention(bodyRange)) {
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
return {
|
||||
start,
|
||||
length,
|
||||
mentionAciBinary: toAciObject(
|
||||
bodyRange.mentionAci
|
||||
).getRawUuidBytes(),
|
||||
};
|
||||
}
|
||||
return {
|
||||
start,
|
||||
length,
|
||||
@@ -632,11 +644,12 @@ class Message {
|
||||
|
||||
const storyContext = new StoryContext();
|
||||
if (this.storyContext.authorAci) {
|
||||
storyContext.authorAci = this.storyContext.authorAci;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
storyContext.authorAciBinary = toAciObject(
|
||||
this.storyContext.authorAci
|
||||
).getRawUuidBytes();
|
||||
} else {
|
||||
storyContext.authorAci = this.storyContext.authorAci;
|
||||
}
|
||||
}
|
||||
storyContext.sentTimestamp = Long.fromNumber(this.storyContext.timestamp);
|
||||
@@ -1424,10 +1437,11 @@ export class MessageSender {
|
||||
sentMessage.destinationE164 = destinationE164;
|
||||
}
|
||||
if (destinationServiceId) {
|
||||
sentMessage.destinationServiceId = destinationServiceId;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
sentMessage.destinationServiceIdBinary =
|
||||
toServiceIdObject(destinationServiceId).getServiceIdBinary();
|
||||
} else {
|
||||
sentMessage.destinationServiceId = destinationServiceId;
|
||||
}
|
||||
}
|
||||
if (expirationStartTimestamp) {
|
||||
@@ -1458,10 +1472,11 @@ export class MessageSender {
|
||||
if (conv) {
|
||||
const serviceId = conv.getServiceId();
|
||||
if (serviceId) {
|
||||
status.destinationServiceId = serviceId;
|
||||
if (isProtoBinaryEncodingEnabled()) {
|
||||
status.destinationServiceIdBinary =
|
||||
toServiceIdObject(serviceId).getServiceIdBinary();
|
||||
} else {
|
||||
status.destinationServiceId = serviceId;
|
||||
}
|
||||
}
|
||||
if (isPniString(serviceId)) {
|
||||
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
import { SECOND, DurationInSeconds } from '../util/durations/index.std.js';
|
||||
import type { AnyPaymentEvent } from '../types/Payment.std.js';
|
||||
import { PaymentEventKind } from '../types/Payment.std.js';
|
||||
import { filterAndClean } from '../types/BodyRange.std.js';
|
||||
import { filterAndClean } from '../util/BodyRange.node.js';
|
||||
import { bytesToUuid } from '../util/uuidToBytes.std.js';
|
||||
import { createName } from '../util/attachmentPath.node.js';
|
||||
import { partitionBodyAndNormalAttachments } from '../util/Attachment.std.js';
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
} from '../util/search.std.js';
|
||||
import { assertDev } from '../util/assert.std.js';
|
||||
import type { AciString } from './ServiceId.std.js';
|
||||
import { normalizeAci } from '../util/normalizeAci.std.js';
|
||||
|
||||
const { isEqual, isNumber, omit, orderBy, partition } = lodash;
|
||||
|
||||
@@ -143,102 +142,6 @@ export type RangeNode = BodyRange<
|
||||
}
|
||||
>;
|
||||
|
||||
const { BOLD, ITALIC, MONOSPACE, SPOILER, STRIKETHROUGH, NONE } =
|
||||
BodyRange.Style;
|
||||
const MAX_PER_TYPE = 250;
|
||||
const MENTION_NAME = 'mention';
|
||||
|
||||
// We drop unknown bodyRanges and remove extra stuff so they serialize properly
|
||||
export function filterAndClean(
|
||||
ranges: ReadonlyArray<Proto.IBodyRange | RawBodyRange> | undefined | null
|
||||
): ReadonlyArray<RawBodyRange> | undefined {
|
||||
if (!ranges) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const countByTypeRecord: Record<
|
||||
BodyRange.Style | typeof MENTION_NAME,
|
||||
number
|
||||
> = {
|
||||
[MENTION_NAME]: 0,
|
||||
[BOLD]: 0,
|
||||
[ITALIC]: 0,
|
||||
[MONOSPACE]: 0,
|
||||
[SPOILER]: 0,
|
||||
[STRIKETHROUGH]: 0,
|
||||
[NONE]: 0,
|
||||
};
|
||||
|
||||
return ranges
|
||||
.map(range => {
|
||||
const { start: startFromRange, length, ...restOfRange } = range;
|
||||
|
||||
const start = startFromRange ?? 0;
|
||||
if (!isNumber(length)) {
|
||||
log.warn('filterAndClean: Dropping bodyRange with non-number length');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let mentionAci: AciString | undefined;
|
||||
if ('mentionAci' in range && range.mentionAci) {
|
||||
mentionAci = normalizeAci(range.mentionAci, 'BodyRange.mentionAci');
|
||||
}
|
||||
|
||||
if (mentionAci) {
|
||||
countByTypeRecord[MENTION_NAME] += 1;
|
||||
if (countByTypeRecord[MENTION_NAME] > MAX_PER_TYPE) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...restOfRange,
|
||||
start,
|
||||
length,
|
||||
mentionAci,
|
||||
};
|
||||
}
|
||||
if ('style' in range && range.style) {
|
||||
countByTypeRecord[range.style] += 1;
|
||||
if (countByTypeRecord[range.style] > MAX_PER_TYPE) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...restOfRange,
|
||||
start,
|
||||
length,
|
||||
style: range.style,
|
||||
};
|
||||
}
|
||||
|
||||
log.warn('filterAndClean: Dropping unknown bodyRange');
|
||||
return undefined;
|
||||
})
|
||||
.filter(isNotNil);
|
||||
}
|
||||
|
||||
export function hydrateRanges(
|
||||
ranges: ReadonlyArray<BodyRange<object>> | undefined,
|
||||
conversationSelector: (id: string) => { id: string; title: string }
|
||||
): Array<HydratedBodyRangeType> | undefined {
|
||||
if (!ranges) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return filterAndClean(ranges)?.map(range => {
|
||||
if (BodyRange.isMention(range)) {
|
||||
const conversation = conversationSelector(range.mentionAci);
|
||||
|
||||
return {
|
||||
...range,
|
||||
conversationID: conversation.id,
|
||||
replacementText: conversation.title,
|
||||
};
|
||||
}
|
||||
|
||||
return range;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a range into an existing range tree, splitting up the range if it intersects
|
||||
* with an existing range
|
||||
@@ -835,7 +738,10 @@ export function applyRangesToText(
|
||||
|
||||
if (options.replaceSpoilers) {
|
||||
state = _applyRangeOfType(state, bodyRange => {
|
||||
return BodyRange.isFormatting(bodyRange) && bodyRange.style === SPOILER;
|
||||
return (
|
||||
BodyRange.isFormatting(bodyRange) &&
|
||||
bodyRange.style === BodyRange.Style.SPOILER
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
129
ts/util/BodyRange.node.ts
Normal file
129
ts/util/BodyRange.node.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import lodash from 'lodash';
|
||||
|
||||
import type { SignalService as Proto } from '../protobuf/index.std.js';
|
||||
import {
|
||||
BodyRange,
|
||||
type RawBodyRange,
|
||||
type HydratedBodyRangeType,
|
||||
} from '../types/BodyRange.std.js';
|
||||
import type { AciString } from '../types/ServiceId.std.js';
|
||||
import { createLogger } from '../logging/log.std.js';
|
||||
import { isNotNil } from './isNotNil.std.js';
|
||||
import { dropNull } from './dropNull.std.js';
|
||||
import { fromAciUuidBytesOrString } from './ServiceId.node.js';
|
||||
|
||||
const { isNumber } = lodash;
|
||||
|
||||
const log = createLogger('BodyRange');
|
||||
|
||||
const { BOLD, ITALIC, MONOSPACE, SPOILER, STRIKETHROUGH, NONE } =
|
||||
BodyRange.Style;
|
||||
const MENTION_NAME = 'mention';
|
||||
const MAX_PER_TYPE = 250;
|
||||
|
||||
// We drop unknown bodyRanges and remove extra stuff so they serialize properly
|
||||
export function filterAndClean(
|
||||
ranges: ReadonlyArray<Proto.IBodyRange | RawBodyRange> | undefined | null
|
||||
): ReadonlyArray<RawBodyRange> | undefined {
|
||||
if (!ranges) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const countByTypeRecord: Record<
|
||||
BodyRange.Style | typeof MENTION_NAME,
|
||||
number
|
||||
> = {
|
||||
[MENTION_NAME]: 0,
|
||||
[BOLD]: 0,
|
||||
[ITALIC]: 0,
|
||||
[MONOSPACE]: 0,
|
||||
[SPOILER]: 0,
|
||||
[STRIKETHROUGH]: 0,
|
||||
[NONE]: 0,
|
||||
};
|
||||
|
||||
return ranges
|
||||
.map(range => {
|
||||
const { start: startFromRange, length, ...restOfRange } = range;
|
||||
|
||||
const start = startFromRange ?? 0;
|
||||
if (!isNumber(length)) {
|
||||
log.warn('filterAndClean: Dropping bodyRange with non-number length');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let rawMentionAci: string | undefined;
|
||||
let mentionAciBinary: Uint8Array | undefined;
|
||||
if ('mentionAci' in range) {
|
||||
rawMentionAci = dropNull(range.mentionAci);
|
||||
}
|
||||
if ('mentionAciBinary' in range) {
|
||||
mentionAciBinary = dropNull(range.mentionAciBinary);
|
||||
}
|
||||
|
||||
let mentionAci: AciString | undefined;
|
||||
if (rawMentionAci != null || mentionAciBinary?.length) {
|
||||
mentionAci = fromAciUuidBytesOrString(
|
||||
mentionAciBinary,
|
||||
rawMentionAci,
|
||||
'BodyRange.mentionAci'
|
||||
);
|
||||
}
|
||||
|
||||
if (mentionAci) {
|
||||
countByTypeRecord[MENTION_NAME] += 1;
|
||||
if (countByTypeRecord[MENTION_NAME] > MAX_PER_TYPE) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...restOfRange,
|
||||
start,
|
||||
length,
|
||||
mentionAci,
|
||||
};
|
||||
}
|
||||
if ('style' in range && range.style) {
|
||||
countByTypeRecord[range.style] += 1;
|
||||
if (countByTypeRecord[range.style] > MAX_PER_TYPE) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
...restOfRange,
|
||||
start,
|
||||
length,
|
||||
style: range.style,
|
||||
};
|
||||
}
|
||||
|
||||
log.warn('filterAndClean: Dropping unknown bodyRange');
|
||||
return undefined;
|
||||
})
|
||||
.filter(isNotNil);
|
||||
}
|
||||
|
||||
export function hydrateRanges(
|
||||
ranges: ReadonlyArray<BodyRange<object>> | undefined,
|
||||
conversationSelector: (id: string) => { id: string; title: string }
|
||||
): Array<HydratedBodyRangeType> | undefined {
|
||||
if (!ranges) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return filterAndClean(ranges)?.map(range => {
|
||||
if (BodyRange.isMention(range)) {
|
||||
const conversation = conversationSelector(range.mentionAci);
|
||||
|
||||
return {
|
||||
...range,
|
||||
conversationID: conversation.id,
|
||||
replacementText: conversation.title,
|
||||
};
|
||||
}
|
||||
|
||||
return range;
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
import type { ConversationAttributesType } from '../model-types.d.ts';
|
||||
import type { DraftPreviewType } from '../state/ducks/conversations.preload.js';
|
||||
import { findAndFormatContact } from './findAndFormatContact.preload.js';
|
||||
import { hydrateRanges } from '../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from './BodyRange.node.js';
|
||||
import { isVoiceMessage } from './Attachment.std.js';
|
||||
import { stripNewlinesForLeftPane } from './stripNewlinesForLeftPane.std.js';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { ConversationAttributesType } from '../model-types.d.ts';
|
||||
import type { LastMessageType } from '../state/ducks/conversations.preload.js';
|
||||
import { dropNull } from './dropNull.std.js';
|
||||
import { findAndFormatContact } from './findAndFormatContact.preload.js';
|
||||
import { hydrateRanges } from '../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from './BodyRange.node.js';
|
||||
import { stripNewlinesForLeftPane } from './stripNewlinesForLeftPane.std.js';
|
||||
|
||||
export function getLastMessage(
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d.ts';
|
||||
import { applyRangesToText, hydrateRanges } from '../types/BodyRange.std.js';
|
||||
import { applyRangesToText } from '../types/BodyRange.std.js';
|
||||
import { hydrateRanges } from './BodyRange.node.js';
|
||||
import { findAndFormatContact } from './findAndFormatContact.preload.js';
|
||||
import { getNotificationDataForMessage } from './getNotificationDataForMessage.preload.js';
|
||||
import { isConversationAccepted } from './isConversationAccepted.preload.js';
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { isTestOrMockEnvironment } from '../environment.std.js';
|
||||
import { isStagingServer } from './isStagingServer.dom.js';
|
||||
|
||||
export function isProtoBinaryEncodingEnabled(): boolean {
|
||||
if (isTestOrMockEnvironment()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isStagingServer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: DESKTOP-8938
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user