Upgrade to React 18
This commit is contained in:
parent
560dcb91d9
commit
14d098f40f
46 changed files with 1210 additions and 1796 deletions
|
@ -6,7 +6,6 @@ module.exports = {
|
||||||
// Detects the type of file being babel'd (either esmodule or commonjs)
|
// Detects the type of file being babel'd (either esmodule or commonjs)
|
||||||
sourceType: 'unambiguous',
|
sourceType: 'unambiguous',
|
||||||
plugins: [
|
plugins: [
|
||||||
'react-hot-loader/babel',
|
|
||||||
'lodash',
|
'lodash',
|
||||||
'@babel/plugin-transform-typescript',
|
'@babel/plugin-transform-typescript',
|
||||||
'@babel/plugin-proposal-class-properties',
|
'@babel/plugin-proposal-class-properties',
|
||||||
|
|
|
@ -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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
## react-intl
|
||||||
|
|
||||||
Copyright 2019 Oath Inc.
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
## react-virtualized
|
||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
25
package.json
25
package.json
|
@ -176,19 +176,16 @@
|
||||||
"protobufjs": "7.3.2",
|
"protobufjs": "7.3.2",
|
||||||
"proxy-agent": "6.4.0",
|
"proxy-agent": "6.4.0",
|
||||||
"qrcode-generator": "1.4.4",
|
"qrcode-generator": "1.4.4",
|
||||||
"react": "17.0.2",
|
"react": "18.3.1",
|
||||||
"react-aria": "3.35.1",
|
"react-aria": "3.35.1",
|
||||||
"react-aria-components": "1.4.1",
|
"react-aria-components": "1.4.1",
|
||||||
"react-blurhash": "0.3.0",
|
"react-blurhash": "0.3.0",
|
||||||
"react-contextmenu": "2.14.0",
|
"react-contextmenu": "2.14.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "18.3.1",
|
||||||
"react-hot-loader": "4.13.1",
|
"react-intl": "7.1.11",
|
||||||
"react-intl": "6.8.7",
|
|
||||||
"react-popper": "2.3.0",
|
"react-popper": "2.3.0",
|
||||||
"react-redux": "8.1.3",
|
"react-redux": "9.2.0",
|
||||||
"react-router-dom": "5.0.1",
|
"react-virtualized": "9.22.6",
|
||||||
"react-textarea-autosize": "8.5.5",
|
|
||||||
"react-virtualized": "9.22.5",
|
|
||||||
"read-last-lines": "1.8.0",
|
"read-last-lines": "1.8.0",
|
||||||
"redux": "5.0.1",
|
"redux": "5.0.1",
|
||||||
"redux-logger": "3.0.6",
|
"redux-logger": "3.0.6",
|
||||||
|
@ -267,8 +264,7 @@
|
||||||
"@types/react": "17.0.45",
|
"@types/react": "17.0.45",
|
||||||
"@types/react-dom": "17.0.17",
|
"@types/react-dom": "17.0.17",
|
||||||
"@types/react-redux": "7.1.34",
|
"@types/react-redux": "7.1.34",
|
||||||
"@types/react-router-dom": "4.3.4",
|
"@types/react-virtualized": "9.22.2",
|
||||||
"@types/react-virtualized": "9.21.30",
|
|
||||||
"@types/redux-logger": "3.0.13",
|
"@types/redux-logger": "3.0.13",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"@types/sinon": "17.0.3",
|
"@types/sinon": "17.0.3",
|
||||||
|
@ -348,13 +344,15 @@
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@storybook/core>node-fetch": "$node-fetch",
|
"@storybook/core>node-fetch": "$node-fetch",
|
||||||
"@types/react": "17.0.45",
|
"@types/react": "18.3.20",
|
||||||
"@types/react-dom": "17.0.17",
|
"@types/react-dom": "18.3.6",
|
||||||
"eslint-config-airbnb-typescript-prettier>eslint-plugin-prettier": "5.2.1",
|
"eslint-config-airbnb-typescript-prettier>eslint-plugin-prettier": "5.2.1",
|
||||||
"canvas": "-",
|
"canvas": "-",
|
||||||
"jsdom": "-",
|
"jsdom": "-",
|
||||||
"thenify-all>thenify": "3.3.1",
|
"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": {
|
"patchedDependencies": {
|
||||||
"@types/backbone@1.4.22": "patches/@types+backbone+1.4.22.patch",
|
"@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",
|
"react-contextmenu@2.14.0": "patches/react-contextmenu+2.14.0.patch",
|
||||||
"@vitest/expect@2.0.5": "patches/@vitest+expect+2.0.5.patch",
|
"@vitest/expect@2.0.5": "patches/@vitest+expect+2.0.5.patch",
|
||||||
"got@11.8.5": "patches/got+11.8.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",
|
"growing-file@0.1.3": "patches/growing-file+0.1.3.patch",
|
||||||
"websocket@1.0.34": "patches/websocket+1.0.34.patch",
|
"websocket@1.0.34": "patches/websocket+1.0.34.patch",
|
||||||
"@types/websocket@1.0.0": "patches/@types+websocket+1.0.0.patch",
|
"@types/websocket@1.0.0": "patches/@types+websocket+1.0.0.patch",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/modules/ContextMenu.js b/modules/ContextMenu.js
|
diff --git a/modules/ContextMenu.js b/modules/ContextMenu.js
|
||||||
index 2f88213..41e47ea 100644
|
index 2f8821393f15a1ce85132385688f08cd6e390bf4..41e47ea740636f81bdd484a7093d0ea3169eb9d5 100644
|
||||||
--- a/modules/ContextMenu.js
|
--- a/modules/ContextMenu.js
|
||||||
+++ b/modules/ContextMenu.js
|
+++ b/modules/ContextMenu.js
|
||||||
@@ -81,6 +81,11 @@ var ContextMenu = function (_AbstractMenu) {
|
@@ -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
|
diff --git a/modules/SubMenu.js b/modules/SubMenu.js
|
||||||
index ad1dc70..c919be8 100644
|
index ad1dc7043c13cbc30f1659d5b82696ed974c996a..c919be8d12329dd5bcf77d3660b85edb83714bb8 100644
|
||||||
--- a/modules/SubMenu.js
|
--- a/modules/SubMenu.js
|
||||||
+++ b/modules/SubMenu.js
|
+++ b/modules/SubMenu.js
|
||||||
@@ -129,6 +129,7 @@ var SubMenu = function (_AbstractMenu) {
|
@@ -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
|
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
|
--- a/src/index.d.ts
|
||||||
+++ b/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,
|
preventHideOnResize?: boolean,
|
||||||
preventHideOnScroll?: boolean,
|
preventHideOnScroll?: boolean,
|
||||||
style?: React.CSSProperties,
|
style?: React.CSSProperties,
|
||||||
+ avoidFocusRestoreOnBlur?: boolean;
|
+ avoidFocusRestoreOnBlur?: boolean;
|
||||||
|
+ children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContextMenuTriggerProps {
|
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>;
|
||||||
|
|
|
@ -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
2243
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -48,6 +48,7 @@
|
||||||
resize: none;
|
resize: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
field-sizing: content;
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2024 Signal Messenger, LLC
|
// Copyright 2024 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { ChangeEvent, KeyboardEvent } from 'react';
|
||||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -35,14 +36,14 @@ export function AutoSizeInput({
|
||||||
const getClassName = getClassNamesFor('AutoSizeInput', moduleClassName);
|
const getClassName = getClassNamesFor('AutoSizeInput', moduleClassName);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
e => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
},
|
},
|
||||||
[onChange]
|
[onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
event => {
|
(event: KeyboardEvent) => {
|
||||||
if (onEnter && event.key === 'Enter') {
|
if (onEnter && event.key === 'Enter') {
|
||||||
onEnter();
|
onEnter();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ export function AvatarEditor({
|
||||||
const [editMode, setEditMode] = useState<EditMode>(EditMode.Main);
|
const [editMode, setEditMode] = useState<EditMode>(EditMode.Main);
|
||||||
|
|
||||||
const getSelectedAvatar = useCallback(
|
const getSelectedAvatar = useCallback(
|
||||||
avatarToFind =>
|
(avatarToFind: AvatarDataType | undefined) =>
|
||||||
localAvatarData.find(avatarData =>
|
localAvatarData.find(avatarData =>
|
||||||
isSameAvatarData(avatarData, avatarToFind)
|
isSameAvatarData(avatarData, avatarToFind)
|
||||||
),
|
),
|
||||||
|
@ -146,7 +146,7 @@ export function AvatarEditor({
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleAvatarLoaded = useCallback(avatarBuffer => {
|
const handleAvatarLoaded = useCallback((avatarBuffer: Uint8Array) => {
|
||||||
setAvatarPreview(avatarBuffer);
|
setAvatarPreview(avatarBuffer);
|
||||||
setInitialAvatar(avatarBuffer);
|
setInitialAvatar(avatarBuffer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -180,14 +180,14 @@ export function CallsNewCall({
|
||||||
}, [directConversations, groupConversations]);
|
}, [directConversations, groupConversations]);
|
||||||
|
|
||||||
const isRowLoaded = useCallback(
|
const isRowLoaded = useCallback(
|
||||||
({ index }) => {
|
({ index }: { index: number }) => {
|
||||||
return rows.at(index) != null;
|
return rows.at(index) != null;
|
||||||
},
|
},
|
||||||
[rows]
|
[rows]
|
||||||
);
|
);
|
||||||
|
|
||||||
const rowHeight = useCallback(
|
const rowHeight = useCallback(
|
||||||
({ index }) => {
|
({ index }: { index: number }) => {
|
||||||
if (rows.at(index)?.kind === 'conversation') {
|
if (rows.at(index)?.kind === 'conversation') {
|
||||||
return ListTile.heightCompact;
|
return ListTile.heightCompact;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import type { MouseEvent } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Manager, Reference } from 'react-popper';
|
import { Manager, Reference } from 'react-popper';
|
||||||
import Quill, { Delta } from '@signalapp/quill-cjs';
|
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 getClassName = getClassNamesFor(BASE_CLASS_NAME, moduleClassName);
|
||||||
|
|
||||||
const onMouseDown = React.useCallback(
|
const onMouseDown = React.useCallback(
|
||||||
event => {
|
(event: MouseEvent) => {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
try {
|
try {
|
||||||
// If the user is actually clicking the format menu, we drop this event
|
// If the user is actually clicking the format menu, we drop this event
|
||||||
|
|
|
@ -7,9 +7,10 @@ import type { EmojiPickDataType } from './emoji/EmojiPicker';
|
||||||
import type { InputApi } from './CompositionInput';
|
import type { InputApi } from './CompositionInput';
|
||||||
import { CompositionInput } from './CompositionInput';
|
import { CompositionInput } from './CompositionInput';
|
||||||
import { EmojiButton } from './emoji/EmojiButton';
|
import { EmojiButton } from './emoji/EmojiButton';
|
||||||
import type {
|
import {
|
||||||
DraftBodyRanges,
|
hydrateRanges,
|
||||||
HydratedBodyRangesType,
|
type DraftBodyRanges,
|
||||||
|
type HydratedBodyRangesType,
|
||||||
} from '../types/BodyRange';
|
} from '../types/BodyRange';
|
||||||
import type { ThemeType } from '../types/Util';
|
import type { ThemeType } from '../types/Util';
|
||||||
import type { Props as EmojiButtonProps } from './emoji/EmojiButton';
|
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 type { EmojiSkinTone } from './fun/data/emojis';
|
||||||
import { FunEmojiPickerButton } from './fun/FunButton';
|
import { FunEmojiPickerButton } from './fun/FunButton';
|
||||||
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
||||||
|
import type { GetConversationByIdType } from '../state/selectors/conversations';
|
||||||
|
|
||||||
export type CompositionTextAreaProps = {
|
export type CompositionTextAreaProps = {
|
||||||
bodyRanges: HydratedBodyRangesType | null;
|
bodyRanges: HydratedBodyRangesType | null;
|
||||||
|
@ -48,6 +50,7 @@ export type CompositionTextAreaProps = {
|
||||||
getPreferredBadge: PreferredBadgeSelectorType;
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
draftText: string;
|
draftText: string;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
|
conversationSelector: GetConversationByIdType;
|
||||||
} & Pick<EmojiButtonProps, 'recentEmojis' | 'emojiSkinToneDefault'>;
|
} & Pick<EmojiButtonProps, 'recentEmojis' | 'emojiSkinToneDefault'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,6 +81,7 @@ export function CompositionTextArea({
|
||||||
emojiSkinToneDefault,
|
emojiSkinToneDefault,
|
||||||
theme,
|
theme,
|
||||||
whenToShowRemainingCount = Infinity,
|
whenToShowRemainingCount = Infinity,
|
||||||
|
conversationSelector,
|
||||||
}: CompositionTextAreaProps): JSX.Element {
|
}: CompositionTextAreaProps): JSX.Element {
|
||||||
const inputApiRef = useRef<InputApi | undefined>();
|
const inputApiRef = useRef<InputApi | undefined>();
|
||||||
const [characterCount, setCharacterCount] = useState(
|
const [characterCount, setCharacterCount] = useState(
|
||||||
|
@ -128,6 +132,10 @@ export function CompositionTextArea({
|
||||||
bodyRanges: updatedBodyRanges,
|
bodyRanges: updatedBodyRanges,
|
||||||
caretLocation,
|
caretLocation,
|
||||||
messageText: newValue,
|
messageText: newValue,
|
||||||
|
}: {
|
||||||
|
bodyRanges: DraftBodyRanges;
|
||||||
|
caretLocation?: number | undefined;
|
||||||
|
messageText: string;
|
||||||
}) => {
|
}) => {
|
||||||
const inputEl = inputApiRef.current;
|
const inputEl = inputApiRef.current;
|
||||||
if (!inputEl) {
|
if (!inputEl) {
|
||||||
|
@ -139,6 +147,9 @@ export function CompositionTextArea({
|
||||||
maxLength
|
maxLength
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hydratedBodyRanges =
|
||||||
|
hydrateRanges(updatedBodyRanges, conversationSelector) ?? [];
|
||||||
|
|
||||||
if (maxLength !== undefined) {
|
if (maxLength !== undefined) {
|
||||||
// if we had to truncate
|
// if we had to truncate
|
||||||
if (newValueSized.length < newValue.length) {
|
if (newValueSized.length < newValue.length) {
|
||||||
|
@ -149,13 +160,13 @@ export function CompositionTextArea({
|
||||||
// was modifying text in the middle of the editor
|
// was modifying text in the middle of the editor
|
||||||
// a better solution would be to prevent the change to begin with, but
|
// a better solution would be to prevent the change to begin with, but
|
||||||
// quill makes this VERY difficult
|
// quill makes this VERY difficult
|
||||||
inputEl.setContents(newValueSized, updatedBodyRanges, true);
|
inputEl.setContents(newValueSized, hydratedBodyRanges, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCharacterCount(newCharacterCount);
|
setCharacterCount(newCharacterCount);
|
||||||
onChange(newValue, updatedBodyRanges, caretLocation);
|
onChange(newValue, hydratedBodyRanges, caretLocation);
|
||||||
},
|
},
|
||||||
[maxLength, onChange]
|
[maxLength, onChange, conversationSelector]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { EmojiSkinTone } from './fun/data/emojis';
|
||||||
import { LoadingState } from '../util/loadable';
|
import { LoadingState } from '../util/loadable';
|
||||||
import { VIDEO_MP4 } from '../types/MIME';
|
import { VIDEO_MP4 } from '../types/MIME';
|
||||||
import { drop } from '../util/drop';
|
import { drop } from '../util/drop';
|
||||||
|
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||||
|
|
||||||
const { i18n } = window.SignalContext;
|
const { i18n } = window.SignalContext;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ function RenderCompositionTextArea(props: SmartCompositionTextAreaProps) {
|
||||||
ourConversationId="me"
|
ourConversationId="me"
|
||||||
platform="darwin"
|
platform="darwin"
|
||||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||||
|
conversationSelector={() => getDefaultConversation()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import React, { type ComponentType, useEffect, useMemo } from 'react';
|
import React, { type ComponentType, useEffect, useMemo, useId } from 'react';
|
||||||
import { useId, VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
import type { LocalizerType } from '../types/I18N';
|
import type { LocalizerType } from '../types/I18N';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import { Modal } from './Modal';
|
import { Modal } from './Modal';
|
||||||
|
|
|
@ -70,6 +70,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
ourConversationId="me"
|
ourConversationId="me"
|
||||||
platform="darwin"
|
platform="darwin"
|
||||||
emojiSkinToneDefault={EmojiSkinTone.None}
|
emojiSkinToneDefault={EmojiSkinTone.None}
|
||||||
|
conversationSelector={() => getDefaultConversation()}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
showToast: action('showToast'),
|
showToast: action('showToast'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// 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 { ClipboardEvent, ReactNode } from 'react';
|
import type { ClipboardEvent, KeyboardEvent, ReactNode } from 'react';
|
||||||
import React, {
|
import React, {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -114,7 +114,7 @@ export const Input = forwardRef<
|
||||||
}, [expandable]);
|
}, [expandable]);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
event => {
|
(event: KeyboardEvent) => {
|
||||||
if (onEnter && event.key === 'Enter') {
|
if (onEnter && event.key === 'Enter') {
|
||||||
onEnter();
|
onEnter();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useId,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
|
@ -27,7 +28,6 @@ import type { LocalizerType } from '../types/Util';
|
||||||
import type { MIMEType } from '../types/MIME';
|
import type { MIMEType } from '../types/MIME';
|
||||||
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
||||||
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
import type { imageToBlurHash } from '../util/imageToBlurHash';
|
||||||
|
|
||||||
import { MediaEditorFabricAnalogTimeSticker } from '../mediaEditor/MediaEditorFabricAnalogTimeSticker';
|
import { MediaEditorFabricAnalogTimeSticker } from '../mediaEditor/MediaEditorFabricAnalogTimeSticker';
|
||||||
import { MediaEditorFabricCropRect } from '../mediaEditor/MediaEditorFabricCropRect';
|
import { MediaEditorFabricCropRect } from '../mediaEditor/MediaEditorFabricCropRect';
|
||||||
import { MediaEditorFabricDigitalTimeSticker } from '../mediaEditor/MediaEditorFabricDigitalTimeSticker';
|
import { MediaEditorFabricDigitalTimeSticker } from '../mediaEditor/MediaEditorFabricDigitalTimeSticker';
|
||||||
|
@ -60,7 +60,6 @@ import { hydrateRanges } from '../types/BodyRange';
|
||||||
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
||||||
import { useFabricHistory } from '../mediaEditor/useFabricHistory';
|
import { useFabricHistory } from '../mediaEditor/useFabricHistory';
|
||||||
import { usePortal } from '../hooks/usePortal';
|
import { usePortal } from '../hooks/usePortal';
|
||||||
import { useUniqueId } from '../hooks/useUniqueId';
|
|
||||||
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
import { isFunPickerEnabled } from './fun/isFunPickerEnabled';
|
||||||
import { FunEmojiPicker } from './fun/FunEmojiPicker';
|
import { FunEmojiPicker } from './fun/FunEmojiPicker';
|
||||||
import { FunEmojiPickerButton, FunStickerPickerButton } from './fun/FunButton';
|
import { FunEmojiPickerButton, FunStickerPickerButton } from './fun/FunButton';
|
||||||
|
@ -195,7 +194,7 @@ export function MediaEditor({
|
||||||
|
|
||||||
const inputApiRef = useRef<InputApi | undefined>();
|
const inputApiRef = useRef<InputApi | undefined>();
|
||||||
|
|
||||||
const canvasId = useUniqueId();
|
const canvasId = useId();
|
||||||
|
|
||||||
const [imageState, setImageState] =
|
const [imageState, setImageState] =
|
||||||
useState<ImageStateType>(INITIAL_IMAGE_STATE);
|
useState<ImageStateType>(INITIAL_IMAGE_STATE);
|
||||||
|
@ -237,7 +236,7 @@ export function MediaEditor({
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePickSticker = useCallback(
|
const handlePickSticker = useCallback(
|
||||||
(_packId, _stickerId, src: string) => {
|
(_packId: string, _stickerId: number, src: string) => {
|
||||||
async function run() {
|
async function run() {
|
||||||
if (!fabricCanvas) {
|
if (!fabricCanvas) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { Key } from 'react';
|
import type { Key, ReactNode } from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Tabs, TabList, Tab, TabPanel } from 'react-aria-components';
|
import { Tabs, TabList, Tab, TabPanel } from 'react-aria-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -189,12 +189,12 @@ export type NavTabsProps = Readonly<{
|
||||||
navTabsCollapsed: boolean;
|
navTabsCollapsed: boolean;
|
||||||
onShowSettings: () => void;
|
onShowSettings: () => void;
|
||||||
onStartUpdate: () => unknown;
|
onStartUpdate: () => unknown;
|
||||||
onNavTabSelected(tab: NavTab): void;
|
onNavTabSelected: (tab: NavTab) => void;
|
||||||
onToggleNavTabsCollapse(collapsed: boolean): void;
|
onToggleNavTabsCollapse: (collapsed: boolean) => void;
|
||||||
onToggleProfileEditor: () => void;
|
onToggleProfileEditor: () => void;
|
||||||
renderCallsTab(props: NavTabPanelProps): JSX.Element;
|
renderCallsTab: () => ReactNode;
|
||||||
renderChatsTab(props: NavTabPanelProps): JSX.Element;
|
renderChatsTab: () => ReactNode;
|
||||||
renderStoriesTab(props: NavTabPanelProps): JSX.Element;
|
renderStoriesTab: () => ReactNode;
|
||||||
selectedNavTab: NavTab;
|
selectedNavTab: NavTab;
|
||||||
storiesEnabled: boolean;
|
storiesEnabled: boolean;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// 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 { AudioDevice } from '@signalapp/ringrtc';
|
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -8,11 +7,11 @@ import React, {
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
|
useId,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { noop, partition } from 'lodash';
|
import { noop, partition } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as LocaleMatcher from '@formatjs/intl-localematcher';
|
import * as LocaleMatcher from '@formatjs/intl-localematcher';
|
||||||
|
|
||||||
import type { MediaDeviceSettings } from '../types/Calling';
|
import type { MediaDeviceSettings } from '../types/Calling';
|
||||||
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
import type { ValidationResultType as BackupValidationResultType } from '../services/backups';
|
||||||
import type {
|
import type {
|
||||||
|
@ -35,12 +34,10 @@ import type {
|
||||||
SentMediaQualityType,
|
SentMediaQualityType,
|
||||||
ThemeType,
|
ThemeType,
|
||||||
} from '../types/Util';
|
} from '../types/Util';
|
||||||
|
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import { ChatColorPicker } from './ChatColorPicker';
|
import { ChatColorPicker } from './ChatColorPicker';
|
||||||
import { Checkbox } from './Checkbox';
|
import { Checkbox } from './Checkbox';
|
||||||
import { WidthBreakpoint } from './_util';
|
import { WidthBreakpoint } from './_util';
|
||||||
|
|
||||||
import { ConfirmationDialog } from './ConfirmationDialog';
|
import { ConfirmationDialog } from './ConfirmationDialog';
|
||||||
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
|
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
|
||||||
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
import { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
||||||
|
@ -57,7 +54,6 @@ import {
|
||||||
} from '../util/expirationTimer';
|
} from '../util/expirationTimer';
|
||||||
import { DurationInSeconds } from '../util/durations';
|
import { DurationInSeconds } from '../util/durations';
|
||||||
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
import { useEscapeHandling } from '../hooks/useEscapeHandling';
|
||||||
import { useUniqueId } from '../hooks/useUniqueId';
|
|
||||||
import { focusableSelector } from '../util/focusableSelectors';
|
import { focusableSelector } from '../util/focusableSelectors';
|
||||||
import { Modal } from './Modal';
|
import { Modal } from './Modal';
|
||||||
import { SearchInput } from './SearchInput';
|
import { SearchInput } from './SearchInput';
|
||||||
|
@ -384,10 +380,10 @@ export function Preferences({
|
||||||
whoCanSeeMe,
|
whoCanSeeMe,
|
||||||
zoomFactor,
|
zoomFactor,
|
||||||
}: PropsType): JSX.Element {
|
}: PropsType): JSX.Element {
|
||||||
const storiesId = useUniqueId();
|
const storiesId = useId();
|
||||||
const themeSelectId = useUniqueId();
|
const themeSelectId = useId();
|
||||||
const zoomSelectId = useUniqueId();
|
const zoomSelectId = useId();
|
||||||
const languageId = useUniqueId();
|
const languageId = useId();
|
||||||
|
|
||||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||||
const [confirmStoriesOff, setConfirmStoriesOff] = useState(false);
|
const [confirmStoriesOff, setConfirmStoriesOff] = useState(false);
|
||||||
|
|
|
@ -352,7 +352,7 @@ export function ProfileEditor({
|
||||||
|
|
||||||
// To make AvatarEditor re-render less often
|
// To make AvatarEditor re-render less often
|
||||||
const handleAvatarLoaded = useCallback(
|
const handleAvatarLoaded = useCallback(
|
||||||
avatar => {
|
(avatar: Uint8Array) => {
|
||||||
setAvatarBuffer(avatar);
|
setAvatarBuffer(avatar);
|
||||||
setOldAvatarBuffer(avatar);
|
setOldAvatarBuffer(avatar);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,31 +1,16 @@
|
||||||
// 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 { ReactNode } from 'react';
|
import type { ProfilerOnRenderCallback, ReactNode } from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as log from '../logging/log';
|
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<{
|
export type PropsType = Readonly<{
|
||||||
id: string;
|
id: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const onRender: InternalPropsType['onRender'] = (
|
const onRender: ProfilerOnRenderCallback = (
|
||||||
id,
|
id,
|
||||||
phase,
|
phase,
|
||||||
actual,
|
actual,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import type { LocalizerType, RenderTextCallbackType } from '../types/Util';
|
import type { LocalizerType, RenderTextCallbackType } from '../types/Util';
|
||||||
|
@ -241,7 +240,7 @@ export const TextAttachment = forwardRef<HTMLTextAreaElement, PropsType>(
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{onChange ? (
|
{onChange ? (
|
||||||
<TextareaAutosize
|
<textarea
|
||||||
dir="auto"
|
dir="auto"
|
||||||
className="TextAttachment__text__container TextAttachment__text__textarea"
|
className="TextAttachment__text__container TextAttachment__text__textarea"
|
||||||
disabled={!isEditingText}
|
disabled={!isEditingText}
|
||||||
|
|
|
@ -538,30 +538,27 @@ export function TextStoryCreator({
|
||||||
data-popper-arrow
|
data-popper-arrow
|
||||||
className="StoryCreator__popper__arrow"
|
className="StoryCreator__popper__arrow"
|
||||||
/>
|
/>
|
||||||
{objectMap<BackgroundStyleType>(
|
{objectMap(BackgroundStyle, (bg, backgroundValue) => (
|
||||||
BackgroundStyle,
|
<button
|
||||||
(bg, backgroundValue) => (
|
aria-label={i18n('icu:StoryCreator__story-bg')}
|
||||||
<button
|
className={classNames({
|
||||||
aria-label={i18n('icu:StoryCreator__story-bg')}
|
StoryCreator__bg: true,
|
||||||
className={classNames({
|
'StoryCreator__bg--selected':
|
||||||
StoryCreator__bg: true,
|
selectedBackground === backgroundValue,
|
||||||
'StoryCreator__bg--selected':
|
})}
|
||||||
selectedBackground === backgroundValue,
|
key={String(bg)}
|
||||||
})}
|
onClick={() => {
|
||||||
key={String(bg)}
|
setSelectedBackground(backgroundValue);
|
||||||
onClick={() => {
|
setIsColorPickerShowing(false);
|
||||||
setSelectedBackground(backgroundValue);
|
}}
|
||||||
setIsColorPickerShowing(false);
|
type="button"
|
||||||
}}
|
style={{
|
||||||
type="button"
|
background: getBackgroundColor(
|
||||||
style={{
|
getBackground(backgroundValue)
|
||||||
background: getBackgroundColor(
|
),
|
||||||
getBackground(backgroundValue)
|
}}
|
||||||
),
|
/>
|
||||||
}}
|
))}
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -182,7 +182,7 @@ export const VoiceNotesPlaybackContext =
|
||||||
React.createContext<Contents>(globalContents);
|
React.createContext<Contents>(globalContents);
|
||||||
|
|
||||||
export type VoiceNotesPlaybackProps = {
|
export type VoiceNotesPlaybackProps = {
|
||||||
children?: React.ReactNode | React.ReactChildren;
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -158,7 +158,7 @@ export function ImageGrid({
|
||||||
);
|
);
|
||||||
|
|
||||||
const showAttachmentOrNoLongerAvailableToast = React.useCallback(
|
const showAttachmentOrNoLongerAvailableToast = React.useCallback(
|
||||||
attachmentIndex =>
|
(attachmentIndex: number) =>
|
||||||
attachments[attachmentIndex].isPermanentlyUndownloadable
|
attachments[attachmentIndex].isPermanentlyUndownloadable
|
||||||
? showMediaNoLongerAvailableToast
|
? showMediaNoLongerAvailableToast
|
||||||
: showVisualAttachment,
|
: showVisualAttachment,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
// 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 React, { useMemo } from 'react';
|
import React, { useMemo, useId } from 'react';
|
||||||
|
|
||||||
import type { ConversationTypeType } from '../../../state/ducks/conversations';
|
import type { ConversationTypeType } from '../../../state/ducks/conversations';
|
||||||
import type { LocalizerType } from '../../../types/Util';
|
import type { LocalizerType } from '../../../types/Util';
|
||||||
import { PanelSection } from './PanelSection';
|
import { PanelSection } from './PanelSection';
|
||||||
|
@ -12,7 +11,6 @@ import { Select } from '../../Select';
|
||||||
import { isConversationMuted } from '../../../util/isConversationMuted';
|
import { isConversationMuted } from '../../../util/isConversationMuted';
|
||||||
import { getMuteOptions } from '../../../util/getMuteOptions';
|
import { getMuteOptions } from '../../../util/getMuteOptions';
|
||||||
import { parseIntOrThrow } from '../../../util/parseIntOrThrow';
|
import { parseIntOrThrow } from '../../../util/parseIntOrThrow';
|
||||||
import { useUniqueId } from '../../../hooks/useUniqueId';
|
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -39,8 +37,8 @@ export function ConversationNotificationsSettings({
|
||||||
setMuteExpiration,
|
setMuteExpiration,
|
||||||
setDontNotifyForMentionsIfMuted,
|
setDontNotifyForMentionsIfMuted,
|
||||||
}: PropsType): JSX.Element {
|
}: PropsType): JSX.Element {
|
||||||
const muteNotificationsSelectId = useUniqueId();
|
const muteNotificationsSelectId = useId();
|
||||||
const mentionsSelectId = useUniqueId();
|
const mentionsSelectId = useId();
|
||||||
const muteOptions = useMemo(
|
const muteOptions = useMemo(
|
||||||
() => [
|
() => [
|
||||||
...(isConversationMuted({ muteExpiresAt })
|
...(isConversationMuted({ muteExpiresAt })
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
import React, { useId, useState } from 'react';
|
||||||
import React, { useState } from 'react';
|
|
||||||
|
|
||||||
import type { ConversationType } from '../../../state/ducks/conversations';
|
import type { ConversationType } from '../../../state/ducks/conversations';
|
||||||
import type { LocalizerType } from '../../../types/Util';
|
import type { LocalizerType } from '../../../types/Util';
|
||||||
|
|
||||||
import { ConfirmationDialog } from '../../ConfirmationDialog';
|
import { ConfirmationDialog } from '../../ConfirmationDialog';
|
||||||
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
|
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
|
||||||
import { PanelRow } from './PanelRow';
|
import { PanelRow } from './PanelRow';
|
||||||
import { PanelSection } from './PanelSection';
|
import { PanelSection } from './PanelSection';
|
||||||
import { Select } from '../../Select';
|
import { Select } from '../../Select';
|
||||||
import { SignalService as Proto } from '../../../protobuf';
|
import { SignalService as Proto } from '../../../protobuf';
|
||||||
|
|
||||||
import { copyGroupLink } from '../../../util/copyLinksWithToast';
|
import { copyGroupLink } from '../../../util/copyLinksWithToast';
|
||||||
import { drop } from '../../../util/drop';
|
import { drop } from '../../../util/drop';
|
||||||
import { useDelayedRestoreFocus } from '../../../hooks/useRestoreFocus';
|
import { useDelayedRestoreFocus } from '../../../hooks/useRestoreFocus';
|
||||||
import { useUniqueId } from '../../../hooks/useUniqueId';
|
|
||||||
|
|
||||||
const AccessControlEnum = Proto.AccessControl.AccessRequired;
|
const AccessControlEnum = Proto.AccessControl.AccessRequired;
|
||||||
|
|
||||||
|
@ -43,8 +38,8 @@ export function GroupLinkManagement({
|
||||||
isAdmin,
|
isAdmin,
|
||||||
setAccessControlAddFromInviteLinkSetting,
|
setAccessControlAddFromInviteLinkSetting,
|
||||||
}: PropsType): JSX.Element {
|
}: PropsType): JSX.Element {
|
||||||
const groupLinkSelectId = useUniqueId();
|
const groupLinkSelectId = useId();
|
||||||
const approveSelectId = useUniqueId();
|
const approveSelectId = useId();
|
||||||
|
|
||||||
if (conversation === undefined) {
|
if (conversation === undefined) {
|
||||||
throw new Error('GroupLinkManagement rendered without a conversation');
|
throw new Error('GroupLinkManagement rendered without a conversation');
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
// Copyright 2020 Signal Messenger, LLC
|
// Copyright 2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
import React, { useId } from 'react';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import type { ConversationType } from '../../../state/ducks/conversations';
|
import type { ConversationType } from '../../../state/ducks/conversations';
|
||||||
import type { LocalizerType } from '../../../types/Util';
|
import type { LocalizerType } from '../../../types/Util';
|
||||||
import { getAccessControlOptions } from '../../../util/getAccessControlOptions';
|
import { getAccessControlOptions } from '../../../util/getAccessControlOptions';
|
||||||
import { SignalService as Proto } from '../../../protobuf';
|
import { SignalService as Proto } from '../../../protobuf';
|
||||||
|
|
||||||
import { PanelRow } from './PanelRow';
|
import { PanelRow } from './PanelRow';
|
||||||
import { PanelSection } from './PanelSection';
|
import { PanelSection } from './PanelSection';
|
||||||
import { Select } from '../../Select';
|
import { Select } from '../../Select';
|
||||||
import { useUniqueId } from '../../../hooks/useUniqueId';
|
|
||||||
|
|
||||||
export type PropsDataType = {
|
export type PropsDataType = {
|
||||||
conversation?: ConversationType;
|
conversation?: ConversationType;
|
||||||
|
@ -33,9 +29,9 @@ export function GroupV2Permissions({
|
||||||
setAccessControlMembersSetting,
|
setAccessControlMembersSetting,
|
||||||
setAnnouncementsOnly,
|
setAnnouncementsOnly,
|
||||||
}: PropsType): JSX.Element {
|
}: PropsType): JSX.Element {
|
||||||
const addMembersSelectId = useUniqueId();
|
const addMembersSelectId = useId();
|
||||||
const groupInfoSelectId = useUniqueId();
|
const groupInfoSelectId = useId();
|
||||||
const announcementSelectId = useUniqueId();
|
const announcementSelectId = useId();
|
||||||
|
|
||||||
if (conversation === undefined) {
|
if (conversation === undefined) {
|
||||||
throw new Error('GroupV2Permissions rendered without a conversation');
|
throw new Error('GroupV2Permissions rendered without a conversation');
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function AttachmentSection({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return missingCaseError(type);
|
throw missingCaseError(type);
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// 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 type { Meta } from '@storybook/react';
|
||||||
import { useId, VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
import { FunGif, FunGifPreview } from './FunGif';
|
import { FunGif, FunGifPreview } from './FunGif';
|
||||||
import { LoadingState } from '../../util/loadable';
|
import { LoadingState } from '../../util/loadable';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
import { getInteractionModality } from '@react-aria/interactions';
|
import { getInteractionModality } from '@react-aria/interactions';
|
||||||
|
@ -19,8 +20,8 @@ export function FunSearch(props: FunSearchProps): JSX.Element {
|
||||||
const { shouldAutoFocus, onChangeShouldAutoFocus } = useFunContext();
|
const { shouldAutoFocus, onChangeShouldAutoFocus } = useFunContext();
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
event => {
|
(event: ChangeEvent<HTMLInputElement>) => {
|
||||||
onSearchInputChange(event.target.value);
|
onSearchInputChange(event.currentTarget.value);
|
||||||
},
|
},
|
||||||
[onSearchInputChange]
|
[onSearchInputChange]
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,8 +12,8 @@ import React, {
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
|
useId,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useId } from 'react-aria';
|
|
||||||
import type { Selection } from 'react-aria-components';
|
import type { Selection } from 'react-aria-components';
|
||||||
import { ListBox, ListBoxItem } from 'react-aria-components';
|
import { ListBox, ListBoxItem } from 'react-aria-components';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
import type { Transition } from 'framer-motion';
|
import type { Transition } from 'framer-motion';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useId } from 'react';
|
||||||
import type { Key } from 'react-aria';
|
import type { Key } from 'react-aria';
|
||||||
import { useId } from 'react-aria';
|
|
||||||
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
|
import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
|
||||||
import type { FunPickerTabKey } from '../constants';
|
import type { FunPickerTabKey } from '../constants';
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,10 @@ import React, {
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
|
useId,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { PressEvent } from 'react-aria';
|
import type { PressEvent } from 'react-aria';
|
||||||
import { useId, VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import { FunItemButton } from '../base/FunItem';
|
import { FunItemButton } from '../base/FunItem';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,27 +1,28 @@
|
||||||
// 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 { useEffect, useState } from 'react';
|
import { useMemo, useSyncExternalStore } from 'react';
|
||||||
|
|
||||||
// Allows this to work in Node process
|
export function useMediaQuery(query: string): boolean {
|
||||||
let reducedMotionQuery: MediaQueryList;
|
const api = useMemo(() => {
|
||||||
function getReducedMotionQuery() {
|
const mediaQuery = window.matchMedia(query);
|
||||||
if (reducedMotionQuery == null) {
|
|
||||||
reducedMotionQuery = window.matchMedia('(prefers-reduced-motion)');
|
function subscribe(onChange: () => void) {
|
||||||
}
|
mediaQuery.addEventListener('change', onChange);
|
||||||
return reducedMotionQuery;
|
return () => {
|
||||||
|
mediaQuery.removeEventListener('change', onChange);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnapshot() {
|
||||||
|
return mediaQuery.matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { subscribe, getSnapshot };
|
||||||
|
}, [query]);
|
||||||
|
return useSyncExternalStore(api.subscribe, api.getSnapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useReducedMotion(): boolean {
|
export function useReducedMotion(): boolean {
|
||||||
const [matches, setMatches] = useState(getReducedMotionQuery().matches);
|
return useMediaQuery('(prefers-reduced-motion)');
|
||||||
useEffect(() => {
|
|
||||||
function onChange(event: MediaQueryListEvent) {
|
|
||||||
setMatches(event.matches);
|
|
||||||
}
|
|
||||||
getReducedMotionQuery().addEventListener('change', onChange);
|
|
||||||
return () => {
|
|
||||||
getReducedMotionQuery().removeEventListener('change', onChange);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
return matches;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -10,6 +10,7 @@ import { useItemsActions } from '../ducks/items';
|
||||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import { useComposerActions } from '../ducks/composer';
|
import { useComposerActions } from '../ducks/composer';
|
||||||
import { getTextFormattingEnabled } from '../selectors/items';
|
import { getTextFormattingEnabled } from '../selectors/items';
|
||||||
|
import { getConversationSelector } from '../selectors/conversations';
|
||||||
|
|
||||||
export type SmartCompositionTextAreaProps = Pick<
|
export type SmartCompositionTextAreaProps = Pick<
|
||||||
CompositionTextAreaProps,
|
CompositionTextAreaProps,
|
||||||
|
@ -39,6 +40,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
||||||
|
|
||||||
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
||||||
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
const isFormattingEnabled = useSelector(getTextFormattingEnabled);
|
||||||
|
const conversationSelector = useSelector(getConversationSelector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CompositionTextArea
|
<CompositionTextArea
|
||||||
|
@ -52,6 +54,7 @@ export const SmartCompositionTextArea = memo(function SmartCompositionTextArea(
|
||||||
onTextTooLong={onTextTooLong}
|
onTextTooLong={onTextTooLong}
|
||||||
platform={platform}
|
platform={platform}
|
||||||
ourConversationId={ourConversationId}
|
ourConversationId={ourConversationId}
|
||||||
|
conversationSelector={conversationSelector}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { getLocalDeleteWarningShown } from '../selectors/items';
|
||||||
import { getDeleteSyncSendEnabled } from '../selectors/items-extra';
|
import { getDeleteSyncSendEnabled } from '../selectors/items-extra';
|
||||||
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
|
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
|
||||||
import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
||||||
|
import type { DurationInSeconds } from '../../util/durations';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -182,7 +183,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
|
||||||
}, [destroyMessages, conversation.id]);
|
}, [destroyMessages, conversation.id]);
|
||||||
|
|
||||||
const onConversationDisappearingMessagesChange = useCallback(
|
const onConversationDisappearingMessagesChange = useCallback(
|
||||||
seconds => {
|
(seconds: DurationInSeconds) => {
|
||||||
setDisappearingMessages(conversation.id, seconds);
|
setDisappearingMessages(conversation.id, seconds);
|
||||||
},
|
},
|
||||||
[setDisappearingMessages, conversation.id]
|
[setDisappearingMessages, conversation.id]
|
||||||
|
@ -197,7 +198,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
|
||||||
}, [onMarkUnread, conversation.id]);
|
}, [onMarkUnread, conversation.id]);
|
||||||
|
|
||||||
const onConversationMuteExpirationChange = useCallback(
|
const onConversationMuteExpirationChange = useCallback(
|
||||||
seconds => {
|
(seconds: number) => {
|
||||||
setMuteExpiration(conversation.id, seconds);
|
setMuteExpiration(conversation.id, seconds);
|
||||||
},
|
},
|
||||||
[setMuteExpiration, conversation.id]
|
[setMuteExpiration, conversation.id]
|
||||||
|
|
|
@ -5,7 +5,10 @@ import React, { useCallback, forwardRef, memo } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useRecentEmojis } from '../selectors/emojis';
|
import { useRecentEmojis } from '../selectors/emojis';
|
||||||
import { useEmojisActions as useEmojiActions } from '../ducks/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 { EmojiPicker } from '../../components/emoji/EmojiPicker';
|
||||||
import { getIntl } from '../selectors/user';
|
import { getIntl } from '../selectors/user';
|
||||||
import { getEmojiSkinToneDefault } from '../selectors/items';
|
import { getEmojiSkinToneDefault } from '../selectors/items';
|
||||||
|
@ -39,7 +42,7 @@ export const SmartEmojiPicker = memo(
|
||||||
const { onUseEmoji } = useEmojiActions();
|
const { onUseEmoji } = useEmojiActions();
|
||||||
|
|
||||||
const handlePickEmoji = useCallback(
|
const handlePickEmoji = useCallback(
|
||||||
data => {
|
(data: EmojiPickDataType) => {
|
||||||
onUseEmoji({ shortName: data.shortName, skinTone: EmojiSkinTone.None });
|
onUseEmoji({ shortName: data.shortName, skinTone: EmojiSkinTone.None });
|
||||||
onPickEmoji(data);
|
onPickEmoji(data);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright 2023 Signal Messenger, LLC
|
// Copyright 2023 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
import React, { memo, useCallback } from 'react';
|
import React, { memo, useCallback } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { NavTabPanelProps } from '../../components/NavTabs';
|
|
||||||
import { NavTabs } from '../../components/NavTabs';
|
import { NavTabs } from '../../components/NavTabs';
|
||||||
import { getIntl, getTheme } from '../selectors/user';
|
import { getIntl, getTheme } from '../selectors/user';
|
||||||
import {
|
import {
|
||||||
|
@ -27,10 +27,10 @@ import { getCallHistoryUnreadCount } from '../selectors/callHistory';
|
||||||
|
|
||||||
export type SmartNavTabsProps = Readonly<{
|
export type SmartNavTabsProps = Readonly<{
|
||||||
navTabsCollapsed: boolean;
|
navTabsCollapsed: boolean;
|
||||||
onToggleNavTabsCollapse(navTabsCollapsed: boolean): void;
|
onToggleNavTabsCollapse: (navTabsCollapsed: boolean) => void;
|
||||||
renderCallsTab(props: NavTabPanelProps): JSX.Element;
|
renderCallsTab: () => ReactNode;
|
||||||
renderChatsTab(props: NavTabPanelProps): JSX.Element;
|
renderChatsTab: () => ReactNode;
|
||||||
renderStoriesTab(props: NavTabPanelProps): JSX.Element;
|
renderStoriesTab: () => ReactNode;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const SmartNavTabs = memo(function SmartNavTabs({
|
export const SmartNavTabs = memo(function SmartNavTabs({
|
||||||
|
|
|
@ -35,6 +35,8 @@ import { useAudioPlayerActions } from '../ducks/audioPlayer';
|
||||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||||
import { useStoriesActions } from '../ducks/stories';
|
import { useStoriesActions } from '../ducks/stories';
|
||||||
import { useIsWindowActive } from '../../hooks/useIsWindowActive';
|
import { useIsWindowActive } from '../../hooks/useIsWindowActive';
|
||||||
|
import type { DraftBodyRanges } from '../../types/BodyRange';
|
||||||
|
import type { StoryViewType } from '../../types/Stories';
|
||||||
|
|
||||||
export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
||||||
const {
|
const {
|
||||||
|
@ -98,17 +100,22 @@ export const SmartStoryViewer = memo(function SmartStoryViewer() {
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleReactToStory = useCallback(
|
const handleReactToStory = useCallback(
|
||||||
async (emoji, story) => {
|
async (emoji: string, story: StoryViewType) => {
|
||||||
const { messageId } = story;
|
const { messageId } = story;
|
||||||
reactToStory(emoji, messageId);
|
reactToStory(emoji, messageId);
|
||||||
},
|
},
|
||||||
[reactToStory]
|
[reactToStory]
|
||||||
);
|
);
|
||||||
const handleReplyToStory = useCallback(
|
const handleReplyToStory = useCallback(
|
||||||
(message, mentions, timestamp, story) => {
|
(
|
||||||
|
message: string,
|
||||||
|
bodyRanges: DraftBodyRanges,
|
||||||
|
timestamp: number,
|
||||||
|
story: StoryViewType
|
||||||
|
) => {
|
||||||
const conversationId = storyInfo?.conversationStory?.conversationId;
|
const conversationId = storyInfo?.conversationStory?.conversationId;
|
||||||
strictAssert(conversationId != null, 'conversationId is required');
|
strictAssert(conversationId != null, 'conversationId is required');
|
||||||
replyToStory(conversationId, message, mentions, timestamp, story);
|
replyToStory(conversationId, message, bodyRanges, timestamp, story);
|
||||||
},
|
},
|
||||||
[storyInfo, replyToStory]
|
[storyInfo, replyToStory]
|
||||||
);
|
);
|
||||||
|
|
|
@ -748,258 +748,6 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2024-11-14T18:53:33.345Z"
|
"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",
|
"rule": "React-useRef",
|
||||||
"path": "ts/calling/useGetCallingFrameBuffer.ts",
|
"path": "ts/calling/useGetCallingFrameBuffer.ts",
|
||||||
|
|
|
@ -88,13 +88,11 @@ const excludedFilesRegexp = RegExp(
|
||||||
'^node_modules/react-aria-components/.+',
|
'^node_modules/react-aria-components/.+',
|
||||||
'^node_modules/react-contextmenu/.+',
|
'^node_modules/react-contextmenu/.+',
|
||||||
'^node_modules/react-dom/.+',
|
'^node_modules/react-dom/.+',
|
||||||
'^node_modules/react-hot-loader/.+',
|
|
||||||
'^node_modules/react-icon-base/.+',
|
'^node_modules/react-icon-base/.+',
|
||||||
'^node_modules/react-input-autosize/.+',
|
'^node_modules/react-input-autosize/.+',
|
||||||
'^node_modules/react-popper/.+',
|
'^node_modules/react-popper/.+',
|
||||||
'^node_modules/react-redux/.+',
|
'^node_modules/react-redux/.+',
|
||||||
'^node_modules/react-router/.+',
|
'^node_modules/react-router/.+',
|
||||||
'^node_modules/react-router-dom/.+',
|
|
||||||
'^node_modules/react-select/.+',
|
'^node_modules/react-select/.+',
|
||||||
'^node_modules/react-transition-group/.+',
|
'^node_modules/react-transition-group/.+',
|
||||||
'^node_modules/react-virtualized/.+',
|
'^node_modules/react-virtualized/.+',
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright 2022 Signal Messenger, LLC
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function objectMap<T>(
|
export function objectMap<T, R>(
|
||||||
obj: Record<string, T>,
|
obj: Record<string, T>,
|
||||||
f: (key: keyof typeof obj, value: (typeof obj)[keyof typeof obj]) => unknown
|
f: (key: keyof typeof obj, value: (typeof obj)[keyof typeof obj]) => R
|
||||||
): Array<unknown> {
|
): Array<R> {
|
||||||
const keys: Array<keyof typeof obj> = Object.keys(obj);
|
const keys: Array<keyof typeof obj> = Object.keys(obj);
|
||||||
return keys.map(key => f(key, obj[key]));
|
return keys.map(key => f(key, obj[key]));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import type { IntlShape } from 'react-intl';
|
import type { IntlShape } from 'react-intl';
|
||||||
import { createIntl, createIntlCache } from 'react-intl';
|
import { createIntl, createIntlCache } from 'react-intl';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
import type { LocaleMessageType, LocaleMessagesType } from '../types/I18N';
|
import type { LocaleMessageType, LocaleMessagesType } from '../types/I18N';
|
||||||
import type {
|
import type {
|
||||||
LocalizerType,
|
LocalizerType,
|
||||||
|
@ -26,7 +27,7 @@ export function isLocaleMessageType(
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SetupI18nOptionsType = Readonly<{
|
export type SetupI18nOptionsType = Readonly<{
|
||||||
renderEmojify: (parts: ReadonlyArray<unknown>) => JSX.Element | void;
|
renderEmojify: (parts: ReadonlyArray<unknown>) => ReactNode;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function createCachedIntl(
|
export function createCachedIntl(
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import { assertDev } from './assert';
|
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');
|
assertDev(false, 'This should never be called. Doing nothing');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue