Upgrade to React 18

This commit is contained in:
Jamie Kyle 2025-04-29 13:27:33 -07:00 committed by GitHub
parent 560dcb91d9
commit 14d098f40f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 1210 additions and 1796 deletions

View file

@ -6,7 +6,6 @@ module.exports = {
// Detects the type of file being babel'd (either esmodule or commonjs)
sourceType: 'unambiguous',
plugins: [
'react-hot-loader/babel',
'lodash',
'@babel/plugin-transform-typescript',
'@babel/plugin-proposal-class-properties',

View file

@ -3882,30 +3882,6 @@ Signal Desktop makes use of the following open source projects.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-hot-loader
MIT License
Copyright (c) 2016 Dan Abramov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-intl
Copyright 2019 Oath Inc.
@ -3984,53 +3960,6 @@ Signal Desktop makes use of the following open source projects.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-router-dom
MIT License
Copyright (c) React Training 2016-2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## react-textarea-autosize
The MIT License (MIT)
Copyright (c) 2013 Andrey Popp
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## react-virtualized
The MIT License (MIT)

View file

@ -176,19 +176,16 @@
"protobufjs": "7.3.2",
"proxy-agent": "6.4.0",
"qrcode-generator": "1.4.4",
"react": "17.0.2",
"react": "18.3.1",
"react-aria": "3.35.1",
"react-aria-components": "1.4.1",
"react-blurhash": "0.3.0",
"react-contextmenu": "2.14.0",
"react-dom": "17.0.2",
"react-hot-loader": "4.13.1",
"react-intl": "6.8.7",
"react-dom": "18.3.1",
"react-intl": "7.1.11",
"react-popper": "2.3.0",
"react-redux": "8.1.3",
"react-router-dom": "5.0.1",
"react-textarea-autosize": "8.5.5",
"react-virtualized": "9.22.5",
"react-redux": "9.2.0",
"react-virtualized": "9.22.6",
"read-last-lines": "1.8.0",
"redux": "5.0.1",
"redux-logger": "3.0.6",
@ -267,8 +264,7 @@
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/react-redux": "7.1.34",
"@types/react-router-dom": "4.3.4",
"@types/react-virtualized": "9.21.30",
"@types/react-virtualized": "9.22.2",
"@types/redux-logger": "3.0.13",
"@types/semver": "7.5.8",
"@types/sinon": "17.0.3",
@ -348,13 +344,15 @@
"pnpm": {
"overrides": {
"@storybook/core>node-fetch": "$node-fetch",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/react": "18.3.20",
"@types/react-dom": "18.3.6",
"eslint-config-airbnb-typescript-prettier>eslint-plugin-prettier": "5.2.1",
"canvas": "-",
"jsdom": "-",
"thenify-all>thenify": "3.3.1",
"@electron/rebuild@3.7.0>@electron/node-gyp": "10.2.0-electron.2"
"@electron/rebuild@3.7.0>@electron/node-gyp": "10.2.0-electron.2",
"react-contextmenu>react": "18.3.1",
"react-contextmenu>react-dom": "18.3.1"
},
"patchedDependencies": {
"@types/backbone@1.4.22": "patches/@types+backbone+1.4.22.patch",
@ -369,7 +367,6 @@
"react-contextmenu@2.14.0": "patches/react-contextmenu+2.14.0.patch",
"@vitest/expect@2.0.5": "patches/@vitest+expect+2.0.5.patch",
"got@11.8.5": "patches/got+11.8.5.patch",
"react-textarea-autosize@8.5.5": "patches/react-textarea-autosize+8.5.5.patch",
"growing-file@0.1.3": "patches/growing-file+0.1.3.patch",
"websocket@1.0.34": "patches/websocket+1.0.34.patch",
"@types/websocket@1.0.0": "patches/@types+websocket+1.0.0.patch",

View file

@ -1,5 +1,5 @@
diff --git a/modules/ContextMenu.js b/modules/ContextMenu.js
index 2f88213..41e47ea 100644
index 2f8821393f15a1ce85132385688f08cd6e390bf4..41e47ea740636f81bdd484a7093d0ea3169eb9d5 100644
--- a/modules/ContextMenu.js
+++ b/modules/ContextMenu.js
@@ -81,6 +81,11 @@ var ContextMenu = function (_AbstractMenu) {
@ -53,7 +53,7 @@ index 2f88213..41e47ea 100644
}
}
diff --git a/modules/SubMenu.js b/modules/SubMenu.js
index ad1dc70..c919be8 100644
index ad1dc7043c13cbc30f1659d5b82696ed974c996a..c919be8d12329dd5bcf77d3660b85edb83714bb8 100644
--- a/modules/SubMenu.js
+++ b/modules/SubMenu.js
@@ -129,6 +129,7 @@ var SubMenu = function (_AbstractMenu) {
@ -142,14 +142,45 @@ index ad1dc70..c919be8 100644
}
}
diff --git a/src/index.d.ts b/src/index.d.ts
index 753ce90..c5971a4 100644
index 753ce9081490fd90b4354e6e73937dc85957689c..e8a8b0815a7ca61ef9bc488299cda08e181267bc 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -14,6 +14,7 @@ declare module "react-contextmenu" {
@@ -14,6 +14,8 @@ declare module "react-contextmenu" {
preventHideOnResize?: boolean,
preventHideOnScroll?: boolean,
preventHideOnScroll?: boolean,
style?: React.CSSProperties,
+ avoidFocusRestoreOnBlur?: boolean;
+ children?: React.ReactNode;
}
export interface ContextMenuTriggerProps {
@@ -25,6 +27,7 @@ declare module "react-contextmenu" {
renderTag?: React.ElementType,
mouseButton?: number,
disableIfShiftIsPressed?: boolean,
+ children?: React.ReactNode,
}
export interface MenuItemProps {
@@ -35,6 +38,7 @@ declare module "react-contextmenu" {
divider?: boolean,
preventClose?: boolean,
onClick?: {(event: React.TouchEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>, data: Object, target: HTMLElement): void} | Function,
+ children?: React.ReactNode,
}
export interface SubMenuProps {
@@ -45,11 +49,13 @@ declare module "react-contextmenu" {
rtl?: boolean,
preventCloseOnClick?: boolean,
onClick?: {(event: React.TouchEvent<HTMLDivElement> | React.MouseEvent<HTMLDivElement>, data: Object, target: HTMLElement): void} | Function,
+ children?: React.ReactNode,
}
export interface ConnectMenuProps {
id: string;
trigger: any;
+ children?: React.ReactNode;
}
export const ContextMenu: React.ComponentClass<ContextMenuProps>;

View file

@ -1,52 +0,0 @@
diff --git a/dist/react-textarea-autosize.browser.cjs.js b/dist/react-textarea-autosize.browser.cjs.js
index 37d2acb..7f76a65 100644
--- a/dist/react-textarea-autosize.browser.cjs.js
+++ b/dist/react-textarea-autosize.browser.cjs.js
@@ -122,7 +122,7 @@ var SIZING_STYLE = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
'tabSize', 'textIndent',
// non-standard
'textRendering', 'textTransform', 'width', 'wordBreak', 'wordSpacing', 'scrollbarGutter'];
-var isIE = !!document.documentElement.currentStyle ;
+var isIE = false;
var getSizingData = function getSizingData(node) {
var style = window.getComputedStyle(node);
if (style === null) {
diff --git a/dist/react-textarea-autosize.browser.development.cjs.js b/dist/react-textarea-autosize.browser.development.cjs.js
index 51e50d5..29a11e3 100644
--- a/dist/react-textarea-autosize.browser.development.cjs.js
+++ b/dist/react-textarea-autosize.browser.development.cjs.js
@@ -122,7 +122,7 @@ var SIZING_STYLE = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
'tabSize', 'textIndent',
// non-standard
'textRendering', 'textTransform', 'width', 'wordBreak', 'wordSpacing', 'scrollbarGutter'];
-var isIE = !!document.documentElement.currentStyle ;
+var isIE = false;
var getSizingData = function getSizingData(node) {
var style = window.getComputedStyle(node);
if (style === null) {
diff --git a/dist/react-textarea-autosize.browser.development.esm.js b/dist/react-textarea-autosize.browser.development.esm.js
index bbaa14c..4eac3f8 100644
--- a/dist/react-textarea-autosize.browser.development.esm.js
+++ b/dist/react-textarea-autosize.browser.development.esm.js
@@ -94,7 +94,7 @@ var SIZING_STYLE = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
'tabSize', 'textIndent',
// non-standard
'textRendering', 'textTransform', 'width', 'wordBreak', 'wordSpacing', 'scrollbarGutter'];
-var isIE = !!document.documentElement.currentStyle ;
+var isIE = false;
var getSizingData = function getSizingData(node) {
var style = window.getComputedStyle(node);
if (style === null) {
diff --git a/dist/react-textarea-autosize.browser.esm.js b/dist/react-textarea-autosize.browser.esm.js
index 7376e08..196b550 100644
--- a/dist/react-textarea-autosize.browser.esm.js
+++ b/dist/react-textarea-autosize.browser.esm.js
@@ -94,7 +94,7 @@ var SIZING_STYLE = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth',
'tabSize', 'textIndent',
// non-standard
'textRendering', 'textTransform', 'width', 'wordBreak', 'wordSpacing', 'scrollbarGutter'];
-var isIE = !!document.documentElement.currentStyle ;
+var isIE = false;
var getSizingData = function getSizingData(node) {
var style = window.getComputedStyle(node);
if (style === null) {

2243
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,7 @@
resize: none;
text-align: center;
width: 100%;
field-sizing: content;
&:disabled {
color: inherit;

View file

@ -1,6 +1,7 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ChangeEvent, KeyboardEvent } from 'react';
import React, { useCallback, useState, useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';
@ -35,14 +36,14 @@ export function AutoSizeInput({
const getClassName = getClassNamesFor('AutoSizeInput', moduleClassName);
const handleChange = useCallback(
e => {
(e: ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
},
[onChange]
);
const handleKeyDown = useCallback(
event => {
(event: KeyboardEvent) => {
if (onEnter && event.key === 'Enter') {
onEnter();
}

View file

@ -76,7 +76,7 @@ export function AvatarEditor({
const [editMode, setEditMode] = useState<EditMode>(EditMode.Main);
const getSelectedAvatar = useCallback(
avatarToFind =>
(avatarToFind: AvatarDataType | undefined) =>
localAvatarData.find(avatarData =>
isSameAvatarData(avatarData, avatarToFind)
),
@ -146,7 +146,7 @@ export function AvatarEditor({
]
);
const handleAvatarLoaded = useCallback(avatarBuffer => {
const handleAvatarLoaded = useCallback((avatarBuffer: Uint8Array) => {
setAvatarPreview(avatarBuffer);
setInitialAvatar(avatarBuffer);
}, []);

View file

@ -180,14 +180,14 @@ export function CallsNewCall({
}, [directConversations, groupConversations]);
const isRowLoaded = useCallback(
({ index }) => {
({ index }: { index: number }) => {
return rows.at(index) != null;
},
[rows]
);
const rowHeight = useCallback(
({ index }) => {
({ index }: { index: number }) => {
if (rows.at(index)?.kind === 'conversation') {
return ListTile.heightCompact;
}

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import type { MouseEvent } from 'react';
import classNames from 'classnames';
import { Manager, Reference } from 'react-popper';
import Quill, { Delta } from '@signalapp/quill-cjs';
@ -950,7 +951,7 @@ export function CompositionInput(props: Props): React.ReactElement {
const getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName);
const onMouseDown = React.useCallback(
event => {
(event: MouseEvent) => {
const target = event.target as HTMLElement;
try {
// If the user is actually clicking the format menu, we drop this event

View file

@ -7,9 +7,10 @@ import type { EmojiPickDataType } from './emoji/EmojiPicker';
import type { InputApi } from './CompositionInput';
import { CompositionInput } from './CompositionInput';
import { EmojiButton } from './emoji/EmojiButton';
import type {
DraftBodyRanges,
HydratedBodyRangesType,
import {
hydrateRanges,
type DraftBodyRanges,
type HydratedBodyRangesType,
} from '../types/BodyRange';
import type { ThemeType } from '../types/Util';
import type { Props as EmojiButtonProps } from './emoji/EmojiButton';
@ -20,6 +21,7 @@ import type { FunEmojiSelection } from './fun/panels/FunPanelEmojis';
import type { EmojiSkinTone } from './fun/data/emojis';
import { FunEmojiPickerButton } from './fun/FunButton';
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
import type { GetConversationByIdType } from '../state/selectors/conversations';
export type CompositionTextAreaProps = {
bodyRanges: HydratedBodyRangesType | null;
@ -48,6 +50,7 @@ export type CompositionTextAreaProps = {
getPreferredBadge: PreferredBadgeSelectorType;
draftText: string;
theme: ThemeType;
conversationSelector: GetConversationByIdType;
} & Pick<EmojiButtonProps, 'recentEmojis' | 'emojiSkinToneDefault'>;
/**
@ -78,6 +81,7 @@ export function CompositionTextArea({
emojiSkinToneDefault,
theme,
whenToShowRemainingCount = Infinity,
conversationSelector,
}: CompositionTextAreaProps): JSX.Element {
const inputApiRef = useRef<InputApi | undefined>();
const [characterCount, setCharacterCount] = useState(
@ -128,6 +132,10 @@ export function CompositionTextArea({
bodyRanges: updatedBodyRanges,
caretLocation,
messageText: newValue,
}: {
bodyRanges: DraftBodyRanges;
caretLocation?: number | undefined;
messageText: string;
}) => {
const inputEl = inputApiRef.current;
if (!inputEl) {
@ -139,6 +147,9 @@ export function CompositionTextArea({
maxLength
);
const hydratedBodyRanges =
hydrateRanges(updatedBodyRanges, conversationSelector) ?? [];
if (maxLength !== undefined) {
// if we had to truncate
if (newValueSized.length < newValue.length) {
@ -149,13 +160,13 @@ export function CompositionTextArea({
// was modifying text in the middle of the editor
// a better solution would be to prevent the change to begin with, but
// quill makes this VERY difficult
inputEl.setContents(newValueSized, updatedBodyRanges, true);
inputEl.setContents(newValueSized, hydratedBodyRanges, true);
}
}
setCharacterCount(newCharacterCount);
onChange(newValue, updatedBodyRanges, caretLocation);
onChange(newValue, hydratedBodyRanges, caretLocation);
},
[maxLength, onChange]
[maxLength, onChange, conversationSelector]
);
return (

View file

@ -14,6 +14,7 @@ import { EmojiSkinTone } from './fun/data/emojis';
import { LoadingState } from '../util/loadable';
import { VIDEO_MP4 } from '../types/MIME';
import { drop } from '../util/drop';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
const { i18n } = window.SignalContext;
@ -38,6 +39,7 @@ function RenderCompositionTextArea(props: SmartCompositionTextAreaProps) {
ourConversationId="me"
platform="darwin"
emojiSkinToneDefault={EmojiSkinTone.None}
conversationSelector={() => getDefaultConversation()}
/>
);
}

View file

@ -1,7 +1,7 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { type ComponentType, useEffect, useMemo } from 'react';
import { useId, VisuallyHidden } from 'react-aria';
import React, { type ComponentType, useEffect, useMemo, useId } from 'react';
import { VisuallyHidden } from 'react-aria';
import type { LocalizerType } from '../types/I18N';
import { Button, ButtonVariant } from './Button';
import { Modal } from './Modal';

View file

@ -70,6 +70,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
ourConversationId="me"
platform="darwin"
emojiSkinToneDefault={EmojiSkinTone.None}
conversationSelector={() => getDefaultConversation()}
/>
),
showToast: action('showToast'),

View file

@ -1,7 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ClipboardEvent, ReactNode } from 'react';
import type { ClipboardEvent, KeyboardEvent, ReactNode } from 'react';
import React, {
forwardRef,
useCallback,
@ -114,7 +114,7 @@ export const Input = forwardRef<
}, [expandable]);
const handleKeyDown = useCallback(
event => {
(event: KeyboardEvent) => {
if (onEnter && event.key === 'Enter') {
onEnter();
}

View file

@ -4,6 +4,7 @@
import React, {
useCallback,
useEffect,
useId,
useMemo,
useRef,
useState,
@ -27,7 +28,6 @@ import type { LocalizerType } from '../types/Util';
import type { MIMEType } from '../types/MIME';
import type { Props as StickerButtonProps } from './stickers/StickerButton';
import type { imageToBlurHash } from '../util/imageToBlurHash';
import { MediaEditorFabricAnalogTimeSticker } from '../mediaEditor/MediaEditorFabricAnalogTimeSticker';
import { MediaEditorFabricCropRect } from '../mediaEditor/MediaEditorFabricCropRect';
import { MediaEditorFabricDigitalTimeSticker } from '../mediaEditor/MediaEditorFabricDigitalTimeSticker';
@ -60,7 +60,6 @@ import { hydrateRanges } from '../types/BodyRange';
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
import { useFabricHistory } from '../mediaEditor/useFabricHistory';
import { usePortal } from '../hooks/usePortal';
import { useUniqueId } from '../hooks/useUniqueId';
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
import { FunEmojiPicker } from './fun/FunEmojiPicker';
import { FunEmojiPickerButton, FunStickerPickerButton } from './fun/FunButton';
@ -195,7 +194,7 @@ export function MediaEditor({
const inputApiRef = useRef<InputApi | undefined>();
const canvasId = useUniqueId();
const canvasId = useId();
const [imageState, setImageState] =
useState<ImageStateType>(INITIAL_IMAGE_STATE);
@ -237,7 +236,7 @@ export function MediaEditor({
);
const handlePickSticker = useCallback(
(_packId, _stickerId, src: string) => {
(_packId: string, _stickerId: number, src: string) => {
async function run() {
if (!fabricCanvas) {
return;

View file

@ -1,7 +1,7 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Key } from 'react';
import type { Key, ReactNode } from 'react';
import React from 'react';
import { Tabs, TabList, Tab, TabPanel } from 'react-aria-components';
import classNames from 'classnames';
@ -189,12 +189,12 @@ export type NavTabsProps = Readonly<{
navTabsCollapsed: boolean;
onShowSettings: () => void;
onStartUpdate: () => unknown;
onNavTabSelected(tab: NavTab): void;
onToggleNavTabsCollapse(collapsed: boolean): void;
onNavTabSelected: (tab: NavTab) => void;
onToggleNavTabsCollapse: (collapsed: boolean) => void;
onToggleProfileEditor: () => void;
renderCallsTab(props: NavTabPanelProps): JSX.Element;
renderChatsTab(props: NavTabPanelProps): JSX.Element;
renderStoriesTab(props: NavTabPanelProps): JSX.Element;
renderCallsTab: () => ReactNode;
renderChatsTab: () => ReactNode;
renderStoriesTab: () => ReactNode;
selectedNavTab: NavTab;
storiesEnabled: boolean;
theme: ThemeType;

View file

@ -1,6 +1,5 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { AudioDevice } from '@signalapp/ringrtc';
import React, {
useCallback,
@ -8,11 +7,11 @@ import React, {
useMemo,
useRef,
useState,
useId,
} from 'react';
import { noop, partition } from 'lodash';
import classNames from 'classnames';
import * as LocaleMatcher from '@formatjs/intl-localematcher';
import type { MediaDeviceSettings } from '../types/Calling';
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
import type {
@ -35,12 +34,10 @@ import type {
SentMediaQualityType,
ThemeType,
} from '../types/Util';
import { Button, ButtonVariant } from './Button';
import { ChatColorPicker } from './ChatColorPicker';
import { Checkbox } from './Checkbox';
import { WidthBreakpoint } from './_util';
import { ConfirmationDialog } from './ConfirmationDialog';
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
@ -57,7 +54,6 @@ import {
} from '../util/expirationTimer';
import { DurationInSeconds } from '../util/durations';
import { useEscapeHandling } from '../hooks/useEscapeHandling';
import { useUniqueId } from '../hooks/useUniqueId';
import { focusableSelector } from '../util/focusableSelectors';
import { Modal } from './Modal';
import { SearchInput } from './SearchInput';
@ -384,10 +380,10 @@ export function Preferences({
whoCanSeeMe,
zoomFactor,
}: PropsType): JSX.Element {
const storiesId = useUniqueId();
const themeSelectId = useUniqueId();
const zoomSelectId = useUniqueId();
const languageId = useUniqueId();
const storiesId = useId();
const themeSelectId = useId();
const zoomSelectId = useId();
const languageId = useId();
const [confirmDelete, setConfirmDelete] = useState(false);
const [confirmStoriesOff, setConfirmStoriesOff] = useState(false);

View file

@ -352,7 +352,7 @@ export function ProfileEditor({
// To make AvatarEditor re-render less often
const handleAvatarLoaded = useCallback(
avatar => {
(avatar: Uint8Array) => {
setAvatarBuffer(avatar);
setOldAvatarBuffer(avatar);
},

View file

@ -1,31 +1,16 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactNode } from 'react';
import type { ProfilerOnRenderCallback, ReactNode } from 'react';
import React from 'react';
import * as log from '../logging/log';
type InternalPropsType = Readonly<{
id: string;
children: ReactNode;
onRender(
id: string,
phase: 'mount' | 'update',
actualDuration: number,
baseDuration: number,
startTime: number,
commitTime: number,
interactions: Set<unknown>
): void;
}>;
export type PropsType = Readonly<{
id: string;
children: ReactNode;
}>;
const onRender: InternalPropsType['onRender'] = (
const onRender: ProfilerOnRenderCallback = (
id,
phase,
actual,

View file

@ -2,7 +2,6 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import classNames from 'classnames';
import type { LocalizerType, RenderTextCallbackType } from '../types/Util';
@ -241,7 +240,7 @@ export const TextAttachment = forwardRef<HTMLTextAreaElement, PropsType>(
}}
>
{onChange ? (
<TextareaAutosize
<textarea
dir="auto"
className="TextAttachment__text__container TextAttachment__text__textarea"
disabled={!isEditingText}

View file

@ -538,30 +538,27 @@ export function TextStoryCreator({
data-popper-arrow
className="StoryCreator__popper__arrow"
/>
{objectMap<BackgroundStyleType>(
BackgroundStyle,
(bg, backgroundValue) => (
<button
aria-label={i18n('icu:StoryCreator__story-bg')}
className={classNames({
StoryCreator__bg: true,
'StoryCreator__bg--selected':
selectedBackground === backgroundValue,
})}
key={String(bg)}
onClick={() => {
setSelectedBackground(backgroundValue);
setIsColorPickerShowing(false);
}}
type="button"
style={{
background: getBackgroundColor(
getBackground(backgroundValue)
),
}}
/>
)
)}
{objectMap(BackgroundStyle, (bg, backgroundValue) => (
<button
aria-label={i18n('icu:StoryCreator__story-bg')}
className={classNames({
StoryCreator__bg: true,
'StoryCreator__bg--selected':
selectedBackground === backgroundValue,
})}
key={String(bg)}
onClick={() => {
setSelectedBackground(backgroundValue);
setIsColorPickerShowing(false);
}}
type="button"
style={{
background: getBackgroundColor(
getBackground(backgroundValue)
),
}}
/>
))}
</div>
)}
<button

View file

@ -182,7 +182,7 @@ export const VoiceNotesPlaybackContext =
React.createContext<Contents>(globalContents);
export type VoiceNotesPlaybackProps = {
children?: React.ReactNode | React.ReactChildren;
children?: React.ReactNode;
};
/**

View file

@ -158,7 +158,7 @@ export function ImageGrid({
);
const showAttachmentOrNoLongerAvailableToast = React.useCallback(
attachmentIndex =>
(attachmentIndex: number) =>
attachments[attachmentIndex].isPermanentlyUndownloadable
? showMediaNoLongerAvailableToast
: showVisualAttachment,

View file

@ -1,8 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useMemo } from 'react';
import React, { useMemo, useId } from 'react';
import type { ConversationTypeType } from '../../../state/ducks/conversations';
import type { LocalizerType } from '../../../types/Util';
import { PanelSection } from './PanelSection';
@ -12,7 +11,6 @@ import { Select } from '../../Select';
import { isConversationMuted } from '../../../util/isConversationMuted';
import { getMuteOptions } from '../../../util/getMuteOptions';
import { parseIntOrThrow } from '../../../util/parseIntOrThrow';
import { useUniqueId } from '../../../hooks/useUniqueId';
export type PropsType = {
id: string;
@ -39,8 +37,8 @@ export function ConversationNotificationsSettings({
setMuteExpiration,
setDontNotifyForMentionsIfMuted,
}: PropsType): JSX.Element {
const muteNotificationsSelectId = useUniqueId();
const mentionsSelectId = useUniqueId();
const muteNotificationsSelectId = useId();
const mentionsSelectId = useId();
const muteOptions = useMemo(
() => [
...(isConversationMuted({ muteExpiresAt })

View file

@ -1,22 +1,17 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import React, { useId, useState } from 'react';
import type { ConversationType } from '../../../state/ducks/conversations';
import type { LocalizerType } from '../../../types/Util';
import { ConfirmationDialog } from '../../ConfirmationDialog';
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
import { PanelRow } from './PanelRow';
import { PanelSection } from './PanelSection';
import { Select } from '../../Select';
import { SignalService as Proto } from '../../../protobuf';
import { copyGroupLink } from '../../../util/copyLinksWithToast';
import { drop } from '../../../util/drop';
import { useDelayedRestoreFocus } from '../../../hooks/useRestoreFocus';
import { useUniqueId } from '../../../hooks/useUniqueId';
const AccessControlEnum = Proto.AccessControl.AccessRequired;
@ -43,8 +38,8 @@ export function GroupLinkManagement({
isAdmin,
setAccessControlAddFromInviteLinkSetting,
}: PropsType): JSX.Element {
const groupLinkSelectId = useUniqueId();
const approveSelectId = useUniqueId();
const groupLinkSelectId = useId();
const approveSelectId = useId();
if (conversation === undefined) {
throw new Error('GroupLinkManagement rendered without a conversation');

View file

@ -1,17 +1,13 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import React, { useId } from 'react';
import type { ConversationType } from '../../../state/ducks/conversations';
import type { LocalizerType } from '../../../types/Util';
import { getAccessControlOptions } from '../../../util/getAccessControlOptions';
import { SignalService as Proto } from '../../../protobuf';
import { PanelRow } from './PanelRow';
import { PanelSection } from './PanelSection';
import { Select } from '../../Select';
import { useUniqueId } from '../../../hooks/useUniqueId';
export type PropsDataType = {
conversation?: ConversationType;
@ -33,9 +29,9 @@ export function GroupV2Permissions({
setAccessControlMembersSetting,
setAnnouncementsOnly,
}: PropsType): JSX.Element {
const addMembersSelectId = useUniqueId();
const groupInfoSelectId = useUniqueId();
const announcementSelectId = useUniqueId();
const addMembersSelectId = useId();
const groupInfoSelectId = useId();
const announcementSelectId = useId();
if (conversation === undefined) {
throw new Error('GroupV2Permissions rendered without a conversation');

View file

@ -59,7 +59,7 @@ export function AttachmentSection({
/>
);
default:
return missingCaseError(type);
throw missingCaseError(type);
}
})}
</div>

View file

@ -1,8 +1,8 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useId } from 'react';
import type { Meta } from '@storybook/react';
import { useId, VisuallyHidden } from 'react-aria';
import { VisuallyHidden } from 'react-aria';
import { FunGif, FunGifPreview } from './FunGif';
import { LoadingState } from '../../util/loadable';

View file

@ -1,5 +1,6 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ChangeEvent } from 'react';
import React, { useCallback } from 'react';
import { VisuallyHidden } from 'react-aria';
import { getInteractionModality } from '@react-aria/interactions';
@ -19,8 +20,8 @@ export function FunSearch(props: FunSearchProps): JSX.Element {
const { shouldAutoFocus, onChangeShouldAutoFocus } = useFunContext();
const handleChange = useCallback(
event => {
onSearchInputChange(event.target.value);
(event: ChangeEvent<HTMLInputElement>) => {
onSearchInputChange(event.currentTarget.value);
},
[onSearchInputChange]
);

View file

@ -12,8 +12,8 @@ import React, {
useMemo,
useRef,
useState,
useId,
} from 'react';
import { useId } from 'react-aria';
import type { Selection } from 'react-aria-components';
import { ListBox, ListBoxItem } from 'react-aria-components';
import {

View file

@ -3,9 +3,8 @@
import type { Transition } from 'framer-motion';
import { AnimatePresence, motion } from 'framer-motion';
import type { ReactNode } from 'react';
import React, { useCallback } from 'react';
import React, { useCallback, useId } from 'react';
import type { Key } from 'react-aria';
import { useId } from 'react-aria';
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
import type { FunPickerTabKey } from '../constants';

View file

@ -9,9 +9,10 @@ import React, {
useMemo,
useRef,
useState,
useId,
} from 'react';
import type { PressEvent } from 'react-aria';
import { useId, VisuallyHidden } from 'react-aria';
import { VisuallyHidden } from 'react-aria';
import { LRUCache } from 'lru-cache';
import { FunItemButton } from '../base/FunItem';
import {

View file

@ -1,27 +1,28 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { useEffect, useState } from 'react';
import { useMemo, useSyncExternalStore } from 'react';
// Allows this to work in Node process
let reducedMotionQuery: MediaQueryList;
function getReducedMotionQuery() {
if (reducedMotionQuery == null) {
reducedMotionQuery = window.matchMedia('(prefers-reduced-motion)');
}
return reducedMotionQuery;
export function useMediaQuery(query: string): boolean {
const api = useMemo(() => {
const mediaQuery = window.matchMedia(query);
function subscribe(onChange: () => void) {
mediaQuery.addEventListener('change', onChange);
return () => {
mediaQuery.removeEventListener('change', onChange);
};
}
function getSnapshot() {
return mediaQuery.matches;
}
return { subscribe, getSnapshot };
}, [query]);
return useSyncExternalStore(api.subscribe, api.getSnapshot);
}
export function useReducedMotion(): boolean {
const [matches, setMatches] = useState(getReducedMotionQuery().matches);
useEffect(() => {
function onChange(event: MediaQueryListEvent) {
setMatches(event.matches);
}
getReducedMotionQuery().addEventListener('change', onChange);
return () => {
getReducedMotionQuery().removeEventListener('change', onChange);
};
}, []);
return matches;
return useMediaQuery('(prefers-reduced-motion)');
}

View file

@ -1,24 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { useMemo, useState } from 'react';
import { v4 as uuid } from 'uuid';
export function useUniqueId(): string {
return useMemo(() => uuid(), []);
}
let nextElementId = 0;
export function useElementId(
namePrefix: string
): [id: string, selector: string] {
// Prefixed to avoid starting with a number (which is invalid in CSS selectors)
const [id] = useState(() => {
const currentId = nextElementId;
nextElementId += 1;
return `${namePrefix}-${currentId}`;
});
// Return the ID and a selector that can be used in CSS or JS
return [id, `#${id}`] as const;
}

View file

@ -10,6 +10,7 @@ import { useItemsActions } from '../ducks/items';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { useComposerActions } from '../ducks/composer';
import { getTextFormattingEnabled } from '../selectors/items';
import { getConversationSelector } from '../selectors/conversations';
export type SmartCompositionTextAreaProps = Pick<
CompositionTextAreaProps,
@ -39,6 +40,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
const conversationSelector = useSelector(getConversationSelector);
return (
<CompositionTextArea
@ -52,6 +54,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
onTextTooLong={onTextTooLong}
platform={platform}
ourConversationId={ourConversationId}
conversationSelector={conversationSelector}
/>
);
});

View file

@ -45,6 +45,7 @@ import { getLocalDeleteWarningShown } from '../selectors/items';
import { getDeleteSyncSendEnabled } from '../selectors/items-extra';
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
import { isDirectConversation } from '../../util/whatTypeOfConversation';
import type { DurationInSeconds } from '../../util/durations';
export type OwnProps = {
id: string;
@ -182,7 +183,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
}, [destroyMessages, conversation.id]);
const onConversationDisappearingMessagesChange = useCallback(
seconds => {
(seconds: DurationInSeconds) => {
setDisappearingMessages(conversation.id, seconds);
},
[setDisappearingMessages, conversation.id]
@ -197,7 +198,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
}, [onMarkUnread, conversation.id]);
const onConversationMuteExpirationChange = useCallback(
seconds => {
(seconds: number) => {
setMuteExpiration(conversation.id, seconds);
},
[setMuteExpiration, conversation.id]

View file

@ -5,7 +5,10 @@ import React, { useCallback, forwardRef, memo } from 'react';
import { useSelector } from 'react-redux';
import { useRecentEmojis } from '../selectors/emojis';
import { useEmojisActions as useEmojiActions } from '../ducks/emojis';
import type { Props as EmojiPickerProps } from '../../components/emoji/EmojiPicker';
import type {
EmojiPickDataType,
Props as EmojiPickerProps,
} from '../../components/emoji/EmojiPicker';
import { EmojiPicker } from '../../components/emoji/EmojiPicker';
import { getIntl } from '../selectors/user';
import { getEmojiSkinToneDefault } from '../selectors/items';
@ -39,7 +42,7 @@ export const SmartEmojiPicker = memo(
const { onUseEmoji } = useEmojiActions();
const handlePickEmoji = useCallback(
data => {
(data: EmojiPickDataType) => {
onUseEmoji({ shortName: data.shortName, skinTone: EmojiSkinTone.None });
onPickEmoji(data);
},

View file

@ -1,9 +1,9 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactNode } from 'react';
import React, { memo, useCallback } from 'react';
import { useSelector } from 'react-redux';
import type { NavTabPanelProps } from '../../components/NavTabs';
import { NavTabs } from '../../components/NavTabs';
import { getIntl, getTheme } from '../selectors/user';
import {
@ -27,10 +27,10 @@ import { getCallHistoryUnreadCount } from '../selectors/callHistory';
export type SmartNavTabsProps = Readonly<{
navTabsCollapsed: boolean;
onToggleNavTabsCollapse(navTabsCollapsed: boolean): void;
renderCallsTab(props: NavTabPanelProps): JSX.Element;
renderChatsTab(props: NavTabPanelProps): JSX.Element;
renderStoriesTab(props: NavTabPanelProps): JSX.Element;
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void;
renderCallsTab: () => ReactNode;
renderChatsTab: () => ReactNode;
renderStoriesTab: () => ReactNode;
}>;
export const SmartNavTabs = memo(function SmartNavTabs({

View file

@ -35,6 +35,8 @@ import { useAudioPlayerActions } from '../ducks/audioPlayer';
import { useGlobalModalActions } from '../ducks/globalModals';
import { useStoriesActions } from '../ducks/stories';
import { useIsWindowActive } from '../../hooks/useIsWindowActive';
import type { DraftBodyRanges } from '../../types/BodyRange';
import type { StoryViewType } from '../../types/Stories';
export const SmartStoryViewer = memo(function SmartStoryViewer() {
const {
@ -98,17 +100,22 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() {
);
const handleReactToStory = useCallback(
async (emoji, story) => {
async (emoji: string, story: StoryViewType) => {
const { messageId } = story;
reactToStory(emoji, messageId);
},
[reactToStory]
);
const handleReplyToStory = useCallback(
(message, mentions, timestamp, story) => {
(
message: string,
bodyRanges: DraftBodyRanges,
timestamp: number,
story: StoryViewType
) => {
const conversationId = storyInfo?.conversationStory?.conversationId;
strictAssert(conversationId != null, 'conversationId is required');
replyToStory(conversationId, message, mentions, timestamp, story);
replyToStory(conversationId, message, bodyRanges, timestamp, story);
},
[storyInfo, replyToStory]
);

View file

@ -748,258 +748,6 @@
"reasonCategory": "usageTrusted",
"updated": "2024-11-14T18:53:33.345Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.cjs.js",
"line": " var heightRef = React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.cjs.js",
"line": " var measurementsCacheRef = React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.cjs.js",
"line": " var heightRef = React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.cjs.js",
"line": " var measurementsCacheRef = React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.esm.js",
"line": " var heightRef = React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.development.esm.js",
"line": " var measurementsCacheRef = React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.esm.js",
"line": " var heightRef = React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.browser.esm.js",
"line": " var measurementsCacheRef = React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.cjs.js",
"line": " React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.cjs.js",
"line": " React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.cjs.js",
"line": " React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.cjs.js",
"line": " React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.esm.js",
"line": " React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.esm.js",
"line": " React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.cjs.js",
"line": " React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.cjs.js",
"line": " React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.esm.js",
"line": " React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.development.worker.esm.js",
"line": " React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.esm.js",
"line": " React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.esm.js",
"line": " React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.cjs.js",
"line": " var libRef = React__namespace.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.cjs.js",
"line": " React__namespace.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.cjs.js",
"line": " React__namespace.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.esm.js",
"line": " var libRef = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.esm.js",
"line": " React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "node_modules/react-textarea-autosize/dist/react-textarea-autosize.worker.esm.js",
"line": " React.useRef();",
"reasonCategory": "usageTrusted",
"updated": "2024-11-16T00:31:42.167Z"
},
{
"rule": "React-useRef",
"path": "ts/calling/useGetCallingFrameBuffer.ts",

View file

@ -88,13 +88,11 @@ const excludedFilesRegexp = RegExp(
'^node_modules/react-aria-components/.+',
'^node_modules/react-contextmenu/.+',
'^node_modules/react-dom/.+',
'^node_modules/react-hot-loader/.+',
'^node_modules/react-icon-base/.+',
'^node_modules/react-input-autosize/.+',
'^node_modules/react-popper/.+',
'^node_modules/react-redux/.+',
'^node_modules/react-router/.+',
'^node_modules/react-router-dom/.+',
'^node_modules/react-select/.+',
'^node_modules/react-transition-group/.+',
'^node_modules/react-virtualized/.+',

View file

@ -1,10 +1,10 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export function objectMap<T>(
export function objectMap<T, R>(
obj: Record<string, T>,
f: (key: keyof typeof obj, value: (typeof obj)[keyof typeof obj]) => unknown
): Array<unknown> {
f: (key: keyof typeof obj, value: (typeof obj)[keyof typeof obj]) => R
): Array<R> {
const keys: Array<keyof typeof obj> = Object.keys(obj);
return keys.map(key => f(key, obj[key]));
}

View file

@ -3,6 +3,7 @@
import type { IntlShape } from 'react-intl';
import { createIntl, createIntlCache } from 'react-intl';
import type { ReactNode } from 'react';
import type { LocaleMessageType, LocaleMessagesType } from '../types/I18N';
import type {
LocalizerType,
@ -26,7 +27,7 @@ export function isLocaleMessageType(
}
export type SetupI18nOptionsType = Readonly<{
renderEmojify: (parts: ReadonlyArray<unknown>) => JSX.Element | void;
renderEmojify: (parts: ReadonlyArray<unknown>) => ReactNode;
}>;
export function createCachedIntl(

View file

@ -3,7 +3,7 @@
import { assertDev } from './assert';
export function shouldNeverBeCalled(..._args: ReadonlyArray<unknown>): void {
export function shouldNeverBeCalled(..._args: ReadonlyArray<unknown>): never {
assertDev(false, 'This should never be called. Doing nothing');
}