Init AxoSwitch/AxoCheckbox & forced-colors mode

This commit is contained in:
Jamie Kyle
2025-09-10 13:25:46 -07:00
committed by GitHub
parent 53d1650844
commit 58f2dd94d2
27 changed files with 622 additions and 290 deletions

View File

@@ -254,6 +254,43 @@ const typescriptRules = {
'import/no-cycle': 'off',
};
const TAILWIND_REPLACEMENTS = [
// inset
{ pattern: 'left-*', fix: 'start-*' },
{ pattern: 'right-*', fix: 'end-*' },
// margin
{ pattern: 'ml-*', fix: 'ms-*' },
{ pattern: 'mr-*', fix: 'me-*' },
// padding
{ pattern: 'pl-*', fix: 'ps-*' },
{ pattern: 'pr-*', fix: 'pe-*' },
// border
{ pattern: 'border-l-*', fix: 'border-s-*' },
{ pattern: 'border-r-*', fix: 'border-e-*' },
// border-radius
{ pattern: 'rounded-l', fix: 'rounded-s' },
{ pattern: 'rounded-r', fix: 'rounded-e' },
{ pattern: 'rounded-tl', fix: 'rounded-ss' },
{ pattern: 'rounded-tr', fix: 'rounded-se' },
{ pattern: 'rounded-bl', fix: 'rounded-es' },
{ pattern: 'rounded-br', fix: 'rounded-ee' },
{ pattern: 'rounded-l-*', fix: 'rounded-s-*' },
{ pattern: 'rounded-r-*', fix: 'rounded-e-*' },
{ pattern: 'rounded-tl-*', fix: 'rounded-ss-*' },
{ pattern: 'rounded-tr-*', fix: 'rounded-se-*' },
{ pattern: 'rounded-bl-*', fix: 'rounded-es-*' },
{ pattern: 'rounded-br-*', fix: 'rounded-ee-*' },
// text-align
{ pattern: 'text-left', fix: 'text-start' },
{ pattern: 'text-right', fix: 'text-end' },
// float
{ pattern: 'float-left', fix: 'float-start' },
{ pattern: 'float-right', fix: 'float-end' },
// clear
{ pattern: 'clear-left', fix: 'clear-start' },
{ pattern: 'clear-right', fix: 'clear-end' },
];
module.exports = {
root: true,
settings: {
@@ -377,6 +414,15 @@ module.exports = {
pattern: '^\\*+:.*', // ex: "*:mx-0",
message: 'No child variants',
},
...TAILWIND_REPLACEMENTS.map(item => {
const pattern = item.pattern.replace('*', '(.*)');
const fix = item.fix.replace('*', '$2');
return {
message: `Use logical property ${item.fix} instead of ${item.pattern}`,
pattern: `^(.*:)?${pattern}$`,
fix: `$1${fix}`,
};
}),
],
},
],

View File

@@ -3,17 +3,11 @@
/** @type {import("prettier").Config} */
module.exports = {
plugins: ['prettier-plugin-tailwindcss'],
singleQuote: true,
arrowParens: 'avoid',
trailingComma: 'es5',
overrides: [
{
files: ['./ts/axo/**.tsx'],
plugins: ['prettier-plugin-tailwindcss'],
options: {
tailwindStylesheet: './stylesheets/tailwind-config.css',
tailwindFunctions: ['tw'],
},
},
],
tailwindStylesheet: './stylesheets/tailwind-config.css',
tailwindFunctions: ['tw'],
tailwindAttributes: [],
};

View File

