Add emoji button to text story creation

This commit is contained in:
Josh Perez 2022-11-28 13:52:16 -05:00 committed by GitHub
parent d6d53f9d18
commit 77f92b6cc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 285 additions and 213 deletions

View file

@ -316,4 +316,10 @@
width: 24px; width: 24px;
} }
} }
&__emoji-button,
&__emoji-button::after {
height: 20px;
width: 20px;
}
} }

View file

@ -55,8 +55,13 @@ export default {
onDistributionListCreated: { action: true }, onDistributionListCreated: { action: true },
onHideMyStoriesFrom: { action: true }, onHideMyStoriesFrom: { action: true },
onSend: { action: true }, onSend: { action: true },
onSetSkinTone: { action: true },
onUseEmoji: { action: true },
onViewersUpdated: { action: true }, onViewersUpdated: { action: true },
processAttachment: { action: true }, processAttachment: { action: true },
recentEmojis: {
defaultValue: [],
},
recentStickers: { recentStickers: {
defaultValue: [], defaultValue: [],
}, },
@ -65,6 +70,9 @@ export default {
signalConnections: { signalConnections: {
defaultValue: Array.from(Array(42), getDefaultConversation), defaultValue: Array.from(Array(42), getDefaultConversation),
}, },
skinTone: {
defaultValue: 0,
},
toggleSignalConnectionsModal: { action: true }, toggleSignalConnectionsModal: { action: true },
}, },
} as Meta; } as Meta;

View file

