Improvements to the media editor
This commit is contained in:
parent
e8eb7638c4
commit
d0296ececa
61 changed files with 1124 additions and 969 deletions
|
@ -2,8 +2,6 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
.MediaEditor {
|
.MediaEditor {
|
||||||
$tools-height: 44px;
|
|
||||||
|
|
||||||
background: $color-gray-95;
|
background: $color-gray-95;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -20,7 +18,7 @@
|
||||||
&__container {
|
&__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-block: 22px;
|
padding-block: 48px;
|
||||||
padding-inline: 60px;
|
padding-inline: 60px;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -47,12 +45,12 @@
|
||||||
&__control {
|
&__control {
|
||||||
@include button-reset;
|
@include button-reset;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 32px;
|
border-radius: 20px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-block: 0;
|
margin-block: 0;
|
||||||
margin-inline: 18px;
|
margin-inline: 20px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
|
||||||
|
@ -119,7 +117,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__toolbar {
|
&__tools {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -127,6 +125,11 @@
|
||||||
padding: 22px;
|
padding: 22px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
&--input {
|
||||||
|
margin-inline: 24px;
|
||||||
|
min-width: 410px;
|
||||||
|
}
|
||||||
|
|
||||||
&--buttons {
|
&--buttons {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -134,18 +137,12 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--space {
|
|
||||||
height: $tools-height;
|
|
||||||
margin-bottom: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__caption {
|
&__caption {
|
||||||
height: $tools-height;
|
height: 44px;
|
||||||
margin-bottom: 22px;
|
|
||||||
|
|
||||||
&__add-caption-button {
|
&__add-caption-button {
|
||||||
@include button-reset;
|
@include button-reset;
|
||||||
border-radius: 9999px;
|
@include rounded-corners;
|
||||||
background: $color-gray-90;
|
background: $color-gray-90;
|
||||||
color: $color-gray-15;
|
color: $color-gray-15;
|
||||||
padding-block: 8px;
|
padding-block: 8px;
|
||||||
|
@ -162,25 +159,36 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__controls {
|
&__tools-row-1 {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
height: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
max-width: 596px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__tools-row-2 {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 36px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
max-width: 596px;
|
max-width: 596px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__tools {
|
&__toolbar {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: $color-gray-90;
|
background-color: $color-gray-90;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: $tools-height;
|
height: 36px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 22px;
|
|
||||||
padding-block: 14px;
|
padding-block: 14px;
|
||||||
padding-inline: 12px;
|
padding-inline: 12px;
|
||||||
|
margin-inline: 16px;
|
||||||
|
|
||||||
&__tool,
|
&__tool,
|
||||||
&__tool__button {
|
&__tool__button {
|
||||||
|
@ -206,13 +214,6 @@
|
||||||
margin-inline: 8px;
|
margin-inline: 8px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
&--words {
|
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
padding-block: 0;
|
|
||||||
padding-inline: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--draw-pen__button {
|
&--draw-pen__button {
|
||||||
@include icon('v3/brush/brush-pen-compact.svg');
|
@include icon('v3/brush/brush-pen-compact.svg');
|
||||||
}
|
}
|
||||||
|
@ -319,4 +320,55 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__history-buttons {
|
||||||
|
inset-inline-start: 24px;
|
||||||
|
position: absolute;
|
||||||
|
top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__close {
|
||||||
|
@include button-reset;
|
||||||
|
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-end: 24px;
|
||||||
|
top: 24px;
|
||||||
|
width: 20px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
@include color-svg('../images/icons/v3/x/x.svg', $color-gray-75);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include dark-theme {
|
||||||
|
@include color-svg('../images/icons/v3/x/x.svg', $color-gray-15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 2px $color-ultramarine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__crop-preset {
|
||||||
|
@include button-reset;
|
||||||
|
color: $color-white;
|
||||||
|
height: 28px;
|
||||||
|
margin-inline: 12px;
|
||||||
|
padding-block: 5px;
|
||||||
|
padding-inline: 12px;
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
@include rounded-corners;
|
||||||
|
background: $color-gray-80;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ import { isGroupV1, isGroupV2 } from './util/whatTypeOfConversation';
|
||||||
import type { ServiceIdString, AciString, PniString } from './types/ServiceId';
|
import type { ServiceIdString, AciString, PniString } from './types/ServiceId';
|
||||||
import {
|
import {
|
||||||
isServiceIdString,
|
isServiceIdString,
|
||||||
normalizeAci,
|
|
||||||
normalizePni,
|
normalizePni,
|
||||||
normalizeServiceId,
|
normalizeServiceId,
|
||||||
} from './types/ServiceId';
|
} from './types/ServiceId';
|
||||||
|
import { normalizeAci } from './util/normalizeAci';
|
||||||
import { sleep } from './util/sleep';
|
import { sleep } from './util/sleep';
|
||||||
import { isNotNil } from './util/isNotNil';
|
import { isNotNil } from './util/isNotNil';
|
||||||
import { MINUTE, SECOND } from './util/durations';
|
import { MINUTE, SECOND } from './util/durations';
|
||||||
|
|
|
@ -142,12 +142,9 @@ import { themeChanged } from './shims/themeChanged';
|
||||||
import { createIPCEvents } from './util/createIPCEvents';
|
import { createIPCEvents } from './util/createIPCEvents';
|
||||||
import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
|
import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
|
||||||
import type { ServiceIdString } from './types/ServiceId';
|
import type { ServiceIdString } from './types/ServiceId';
|
||||||
import {
|
import { ServiceIdKind, isServiceIdString } from './types/ServiceId';
|
||||||
ServiceIdKind,
|
import { isAciString } from './util/isAciString';
|
||||||
isAciString,
|
import { normalizeAci } from './util/normalizeAci';
|
||||||
isServiceIdString,
|
|
||||||
normalizeAci,
|
|
||||||
} from './types/ServiceId';
|
|
||||||
import * as log from './logging/log';
|
import * as log from './logging/log';
|
||||||
import { loadRecentEmojis } from './util/loadRecentEmojis';
|
import { loadRecentEmojis } from './util/loadRecentEmojis';
|
||||||
import { deleteAllLogs } from './util/deleteAllLogs';
|
import { deleteAllLogs } from './util/deleteAllLogs';
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import type { Meta, Story } from '@storybook/react';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
import type { Props } from './AddCaptionModal';
|
|
||||||
import type { SmartCompositionTextAreaProps } from '../state/smart/CompositionTextArea';
|
|
||||||
import { AddCaptionModal } from './AddCaptionModal';
|
|
||||||
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
|
||||||
import { setupI18n } from '../util/setupI18n';
|
|
||||||
import { CompositionTextArea } from './CompositionTextArea';
|
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Components/AddCaptionModal',
|
|
||||||
component: AddCaptionModal,
|
|
||||||
argTypes: {
|
|
||||||
i18n: {
|
|
||||||
defaultValue: i18n,
|
|
||||||
},
|
|
||||||
RenderCompositionTextArea: {
|
|
||||||
defaultValue: (props: SmartCompositionTextAreaProps) => (
|
|
||||||
<CompositionTextArea
|
|
||||||
{...props}
|
|
||||||
getPreferredBadge={() => undefined}
|
|
||||||
i18n={i18n}
|
|
||||||
isFormattingEnabled
|
|
||||||
isFormattingFlagEnabled
|
|
||||||
isFormattingSpoilersFlagEnabled
|
|
||||||
onPickEmoji={action('onPickEmoji')}
|
|
||||||
onChange={action('onChange')}
|
|
||||||
onTextTooLong={action('onTextTooLong')}
|
|
||||||
onSetSkinTone={action('onSetSkinTone')}
|
|
||||||
platform="darwin"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as Meta;
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/function-component-definition
|
|
||||||
const Template: Story<Props> = args => (
|
|
||||||
<AddCaptionModal {...args} theme={React.useContext(StorybookThemeContext)} />
|
|
||||||
);
|
|
||||||
|
|
||||||
export const Modal = Template.bind({});
|
|
||||||
Modal.args = {
|
|
||||||
draftText: 'Some caption text',
|
|
||||||
};
|
|
|
@ -1,95 +0,0 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { noop } from 'lodash';
|
|
||||||
import { Button } from './Button';
|
|
||||||
import { Modal } from './Modal';
|
|
||||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
|
||||||
import type { SmartCompositionTextAreaProps } from '../state/smart/CompositionTextArea';
|
|
||||||
import type { HydratedBodyRangesType } from '../types/BodyRange';
|
|
||||||
import { isScrolled, isScrolledToBottom } from '../hooks/useSizeObserver';
|
|
||||||
|
|
||||||
export type Props = {
|
|
||||||
i18n: LocalizerType;
|
|
||||||
onClose: () => void;
|
|
||||||
onSubmit: (
|
|
||||||
text: string,
|
|
||||||
bodyRanges: HydratedBodyRangesType | undefined
|
|
||||||
) => void;
|
|
||||||
draftText: string;
|
|
||||||
draftBodyRanges: HydratedBodyRangesType | undefined;
|
|
||||||
theme: ThemeType;
|
|
||||||
RenderCompositionTextArea: (
|
|
||||||
props: SmartCompositionTextAreaProps
|
|
||||||
) => JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AddCaptionModal({
|
|
||||||
i18n,
|
|
||||||
onClose,
|
|
||||||
onSubmit,
|
|
||||||
draftText,
|
|
||||||
draftBodyRanges,
|
|
||||||
RenderCompositionTextArea,
|
|
||||||
theme,
|
|
||||||
}: Props): JSX.Element {
|
|
||||||
const [messageText, setMessageText] = React.useState('');
|
|
||||||
const [bodyRanges, setBodyRanges] = React.useState<
|
|
||||||
HydratedBodyRangesType | undefined
|
|
||||||
>();
|
|
||||||
|
|
||||||
const [scrolled, setScrolled] = React.useState(false);
|
|
||||||
// We don't know that this is true, but it most likely is
|
|
||||||
const [scrolledToBottom, setScrolledToBottom] = React.useState(true);
|
|
||||||
|
|
||||||
const scrollerRef = React.useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
// add footer/header dividers depending on the state of scroll
|
|
||||||
const updateScrollState = React.useCallback(() => {
|
|
||||||
const scrollerEl = scrollerRef.current;
|
|
||||||
if (scrollerEl) {
|
|
||||||
setScrolled(isScrolled(scrollerEl));
|
|
||||||
setScrolledToBottom(isScrolledToBottom(scrollerEl));
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(() => {
|
|
||||||
onSubmit(messageText, bodyRanges);
|
|
||||||
}, [bodyRanges, messageText, onSubmit]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
i18n={i18n}
|
|
||||||
modalName="AddCaptionModal"
|
|
||||||
hasXButton
|
|
||||||
hasHeaderDivider={scrolled}
|
|
||||||
hasFooterDivider={!scrolledToBottom}
|
|
||||||
moduleClassName="AddCaptionModal"
|
|
||||||
padded={false}
|
|
||||||
title={i18n('icu:AddCaptionModal__title')}
|
|
||||||
onClose={onClose}
|
|
||||||
modalFooter={
|
|
||||||
<Button onClick={handleSubmit}>
|
|
||||||
{i18n('icu:AddCaptionModal__submit-button')}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<RenderCompositionTextArea
|
|
||||||
maxLength={1500}
|
|
||||||
whenToShowRemainingCount={1450}
|
|
||||||
placeholder={i18n('icu:AddCaptionModal__placeholder')}
|
|
||||||
onChange={(updatedMessageText, updatedBodyRanges) => {
|
|
||||||
setMessageText(updatedMessageText);
|
|
||||||
setBodyRanges(updatedBodyRanges);
|
|
||||||
}}
|
|
||||||
scrollerRef={scrollerRef}
|
|
||||||
draftText={draftText}
|
|
||||||
bodyRanges={draftBodyRanges}
|
|
||||||
onSubmit={noop}
|
|
||||||
onScroll={updateScrollState}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -34,6 +34,7 @@ export default {
|
||||||
const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
addAttachment: action('addAttachment'),
|
addAttachment: action('addAttachment'),
|
||||||
conversationId: '123',
|
conversationId: '123',
|
||||||
|
convertDraftBodyRangesIntoHydrated: () => undefined,
|
||||||
discardEditMessage: action('discardEditMessage'),
|
discardEditMessage: action('discardEditMessage'),
|
||||||
focusCounter: 0,
|
focusCounter: 0,
|
||||||
sendCounter: 0,
|
sendCounter: 0,
|
||||||
|
|
|
@ -5,7 +5,10 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReadonlyDeep } from 'type-fest';
|
import type { ReadonlyDeep } from 'type-fest';
|
||||||
|
|
||||||
import type { DraftBodyRanges } from '../types/BodyRange';
|
import type {
|
||||||
|
DraftBodyRanges,
|
||||||
|
HydratedBodyRangesType,
|
||||||
|
} from '../types/BodyRange';
|
||||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||||
import type { ErrorDialogAudioRecorderType } from '../types/AudioRecorder';
|
import type { ErrorDialogAudioRecorderType } from '../types/AudioRecorder';
|
||||||
import { RecordingState } from '../types/AudioRecorder';
|
import { RecordingState } from '../types/AudioRecorder';
|
||||||
|
@ -85,6 +88,9 @@ export type OwnProps = Readonly<{
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
onRecordingComplete: (rec: InMemoryAttachmentDraftType) => unknown
|
onRecordingComplete: (rec: InMemoryAttachmentDraftType) => unknown
|
||||||
) => unknown;
|
) => unknown;
|
||||||
|
convertDraftBodyRangesIntoHydrated: (
|
||||||
|
bodyRanges: DraftBodyRanges | undefined
|
||||||
|
) => HydratedBodyRangesType | undefined;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
discardEditMessage: (id: string) => unknown;
|
discardEditMessage: (id: string) => unknown;
|
||||||
draftEditMessage?: DraftEditMessageType;
|
draftEditMessage?: DraftEditMessageType;
|
||||||
|
@ -221,6 +227,7 @@ export function CompositionArea({
|
||||||
// Base props
|
// Base props
|
||||||
addAttachment,
|
addAttachment,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
convertDraftBodyRangesIntoHydrated,
|
||||||
discardEditMessage,
|
discardEditMessage,
|
||||||
draftEditMessage,
|
draftEditMessage,
|
||||||
focusCounter,
|
focusCounter,
|
||||||
|
@ -853,12 +860,25 @@ export function CompositionArea({
|
||||||
'url' in attachmentToEdit &&
|
'url' in attachmentToEdit &&
|
||||||
attachmentToEdit.url && (
|
attachmentToEdit.url && (
|
||||||
<MediaEditor
|
<MediaEditor
|
||||||
|
draftBodyRanges={draftBodyRanges}
|
||||||
|
draftText={draftText}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
imageSrc={attachmentToEdit.url}
|
imageSrc={attachmentToEdit.url}
|
||||||
imageToBlurHash={imageToBlurHash}
|
imageToBlurHash={imageToBlurHash}
|
||||||
|
installedPacks={installedPacks}
|
||||||
|
isFormattingEnabled={isFormattingEnabled}
|
||||||
|
isFormattingFlagEnabled={isFormattingFlagEnabled}
|
||||||
|
isFormattingSpoilersFlagEnabled={isFormattingSpoilersFlagEnabled}
|
||||||
isSending={false}
|
isSending={false}
|
||||||
onClose={() => setAttachmentToEdit(undefined)}
|
onClose={() => setAttachmentToEdit(undefined)}
|
||||||
onDone={({ data, contentType, blurHash }) => {
|
onDone={({
|
||||||
|
caption,
|
||||||
|
captionBodyRanges,
|
||||||
|
data,
|
||||||
|
contentType,
|
||||||
|
blurHash,
|
||||||
|
}) => {
|
||||||
const newAttachment = {
|
const newAttachment = {
|
||||||
...attachmentToEdit,
|
...attachmentToEdit,
|
||||||
contentType,
|
contentType,
|
||||||
|
@ -869,9 +889,25 @@ export function CompositionArea({
|
||||||
|
|
||||||
addAttachment(conversationId, newAttachment);
|
addAttachment(conversationId, newAttachment);
|
||||||
setAttachmentToEdit(undefined);
|
setAttachmentToEdit(undefined);
|
||||||
|
onEditorStateChange?.({
|
||||||
|
bodyRanges: captionBodyRanges ?? [],
|
||||||
|
conversationId,
|
||||||
|
messageText: caption ?? '',
|
||||||
|
sendCounter,
|
||||||
|
});
|
||||||
|
|
||||||
|
inputApiRef.current?.setContents(
|
||||||
|
caption ?? '',
|
||||||
|
convertDraftBodyRangesIntoHydrated(captionBodyRanges),
|
||||||
|
true
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
installedPacks={installedPacks}
|
onPickEmoji={onPickEmoji}
|
||||||
|
onTextTooLong={onTextTooLong}
|
||||||
|
platform={platform}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
|
skinTone={skinTone}
|
||||||
|
sortedGroupMembers={sortedGroupMembers}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="CompositionArea__toggle-large">
|
<div className="CompositionArea__toggle-large">
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { BodyRange, 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';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from '../util/isAciString';
|
||||||
import { MentionBlot } from '../quill/mentions/blot';
|
import { MentionBlot } from '../quill/mentions/blot';
|
||||||
import {
|
import {
|
||||||
matchEmojiImage,
|
matchEmojiImage,
|
||||||
|
|
|
@ -1,79 +1,85 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { Meta, Story } from '@storybook/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
|
|
||||||
import type { PropsType } from './MediaEditor';
|
import type { PropsType } from './MediaEditor';
|
||||||
import { MediaEditor } from './MediaEditor';
|
import { MediaEditor } from './MediaEditor';
|
||||||
import enMessages from '../../_locales/en/messages.json';
|
import enMessages from '../../_locales/en/messages.json';
|
||||||
import { setupI18n } from '../util/setupI18n';
|
import { setupI18n } from '../util/setupI18n';
|
||||||
import { Stickers, installedPacks } from '../test-both/helpers/getStickerPacks';
|
import { Stickers, installedPacks } from '../test-both/helpers/getStickerPacks';
|
||||||
import { CompositionTextArea } from './CompositionTextArea';
|
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Components/MediaEditor',
|
|
||||||
};
|
|
||||||
|
|
||||||
const IMAGE_1 = '/fixtures/nathan-anderson-316188-unsplash.jpg';
|
const IMAGE_1 = '/fixtures/nathan-anderson-316188-unsplash.jpg';
|
||||||
const IMAGE_2 = '/fixtures/tina-rolf-269345-unsplash.jpg';
|
const IMAGE_2 = '/fixtures/tina-rolf-269345-unsplash.jpg';
|
||||||
const IMAGE_3 = '/fixtures/kitten-4-112-112.jpg';
|
const IMAGE_3 = '/fixtures/kitten-4-112-112.jpg';
|
||||||
const IMAGE_4 = '/fixtures/snow.jpg';
|
const IMAGE_4 = '/fixtures/snow.jpg';
|
||||||
|
|
||||||
const getDefaultProps = (): PropsType => ({
|
export default {
|
||||||
i18n,
|
title: 'Components/MediaEditor',
|
||||||
imageSrc: IMAGE_2,
|
component: MediaEditor,
|
||||||
onClose: action('onClose'),
|
argTypes: {
|
||||||
onDone: action('onDone'),
|
getPreferredBadge: { action: true },
|
||||||
isSending: false,
|
i18n: {
|
||||||
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
|
defaultValue: i18n,
|
||||||
|
},
|
||||||
|
imageToBlurHash: { action: true },
|
||||||
|
imageSrc: {
|
||||||
|
defaultValue: IMAGE_2,
|
||||||
|
},
|
||||||
|
installedPacks: {
|
||||||
|
defaultValue: installedPacks,
|
||||||
|
},
|
||||||
|
isFormattingEnabled: {
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
isFormattingFlagEnabled: {
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
isFormattingSpoilersFlagEnabled: {
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
isSending: {
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
onClose: { action: true },
|
||||||
|
onDone: { action: true },
|
||||||
|
onPickEmoji: { action: true },
|
||||||
|
onTextTooLong: { action: true },
|
||||||
|
platform: {
|
||||||
|
defaultValue: 'darwin',
|
||||||
|
},
|
||||||
|
recentStickers: {
|
||||||
|
defaultValue: [Stickers.wide, Stickers.tall, Stickers.abe],
|
||||||
|
},
|
||||||
|
skinTone: {
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
// StickerButtonProps
|
// eslint-disable-next-line react/function-component-definition
|
||||||
installedPacks,
|
const Template: Story<PropsType> = args => <MediaEditor {...args} />;
|
||||||
recentStickers: [Stickers.wide, Stickers.tall, Stickers.abe],
|
|
||||||
});
|
|
||||||
|
|
||||||
export function ExtraLarge(): JSX.Element {
|
export const ExtraLarge = Template.bind({});
|
||||||
return <MediaEditor {...getDefaultProps()} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Large(): JSX.Element {
|
export const Large = Template.bind({});
|
||||||
return <MediaEditor {...getDefaultProps()} imageSrc={IMAGE_1} />;
|
Large.args = {
|
||||||
}
|
imageSrc: IMAGE_1,
|
||||||
|
};
|
||||||
|
|
||||||
export function Smol(): JSX.Element {
|
export const Smol = Template.bind({});
|
||||||
return <MediaEditor {...getDefaultProps()} imageSrc={IMAGE_3} />;
|
Smol.args = {
|
||||||
}
|
imageSrc: IMAGE_3,
|
||||||
|
};
|
||||||
|
|
||||||
export function Portrait(): JSX.Element {
|
export const Portrait = Template.bind({});
|
||||||
return <MediaEditor {...getDefaultProps()} imageSrc={IMAGE_4} />;
|
Portrait.args = {
|
||||||
}
|
imageSrc: IMAGE_4,
|
||||||
|
};
|
||||||
|
|
||||||
export function Sending(): JSX.Element {
|
export const Sending = Template.bind({});
|
||||||
return <MediaEditor {...getDefaultProps()} isSending />;
|
Sending.args = {
|
||||||
}
|
isSending: true,
|
||||||
|
};
|
||||||
export function WithCaption(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<MediaEditor
|
|
||||||
{...getDefaultProps()}
|
|
||||||
supportsCaption
|
|
||||||
renderCompositionTextArea={props => (
|
|
||||||
<CompositionTextArea
|
|
||||||
{...props}
|
|
||||||
getPreferredBadge={() => undefined}
|
|
||||||
i18n={i18n}
|
|
||||||
isFormattingEnabled
|
|
||||||
isFormattingFlagEnabled
|
|
||||||
isFormattingSpoilersFlagEnabled
|
|
||||||
onPickEmoji={action('onPickEmoji')}
|
|
||||||
onSetSkinTone={action('onSetSkinTone')}
|
|
||||||
onTextTooLong={action('onTextTooLong')}
|
|
||||||
platform="darwin"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,6 +17,7 @@ import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
|
||||||
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
|
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
|
||||||
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
import type { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator';
|
import type { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator';
|
||||||
|
import type { PropsType as MediaEditorPropsType } from './MediaEditor';
|
||||||
|
|
||||||
import { TEXT_ATTACHMENT } from '../types/MIME';
|
import { TEXT_ATTACHMENT } from '../types/MIME';
|
||||||
import { isVideoAttachment } from '../types/Attachment';
|
import { isVideoAttachment } from '../types/Attachment';
|
||||||
|
@ -24,7 +25,6 @@ import { SendStoryModal } from './SendStoryModal';
|
||||||
|
|
||||||
import { MediaEditor } from './MediaEditor';
|
import { MediaEditor } from './MediaEditor';
|
||||||
import { TextStoryCreator } from './TextStoryCreator';
|
import { TextStoryCreator } from './TextStoryCreator';
|
||||||
import type { SmartCompositionTextAreaProps } from '../state/smart/CompositionTextArea';
|
|
||||||
import type { DraftBodyRanges } from '../types/BodyRange';
|
import type { DraftBodyRanges } from '../types/BodyRange';
|
||||||
|
|
||||||
function usePortalElement(testid: string): HTMLDivElement | null {
|
function usePortalElement(testid: string): HTMLDivElement | null {
|
||||||
|
@ -63,9 +63,6 @@ export type PropsType = {
|
||||||
processAttachment: (
|
processAttachment: (
|
||||||
file: File
|
file: File
|
||||||
) => Promise<void | InMemoryAttachmentDraftType>;
|
) => Promise<void | InMemoryAttachmentDraftType>;
|
||||||
renderCompositionTextArea: (
|
|
||||||
props: SmartCompositionTextAreaProps
|
|
||||||
) => JSX.Element;
|
|
||||||
sendStoryModalOpenStateChanged: (isOpen: boolean) => unknown;
|
sendStoryModalOpenStateChanged: (isOpen: boolean) => unknown;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'> &
|
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'> &
|
||||||
|
@ -96,6 +93,15 @@ export type PropsType = {
|
||||||
Pick<
|
Pick<
|
||||||
TextStoryCreatorPropsType,
|
TextStoryCreatorPropsType,
|
||||||
'onUseEmoji' | 'skinTone' | 'onSetSkinTone' | 'recentEmojis'
|
'onUseEmoji' | 'skinTone' | 'onSetSkinTone' | 'recentEmojis'
|
||||||
|
> &
|
||||||
|
Pick<
|
||||||
|
MediaEditorPropsType,
|
||||||
|
| 'isFormattingEnabled'
|
||||||
|
| 'isFormattingFlagEnabled'
|
||||||
|
| 'isFormattingSpoilersFlagEnabled'
|
||||||
|
| 'onPickEmoji'
|
||||||
|
| 'onTextTooLong'
|
||||||
|
| 'platform'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export function StoryCreator({
|
export function StoryCreator({
|
||||||
|
@ -110,6 +116,9 @@ export function StoryCreator({
|
||||||
i18n,
|
i18n,
|
||||||
imageToBlurHash,
|
imageToBlurHash,
|
||||||
installedPacks,
|
installedPacks,
|
||||||
|
isFormattingEnabled,
|
||||||
|
isFormattingFlagEnabled,
|
||||||
|
isFormattingSpoilersFlagEnabled,
|
||||||
isSending,
|
isSending,
|
||||||
linkPreview,
|
linkPreview,
|
||||||
me,
|
me,
|
||||||
|
@ -118,19 +127,21 @@ export function StoryCreator({
|
||||||
onDeleteList,
|
onDeleteList,
|
||||||
onDistributionListCreated,
|
onDistributionListCreated,
|
||||||
onHideMyStoriesFrom,
|
onHideMyStoriesFrom,
|
||||||
|
onMediaPlaybackStart,
|
||||||
|
onPickEmoji,
|
||||||
onRemoveMembers,
|
onRemoveMembers,
|
||||||
onRepliesNReactionsChanged,
|
onRepliesNReactionsChanged,
|
||||||
onSelectedStoryList,
|
onSelectedStoryList,
|
||||||
onSend,
|
onSend,
|
||||||
onSetSkinTone,
|
onSetSkinTone,
|
||||||
|
onTextTooLong,
|
||||||
onUseEmoji,
|
onUseEmoji,
|
||||||
onViewersUpdated,
|
onViewersUpdated,
|
||||||
onMediaPlaybackStart,
|
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
|
platform,
|
||||||
processAttachment,
|
processAttachment,
|
||||||
recentEmojis,
|
recentEmojis,
|
||||||
recentStickers,
|
recentStickers,
|
||||||
renderCompositionTextArea,
|
|
||||||
sendStoryModalOpenStateChanged,
|
sendStoryModalOpenStateChanged,
|
||||||
setMyStoriesToAllSignalConnections,
|
setMyStoriesToAllSignalConnections,
|
||||||
signalConnections,
|
signalConnections,
|
||||||
|
@ -234,17 +245,19 @@ export function StoryCreator({
|
||||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{draftAttachment && !isReadyToSend && attachmentUrl && (
|
{draftAttachment && attachmentUrl && (
|
||||||
<MediaEditor
|
<MediaEditor
|
||||||
doneButtonLabel={i18n('icu:next2')}
|
doneButtonLabel={i18n('icu:next2')}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
imageSrc={attachmentUrl}
|
imageSrc={attachmentUrl}
|
||||||
|
imageToBlurHash={imageToBlurHash}
|
||||||
installedPacks={installedPacks}
|
installedPacks={installedPacks}
|
||||||
|
isFormattingEnabled={isFormattingEnabled}
|
||||||
|
isFormattingFlagEnabled={isFormattingFlagEnabled}
|
||||||
|
isFormattingSpoilersFlagEnabled={isFormattingSpoilersFlagEnabled}
|
||||||
isSending={isSending}
|
isSending={isSending}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
supportsCaption
|
|
||||||
renderCompositionTextArea={renderCompositionTextArea}
|
|
||||||
imageToBlurHash={imageToBlurHash}
|
|
||||||
onDone={({
|
onDone={({
|
||||||
contentType,
|
contentType,
|
||||||
data,
|
data,
|
||||||
|
@ -263,7 +276,11 @@ export function StoryCreator({
|
||||||
setBodyRanges(captionBodyRanges);
|
setBodyRanges(captionBodyRanges);
|
||||||
setIsReadyToSend(true);
|
setIsReadyToSend(true);
|
||||||
}}
|
}}
|
||||||
|
onPickEmoji={onPickEmoji}
|
||||||
|
onTextTooLong={onTextTooLong}
|
||||||
|
platform={platform}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
|
skinTone={skinTone}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!file && (
|
{!file && (
|
||||||
|
|
|
@ -25,6 +25,7 @@ export type OwnProps = Readonly<{
|
||||||
emoji?: string;
|
emoji?: string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
onClose?: () => unknown;
|
onClose?: () => unknown;
|
||||||
|
onOpen?: () => unknown;
|
||||||
emojiButtonApi?: MutableRefObject<EmojiButtonAPI | undefined>;
|
emojiButtonApi?: MutableRefObject<EmojiButtonAPI | undefined>;
|
||||||
variant?: EmojiButtonVariant;
|
variant?: EmojiButtonVariant;
|
||||||
}>;
|
}>;
|
||||||
|
@ -47,6 +48,7 @@ export const EmojiButton = React.memo(function EmojiButtonInner({
|
||||||
i18n,
|
i18n,
|
||||||
doSend,
|
doSend,
|
||||||
onClose,
|
onClose,
|
||||||
|
onOpen,
|
||||||
onPickEmoji,
|
onPickEmoji,
|
||||||
skinTone,
|
skinTone,
|
||||||
onSetSkinTone,
|
onSetSkinTone,
|
||||||
|
@ -58,6 +60,13 @@ export const EmojiButton = React.memo(function EmojiButtonInner({
|
||||||
const popperRef = React.useRef<HTMLDivElement | null>(null);
|
const popperRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
const refMerger = useRefMerger();
|
const refMerger = useRefMerger();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onOpen?.();
|
||||||
|
}, [open, onOpen]);
|
||||||
|
|
||||||
const handleClickButton = React.useCallback(() => {
|
const handleClickButton = React.useCallback(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
|
@ -77,10 +77,10 @@ import type { AvatarDataType } from './types/Avatar';
|
||||||
import type { ServiceIdString, AciString, PniString } from './types/ServiceId';
|
import type { ServiceIdString, AciString, PniString } from './types/ServiceId';
|
||||||
import {
|
import {
|
||||||
ServiceIdKind,
|
ServiceIdKind,
|
||||||
isAciString,
|
|
||||||
isPniString,
|
isPniString,
|
||||||
isServiceIdString,
|
isServiceIdString,
|
||||||
} from './types/ServiceId';
|
} from './types/ServiceId';
|
||||||
|
import { isAciString } from './util/isAciString';
|
||||||
import * as Errors from './types/errors';
|
import * as Errors from './types/errors';
|
||||||
import { SignalService as Proto } from './protobuf';
|
import { SignalService as Proto } from './protobuf';
|
||||||
import { isNotNil } from './util/isNotNil';
|
import { isNotNil } from './util/isNotNil';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { assertDev } from '../../util/assert';
|
||||||
import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import type { ConversationAttributesType } from '../../model-types.d';
|
import type { ConversationAttributesType } from '../../model-types.d';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import type { reportSpamJobQueue } from '../reportSpamJobQueue';
|
import type { reportSpamJobQueue } from '../reportSpamJobQueue';
|
||||||
|
|
||||||
export async function addReportSpamJob({
|
export async function addReportSpamJob({
|
||||||
|
|
|
@ -51,7 +51,7 @@ import { isConversationAccepted } from '../../util/isConversationAccepted';
|
||||||
import { sendToGroup } from '../../util/sendToGroup';
|
import { sendToGroup } from '../../util/sendToGroup';
|
||||||
import type { DurationInSeconds } from '../../util/durations';
|
import type { DurationInSeconds } from '../../util/durations';
|
||||||
import type { ServiceIdString } from '../../types/ServiceId';
|
import type { ServiceIdString } from '../../types/ServiceId';
|
||||||
import { normalizeAci } from '../../types/ServiceId';
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import * as Bytes from '../../Bytes';
|
import * as Bytes from '../../Bytes';
|
||||||
|
|
||||||
const LONG_ATTACHMENT_LIMIT = 2048;
|
const LONG_ATTACHMENT_LIMIT = 2048;
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { ourProfileKeyService } from '../../services/ourProfileKey';
|
||||||
import { canReact, isStory } from '../../state/selectors/message';
|
import { canReact, isStory } from '../../state/selectors/message';
|
||||||
import { findAndFormatContact } from '../../util/findAndFormatContact';
|
import { findAndFormatContact } from '../../util/findAndFormatContact';
|
||||||
import type { AciString, ServiceIdString } from '../../types/ServiceId';
|
import type { AciString, ServiceIdString } from '../../types/ServiceId';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import { handleMultipleSendErrors } from './handleMultipleSendErrors';
|
import { handleMultipleSendErrors } from './handleMultipleSendErrors';
|
||||||
import { incrementMessageCounter } from '../../util/incrementMessageCounter';
|
import { incrementMessageCounter } from '../../util/incrementMessageCounter';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { chunk } from 'lodash';
|
import { chunk } from 'lodash';
|
||||||
import type { LoggerType } from '../../types/Logging';
|
import type { LoggerType } from '../../types/Logging';
|
||||||
import type { AciString } from '../../types/ServiceId';
|
import type { AciString } from '../../types/ServiceId';
|
||||||
import { normalizeAci } from '../../types/ServiceId';
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import { getSendOptions } from '../../util/getSendOptions';
|
import { getSendOptions } from '../../util/getSendOptions';
|
||||||
import type { SendTypesType } from '../../util/handleMessageSend';
|
import type { SendTypesType } from '../../util/handleMessageSend';
|
||||||
import { handleMessageSend } from '../../util/handleMessageSend';
|
import { handleMessageSend } from '../../util/handleMessageSend';
|
||||||
|
|
|
@ -29,26 +29,19 @@ export class MediaEditorFabricCropRect extends fabric.Rect {
|
||||||
const canvasHeight = this.canvas.getHeight();
|
const canvasHeight = this.canvas.getHeight();
|
||||||
const canvasWidth = this.canvas.getWidth();
|
const canvasWidth = this.canvas.getWidth();
|
||||||
|
|
||||||
if (height > canvasHeight || width > canvasWidth) {
|
const nextLeft = clamp(
|
||||||
this.canvas.discardActiveObject();
|
left / zoom,
|
||||||
} else {
|
MediaEditorFabricCropRect.PADDING / zoom,
|
||||||
this.set(
|
(canvasWidth - width - MediaEditorFabricCropRect.PADDING) / zoom
|
||||||
'left',
|
);
|
||||||
clamp(
|
const nextTop = clamp(
|
||||||
left / zoom,
|
top / zoom,
|
||||||
MediaEditorFabricCropRect.PADDING / zoom,
|
MediaEditorFabricCropRect.PADDING / zoom,
|
||||||
(canvasWidth - width - MediaEditorFabricCropRect.PADDING) / zoom
|
(canvasHeight - height - MediaEditorFabricCropRect.PADDING) / zoom
|
||||||
)
|
);
|
||||||
);
|
|
||||||
this.set(
|
this.set('left', nextLeft);
|
||||||
'top',
|
this.set('top', nextTop);
|
||||||
clamp(
|
|
||||||
top / zoom,
|
|
||||||
MediaEditorFabricCropRect.PADDING / zoom,
|
|
||||||
(canvasHeight - height - MediaEditorFabricCropRect.PADDING) / zoom
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setCoords();
|
this.setCoords();
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,10 +67,10 @@ import { IMAGE_JPEG, IMAGE_WEBP } from '../types/MIME';
|
||||||
import type { AciString, PniString, ServiceIdString } from '../types/ServiceId';
|
import type { AciString, PniString, ServiceIdString } from '../types/ServiceId';
|
||||||
import {
|
import {
|
||||||
ServiceIdKind,
|
ServiceIdKind,
|
||||||
isAciString,
|
|
||||||
normalizeServiceId,
|
normalizeServiceId,
|
||||||
normalizePni,
|
normalizePni,
|
||||||
} from '../types/ServiceId';
|
} from '../types/ServiceId';
|
||||||
|
import { isAciString } from '../util/isAciString';
|
||||||
import {
|
import {
|
||||||
constantTimeEqual,
|
constantTimeEqual,
|
||||||
decryptProfile,
|
decryptProfile,
|
||||||
|
|
|
@ -45,7 +45,8 @@ import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
|
||||||
|
|
||||||
import type { ReactionType } from '../types/Reactions';
|
import type { ReactionType } from '../types/Reactions';
|
||||||
import type { ServiceIdString } from '../types/ServiceId';
|
import type { ServiceIdString } from '../types/ServiceId';
|
||||||
import { isAciString, normalizeServiceId } from '../types/ServiceId';
|
import { normalizeServiceId } from '../types/ServiceId';
|
||||||
|
import { isAciString } from '../util/isAciString';
|
||||||
import * as reactionUtil from '../reactions/util';
|
import * as reactionUtil from '../reactions/util';
|
||||||
import * as Stickers from '../types/Stickers';
|
import * as Stickers from '../types/Stickers';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { get } from 'lodash';
|
||||||
|
|
||||||
import type { ConversationType } from '../state/ducks/conversations';
|
import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import type { AciString } from '../types/ServiceId';
|
import type { AciString } from '../types/ServiceId';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from '../util/isAciString';
|
||||||
import { filter, map } from '../util/iterables';
|
import { filter, map } from '../util/iterables';
|
||||||
import { removeDiacritics } from '../util/removeDiacritics';
|
import { removeDiacritics } from '../util/removeDiacritics';
|
||||||
import { isNotNil } from '../util/isNotNil';
|
import { isNotNil } from '../util/isNotNil';
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Parchment from 'parchment';
|
||||||
import Quill from 'quill';
|
import Quill from 'quill';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import { Emojify } from '../../components/conversation/Emojify';
|
import { Emojify } from '../../components/conversation/Emojify';
|
||||||
import { normalizeAci } from '../../types/ServiceId';
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import type { MentionBlotValue } from '../util';
|
import type { MentionBlotValue } from '../util';
|
||||||
|
|
||||||
declare class QuillEmbed extends Parchment.Embed {
|
declare class QuillEmbed extends Parchment.Embed {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type { RefObject } from 'react';
|
||||||
import type { Matcher, AttributeMap } from 'quill';
|
import type { Matcher, AttributeMap } from 'quill';
|
||||||
|
|
||||||
import { assertDev } from '../../util/assert';
|
import { assertDev } from '../../util/assert';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import type { MemberRepository } from '../memberRepository';
|
import type { MemberRepository } from '../memberRepository';
|
||||||
|
|
||||||
export const matchMention: (
|
export const matchMention: (
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { isDirectConversation } from '../util/whatTypeOfConversation';
|
||||||
import { incrementMessageCounter } from '../util/incrementMessageCounter';
|
import { incrementMessageCounter } from '../util/incrementMessageCounter';
|
||||||
import { repeat, zipObject } from '../util/iterables';
|
import { repeat, zipObject } from '../util/iterables';
|
||||||
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
|
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from '../util/isAciString';
|
||||||
import { SendStatus } from '../messages/MessageSendState';
|
import { SendStatus } from '../messages/MessageSendState';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,8 @@ import {
|
||||||
findBestMatchingCameraId,
|
findBestMatchingCameraId,
|
||||||
} from '../calling/findBestMatchingDevice';
|
} from '../calling/findBestMatchingDevice';
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import { normalizeAci, isAciString } from '../types/ServiceId';
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
|
import { isAciString } from '../util/isAciString';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
import type { ConversationModel } from '../models/conversations';
|
import type { ConversationModel } from '../models/conversations';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import PQueue from 'p-queue';
|
||||||
|
|
||||||
import type { ContactSyncEvent } from '../textsecure/messageReceiverEvents';
|
import type { ContactSyncEvent } from '../textsecure/messageReceiverEvents';
|
||||||
import type { ModifiedContactDetails } from '../textsecure/ContactsParser';
|
import type { ModifiedContactDetails } from '../textsecure/ContactsParser';
|
||||||
import { normalizeAci } from '../types/ServiceId';
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import * as Conversation from '../types/Conversation';
|
import * as Conversation from '../types/Conversation';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
import type { ValidateConversationType } from '../model-types.d';
|
import type { ValidateConversationType } from '../model-types.d';
|
||||||
|
|
|
@ -47,10 +47,10 @@ import type { StoryDistributionIdString } from '../types/StoryDistributionId';
|
||||||
import type { ServiceIdString } from '../types/ServiceId';
|
import type { ServiceIdString } from '../types/ServiceId';
|
||||||
import {
|
import {
|
||||||
normalizeServiceId,
|
normalizeServiceId,
|
||||||
normalizeAci,
|
|
||||||
normalizePni,
|
normalizePni,
|
||||||
ServiceIdKind,
|
ServiceIdKind,
|
||||||
} from '../types/ServiceId';
|
} from '../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import * as Stickers from '../types/Stickers';
|
import * as Stickers from '../types/Stickers';
|
||||||
import type {
|
import type {
|
||||||
StoryDistributionWithMembersType,
|
StoryDistributionWithMembersType,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { omit } from 'lodash';
|
||||||
|
|
||||||
import type { LoggerType } from '../../types/Logging';
|
import type { LoggerType } from '../../types/Logging';
|
||||||
import type { AciString, ServiceIdString } from '../../types/ServiceId';
|
import type { AciString, ServiceIdString } from '../../types/ServiceId';
|
||||||
import { normalizeAci } from '../../types/ServiceId';
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import { isNotNil } from '../../util/isNotNil';
|
import { isNotNil } from '../../util/isNotNil';
|
||||||
import { assertDev } from '../../util/assert';
|
import { assertDev } from '../../util/assert';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -10,11 +10,8 @@ import type {
|
||||||
AciString,
|
AciString,
|
||||||
PniString,
|
PniString,
|
||||||
} from '../../types/ServiceId';
|
} from '../../types/ServiceId';
|
||||||
import {
|
import { normalizeServiceId, normalizePni } from '../../types/ServiceId';
|
||||||
normalizeServiceId,
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
normalizeAci,
|
|
||||||
normalizePni,
|
|
||||||
} from '../../types/ServiceId';
|
|
||||||
import type { JSONWithUnknownFields } from '../../types/Util';
|
import type { JSONWithUnknownFields } from '../../types/Util';
|
||||||
import { isNotNil } from '../../util/isNotNil';
|
import { isNotNil } from '../../util/isNotNil';
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { CallMode } from '../../types/Calling';
|
||||||
import type { MessageType, ConversationType } from '../Interface';
|
import type { MessageType, ConversationType } from '../Interface';
|
||||||
import { strictAssert } from '../../util/assert';
|
import { strictAssert } from '../../util/assert';
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
|
|
||||||
// Legacy type for calls that never had a call id
|
// Legacy type for calls that never had a call id
|
||||||
type DirectCallHistoryDetailsType = {
|
type DirectCallHistoryDetailsType = {
|
||||||
|
|
|
@ -70,7 +70,7 @@ import type {
|
||||||
AciString,
|
AciString,
|
||||||
PniString,
|
PniString,
|
||||||
} from '../../types/ServiceId';
|
} from '../../types/ServiceId';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import { MY_STORY_ID, StorySendMode } from '../../types/Stories';
|
import { MY_STORY_ID, StorySendMode } from '../../types/Stories';
|
||||||
import * as Errors from '../../types/errors';
|
import * as Errors from '../../types/errors';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import type { StoryViewTargetType, StoryViewType } from '../../types/Stories';
|
||||||
import type { SyncType } from '../../jobs/helpers/syncHelpers';
|
import type { SyncType } from '../../jobs/helpers/syncHelpers';
|
||||||
import type { StoryDistributionIdString } from '../../types/StoryDistributionId';
|
import type { StoryDistributionIdString } from '../../types/StoryDistributionId';
|
||||||
import type { ServiceIdString } from '../../types/ServiceId';
|
import type { ServiceIdString } from '../../types/ServiceId';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { TARGETED_CONVERSATION_CHANGED } from './conversations';
|
import { TARGETED_CONVERSATION_CHANGED } from './conversations';
|
||||||
import { SIGNAL_ACI } from '../../types/SignalConversation';
|
import { SIGNAL_ACI } from '../../types/SignalConversation';
|
||||||
|
|
|
@ -17,7 +17,7 @@ import type {
|
||||||
ActiveCallType,
|
ActiveCallType,
|
||||||
GroupCallRemoteParticipantType,
|
GroupCallRemoteParticipantType,
|
||||||
} from '../../types/Calling';
|
} from '../../types/Calling';
|
||||||
import { isAciString } from '../../types/ServiceId';
|
import { isAciString } from '../../util/isAciString';
|
||||||
import type { AciString } from '../../types/ServiceId';
|
import type { AciString } from '../../types/ServiceId';
|
||||||
import { CallMode, CallState } from '../../types/Calling';
|
import { CallMode, CallState } from '../../types/Calling';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
|
|
|
@ -9,6 +9,10 @@ import { mapDispatchToProps } from '../actions';
|
||||||
import type { Props as ComponentPropsType } from '../../components/CompositionArea';
|
import type { Props as ComponentPropsType } from '../../components/CompositionArea';
|
||||||
import { CompositionArea } from '../../components/CompositionArea';
|
import { CompositionArea } from '../../components/CompositionArea';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
|
import type {
|
||||||
|
DraftBodyRanges,
|
||||||
|
HydratedBodyRangesType,
|
||||||
|
} from '../../types/BodyRange';
|
||||||
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
|
||||||
import { dropNull } from '../../util/dropNull';
|
import { dropNull } from '../../util/dropNull';
|
||||||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||||
|
@ -53,7 +57,7 @@ import type { SmartCompositionRecordingProps } from './CompositionRecording';
|
||||||
import { SmartCompositionRecording } from './CompositionRecording';
|
import { SmartCompositionRecording } from './CompositionRecording';
|
||||||
import type { SmartCompositionRecordingDraftProps } from './CompositionRecordingDraft';
|
import type { SmartCompositionRecordingDraftProps } from './CompositionRecordingDraft';
|
||||||
import { SmartCompositionRecordingDraft } from './CompositionRecordingDraft';
|
import { SmartCompositionRecordingDraft } from './CompositionRecordingDraft';
|
||||||
import { BodyRange } from '../../types/BodyRange';
|
import { hydrateRanges } from '../../types/BodyRange';
|
||||||
|
|
||||||
type ExternalProps = {
|
type ExternalProps = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -133,6 +137,12 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
|
|
||||||
const lastEditableMessageId = getLastEditableMessageId(state);
|
const lastEditableMessageId = getLastEditableMessageId(state);
|
||||||
|
|
||||||
|
const convertDraftBodyRangesIntoHydrated = (
|
||||||
|
bodyRanges: DraftBodyRanges | undefined
|
||||||
|
): HydratedBodyRangesType | undefined => {
|
||||||
|
return hydrateRanges(bodyRanges, conversationSelector);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Base
|
// Base
|
||||||
conversationId: id,
|
conversationId: id,
|
||||||
|
@ -150,6 +160,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
sendCounter,
|
sendCounter,
|
||||||
shouldHidePopovers,
|
shouldHidePopovers,
|
||||||
theme: getTheme(state),
|
theme: getTheme(state),
|
||||||
|
convertDraftBodyRangesIntoHydrated,
|
||||||
|
|
||||||
// AudioCapture
|
// AudioCapture
|
||||||
errorDialogAudioRecorderType:
|
errorDialogAudioRecorderType:
|
||||||
|
@ -204,19 +215,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
groupAdmins: getGroupAdminsSelector(state)(conversation.id),
|
groupAdmins: getGroupAdminsSelector(state)(conversation.id),
|
||||||
|
|
||||||
draftText: dropNull(draftText),
|
draftText: dropNull(draftText),
|
||||||
draftBodyRanges: draftBodyRanges?.map(bodyRange => {
|
draftBodyRanges: hydrateRanges(draftBodyRanges, conversationSelector),
|
||||||
if (BodyRange.isMention(bodyRange)) {
|
|
||||||
const mentionConvo = conversationSelector(bodyRange.mentionAci);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...bodyRange,
|
|
||||||
conversationID: mentionConvo.id,
|
|
||||||
replacementText: mentionConvo.title,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return bodyRange;
|
|
||||||
}),
|
|
||||||
renderSmartCompositionRecording: (
|
renderSmartCompositionRecording: (
|
||||||
recProps: SmartCompositionRecordingProps
|
recProps: SmartCompositionRecordingProps
|
||||||
) => {
|
) => {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { useSelector } from 'react-redux';
|
||||||
import { ThemeType, type LocalizerType } from '../../types/Util';
|
import { ThemeType, type LocalizerType } from '../../types/Util';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
||||||
import { SmartCompositionTextArea } from './CompositionTextArea';
|
|
||||||
import { StoryCreator } from '../../components/StoryCreator';
|
import { StoryCreator } from '../../components/StoryCreator';
|
||||||
import {
|
import {
|
||||||
getAllSignalConnections,
|
getAllSignalConnections,
|
||||||
|
@ -18,29 +17,35 @@ import {
|
||||||
selectMostRecentActiveStoryTimestampByGroupOrDistributionList,
|
selectMostRecentActiveStoryTimestampByGroupOrDistributionList,
|
||||||
} from '../selectors/conversations';
|
} from '../selectors/conversations';
|
||||||
import { getDistributionListsWithMembers } from '../selectors/storyDistributionLists';
|
import { getDistributionListsWithMembers } from '../selectors/storyDistributionLists';
|
||||||
import { getIntl, getUserConversationId } from '../selectors/user';
|
import { getIntl, getPlatform, getUserConversationId } from '../selectors/user';
|
||||||
import {
|
import {
|
||||||
getInstalledStickerPacks,
|
getInstalledStickerPacks,
|
||||||
getRecentStickers,
|
getRecentStickers,
|
||||||
} from '../selectors/stickers';
|
} from '../selectors/stickers';
|
||||||
import { getAddStoryData } from '../selectors/stories';
|
import { getAddStoryData } from '../selectors/stories';
|
||||||
import {
|
import {
|
||||||
getEmojiSkinTone,
|
getIsFormattingFlagEnabled,
|
||||||
getHasSetMyStoriesPrivacy,
|
getIsFormattingSpoilersFlagEnabled,
|
||||||
} from '../selectors/items';
|
} from '../selectors/composer';
|
||||||
import { getLinkPreview } from '../selectors/linkPreviews';
|
import { getLinkPreview } from '../selectors/linkPreviews';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
|
import {
|
||||||
|
getEmojiSkinTone,
|
||||||
|
getHasSetMyStoriesPrivacy,
|
||||||
|
getTextFormattingEnabled,
|
||||||
|
} from '../selectors/items';
|
||||||
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
import { imageToBlurHash } from '../../util/imageToBlurHash';
|
||||||
import { processAttachment } from '../../util/processAttachment';
|
import { processAttachment } from '../../util/processAttachment';
|
||||||
import { useConversationsActions } from '../ducks/conversations';
|
|
||||||
import { useActions as useEmojisActions } from '../ducks/emojis';
|
import { useActions as useEmojisActions } from '../ducks/emojis';
|
||||||
|
import { useAudioPlayerActions } from '../ducks/audioPlayer';
|
||||||
|
import { useComposerActions } from '../ducks/composer';
|
||||||
|
import { useConversationsActions } from '../ducks/conversations';
|
||||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||||
import { useItemsActions } from '../ducks/items';
|
import { useItemsActions } from '../ducks/items';
|
||||||
import { useLinkPreviewActions } from '../ducks/linkPreviews';
|
import { useLinkPreviewActions } from '../ducks/linkPreviews';
|
||||||
import { useRecentEmojis } from '../selectors/emojis';
|
import { useRecentEmojis } from '../selectors/emojis';
|
||||||
import { useStoriesActions } from '../ducks/stories';
|
import { useStoriesActions } from '../ducks/stories';
|
||||||
import { useStoryDistributionListsActions } from '../ducks/storyDistributionLists';
|
import { useStoryDistributionListsActions } from '../ducks/storyDistributionLists';
|
||||||
import { useAudioPlayerActions } from '../ducks/audioPlayer';
|
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
file?: File;
|
file?: File;
|
||||||
|
@ -99,6 +104,15 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
const { onSetSkinTone } = useItemsActions();
|
const { onSetSkinTone } = useItemsActions();
|
||||||
const { onUseEmoji } = useEmojisActions();
|
const { onUseEmoji } = useEmojisActions();
|
||||||
const { pauseVoiceNotePlayer } = useAudioPlayerActions();
|
const { pauseVoiceNotePlayer } = useAudioPlayerActions();
|
||||||
|
const { onTextTooLong } = useComposerActions();
|
||||||
|
const { onUseEmoji: onPickEmoji } = useEmojisActions();
|
||||||
|
|
||||||
|
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
||||||
|
const isFormattingFlagEnabled = useSelector(getIsFormattingFlagEnabled);
|
||||||
|
const isFormattingSpoilersFlagEnabled = useSelector(
|
||||||
|
getIsFormattingSpoilersFlagEnabled
|
||||||
|
);
|
||||||
|
const platform = useSelector(getPlatform);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StoryCreator
|
<StoryCreator
|
||||||
|
@ -113,6 +127,9 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
imageToBlurHash={imageToBlurHash}
|
imageToBlurHash={imageToBlurHash}
|
||||||
installedPacks={installedPacks}
|
installedPacks={installedPacks}
|
||||||
|
isFormattingEnabled={isFormattingEnabled}
|
||||||
|
isFormattingFlagEnabled={isFormattingFlagEnabled}
|
||||||
|
isFormattingSpoilersFlagEnabled={isFormattingSpoilersFlagEnabled}
|
||||||
isSending={isSending}
|
isSending={isSending}
|
||||||
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
|
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
|
||||||
me={me}
|
me={me}
|
||||||
|
@ -123,19 +140,21 @@ export function SmartStoryCreator(): JSX.Element | null {
|
||||||
onDeleteList={deleteDistributionList}
|
onDeleteList={deleteDistributionList}
|
||||||
onDistributionListCreated={createDistributionList}
|
onDistributionListCreated={createDistributionList}
|
||||||
onHideMyStoriesFrom={hideMyStoriesFrom}
|
onHideMyStoriesFrom={hideMyStoriesFrom}
|
||||||
|
onMediaPlaybackStart={pauseVoiceNotePlayer}
|
||||||
|
onPickEmoji={onPickEmoji}
|
||||||
onRemoveMembers={removeMembersFromDistributionList}
|
onRemoveMembers={removeMembersFromDistributionList}
|
||||||
onRepliesNReactionsChanged={allowsRepliesChanged}
|
onRepliesNReactionsChanged={allowsRepliesChanged}
|
||||||
onSelectedStoryList={verifyStoryListMembers}
|
onSelectedStoryList={verifyStoryListMembers}
|
||||||
onSend={sendStoryMessage}
|
onSend={sendStoryMessage}
|
||||||
onSetSkinTone={onSetSkinTone}
|
onSetSkinTone={onSetSkinTone}
|
||||||
|
onTextTooLong={onTextTooLong}
|
||||||
onUseEmoji={onUseEmoji}
|
onUseEmoji={onUseEmoji}
|
||||||
onViewersUpdated={updateStoryViewers}
|
onViewersUpdated={updateStoryViewers}
|
||||||
onMediaPlaybackStart={pauseVoiceNotePlayer}
|
|
||||||
ourConversationId={ourConversationId}
|
ourConversationId={ourConversationId}
|
||||||
|
platform={platform}
|
||||||
processAttachment={processAttachment}
|
processAttachment={processAttachment}
|
||||||
recentEmojis={recentEmojis}
|
recentEmojis={recentEmojis}
|
||||||
recentStickers={recentStickers}
|
recentStickers={recentStickers}
|
||||||
renderCompositionTextArea={SmartCompositionTextArea}
|
|
||||||
sendStoryModalOpenStateChanged={sendStoryModalOpenStateChanged}
|
sendStoryModalOpenStateChanged={sendStoryModalOpenStateChanged}
|
||||||
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
||||||
signalConnections={signalConnections}
|
signalConnections={signalConnections}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
|
|
||||||
import { normalizeAci } from '../types/ServiceId';
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import {
|
import {
|
||||||
getCountryCodeValue,
|
getCountryCodeValue,
|
||||||
getBucketValue,
|
getBucketValue,
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
import type { ConversationType } from '../../state/ducks/conversations';
|
||||||
import { generateAci, normalizeAci } from '../../types/ServiceId';
|
import { generateAci } from '../../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import type { ServiceIdString } from '../../types/ServiceId';
|
import type { ServiceIdString } from '../../types/ServiceId';
|
||||||
import { getDefaultConversationWithServiceId } from '../helpers/getDefaultConversation';
|
import { getDefaultConversationWithServiceId } from '../helpers/getDefaultConversation';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { generateKeyPair } from '../../Curve';
|
||||||
import type { UploadKeysType } from '../../textsecure/WebAPI';
|
import type { UploadKeysType } from '../../textsecure/WebAPI';
|
||||||
import AccountManager from '../../textsecure/AccountManager';
|
import AccountManager from '../../textsecure/AccountManager';
|
||||||
import type { PreKeyType } from '../../textsecure/Types.d';
|
import type { PreKeyType } from '../../textsecure/Types.d';
|
||||||
import { ServiceIdKind, normalizeAci } from '../../types/ServiceId';
|
import { ServiceIdKind } from '../../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
|
|
||||||
const { textsecure } = window;
|
const { textsecure } = window;
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,8 @@ import sinon from 'sinon';
|
||||||
import { ConversationModel } from '../models/conversations';
|
import { ConversationModel } from '../models/conversations';
|
||||||
import type { ConversationAttributesType } from '../model-types.d';
|
import type { ConversationAttributesType } from '../model-types.d';
|
||||||
import type { WebAPIType } from '../textsecure/WebAPI';
|
import type { WebAPIType } from '../textsecure/WebAPI';
|
||||||
import {
|
import { generateAci, normalizeServiceId } from '../types/ServiceId';
|
||||||
generateAci,
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
normalizeAci,
|
|
||||||
normalizeServiceId,
|
|
||||||
} from '../types/ServiceId';
|
|
||||||
|
|
||||||
import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup';
|
import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup';
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
|
|
||||||
import { generateAci, isAciString } from '../../types/ServiceId';
|
import { generateAci } from '../../types/ServiceId';
|
||||||
|
import { isAciString } from '../../util/isAciString';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
import type { ConversationType } from '../../state/ducks/conversations';
|
||||||
import { MemberRepository, _toMembers } from '../../quill/memberRepository';
|
import { MemberRepository, _toMembers } from '../../quill/memberRepository';
|
||||||
import { getDefaultConversationWithServiceId } from '../../test-both/helpers/getDefaultConversation';
|
import { getDefaultConversationWithServiceId } from '../../test-both/helpers/getDefaultConversation';
|
||||||
|
|
|
@ -13,7 +13,8 @@ import type {
|
||||||
PniString,
|
PniString,
|
||||||
ServiceIdString,
|
ServiceIdString,
|
||||||
} from '../../types/ServiceId';
|
} from '../../types/ServiceId';
|
||||||
import { normalizeAci, normalizePni } from '../../types/ServiceId';
|
import { normalizePni } from '../../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import type {
|
import type {
|
||||||
KyberPreKeyType,
|
KyberPreKeyType,
|
||||||
PreKeyType,
|
PreKeyType,
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { range } from 'lodash';
|
||||||
|
|
||||||
import { getTableData, insertData, updateToVersion } from './helpers';
|
import { getTableData, insertData, updateToVersion } from './helpers';
|
||||||
import type { ServiceIdString } from '../../types/ServiceId';
|
import type { ServiceIdString } from '../../types/ServiceId';
|
||||||
import { normalizeAci, normalizePni } from '../../types/ServiceId';
|
import { normalizePni } from '../../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import type { PreKeyType } from '../../sql/Interface';
|
import type { PreKeyType } from '../../sql/Interface';
|
||||||
|
|
||||||
type TestingPreKey = Omit<
|
type TestingPreKey = Omit<
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { range } from 'lodash';
|
||||||
|
|
||||||
import { insertData, updateToVersion } from './helpers';
|
import { insertData, updateToVersion } from './helpers';
|
||||||
import type { ServiceIdString } from '../../types/ServiceId';
|
import type { ServiceIdString } from '../../types/ServiceId';
|
||||||
import { normalizeAci, normalizePni } from '../../types/ServiceId';
|
import { normalizePni } from '../../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../../util/normalizeAci';
|
||||||
import type { KyberPreKeyType, SignedPreKeyType } from '../../sql/Interface';
|
import type { KyberPreKeyType, SignedPreKeyType } from '../../sql/Interface';
|
||||||
|
|
||||||
type TestingKyberKey = Omit<
|
type TestingKyberKey = Omit<
|
||||||
|
|
|
@ -43,11 +43,11 @@ import {
|
||||||
import type { ServiceIdString, AciString, PniString } from '../types/ServiceId';
|
import type { ServiceIdString, AciString, PniString } from '../types/ServiceId';
|
||||||
import {
|
import {
|
||||||
ServiceIdKind,
|
ServiceIdKind,
|
||||||
normalizeAci,
|
|
||||||
normalizePni,
|
normalizePni,
|
||||||
toTaggedPni,
|
toTaggedPni,
|
||||||
isUntaggedPniString,
|
isUntaggedPniString,
|
||||||
} from '../types/ServiceId';
|
} from '../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
|
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
|
||||||
import { ourProfileKeyService } from '../services/ourProfileKey';
|
import { ourProfileKeyService } from '../services/ourProfileKey';
|
||||||
import { assertDev, strictAssert } from '../util/assert';
|
import { assertDev, strictAssert } from '../util/assert';
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import protobuf from '../protobuf/wrap';
|
import protobuf from '../protobuf/wrap';
|
||||||
|
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
import { normalizeAci } from '../types/ServiceId';
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import { DurationInSeconds } from '../util/durations';
|
import { DurationInSeconds } from '../util/durations';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
|
|
|
@ -56,14 +56,14 @@ import { normalizeStoryDistributionId } from '../types/StoryDistributionId';
|
||||||
import type { ServiceIdString } from '../types/ServiceId';
|
import type { ServiceIdString } from '../types/ServiceId';
|
||||||
import {
|
import {
|
||||||
ServiceIdKind,
|
ServiceIdKind,
|
||||||
normalizeAci,
|
|
||||||
normalizeServiceId,
|
normalizeServiceId,
|
||||||
normalizePni,
|
normalizePni,
|
||||||
isAciString,
|
|
||||||
isPniString,
|
isPniString,
|
||||||
isServiceIdString,
|
isServiceIdString,
|
||||||
fromPniObject,
|
fromPniObject,
|
||||||
} from '../types/ServiceId';
|
} from '../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
|
import { isAciString } from '../util/isAciString';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
|
|
@ -13,7 +13,8 @@ import {
|
||||||
import { calculateAgreement, createKeyPair, generateKeyPair } from '../Curve';
|
import { calculateAgreement, createKeyPair, generateKeyPair } from '../Curve';
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
import type { PniString, AciString } from '../types/ServiceId';
|
import type { PniString, AciString } from '../types/ServiceId';
|
||||||
import { normalizeAci, normalizePni } from '../types/ServiceId';
|
import { normalizePni } from '../types/ServiceId';
|
||||||
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
import { strictAssert } from '../util/assert';
|
import { strictAssert } from '../util/assert';
|
||||||
|
|
||||||
type ProvisionDecryptResult = {
|
type ProvisionDecryptResult = {
|
||||||
|
|
|
@ -9,11 +9,8 @@ import Long from 'long';
|
||||||
|
|
||||||
import type { LoggerType } from '../../types/Logging';
|
import type { LoggerType } from '../../types/Logging';
|
||||||
import { strictAssert } from '../../util/assert';
|
import { strictAssert } from '../../util/assert';
|
||||||
import {
|
import { isUntaggedPniString, toTaggedPni } from '../../types/ServiceId';
|
||||||
isAciString,
|
import { isAciString } from '../../util/isAciString';
|
||||||
isUntaggedPniString,
|
|
||||||
toTaggedPni,
|
|
||||||
} from '../../types/ServiceId';
|
|
||||||
import * as Bytes from '../../Bytes';
|
import * as Bytes from '../../Bytes';
|
||||||
import { UUID_BYTE_SIZE } from '../../Crypto';
|
import { UUID_BYTE_SIZE } from '../../Crypto';
|
||||||
import { uuidToBytes, bytesToUuid } from '../../util/uuidToBytes';
|
import { uuidToBytes, bytesToUuid } from '../../util/uuidToBytes';
|
||||||
|
|
|
@ -29,7 +29,8 @@ import { SECOND, DurationInSeconds } from '../util/durations';
|
||||||
import type { AnyPaymentEvent } from '../types/Payment';
|
import type { AnyPaymentEvent } from '../types/Payment';
|
||||||
import { PaymentEventKind } from '../types/Payment';
|
import { PaymentEventKind } from '../types/Payment';
|
||||||
import { filterAndClean } from '../types/BodyRange';
|
import { filterAndClean } from '../types/BodyRange';
|
||||||
import { isAciString, normalizeAci } from '../types/ServiceId';
|
import { isAciString } from '../util/isAciString';
|
||||||
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
|
|
||||||
const FLAGS = Proto.DataMessage.Flags;
|
const FLAGS = Proto.DataMessage.Flags;
|
||||||
export const ATTACHMENT_MAX = 32;
|
export const ATTACHMENT_MAX = 32;
|
||||||
|
|
|
@ -10,7 +10,8 @@ import type {
|
||||||
PniString,
|
PniString,
|
||||||
ServiceIdString,
|
ServiceIdString,
|
||||||
} from '../../types/ServiceId';
|
} from '../../types/ServiceId';
|
||||||
import { ServiceIdKind, isAciString, isPniString } from '../../types/ServiceId';
|
import { ServiceIdKind, isPniString } from '../../types/ServiceId';
|
||||||
|
import { isAciString } from '../../util/isAciString';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
|
||||||
import Helpers from '../Helpers';
|
import Helpers from '../Helpers';
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
} from '../util/search';
|
} from '../util/search';
|
||||||
import { assertDev } from '../util/assert';
|
import { assertDev } from '../util/assert';
|
||||||
import type { AciString } from './ServiceId';
|
import type { AciString } from './ServiceId';
|
||||||
import { normalizeAci } from './ServiceId';
|
import { normalizeAci } from '../util/normalizeAci';
|
||||||
|
|
||||||
// Cold storage of body ranges
|
// Cold storage of body ranges
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Aci, Pni, ServiceId } from '@signalapp/libsignal-client';
|
||||||
import { isValidUuid } from '../util/isValidUuid';
|
import { isValidUuid } from '../util/isValidUuid';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import type { LoggerType } from './Logging';
|
import type { LoggerType } from './Logging';
|
||||||
|
import { isAciString } from '../util/isAciString';
|
||||||
|
|
||||||
export enum ServiceIdKind {
|
export enum ServiceIdKind {
|
||||||
ACI = 'ACI',
|
ACI = 'ACI',
|
||||||
|
@ -26,10 +27,6 @@ export function isServiceIdString(
|
||||||
return isAciString(value) || isPniString(value);
|
return isAciString(value) || isPniString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAciString(value?: string | null): value is AciString {
|
|
||||||
return isValidUuid(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPniString(value?: string | null): value is PniString {
|
export function isPniString(value?: string | null): value is PniString {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -87,41 +84,6 @@ export function normalizeServiceId(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeAci(
|
|
||||||
rawAci: string,
|
|
||||||
context: string,
|
|
||||||
logger?: Pick<LoggerType, 'warn'>
|
|
||||||
): AciString;
|
|
||||||
|
|
||||||
export function normalizeAci(
|
|
||||||
rawAci: string | undefined | null,
|
|
||||||
context: string,
|
|
||||||
logger?: Pick<LoggerType, 'warn'>
|
|
||||||
): AciString | undefined;
|
|
||||||
|
|
||||||
export function normalizeAci(
|
|
||||||
rawAci: string | undefined | null,
|
|
||||||
context: string,
|
|
||||||
logger: Pick<LoggerType, 'warn'> = log
|
|
||||||
): AciString | undefined {
|
|
||||||
if (rawAci == null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = rawAci.toLowerCase();
|
|
||||||
|
|
||||||
if (!isAciString(result)) {
|
|
||||||
logger.warn(
|
|
||||||
`Normalizing invalid serviceId: ${rawAci} to ${result} in context "${context}"`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Cast anyway we don't want to throw here
|
|
||||||
return result as AciString;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizePni(
|
export function normalizePni(
|
||||||
rawPni: string,
|
rawPni: string,
|
||||||
context: string,
|
context: string,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
GroupCallJoinState,
|
GroupCallJoinState,
|
||||||
} from '../types/Calling';
|
} from '../types/Calling';
|
||||||
import type { AciString } from '../types/ServiceId';
|
import type { AciString } from '../types/ServiceId';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from './isAciString';
|
||||||
import { isMe } from './whatTypeOfConversation';
|
import { isMe } from './whatTypeOfConversation';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import * as Errors from '../types/errors';
|
import * as Errors from '../types/errors';
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type { MessageModel } from '../models/messages';
|
||||||
import type { SignalService as Proto } from '../protobuf';
|
import type { SignalService as Proto } from '../protobuf';
|
||||||
import type { AciString } from '../types/ServiceId';
|
import type { AciString } from '../types/ServiceId';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { normalizeAci } from '../types/ServiceId';
|
import { normalizeAci } from './normalizeAci';
|
||||||
import { filter } from './iterables';
|
import { filter } from './iterables';
|
||||||
import { getContactId } from '../messages/helpers';
|
import { getContactId } from '../messages/helpers';
|
||||||
import { getTimestampFromLong } from './timestampLongUtils';
|
import { getTimestampFromLong } from './timestampLongUtils';
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { ReadStatus } from '../messages/MessageReadStatus';
|
||||||
import dataInterface from '../sql/Client';
|
import dataInterface from '../sql/Client';
|
||||||
import { drop } from './drop';
|
import { drop } from './drop';
|
||||||
import { getAttachmentSignature, isVoiceMessage } from '../types/Attachment';
|
import { getAttachmentSignature, isVoiceMessage } from '../types/Attachment';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from './isAciString';
|
||||||
import { getMessageIdForLogging } from './idForLogging';
|
import { getMessageIdForLogging } from './idForLogging';
|
||||||
import { hasErrors } from '../state/selectors/message';
|
import { hasErrors } from '../state/selectors/message';
|
||||||
import { isIncoming, isOutgoing } from '../messages/helpers';
|
import { isIncoming, isOutgoing } from '../messages/helpers';
|
||||||
|
|
9
ts/util/isAciString.ts
Normal file
9
ts/util/isAciString.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { AciString } from '../types/ServiceId';
|
||||||
|
import { isValidUuid } from './isValidUuid';
|
||||||
|
|
||||||
|
export function isAciString(value?: string | null): value is AciString {
|
||||||
|
return isValidUuid(value);
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { ConversationType } from '../state/ducks/conversations';
|
import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from './isAciString';
|
||||||
|
|
||||||
export const isSafetyNumberNotAvailable = (
|
export const isSafetyNumberNotAvailable = (
|
||||||
contact?: ConversationType
|
contact?: ConversationType
|
||||||
|
|
|
@ -2004,13 +2004,6 @@
|
||||||
"updated": "2021-12-10T23:24:03.829Z",
|
"updated": "2021-12-10T23:24:03.829Z",
|
||||||
"reasonDetail": "Doesn't touch the DOM."
|
"reasonDetail": "Doesn't touch the DOM."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"rule": "React-useRef",
|
|
||||||
"path": "ts/components/AddCaptionModal.tsx",
|
|
||||||
"line": " const scrollerRef = React.useRef<HTMLDivElement>(null);",
|
|
||||||
"reasonCategory": "usageTrusted",
|
|
||||||
"updated": "2022-10-03T16:06:12.837Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/AvatarTextEditor.tsx",
|
"path": "ts/components/AvatarTextEditor.tsx",
|
||||||
|
@ -2390,6 +2383,13 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2022-11-11T17:11:07.659Z"
|
"updated": "2022-11-11T17:11:07.659Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/MediaEditor.tsx",
|
||||||
|
"line": " const inputApiRef = useRef<InputApi | undefined>();",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2023-09-11T20:19:18.681Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/MediaQualitySelector.tsx",
|
"path": "ts/components/MediaQualitySelector.tsx",
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {
|
||||||
} from '../jobs/conversationJobQueue';
|
} from '../jobs/conversationJobQueue';
|
||||||
import { ReceiptType } from '../types/Receipt';
|
import { ReceiptType } from '../types/Receipt';
|
||||||
import type { AciString } from '../types/ServiceId';
|
import type { AciString } from '../types/ServiceId';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from './isAciString';
|
||||||
|
|
||||||
export async function markConversationRead(
|
export async function markConversationRead(
|
||||||
conversationAttrs: ConversationAttributesType,
|
conversationAttrs: ConversationAttributesType,
|
||||||
|
|
42
ts/util/normalizeAci.ts
Normal file
42
ts/util/normalizeAci.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { AciString } from '../types/ServiceId';
|
||||||
|
import type { LoggerType } from '../types/Logging';
|
||||||
|
import * as log from '../logging/log';
|
||||||
|
import { isAciString } from './isAciString';
|
||||||
|
|
||||||
|
export function normalizeAci(
|
||||||
|
rawAci: string,
|
||||||
|
context: string,
|
||||||
|
logger?: Pick<LoggerType, 'warn'>
|
||||||
|
): AciString;
|
||||||
|
|
||||||
|
export function normalizeAci(
|
||||||
|
rawAci: string | undefined | null,
|
||||||
|
context: string,
|
||||||
|
logger?: Pick<LoggerType, 'warn'>
|
||||||
|
): AciString | undefined;
|
||||||
|
|
||||||
|
export function normalizeAci(
|
||||||
|
rawAci: string | undefined | null,
|
||||||
|
context: string,
|
||||||
|
logger: Pick<LoggerType, 'warn'> = log
|
||||||
|
): AciString | undefined {
|
||||||
|
if (rawAci == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = rawAci.toLowerCase();
|
||||||
|
|
||||||
|
if (!isAciString(result)) {
|
||||||
|
logger.warn(
|
||||||
|
`Normalizing invalid serviceId: ${rawAci} to ${result} in context "${context}"`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cast anyway we don't want to throw here
|
||||||
|
return result as AciString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ import {
|
||||||
SafetyNumberIdentifierType,
|
SafetyNumberIdentifierType,
|
||||||
SafetyNumberMode,
|
SafetyNumberMode,
|
||||||
} from '../types/safetyNumber';
|
} from '../types/safetyNumber';
|
||||||
import { isAciString } from '../types/ServiceId';
|
import { isAciString } from './isAciString';
|
||||||
|
|
||||||
const ITERATION_COUNT = 5200;
|
const ITERATION_COUNT = 5200;
|
||||||
const E164_VERSION = 1;
|
const E164_VERSION = 1;
|
||||||
|
|
Loading…
Reference in a new issue