@@ -17,6 +17,7 @@ import { HourCyclePreference } from '../ts/types/I18N';
import { Provider } from 'react-redux';
import { Store, combineReducers, createStore } from 'redux';
import { Globals } from '@react-spring/web';
import { AxoProvider } from '../ts/axo/AxoProvider';
import { StateType } from '../ts/state/reducer';
import {
ScrollerLockContext,
@@ -254,8 +255,17 @@ function withFunProvider(Story, context) {
);
}
function withAxoProvider(Story, context) {
return (
<AxoProvider dir={context.globals.direction ?? 'ltr'}>
<Story {...context} />
</AxoProvider>
);
}
export const decorators = [
withStrictMode,
withAxoProvider,
withGlobalTypesProvider,
withMockStoreProvider,
withScrollLockProvider,

View File

@@ -6,9 +6,9 @@
*/
@custom-variant dark (&:where(.dark-theme, .dark-theme *));
@custom-variant hovered (&:hover:not(:disabled));
@custom-variant pressed (&:active:not(:disabled));
@custom-variant focused (.keyboard-mode &:focus);
@custom-variant hovered (&:where(:hover:not(:disabled)));
@custom-variant pressed (&:where(:active:not(:disabled)));
@custom-variant focused (:where(.keyboard-mode) &:where(:focus));
/**
* Color
@@ -325,6 +325,9 @@
/* box-shadow: inset */
--inset-shadow-*: initial; /* reset defaults */
--inset-shadow-on-color:
inset 0 0.5px 1px 0 --alpha(#000 / 12%);
/* filter: drop-shadow() */
--drop-shadow-*: initial; /* reset defaults */
--drop-shadow-elevation-0: var(--shadow-elevation-0);

View File

@@ -11,7 +11,8 @@ const Namespace = 'AxoButton';
const baseAxoButtonStyles = tw(
'flex items-center-safe justify-center-safe gap-1 truncate rounded-full select-none',
'outline-0 outline-border-focused focused:outline-[2.5px]'
'outline-0 outline-border-focused focused:outline-[2.5px]',
'forced-colors:border'
);
const AxoButtonTypes = {

View File

@@ -0,0 +1,40 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import type { Meta } from '@storybook/react';
import { AxoCheckbox } from './AxoCheckbox';
import { tw } from './tw';
export default {
title: 'Axo/AxoCheckbox',
} satisfies Meta;
function Template(props: {
label: string;
defaultChecked: boolean;
disabled?: boolean;
}): JSX.Element {
const [checked, setChecked] = useState(props.defaultChecked);
return (
<label className={tw('my-2 flex items-center gap-2')}>
<AxoCheckbox
checked={checked}
onCheckedChange={setChecked}
disabled={props.disabled}
/>
{props.label}
</label>
);
}
export function Basic(): JSX.Element {
return (
<>
<h1 className={tw('type-title-large')}>AxoCheckbox</h1>
<Template label="Unchecked" defaultChecked={false} />
<Template label="Checked" defaultChecked />
<Template label="Unchecked+Disabled" defaultChecked={false} disabled />
<Template label="Checked+Disabled" defaultChecked disabled />
</>
);
}

57
ts/axo/AxoCheckbox.tsx Normal file
View File

@@ -0,0 +1,57 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { memo } from 'react';
import { Checkbox } from 'radix-ui';
import { AxoSymbol } from './AxoSymbol';
import { tw } from './tw';
const Namespace = 'AxoCheckbox';
type AxoCheckboxProps = Readonly<{
id?: string;
checked: boolean;
onCheckedChange: (nextChecked: boolean) => void;
disabled?: boolean;
required?: boolean;
}>;
// eslint-disable-next-line import/export
export const AxoCheckbox = memo((props: AxoCheckboxProps) => {
return (
<Checkbox.Root
id={props.id}
checked={props.checked}
onCheckedChange={props.onCheckedChange}
disabled={props.disabled}
required={props.required}
className={tw(
'flex size-5 items-center justify-center rounded-full',
'border border-border-primary inset-shadow-on-color',
'data-[state=unchecked]:bg-fill-primary',
'data-[state=unchecked]:pressed:bg-fill-primary-pressed',
'data-[state=checked]:bg-color-fill-primary',
'data-[state=checked]:pressed:bg-color-fill-primary-pressed',
'data-[disabled]:border-border-secondary',
'outline-0 outline-border-focused focused:outline-[2.5px]',
'overflow-hidden'
)}
>
<Checkbox.Indicator
className={tw(
'data-[state=checked]:text-label-primary-on-color',
'data-[state=checked]:data-[disabled]:text-label-disabled-on-color'
)}
>
<AxoSymbol.Icon symbol="check" size={14} label={null} />
</Checkbox.Indicator>
</Checkbox.Root>
);
});
AxoCheckbox.displayName = `${Namespace}`;
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/no-redeclare, import/export
export namespace AxoCheckbox {
export type Props = AxoCheckboxProps;
}

View File

@@ -217,7 +217,7 @@ export namespace AxoContextMenu {
</AxoBaseMenu.ItemLeadingSlot>
<AxoBaseMenu.ItemContentSlot>
{props.symbol && (
<span className={tw('mr-2')}>
<span className={tw('me-2')}>
<AxoBaseMenu.ItemSymbol symbol={props.symbol} />
</span>
)}
@@ -352,7 +352,7 @@ export namespace AxoContextMenu {
)}
<AxoBaseMenu.ItemContentSlot>
<AxoBaseMenu.ItemText>{props.children}</AxoBaseMenu.ItemText>
<span className={tw('ml-auto')}>
<span className={tw('ms-auto')}>
<AxoSymbol.Icon size={14} symbol="chevron-[end]" label={null} />
</span>
</AxoBaseMenu.ItemContentSlot>

View File

@@ -231,7 +231,7 @@ export namespace AxoDropdownMenu {
</AxoBaseMenu.ItemLeadingSlot>
<AxoBaseMenu.ItemContentSlot>
{props.symbol && (
<span className={tw('mr-2')}>
<span className={tw('me-2')}>
<AxoBaseMenu.ItemSymbol symbol={props.symbol} />
</span>
)}
@@ -366,7 +366,7 @@ export namespace AxoDropdownMenu {
)}
<AxoBaseMenu.ItemContentSlot>
<AxoBaseMenu.ItemText>{props.children}</AxoBaseMenu.ItemText>
<span className={tw('ml-auto')}>
<span className={tw('ms-auto')}>
<AxoSymbol.Icon size={14} symbol="chevron-[end]" label={null} />
</span>
</AxoBaseMenu.ItemContentSlot>

18
ts/axo/AxoProvider.tsx Normal file
View File

@@ -0,0 +1,18 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { FC, ReactNode } from 'react';
import React, { memo } from 'react';
import { Direction } from 'radix-ui';
type AxoProviderProps = Readonly<{
dir: 'ltr' | 'rtl';
children: ReactNode;
}>;
export const AxoProvider: FC<AxoProviderProps> = memo(props => {
return (
<Direction.Provider dir={props.dir}>{props.children}</Direction.Provider>
);
});
AxoProvider.displayName = 'AxoProvider';

View File

@@ -82,7 +82,8 @@ export namespace AxoSelect {
'flex',
'rounded-full py-[5px] ps-3 pe-2.5 type-body-medium text-label-primary',
'disabled:text-label-disabled',
'outline-0 outline-border-focused focused:outline-[2.5px]'
'outline-0 outline-border-focused focused:outline-[2.5px]',
'forced-colors:border'
);
const TriggerVariants = {
@@ -137,7 +138,7 @@ export namespace AxoSelect {
{props.children}
</Select.Value>
</AxoBaseMenu.ItemText>
<Select.Icon className={tw('ml-2')}>
<Select.Icon className={tw('ms-2')}>
<AxoSymbol.Icon symbol="chevron-down" size={14} label={null} />
</Select.Icon>
</Select.Trigger>

View File

@@ -0,0 +1,40 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import type { Meta } from '@storybook/react';
import { AxoSwitch } from './AxoSwitch';
import { tw } from './tw';
export default {
title: 'Axo/AxoSwitch',
} satisfies Meta;
function Template(props: {
label: string;
defaultChecked: boolean;
disabled?: boolean;
}): JSX.Element {
const [checked, setChecked] = useState(props.defaultChecked);
return (
<label className={tw('my-2 flex items-center gap-2')}>
<AxoSwitch
checked={checked}
onCheckedChange={setChecked}
disabled={props.disabled}
/>
{props.label}
</label>
);
}
export function Basic(): JSX.Element {
return (
<>
<h1 className={tw('type-title-large')}>AxoSwitch</h1>
<Template label="Unchecked" defaultChecked={false} />
<Template label="Checked" defaultChecked />
<Template label="UncheckedDisabled" defaultChecked={false} disabled />
<Template label="CheckedDisabled" defaultChecked disabled />
</>
);
}

83
ts/axo/AxoSwitch.tsx Normal file
View File

@@ -0,0 +1,83 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { memo } from 'react';
import { Switch } from 'radix-ui';
import { tw } from './tw';
import { AxoSymbol } from './AxoSymbol';
const Namespace = 'AxoSwitch';
type AxoSwitchProps = Readonly<{
checked: boolean;
onCheckedChange: (nextChecked: boolean) => void;
disabled?: boolean;
required?: boolean;
}>;
// eslint-disable-next-line import/export
export const AxoSwitch = memo((props: AxoSwitchProps) => {
return (
<Switch.Root
checked={props.checked}
onCheckedChange={props.onCheckedChange}
disabled={props.disabled}
required={props.required}
className={tw(
'group relative z-0 flex h-[18px] w-8 items-center rounded-full',
'border border-border-secondary inset-shadow-on-color',
'bg-fill-secondary',
'data-[disabled]:bg-fill-primary',
'pressed:bg-fill-secondary-pressed',
'outline-0 outline-border-focused focused:outline-[2.5px]',
'overflow-hidden'
)}
>
<span
className={tw(
'absolute top-0 bottom-0',
'w-5.5 rounded-s-full',
'group-data-[disabled]:w-7.5 group-data-[disabled]:rounded-full',
'opacity-0 group-data-[state=checked]:opacity-100',
'-translate-x-3.5 group-data-[state=checked]:translate-x-0 rtl:translate-x-3.5',
'bg-color-fill-primary group-pressed:bg-color-fill-primary-pressed',
'transition-all duration-200 ease-out-cubic',
'forced-colors:bg-[AccentColor]',
'forced-colors:group-data-[disabled]:bg-[GrayText]'
)}
/>
<span
className={tw(
'invisible forced-colors:visible',
'absolute start-0.5 z-0 text-[12px]',
'forced-color-adjust-none',
'forced-colors:text-[AccentColorText]'
)}
>
<AxoSymbol.InlineGlyph symbol="check" label={null} />
</span>
<Switch.Thumb
className={tw(
'z-10 block size-4 rounded-full',
// eslint-disable-next-line better-tailwindcss/no-restricted-classes
'shadow-[#000]/12',
'shadow-[0.5px_0_0.5px_0.5px,-0.5px_0_0.5px_0.5px]',
'bg-label-primary-on-color',
'data-[disabled]:bg-label-disabled-on-color',
'transition-all duration-200 ease-out-cubic',
'data-[state=checked]:translate-x-3.5',
'rtl:data-[state=checked]:-translate-x-3.5',
'forced-colors:border',
'forced-colors:data-[disabled]:bg-[ButtonFace]'
)}
/>
</Switch.Root>
);
});
AxoSwitch.displayName = `${Namespace}`;
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/no-redeclare, import/export
export namespace AxoSwitch {
export type Props = AxoSwitchProps;
}

View File

@@ -43,12 +43,12 @@ const SymbolInfo = memo(function SymbolInfo(props: {
{variant.title}
</span>
<span className={tw('text-[20px] leading-none')}>
<Direction.DirectionProvider dir={variant.dir}>
<Direction.Provider dir={variant.dir}>
<AxoSymbol.InlineGlyph
symbol={props.symbolName}
label={null}
/>
</Direction.DirectionProvider>
</Direction.Provider>
</span>
<code className={tw('type-caption text-label-secondary')}>
{Array.from(variant.text, char => {

View File

@@ -9,13 +9,16 @@ import { AxoSymbol, type AxoSymbolName } from '../AxoSymbol';
export namespace AxoBaseMenu {
// <Content/SubContent>
const baseContentStyles = tw(
'max-w-[300px] min-w-[200px] p-1.5',
'max-w-[300px] min-w-[200px]',
'select-none',
'rounded-xl bg-elevated-background-tertiary shadow-elevation-3',
'data-[state=closed]:animate-fade-out'
'data-[state=closed]:animate-fade-out',
'forced-colors:border',
'forced-colors:bg-[Canvas]',
'forced-colors:text-[CanvasText]'
);
const baseContentGridStyles = tw('grid grid-cols-[min-content_auto]');
const baseContentGridStyles = tw('grid grid-cols-[min-content_auto] p-1.5');
// <Group/RadioGroup>
const baseGroupStyles = tw('col-span-full grid grid-cols-subgrid');
@@ -34,7 +37,12 @@ export namespace AxoBaseMenu {
'rounded-md type-body-medium',
'outline-0 data-[highlighted]:bg-fill-secondary-pressed',
'data-[disabled]:text-label-disabled',
'outline-0 outline-border-focused focused:outline-[2.5px]'
'outline-0 outline-border-focused focused:outline-[2.5px]',
'forced-colors:text-[CanvasText]',
'forced-colors:data-[highlighted]:bg-[Highlight]',
'forced-colors:data-[highlighted]:text-[HighlightText]',
'forced-colors:data-[disabled]:text-[GrayText]',
'forced-color-adjust-none'
);
/**
@@ -143,7 +151,10 @@ export namespace AxoBaseMenu {
): JSX.Element {
return (
<span
className={tw('ml-auto px-1 type-body-medium text-label-secondary')}
dir="auto"
className={tw(
'ms-auto px-1 type-body-medium text-label-secondary forced-colors:text-[inherit]'
)}
>
{props.keyboardShortcut}
</span>
@@ -332,7 +343,9 @@ export namespace AxoBaseMenu {
export const menuSubTriggerStyles = tw(
navigableItemStyles,
'data-[state=open]:not-data-[highlighted]:bg-fill-secondary'
'data-[state=open]:not-data-[highlighted]:bg-fill-secondary',
'forced-colors:data-[state=open]:not-data-[highlighted]:bg-[Highlight]',
'forced-colors:data-[state=open]:not-data-[highlighted]:text-[HighlightText]'
);
/**

View File

@@ -946,7 +946,7 @@ export function CallScreen({
</div>
{(isConnecting || isRinging) && (
<>
<div className="module-CallingPreCallInfo-spacer " />
<div className="module-CallingPreCallInfo-spacer" />
<CallingPreCallInfo
conversation={conversation}
groupMembers={groupMembers}

View File

@@ -398,7 +398,7 @@ export function renderBackupsSubscriptionDetails({
case 'expired':
return (
<>
<div className="Preferences--backups-summary__status-container ">
<div className="Preferences--backups-summary__status-container">
<div className="Preferences--backups-summary__content">
{i18n('icu:Preferences--backup-plan-not-found__description')}
</div>
@@ -466,7 +466,7 @@ export function renderBackupsSubscriptionSummary({
case 'not-found':
case 'expired':
return (
<div className="Preferences--backups-summary__status-container ">
<div className="Preferences--backups-summary__status-container">
<div className="Preferences--backups-summary__content">
{i18n('icu:Preferences--backup-plan-not-found__description')}
</div>

View File

@@ -209,10 +209,7 @@ function TypingBubbleGroupAvatars({
<div className="module-message__author-avatar-container module-message__author-avatar-container--typing">
<div className="module-message__typing-avatar-spacer" />
{typingContactsOverflowCount > 0 && (
<div
className="module-message__typing-avatar module-message__typing-avatar--overflow-count
"
>
<div className="module-message__typing-avatar module-message__typing-avatar--overflow-count">
<div
aria-label={i18n('icu:TypingBubble__avatar--overflow-count', {
count: typingContactsOverflowCount,

View File

@@ -9,6 +9,7 @@ import { Emojify } from '../../components/conversation/Emojify';
import { normalizeAci } from '../../util/normalizeAci';
import type { MentionBlotValue } from '../util';
import { FunEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../../axo/AxoProvider';
export class MentionBlot extends EmbedBlot {
static override blotName = 'mention';
@@ -48,14 +49,16 @@ export class MentionBlot extends EmbedBlot {
createRoot(mentionSpan).render(
<StrictMode>
<FunEmojiLocalizationProvider i18n={window.i18n}>
<span className="module-composition-input__at-mention">
<bdi>
@
<Emojify text={mention.title} />
</bdi>
</span>
</FunEmojiLocalizationProvider>
<AxoProvider dir={window.i18n.getLocaleDirection()}>
<FunEmojiLocalizationProvider i18n={window.i18n}>
<span className="module-composition-input__at-mention">
<bdi>
@
<Emojify text={mention.title} />
</bdi>
</span>
</FunEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);

View File

@@ -8,15 +8,18 @@ import { ClearingData } from '../components/ClearingData';
import { strictAssert } from '../util/assert';
import { deleteAllData } from './deleteAllData';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../axo/AxoProvider';
export function renderClearingDataView(): void {
const appContainer = document.getElementById('app-container');
strictAssert(appContainer != null, 'No #app-container');
createRoot(appContainer).render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<ClearingData deleteAllData={deleteAllData} i18n={window.i18n} />
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={window.i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<ClearingData deleteAllData={deleteAllData} i18n={window.i18n} />
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);
}

View File

@@ -88,6 +88,7 @@ import { SmartPreferencesChatFoldersPage } from './PreferencesChatFoldersPage';
import type { SmartPreferencesEditChatFolderPageProps } from './PreferencesEditChatFolderPage';
import { SmartPreferencesEditChatFolderPage } from './PreferencesEditChatFolderPage';
import { isProduction } from '../../util/version';
import { AxoProvider } from '../../axo/AxoProvider';
const DEFAULT_NOTIFICATION_SETTING = 'message';
@@ -740,179 +741,183 @@ export function SmartPreferences(): JSX.Element | null {
return (
<StrictMode>
<Preferences
accountEntropyPool={accountEntropyPool}
addCustomColor={addCustomColor}
autoDownloadAttachment={autoDownloadAttachment}
availableCameras={availableCameras}
availableLocales={availableLocales}
availableMicrophones={availableMicrophones}
availableSpeakers={availableSpeakers}
backupFeatureEnabled={backupFeatureEnabled}
backupKeyViewed={backupKeyViewed}
backupSubscriptionStatus={backupSubscriptionStatus ?? { status: 'off' }}
backupMediaDownloadStatus={{
completedBytes: backupMediaDownloadCompletedBytes ?? 0,
totalBytes: backupMediaDownloadTotalBytes ?? 0,
isPaused: Boolean(backupMediaDownloadPaused),
isIdle: Boolean(attachmentDownloadManagerIdled),
}}
backupLocalBackupsEnabled={backupLocalBackupsEnabled}
badge={badge}
blockedCount={blockedCount}
cloudBackupStatus={cloudBackupStatus}
customColors={customColors}
defaultConversationColor={defaultConversationColor}
deviceName={deviceName}
donationsFeatureEnabled={donationsFeatureEnabled}
emojiSkinToneDefault={emojiSkinToneDefault}
exportLocalBackup={exportLocalBackup}
phoneNumber={phoneNumber}
doDeleteAllData={doDeleteAllData}
editCustomColor={editCustomColor}
getConversationsWithCustomColor={getConversationsWithCustomColor}
getMessageCountBySchemaVersion={
DataReader.getMessageCountBySchemaVersion
}
getMessageSampleForSchemaVersion={
DataReader.getMessageSampleForSchemaVersion
}
hasAudioNotifications={hasAudioNotifications}
hasAutoConvertEmoji={hasAutoConvertEmoji}
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
hasAutoLaunch={hasAutoLaunch}
hasKeepMutedChatsArchived={hasKeepMutedChatsArchived}
hasCallNotifications={hasCallNotifications}
hasCallRingtoneNotification={hasCallRingtoneNotification}
hasContentProtection={hasContentProtection}
hasCountMutedConversations={hasCountMutedConversations}
hasFailedStorySends={hasFailedStorySends}
hasHideMenuBar={hasHideMenuBar}
hasIncomingCallNotifications={hasIncomingCallNotifications}
hasLinkPreviews={hasLinkPreviews}
hasMediaCameraPermissions={hasMediaCameraPermissions}
hasMediaPermissions={hasMediaPermissions}
hasMessageAudio={hasMessageAudio}
hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray}
hasMinimizeToSystemTray={hasMinimizeToSystemTray}
hasNotificationAttention={hasNotificationAttention}
hasNotifications={hasNotifications}
hasReadReceipts={hasReadReceipts}
hasRelayCalls={hasRelayCalls}
hasSpellCheck={hasSpellCheck}
hasStoriesDisabled={hasStoriesDisabled}
hasTextFormatting={hasTextFormatting}
hasTypingIndicators={hasTypingIndicators}
i18n={i18n}
initialSpellCheckSetting={initialSpellCheckSetting}
isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported}
isAutoLaunchSupported={isAutoLaunchSupported}
isContentProtectionNeeded={isContentProtectionNeeded}
isContentProtectionSupported={isContentProtectionSupported}
isHideMenuBarSupported={isHideMenuBarSupported}
isMinimizeToAndStartInSystemTraySupported={
isMinimizeToAndStartInSystemTraySupported
}
isNotificationAttentionSupported={isNotificationAttentionSupported}
isSyncSupported={isSyncSupported}
isSystemTraySupported={isSystemTraySupported}
isInternalUser={isInternalUser}
lastSyncTime={lastSyncTime}
localBackupFolder={localBackupFolder}
localeOverride={localeOverride}
makeSyncRequest={makeSyncRequest}
me={me}
navTabsCollapsed={navTabsCollapsed}
notificationContent={notificationContent}
onAudioNotificationsChange={onAudioNotificationsChange}
onAutoConvertEmojiChange={onAutoConvertEmojiChange}
onAutoDownloadAttachmentChange={onAutoDownloadAttachmentChange}
onAutoDownloadUpdateChange={onAutoDownloadUpdateChange}
onAutoLaunchChange={onAutoLaunchChange}
onBackupKeyViewedChange={onBackupKeyViewedChange}
onCallNotificationsChange={onCallNotificationsChange}
onCallRingtoneNotificationChange={onCallRingtoneNotificationChange}
onContentProtectionChange={onContentProtectionChange}
onCountMutedConversationsChange={onCountMutedConversationsChange}
onEmojiSkinToneDefaultChange={onEmojiSkinToneDefaultChange}
onHasStoriesDisabledChanged={onHasStoriesDisabledChanged}
onHideMenuBarChange={onHideMenuBarChange}
onIncomingCallNotificationsChange={onIncomingCallNotificationsChange}
onKeepMutedChatsArchivedChange={onKeepMutedChatsArchivedChange}
onLastSyncTimeChange={onLastSyncTimeChange}
onLocaleChange={onLocaleChange}
onMediaCameraPermissionsChange={onMediaCameraPermissionsChange}
onMediaPermissionsChange={onMediaPermissionsChange}
onMessageAudioChange={onMessageAudioChange}
onMinimizeToAndStartInSystemTrayChange={
onMinimizeToAndStartInSystemTrayChange
}
onMinimizeToSystemTrayChange={onMinimizeToSystemTrayChange}
onNotificationAttentionChange={onNotificationAttentionChange}
onNotificationContentChange={onNotificationContentChange}
onNotificationsChange={onNotificationsChange}
onStartUpdate={startUpdate}
onRelayCallsChange={onRelayCallsChange}
onSelectedCameraChange={onSelectedCameraChange}
onSelectedMicrophoneChange={onSelectedMicrophoneChange}
onSelectedSpeakerChange={onSelectedSpeakerChange}
onSentMediaQualityChange={onSentMediaQualityChange}
onSpellCheckChange={onSpellCheckChange}
onTextFormattingChange={onTextFormattingChange}
onThemeChange={onThemeChange}
onToggleNavTabsCollapse={toggleNavTabsCollapse}
onUniversalExpireTimerChange={onUniversalExpireTimerChange}
onWhoCanFindMeChange={onWhoCanFindMeChange}
onWhoCanSeeMeChange={onWhoCanSeeMeChange}
onZoomFactorChange={onZoomFactorChange}
otherTabsUnreadStats={otherTabsUnreadStats}
page={page}
pickLocalBackupFolder={pickLocalBackupFolder}
preferredSystemLocales={preferredSystemLocales}
preferredWidthFromStorage={preferredWidthFromStorage}
refreshCloudBackupStatus={refreshCloudBackupStatus}
refreshBackupSubscriptionStatus={refreshBackupSubscriptionStatus}
removeCustomColorOnConversations={removeCustomColorOnConversations}
removeCustomColor={removeCustomColor}
renderDonationsPane={renderDonationsPane}
renderProfileEditor={renderProfileEditor}
renderToastManager={renderToastManager}
renderUpdateDialog={renderUpdateDialog}
renderPreferencesChatFoldersPage={renderPreferencesChatFoldersPage}
renderPreferencesEditChatFolderPage={
renderPreferencesEditChatFolderPage
}
promptOSAuth={promptOSAuth}
resetAllChatColors={resetAllChatColors}
resetDefaultChatColor={resetDefaultChatColor}
resolvedLocale={resolvedLocale}
savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
resumeBackupMediaDownload={resumeBackupMediaDownload}
pauseBackupMediaDownload={pauseBackupMediaDownload}
cancelBackupMediaDownload={cancelBackupMediaDownload}
selectedCamera={selectedCamera}
selectedMicrophone={selectedMicrophone}
selectedSpeaker={selectedSpeaker}
sentMediaQualitySetting={sentMediaQualitySetting}
setGlobalDefaultConversationColor={setGlobalDefaultConversationColor}
setPage={setPage}
shouldShowUpdateDialog={shouldShowUpdateDialog}
showToast={showToast}
theme={theme}
themeSetting={themeSetting}
universalExpireTimer={universalExpireTimer}
validateBackup={validateBackup}
whoCanFindMe={whoCanFindMe}
whoCanSeeMe={whoCanSeeMe}
zoomFactor={zoomFactor}
donationReceipts={donationReceipts}
internalAddDonationReceipt={internalAddDonationReceipt}
saveAttachmentToDisk={window.Signal.Migrations.saveAttachmentToDisk}
generateDonationReceiptBlob={generateDonationReceiptBlob}
__dangerouslyRunAbitraryReadOnlySqlQuery={
__dangerouslyRunAbitraryReadOnlySqlQuery
}
/>
<AxoProvider dir={i18n.getLocaleDirection()}>
<Preferences
accountEntropyPool={accountEntropyPool}
addCustomColor={addCustomColor}
autoDownloadAttachment={autoDownloadAttachment}
availableCameras={availableCameras}
availableLocales={availableLocales}
availableMicrophones={availableMicrophones}
availableSpeakers={availableSpeakers}
backupFeatureEnabled={backupFeatureEnabled}
backupKeyViewed={backupKeyViewed}
backupSubscriptionStatus={
backupSubscriptionStatus ?? { status: 'off' }
}
backupMediaDownloadStatus={{
completedBytes: backupMediaDownloadCompletedBytes ?? 0,
totalBytes: backupMediaDownloadTotalBytes ?? 0,
isPaused: Boolean(backupMediaDownloadPaused),
isIdle: Boolean(attachmentDownloadManagerIdled),
}}
backupLocalBackupsEnabled={backupLocalBackupsEnabled}
badge={badge}
blockedCount={blockedCount}
cloudBackupStatus={cloudBackupStatus}
customColors={customColors}
defaultConversationColor={defaultConversationColor}
deviceName={deviceName}
donationsFeatureEnabled={donationsFeatureEnabled}
emojiSkinToneDefault={emojiSkinToneDefault}
exportLocalBackup={exportLocalBackup}
phoneNumber={phoneNumber}
doDeleteAllData={doDeleteAllData}
editCustomColor={editCustomColor}
getConversationsWithCustomColor={getConversationsWithCustomColor}
getMessageCountBySchemaVersion={
DataReader.getMessageCountBySchemaVersion
}
getMessageSampleForSchemaVersion={
DataReader.getMessageSampleForSchemaVersion
}
hasAudioNotifications={hasAudioNotifications}
hasAutoConvertEmoji={hasAutoConvertEmoji}
hasAutoDownloadUpdate={hasAutoDownloadUpdate}
hasAutoLaunch={hasAutoLaunch}
hasKeepMutedChatsArchived={hasKeepMutedChatsArchived}
hasCallNotifications={hasCallNotifications}
hasCallRingtoneNotification={hasCallRingtoneNotification}
hasContentProtection={hasContentProtection}
hasCountMutedConversations={hasCountMutedConversations}
hasFailedStorySends={hasFailedStorySends}
hasHideMenuBar={hasHideMenuBar}
hasIncomingCallNotifications={hasIncomingCallNotifications}
hasLinkPreviews={hasLinkPreviews}
hasMediaCameraPermissions={hasMediaCameraPermissions}
hasMediaPermissions={hasMediaPermissions}
hasMessageAudio={hasMessageAudio}
hasMinimizeToAndStartInSystemTray={hasMinimizeToAndStartInSystemTray}
hasMinimizeToSystemTray={hasMinimizeToSystemTray}
hasNotificationAttention={hasNotificationAttention}
hasNotifications={hasNotifications}
hasReadReceipts={hasReadReceipts}
hasRelayCalls={hasRelayCalls}
hasSpellCheck={hasSpellCheck}
hasStoriesDisabled={hasStoriesDisabled}
hasTextFormatting={hasTextFormatting}
hasTypingIndicators={hasTypingIndicators}
i18n={i18n}
initialSpellCheckSetting={initialSpellCheckSetting}
isAutoDownloadUpdatesSupported={isAutoDownloadUpdatesSupported}
isAutoLaunchSupported={isAutoLaunchSupported}
isContentProtectionNeeded={isContentProtectionNeeded}
isContentProtectionSupported={isContentProtectionSupported}
isHideMenuBarSupported={isHideMenuBarSupported}
isMinimizeToAndStartInSystemTraySupported={
isMinimizeToAndStartInSystemTraySupported
}
isNotificationAttentionSupported={isNotificationAttentionSupported}
isSyncSupported={isSyncSupported}
isSystemTraySupported={isSystemTraySupported}
isInternalUser={isInternalUser}
lastSyncTime={lastSyncTime}
localBackupFolder={localBackupFolder}
localeOverride={localeOverride}
makeSyncRequest={makeSyncRequest}
me={me}
navTabsCollapsed={navTabsCollapsed}
notificationContent={notificationContent}
onAudioNotificationsChange={onAudioNotificationsChange}
onAutoConvertEmojiChange={onAutoConvertEmojiChange}
onAutoDownloadAttachmentChange={onAutoDownloadAttachmentChange}
onAutoDownloadUpdateChange={onAutoDownloadUpdateChange}
onAutoLaunchChange={onAutoLaunchChange}
onBackupKeyViewedChange={onBackupKeyViewedChange}
onCallNotificationsChange={onCallNotificationsChange}
onCallRingtoneNotificationChange={onCallRingtoneNotificationChange}
onContentProtectionChange={onContentProtectionChange}
onCountMutedConversationsChange={onCountMutedConversationsChange}
onEmojiSkinToneDefaultChange={onEmojiSkinToneDefaultChange}
onHasStoriesDisabledChanged={onHasStoriesDisabledChanged}
onHideMenuBarChange={onHideMenuBarChange}
onIncomingCallNotificationsChange={onIncomingCallNotificationsChange}
onKeepMutedChatsArchivedChange={onKeepMutedChatsArchivedChange}
onLastSyncTimeChange={onLastSyncTimeChange}
onLocaleChange={onLocaleChange}
onMediaCameraPermissionsChange={onMediaCameraPermissionsChange}
onMediaPermissionsChange={onMediaPermissionsChange}
onMessageAudioChange={onMessageAudioChange}
onMinimizeToAndStartInSystemTrayChange={
onMinimizeToAndStartInSystemTrayChange
}
onMinimizeToSystemTrayChange={onMinimizeToSystemTrayChange}
onNotificationAttentionChange={onNotificationAttentionChange}
onNotificationContentChange={onNotificationContentChange}
onNotificationsChange={onNotificationsChange}
onStartUpdate={startUpdate}
onRelayCallsChange={onRelayCallsChange}
onSelectedCameraChange={onSelectedCameraChange}
onSelectedMicrophoneChange={onSelectedMicrophoneChange}
onSelectedSpeakerChange={onSelectedSpeakerChange}
onSentMediaQualityChange={onSentMediaQualityChange}
onSpellCheckChange={onSpellCheckChange}
onTextFormattingChange={onTextFormattingChange}
onThemeChange={onThemeChange}
onToggleNavTabsCollapse={toggleNavTabsCollapse}
onUniversalExpireTimerChange={onUniversalExpireTimerChange}
onWhoCanFindMeChange={onWhoCanFindMeChange}
onWhoCanSeeMeChange={onWhoCanSeeMeChange}
onZoomFactorChange={onZoomFactorChange}
otherTabsUnreadStats={otherTabsUnreadStats}
page={page}
pickLocalBackupFolder={pickLocalBackupFolder}
preferredSystemLocales={preferredSystemLocales}
preferredWidthFromStorage={preferredWidthFromStorage}
refreshCloudBackupStatus={refreshCloudBackupStatus}
refreshBackupSubscriptionStatus={refreshBackupSubscriptionStatus}
removeCustomColorOnConversations={removeCustomColorOnConversations}
removeCustomColor={removeCustomColor}
renderDonationsPane={renderDonationsPane}
renderProfileEditor={renderProfileEditor}
renderToastManager={renderToastManager}
renderUpdateDialog={renderUpdateDialog}
renderPreferencesChatFoldersPage={renderPreferencesChatFoldersPage}
renderPreferencesEditChatFolderPage={
renderPreferencesEditChatFolderPage
}
promptOSAuth={promptOSAuth}
resetAllChatColors={resetAllChatColors}
resetDefaultChatColor={resetDefaultChatColor}
resolvedLocale={resolvedLocale}
savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
resumeBackupMediaDownload={resumeBackupMediaDownload}
pauseBackupMediaDownload={pauseBackupMediaDownload}
cancelBackupMediaDownload={cancelBackupMediaDownload}
selectedCamera={selectedCamera}
selectedMicrophone={selectedMicrophone}
selectedSpeaker={selectedSpeaker}
sentMediaQualitySetting={sentMediaQualitySetting}
setGlobalDefaultConversationColor={setGlobalDefaultConversationColor}
setPage={setPage}
shouldShowUpdateDialog={shouldShowUpdateDialog}
showToast={showToast}
theme={theme}
themeSetting={themeSetting}
universalExpireTimer={universalExpireTimer}
validateBackup={validateBackup}
whoCanFindMe={whoCanFindMe}
whoCanSeeMe={whoCanSeeMe}
zoomFactor={zoomFactor}
donationReceipts={donationReceipts}
internalAddDonationReceipt={internalAddDonationReceipt}
saveAttachmentToDisk={window.Signal.Migrations.saveAttachmentToDisk}
generateDonationReceiptBlob={generateDonationReceiptBlob}
__dangerouslyRunAbitraryReadOnlySqlQuery={
__dangerouslyRunAbitraryReadOnlySqlQuery
}
/>
</AxoProvider>
</StrictMode>
);
}

View File

@@ -10,6 +10,7 @@ import { ProgressModal } from '../components/ProgressModal';
import { clearTimeoutIfNecessary } from './clearTimeoutIfNecessary';
import { sleep } from './sleep';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../axo/AxoProvider';
const log = createLogger('longRunningTaskWrapper');
@@ -37,9 +38,11 @@ export async function longRunningTaskWrapper<T>({
progressRoot = createRoot(progressNode);
progressRoot.render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<ProgressModal i18n={window.i18n} />
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={window.i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<ProgressModal i18n={window.i18n} />
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);
spinnerStart = Date.now();

View File

@@ -5,6 +5,7 @@ import React, { StrictMode } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { ConfirmationDialog } from '../components/ConfirmationDialog';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../axo/AxoProvider';
type ConfirmationDialogViewProps = {
onTopOfEverything?: boolean;
@@ -57,37 +58,39 @@ export function showConfirmationDialog(
confirmationDialogRoot = createRoot(confirmationDialogViewNode);
confirmationDialogRoot.render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<ConfirmationDialog
dialogName={options.dialogName}
onTopOfEverything={options.onTopOfEverything}
actions={[
{
action: () => {
options.resolve();
<AxoProvider dir={window.i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<ConfirmationDialog
dialogName={options.dialogName}
onTopOfEverything={options.onTopOfEverything}
actions={[
{
action: () => {
options.resolve();
},
style: options.confirmStyle,
text: options.okText || window.i18n('icu:ok'),
},
style: options.confirmStyle,
text: options.okText || window.i18n('icu:ok'),
},
]}
cancelText={options.cancelText || window.i18n('icu:cancel')}
i18n={window.i18n}
onCancel={() => {
if (options.reject) {
options.reject(
new Error('showConfirmationDialog: onCancel called')
);
}
}}
onClose={() => {
removeConfirmationDialog();
}}
title={options.title}
noMouseClose={options.noMouseClose}
>
{options.description}
</ConfirmationDialog>
</FunDefaultEnglishEmojiLocalizationProvider>
]}
cancelText={options.cancelText || window.i18n('icu:cancel')}
i18n={window.i18n}
onCancel={() => {
if (options.reject) {
options.reject(
new Error('showConfirmationDialog: onCancel called')
);
}
}}
onClose={() => {
removeConfirmationDialog();
}}
title={options.title}
noMouseClose={options.noMouseClose}
>
{options.description}
</ConfirmationDialog>
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);
}

View File

@@ -8,6 +8,7 @@ import { About } from '../../components/About';
import { i18n } from '../sandboxedInit';
import { strictAssert } from '../../util/assert';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../../axo/AxoProvider';
const { AboutWindowProps } = window.Signal;
@@ -18,15 +19,17 @@ strictAssert(app != null, 'No #app');
createRoot(app).render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<About
closeAbout={() => window.SignalContext.executeMenuRole('close')}
appEnv={AboutWindowProps.appEnv}
platform={AboutWindowProps.platform}
arch={AboutWindowProps.arch}
i18n={i18n}
version={window.SignalContext.getVersion()}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<About
closeAbout={() => window.SignalContext.executeMenuRole('close')}
appEnv={AboutWindowProps.appEnv}
platform={AboutWindowProps.platform}
arch={AboutWindowProps.arch}
i18n={i18n}
version={window.SignalContext.getVersion()}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);

View File

@@ -7,6 +7,7 @@ import { DebugLogWindow } from '../../components/DebugLogWindow';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider';
import { i18n } from '../sandboxedInit';
import { strictAssert } from '../../util/assert';
import { AxoProvider } from '../../axo/AxoProvider';
const { DebugLogWindowProps } = window.Signal;
@@ -17,14 +18,16 @@ strictAssert(app != null, 'No #app');
createRoot(app).render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<DebugLogWindow
closeWindow={() => window.SignalContext.executeMenuRole('close')}
downloadLog={DebugLogWindowProps.downloadLog}
i18n={i18n}
fetchLogs={DebugLogWindowProps.fetchLogs}
uploadLogs={DebugLogWindowProps.uploadLogs}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<DebugLogWindow
closeWindow={() => window.SignalContext.executeMenuRole('close')}
downloadLog={DebugLogWindowProps.downloadLog}
i18n={i18n}
fetchLogs={DebugLogWindowProps.fetchLogs}
uploadLogs={DebugLogWindowProps.uploadLogs}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);

View File

@@ -8,6 +8,7 @@ import { PermissionsPopup } from '../../components/PermissionsPopup';
import { i18n } from '../sandboxedInit';
import { strictAssert } from '../../util/assert';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../../axo/AxoProvider';
const { PermissionsWindowProps } = window.Signal;
@@ -31,13 +32,15 @@ strictAssert(app != null, 'No #app');
createRoot(app).render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<PermissionsPopup
i18n={i18n}
message={message}
onAccept={PermissionsWindowProps.onAccept}
onClose={PermissionsWindowProps.onClose}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<PermissionsPopup
i18n={i18n}
message={message}
onAccept={PermissionsWindowProps.onAccept}
onClose={PermissionsWindowProps.onClose}
/>
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);

View File

@@ -10,6 +10,7 @@ import { strictAssert } from '../../util/assert';
import { drop } from '../../util/drop';
import { parseEnvironment, setEnvironment } from '../../environment';
import { FunDefaultEnglishEmojiLocalizationProvider } from '../../components/fun/FunEmojiLocalizationProvider';
import { AxoProvider } from '../../axo/AxoProvider';
const { ScreenShareWindowProps } = window.Signal;
@@ -33,17 +34,19 @@ function render() {
createRoot(app).render(
<StrictMode>
<FunDefaultEnglishEmojiLocalizationProvider>
<div className="App dark-theme">
<CallingScreenSharingController
i18n={i18n}
onCloseController={onCloseController}
onStopSharing={ScreenShareWindowProps.onStopSharing}
status={ScreenShareWindowProps.getStatus()}
presentedSourceName={ScreenShareWindowProps.presentedSourceName}
/>
</div>
</FunDefaultEnglishEmojiLocalizationProvider>
<AxoProvider dir={i18n.getLocaleDirection()}>
<FunDefaultEnglishEmojiLocalizationProvider>
<div className="App dark-theme">
<CallingScreenSharingController
i18n={i18n}
onCloseController={onCloseController}
onStopSharing={ScreenShareWindowProps.onStopSharing}
status={ScreenShareWindowProps.getStatus()}
presentedSourceName={ScreenShareWindowProps.presentedSourceName}
/>
</div>
</FunDefaultEnglishEmojiLocalizationProvider>
</AxoProvider>
</StrictMode>
);
}