@ -15,6 +15,7 @@ import type { Props as StickerButtonProps } from './stickers/StickerButton';
import type { PropsType as SendStoryModalPropsType } from './SendStoryModal'; import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
import type { UUIDStringType } from '../types/UUID'; import type { UUIDStringType } from '../types/UUID';
import type { imageToBlurHash } from '../util/imageToBlurHash'; import type { imageToBlurHash } from '../util/imageToBlurHash';
import type { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator';
import { TEXT_ATTACHMENT } from '../types/MIME'; import { TEXT_ATTACHMENT } from '../types/MIME';
import { isVideoAttachment } from '../types/Attachment'; import { isVideoAttachment } from '../types/Attachment';
@ -70,6 +71,10 @@ export type PropsType = {
| 'toggleGroupsForStorySend' | 'toggleGroupsForStorySend'
| 'mostRecentActiveStoryTimestampByGroupOrDistributionList' | 'mostRecentActiveStoryTimestampByGroupOrDistributionList'
| 'toggleSignalConnectionsModal' | 'toggleSignalConnectionsModal'
> &
Pick<
TextStoryCreatorPropsType,
'onUseEmoji' | 'skinTone' | 'onSetSkinTone' | 'recentEmojis'
>; >;
export function StoryCreator({ export function StoryCreator({
@ -87,7 +92,7 @@ export function StoryCreator({
isSending, isSending,
linkPreview, linkPreview,
me, me,
ourConversationId, mostRecentActiveStoryTimestampByGroupOrDistributionList,
onClose, onClose,
onDeleteList, onDeleteList,
onDistributionListCreated, onDistributionListCreated,
@ -96,15 +101,19 @@ export function StoryCreator({
onRepliesNReactionsChanged, onRepliesNReactionsChanged,
onSelectedStoryList, onSelectedStoryList,
onSend, onSend,
onSetSkinTone,
onUseEmoji,
onViewersUpdated, onViewersUpdated,
ourConversationId,
processAttachment, processAttachment,
recentEmojis,
recentStickers, recentStickers,
renderCompositionTextArea, renderCompositionTextArea,
sendStoryModalOpenStateChanged, sendStoryModalOpenStateChanged,
setMyStoriesToAllSignalConnections, setMyStoriesToAllSignalConnections,
signalConnections, signalConnections,
skinTone,
toggleGroupsForStorySend, toggleGroupsForStorySend,
mostRecentActiveStoryTimestampByGroupOrDistributionList,
toggleSignalConnectionsModal, toggleSignalConnectionsModal,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
const [draftAttachment, setDraftAttachment] = useState< const [draftAttachment, setDraftAttachment] = useState<
@ -236,6 +245,10 @@ export function StoryCreator({
}); });
setIsReadyToSend(true); setIsReadyToSend(true);
}} }}
onUseEmoji={onUseEmoji}
onSetSkinTone={onSetSkinTone}
recentEmojis={recentEmojis}
skinTone={skinTone}
/> />
)} )}
</> </>

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import Measure from 'react-measure'; import Measure from 'react-measure';
import React, { useEffect, useRef, useState } from 'react'; import React, { forwardRef, useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize'; import TextareaAutosize from 'react-textarea-autosize';
import classNames from 'classnames'; import classNames from 'classnames';
@ -21,6 +21,7 @@ import {
getBackgroundColor, getBackgroundColor,
} from '../util/getStoryBackground'; } from '../util/getStoryBackground';
import { SECOND } from '../util/durations'; import { SECOND } from '../util/durations';
import { useRefMerger } from '../hooks/useRefMerger';
const renderNewLines: RenderTextCallbackType = ({ const renderNewLines: RenderTextCallbackType = ({
text: textWithNewLines, text: textWithNewLines,
@ -105,207 +106,214 @@ function getTextStyles(
}; };
} }
export function TextAttachment({ export const TextAttachment = forwardRef<HTMLTextAreaElement, PropsType>(
disableLinkPreviewPopup, function TextAttachmentForwarded(
i18n, {
isEditingText, disableLinkPreviewPopup,
isThumbnail, i18n,
onChange, isEditingText,
onClick, isThumbnail,
onRemoveLinkPreview, onChange,
textAttachment, onClick,
}: PropsType): JSX.Element | null { onRemoveLinkPreview,
const linkPreview = useRef<HTMLDivElement | null>(null); textAttachment,
const [linkPreviewOffsetTop, setLinkPreviewOffsetTop] = useState< },
number | undefined forwardedTextEditorRef
>(); ): JSX.Element | null {
const linkPreview = useRef<HTMLDivElement | null>(null);
const [linkPreviewOffsetTop, setLinkPreviewOffsetTop] = useState<
number | undefined
>();
const textContent = textAttachment.text || ''; const textContent = textAttachment.text || '';
const textEditorRef = useRef<HTMLTextAreaElement | null>(null);
const refMerger = useRefMerger();
const textEditorRef = useRef<HTMLTextAreaElement | null>(null); useEffect(() => {
const node = textEditorRef?.current;
useEffect(() => { if (!node) {
const node = textEditorRef.current; return;
if (!node) {
return;
}
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}, [isEditingText]);
useEffect(() => {
setLinkPreviewOffsetTop(undefined);
}, [textAttachment.preview?.url]);
const [isHoveringOverTooltip, setIsHoveringOverTooltip] = useState(false);
function showTooltip() {
if (disableLinkPreviewPopup) {
return;
}
setIsHoveringOverTooltip(true);
setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
}
useEffect(() => {
const timeout = setTimeout(() => {
if (!isHoveringOverTooltip) {
setLinkPreviewOffsetTop(undefined);
} }
}, 5 * SECOND);
return () => { node.focus();
clearTimeout(timeout); node.setSelectionRange(node.value.length, node.value.length);
}, [isEditingText]);
useEffect(() => {
setLinkPreviewOffsetTop(undefined);
}, [textAttachment.preview?.url]);
const [isHoveringOverTooltip, setIsHoveringOverTooltip] = useState(false);
function showTooltip() {
if (disableLinkPreviewPopup) {
return;
}
setIsHoveringOverTooltip(true);
setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
}
useEffect(() => {
const timeout = setTimeout(() => {
if (!isHoveringOverTooltip) {
setLinkPreviewOffsetTop(undefined);
}
}, 5 * SECOND);
return () => {
clearTimeout(timeout);
};
}, [isHoveringOverTooltip]);
const storyBackgroundColor = {
background: getBackgroundColor(textAttachment),
}; };
}, [isHoveringOverTooltip]);
const storyBackgroundColor = { return (
background: getBackgroundColor(textAttachment), <Measure bounds>
}; {({ contentRect, measureRef }) => {
const scaleFactor = (contentRect.bounds?.height || 1) / 1280;
return ( return (
<Measure bounds> // eslint-disable-next-line jsx-a11y/no-static-element-interactions
{({ contentRect, measureRef }) => { <div
const scaleFactor = (contentRect.bounds?.height || 1) / 1280; className="TextAttachment"
onClick={() => {
return ( if (linkPreviewOffsetTop) {
// eslint-disable-next-line jsx-a11y/no-static-element-interactions setLinkPreviewOffsetTop(undefined);
<div }
className="TextAttachment" onClick?.();
onClick={() => { }}
if (linkPreviewOffsetTop) { onKeyUp={ev => {
setLinkPreviewOffsetTop(undefined); if (ev.key === 'Escape' && linkPreviewOffsetTop) {
} setLinkPreviewOffsetTop(undefined);
onClick?.(); }
}} }}
onKeyUp={ev => { ref={measureRef}
if (ev.key === 'Escape' && linkPreviewOffsetTop) { style={isThumbnail ? storyBackgroundColor : undefined}
setLinkPreviewOffsetTop(undefined); >
} {/*
}}
ref={measureRef}
style={isThumbnail ? storyBackgroundColor : undefined}
>
{/*
The tooltip must be outside of the scaled area, as it should not scale with The tooltip must be outside of the scaled area, as it should not scale with
the story, but it must be positioned using the scaled offset the story, but it must be positioned using the scaled offset
*/} */}
{textAttachment.preview && {textAttachment.preview &&
textAttachment.preview.url && textAttachment.preview.url &&
linkPreviewOffsetTop && linkPreviewOffsetTop &&
!isThumbnail && ( !isThumbnail && (
<a <a
className="TextAttachment__preview__tooltip" className="TextAttachment__preview__tooltip"
href={textAttachment.preview.url} href={textAttachment.preview.url}
rel="noreferrer" rel="noreferrer"
style={{ style={{
top: linkPreviewOffsetTop * scaleFactor - 89, // minus height of tooltip and some spacing top: linkPreviewOffsetTop * scaleFactor - 89, // minus height of tooltip and some spacing
}} }}
target="_blank" target="_blank"
> >
<div> <div>
<div className="TextAttachment__preview__tooltip__title"> <div className="TextAttachment__preview__tooltip__title">
{i18n('TextAttachment__preview__link')} {i18n('TextAttachment__preview__link')}
</div> </div>
<div className="TextAttachment__preview__tooltip__url"> <div className="TextAttachment__preview__tooltip__url">
{textAttachment.preview.url} {textAttachment.preview.url}
</div>
</div> </div>
<div className="TextAttachment__preview__tooltip__arrow" />
</a>
)}
<div
className="TextAttachment__story"
style={{
...(isThumbnail ? {} : storyBackgroundColor),
transform: `scale(${scaleFactor})`,
}}
>
{(textContent || onChange) && (
<div
className={classNames('TextAttachment__text', {
'TextAttachment__text--with-bg': Boolean(
textAttachment.textBackgroundColor
),
})}
style={{
backgroundColor: textAttachment.textBackgroundColor
? getHexFromNumber(textAttachment.textBackgroundColor)
: 'transparent',
}}
>
{onChange ? (
<TextareaAutosize
className="TextAttachment__text__container TextAttachment__text__textarea"
disabled={!isEditingText}
onChange={ev => onChange(ev.currentTarget.value)}
placeholder={i18n('TextAttachment__placeholder')}
ref={refMerger(forwardedTextEditorRef, textEditorRef)}
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
value={textContent}
/>
) : (
<div
className="TextAttachment__text__container"
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
>
<Emojify
text={textContent}
renderNonEmoji={renderNewLines}
/>
</div>
)}
</div> </div>
<div className="TextAttachment__preview__tooltip__arrow" /> )}
</a> {textAttachment.preview && textAttachment.preview.url && (
)} <div
<div className={classNames('TextAttachment__preview-container', {
className="TextAttachment__story" 'TextAttachment__preview-container--large': Boolean(
style={{ textAttachment.preview.title
...(isThumbnail ? {} : storyBackgroundColor), ),
transform: `scale(${scaleFactor})`, })}
}} ref={linkPreview}
> onBlur={() => setIsHoveringOverTooltip(false)}
{(textContent || onChange) && ( onFocus={showTooltip}
<div onMouseOut={() => setIsHoveringOverTooltip(false)}
className={classNames('TextAttachment__text', { onMouseOver={showTooltip}
'TextAttachment__text--with-bg': Boolean( >
textAttachment.textBackgroundColor {onRemoveLinkPreview && (
), <div className="TextAttachment__preview__remove">
})} <button
style={{ aria-label={i18n(
backgroundColor: textAttachment.textBackgroundColor 'Keyboard--remove-draft-link-preview'
? getHexFromNumber(textAttachment.textBackgroundColor) )}
: 'transparent', type="button"
}} onClick={onRemoveLinkPreview}
> />
{onChange ? ( </div>
<TextareaAutosize )}
className="TextAttachment__text__container TextAttachment__text__textarea" <StoryLinkPreview
disabled={!isEditingText} {...textAttachment.preview}
onChange={ev => onChange(ev.currentTarget.value)} domain={getDomain(String(textAttachment.preview.url))}
placeholder={i18n('TextAttachment__placeholder')} forceCompactMode={
ref={textEditorRef} getTextSize(textContent) !== TextSize.Large
style={getTextStyles( }
textContent, i18n={i18n}
textAttachment.textForegroundColor, title={textAttachment.preview.title || undefined}
textAttachment.textStyle, url={textAttachment.preview.url}
i18n
)}
value={textContent}
/> />
) : ( </div>
<div )}
className="TextAttachment__text__container" </div>
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
>
<Emojify
text={textContent}
renderNonEmoji={renderNewLines}
/>
</div>
)}
</div>
)}
{textAttachment.preview && textAttachment.preview.url && (
<div
className={classNames('TextAttachment__preview-container', {
'TextAttachment__preview-container--large': Boolean(
textAttachment.preview.title
),
})}
ref={linkPreview}
onBlur={() => setIsHoveringOverTooltip(false)}
onFocus={showTooltip}
onMouseOut={() => setIsHoveringOverTooltip(false)}
onMouseOver={showTooltip}
>
{onRemoveLinkPreview && (
<div className="TextAttachment__preview__remove">
<button
aria-label={i18n('Keyboard--remove-draft-link-preview')}
type="button"
onClick={onRemoveLinkPreview}
/>
</div>
)}
<StoryLinkPreview
{...textAttachment.preview}
domain={getDomain(String(textAttachment.preview.url))}
forceCompactMode={
getTextSize(textContent) !== TextSize.Large
}
i18n={i18n}
title={textAttachment.preview.title || undefined}
url={textAttachment.preview.url}
/>
</div>
)}
</div> </div>
</div> );
); }}
}} </Measure>
</Measure> );
); }
} );

View file

@ -7,12 +7,15 @@ import classNames from 'classnames';
import { get, has, noop } from 'lodash'; import { get, has, noop } from 'lodash';
import { usePopper } from 'react-popper'; import { usePopper } from 'react-popper';
import type { EmojiPickDataType } from './emoji/EmojiPicker';
import type { LinkPreviewType } from '../types/message/LinkPreviews'; import type { LinkPreviewType } from '../types/message/LinkPreviews';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import type { Props as EmojiButtonPropsType } from './emoji/EmojiButton';
import type { TextAttachmentType } from '../types/Attachment'; import type { TextAttachmentType } from '../types/Attachment';
import { Button, ButtonVariant } from './Button'; import { Button, ButtonVariant } from './Button';
import { ContextMenu } from './ContextMenu'; import { ContextMenu } from './ContextMenu';
import { EmojiButton } from './emoji/EmojiButton';
import { LinkPreviewSourceType, findLinks } from '../types/LinkPreview'; import { LinkPreviewSourceType, findLinks } from '../types/LinkPreview';
import type { MaybeGrabLinkPreviewOptionsType } from '../types/LinkPreview'; import type { MaybeGrabLinkPreviewOptionsType } from '../types/LinkPreview';
import { Input } from './Input'; import { Input } from './Input';
@ -26,6 +29,7 @@ import {
COLOR_WHITE_INT, COLOR_WHITE_INT,
getBackgroundColor, getBackgroundColor,
} from '../util/getStoryBackground'; } from '../util/getStoryBackground';
import { convertShortName } from './emoji/lib';
import { objectMap } from '../util/objectMap'; import { objectMap } from '../util/objectMap';
import { handleOutsideClick } from '../util/handleOutsideClick'; import { handleOutsideClick } from '../util/handleOutsideClick';
import { ConfirmDiscardDialog } from './ConfirmDiscardDialog'; import { ConfirmDiscardDialog } from './ConfirmDiscardDialog';
@ -42,7 +46,8 @@ export type PropsType = {
linkPreview?: LinkPreviewType; linkPreview?: LinkPreviewType;
onClose: () => unknown; onClose: () => unknown;
onDone: (textAttachment: TextAttachmentType) => unknown; onDone: (textAttachment: TextAttachmentType) => unknown;
}; onUseEmoji: (_: EmojiPickDataType) => unknown;
} & Pick<EmojiButtonPropsType, 'onSetSkinTone' | 'recentEmojis' | 'skinTone'>;
enum LinkPreviewApplied { enum LinkPreviewApplied {
None = 'None', None = 'None',
@ -128,6 +133,10 @@ export function TextStoryCreator({
linkPreview, linkPreview,
onClose, onClose,
onDone, onDone,
onSetSkinTone,
onUseEmoji,
recentEmojis,
skinTone,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
const [showConfirmDiscardModal, setShowConfirmDiscardModal] = useState(false); const [showConfirmDiscardModal, setShowConfirmDiscardModal] = useState(false);
@ -145,16 +154,6 @@ export function TextStoryCreator({
const [sliderValue, setSliderValue] = useState<number>(100); const [sliderValue, setSliderValue] = useState<number>(100);
const [text, setText] = useState<string>(''); const [text, setText] = useState<string>('');
const textEditorRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
if (isEditingText) {
textEditorRef.current?.focus();
} else {
textEditorRef.current?.blur();
}
}, [isEditingText]);
const [isColorPickerShowing, setIsColorPickerShowing] = useState(false); const [isColorPickerShowing, setIsColorPickerShowing] = useState(false);
const [colorPickerPopperButtonRef, setColorPickerPopperButtonRef] = const [colorPickerPopperButtonRef, setColorPickerPopperButtonRef] =
useState<HTMLButtonElement | null>(null); useState<HTMLButtonElement | null>(null);
@ -328,6 +327,8 @@ export function TextStoryCreator({
const hasChanges = Boolean(text || hasLinkPreviewApplied); const hasChanges = Boolean(text || hasLinkPreviewApplied);
const textEditorRef = useRef<HTMLTextAreaElement | null>(null);
return ( return (
<FocusTrap focusTrapOptions={{ allowOutsideClick: true }}> <FocusTrap focusTrapOptions={{ allowOutsideClick: true }}>
<div className="StoryCreator"> <div className="StoryCreator">
@ -345,6 +346,7 @@ export function TextStoryCreator({
onRemoveLinkPreview={() => { onRemoveLinkPreview={() => {
setLinkPreviewApplied(LinkPreviewApplied.None); setLinkPreviewApplied(LinkPreviewApplied.None);
}} }}
ref={textEditorRef}
textAttachment={textAttachment} textAttachment={textAttachment}
/> />
</div> </div>
@ -428,6 +430,26 @@ export function TextStoryCreator({
}} }}
type="button" type="button"
/> />
<EmojiButton
className="StoryCreator__emoji-button"
i18n={i18n}
onPickEmoji={data => {
onUseEmoji(data);
const emoji = convertShortName(data.shortName, data.skinTone);
const insertAt =
textEditorRef.current?.selectionEnd ?? text.length;
setText(
originalText =>
`${originalText.substr(
0,
insertAt
)}${emoji}${originalText.substr(insertAt, text.length)}`
);
}}
recentEmojis={recentEmojis}
skinTone={skinTone}
onSetSkinTone={onSetSkinTone}
/>
</div> </div>
) : ( ) : (
<div className="StoryCreator__toolbar--space" /> <div className="StoryCreator__toolbar--space" />

View file

@ -7,6 +7,7 @@ import { useSelector } from 'react-redux';
import type { LocalizerType } from '../../types/Util'; import 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,
@ -22,18 +23,23 @@ import {
getInstalledStickerPacks, getInstalledStickerPacks,
getRecentStickers, getRecentStickers,
} from '../selectors/stickers'; } from '../selectors/stickers';
import { getHasSetMyStoriesPrivacy } from '../selectors/items'; import { getAddStoryData } from '../selectors/stories';
import {
getEmojiSkinTone,
getHasSetMyStoriesPrivacy,
} from '../selectors/items';
import { getLinkPreview } from '../selectors/linkPreviews'; import { getLinkPreview } from '../selectors/linkPreviews';
import { getPreferredBadgeSelector } from '../selectors/badges'; import { getPreferredBadgeSelector } from '../selectors/badges';
import { processAttachment } from '../../util/processAttachment';
import { imageToBlurHash } from '../../util/imageToBlurHash'; import { imageToBlurHash } from '../../util/imageToBlurHash';
import { processAttachment } from '../../util/processAttachment';
import { useConversationsActions } from '../ducks/conversations'; import { useConversationsActions } from '../ducks/conversations';
import { useActions as useEmojisActions } from '../ducks/emojis';
import { useGlobalModalActions } from '../ducks/globalModals'; import { useGlobalModalActions } from '../ducks/globalModals';
import { useActions as useItemsActions } from '../ducks/items';
import { useLinkPreviewActions } from '../ducks/linkPreviews'; import { useLinkPreviewActions } from '../ducks/linkPreviews';
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 { SmartCompositionTextArea } from './CompositionTextArea';
import { getAddStoryData } from '../selectors/stories';
export type PropsType = { export type PropsType = {
file?: File; file?: File;
@ -81,6 +87,11 @@ export function SmartStoryCreator(): JSX.Element | null {
const file = addStoryData?.type === 'Media' ? addStoryData.file : undefined; const file = addStoryData?.type === 'Media' ? addStoryData.file : undefined;
const isSending = addStoryData?.sending || false; const isSending = addStoryData?.sending || false;
const recentEmojis = useRecentEmojis();
const skinTone = useSelector<StateType, number>(getEmojiSkinTone);
const { onSetSkinTone } = useItemsActions();
const { onUseEmoji } = useEmojisActions();
return ( return (
<StoryCreator <StoryCreator
candidateConversations={candidateConversations} candidateConversations={candidateConversations}
@ -97,7 +108,9 @@ export function SmartStoryCreator(): JSX.Element | null {
isSending={isSending} isSending={isSending}
linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)} linkPreview={linkPreviewForSource(LinkPreviewSourceType.StoryCreator)}
me={me} me={me}
ourConversationId={ourConversationId} mostRecentActiveStoryTimestampByGroupOrDistributionList={
mostRecentActiveStoryTimestampByGroupOrDistributionList
}
onClose={() => setAddStoryData(undefined)} onClose={() => setAddStoryData(undefined)}
onDeleteList={deleteDistributionList} onDeleteList={deleteDistributionList}
onDistributionListCreated={createDistributionList} onDistributionListCreated={createDistributionList}
@ -106,17 +119,19 @@ export function SmartStoryCreator(): JSX.Element | null {
onRepliesNReactionsChanged={allowsRepliesChanged} onRepliesNReactionsChanged={allowsRepliesChanged}
onSelectedStoryList={verifyStoryListMembers} onSelectedStoryList={verifyStoryListMembers}
onSend={sendStoryMessage} onSend={sendStoryMessage}
onSetSkinTone={onSetSkinTone}
onUseEmoji={onUseEmoji}
onViewersUpdated={updateStoryViewers} onViewersUpdated={updateStoryViewers}
ourConversationId={ourConversationId}
processAttachment={processAttachment} processAttachment={processAttachment}
recentEmojis={recentEmojis}
recentStickers={recentStickers} recentStickers={recentStickers}
renderCompositionTextArea={SmartCompositionTextArea} renderCompositionTextArea={SmartCompositionTextArea}
sendStoryModalOpenStateChanged={sendStoryModalOpenStateChanged} sendStoryModalOpenStateChanged={sendStoryModalOpenStateChanged}
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections} setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
signalConnections={signalConnections} signalConnections={signalConnections}
skinTone={skinTone}
toggleGroupsForStorySend={toggleGroupsForStorySend} toggleGroupsForStorySend={toggleGroupsForStorySend}
mostRecentActiveStoryTimestampByGroupOrDistributionList={
mostRecentActiveStoryTimestampByGroupOrDistributionList
}
toggleSignalConnectionsModal={toggleSignalConnectionsModal} toggleSignalConnectionsModal={toggleSignalConnectionsModal}
/> />
); );

View file

@ -9093,7 +9093,7 @@
{ {
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/TextStoryCreator.tsx", "path": "ts/components/TextStoryCreator.tsx",
"line": " const textEditorRef = useRef<HTMLInputElement | null>(null);", "line": " const textEditorRef = useRef<HTMLTextAreaElement | null>(null);",
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2022-06-16T23:23:32.306Z" "updated": "2022-06-16T23:23:32.306Z"
}, },