refactor(app): merge duplicated components and stores (#3383)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-10 23:17:29 +02:00
1 parent c16d414d93
commit 5695510980
202 files changed
+2924 -6532

No files matched your search

@@ -23,7 +23,7 @@ import {Input} from '@app/features/ui/components/form/FormInput';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import * as UserSettingsCommands from '@app/features/user/commands/UserSettingsCommands';
import styles from '@app/features/user/components/modals/CustomStatusBottomSheet.module.css';
import {type CustomStatus, normalizeCustomStatus} from '@app/features/user/state/CustomStatus';
import {buildDraftStatus, normalizeCustomStatus} from '@app/features/user/state/CustomStatus';
import Users from '@app/features/user/state/Users';
import {handleAccountLimitedError} from '@app/features/user/utils/AccountLimitUtils';
import {msg} from '@lingui/core/macro';
@@ -67,19 +67,6 @@ interface CustomStatusBottomSheetProps {
onClose: () => void;
}
const buildDraftStatus = (params: {
text: string;
emojiId: string | null;
emojiName: string | null;
expiresAt: string | null;
}): CustomStatus | null => {
return normalizeCustomStatus({
text: params.text || null,
emojiId: params.emojiId,
emojiName: params.emojiName,
expiresAt: params.expiresAt,
});
};
export const CustomStatusBottomSheet = observer(({isOpen, onClose}: CustomStatusBottomSheetProps) => {
const {i18n} = useLingui();
const currentUser = Users.getCurrentUser();
@@ -28,7 +28,7 @@ import {getRelativeDayLabelLower} from '@app/features/ui/utils/RelativeDayLabels
import * as UserSettingsCommands from '@app/features/user/commands/UserSettingsCommands';
import styles from '@app/features/user/components/modals/CustomStatusModal.module.css';
import {ProfilePreview} from '@app/features/user/components/profile/ProfilePreview';
import {type CustomStatus, normalizeCustomStatus} from '@app/features/user/state/CustomStatus';
import {buildDraftStatus, type CustomStatus, normalizeCustomStatus} from '@app/features/user/state/CustomStatus';
import UserSettings from '@app/features/user/state/UserSettings';
import Users from '@app/features/user/state/Users';
import {handleAccountLimitedError} from '@app/features/user/utils/AccountLimitUtils';
@@ -129,19 +129,6 @@ const formatRelativeDayTimeLabel = (reference: Date, target: Date): TimeLabel =>
const dayLabel = getFormattedShortDate(target, getCurrentLocale());
return {dayLabel, timeString};
};
const buildDraftStatus = (params: {
text: string;
emojiId: string | null;
emojiName: string | null;
expiresAt: string | null;
}): CustomStatus | null => {
return normalizeCustomStatus({
text: params.text || null,
emojiId: params.emojiId,
emojiName: params.emojiName,
expiresAt: params.expiresAt,
});
};
export const CustomStatusModal = observer(() => {
const {i18n} = useLingui();
const initialStatus = normalizeCustomStatus(UserSettings.customStatus);
@@ -13,7 +13,7 @@ import {getCachedNumberFormat} from '@app/features/i18n/utils/IntlCache';
import {Message as MessageModel} from '@app/features/messaging/models/MessagingMessage';
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
import {Button} from '@app/features/ui/button/Button';
import type {ComboboxOption} from '@app/features/ui/components/form/FormCombobox';
import {type ComboboxOption, resolveNearestNumericOption} from '@app/features/ui/components/form/FormCombobox';
import {Switch} from '@app/features/ui/components/form/FormSwitch';
import {MockAvatar} from '@app/features/ui/components/MockAvatar';
import MobileLayout from '@app/features/ui/state/MobileLayout';
@@ -115,18 +115,6 @@ const getNearestTtsRate = (value: number): number => {
return nearest;
};
const resolveTtsRateInput = (
inputValue: string,
options: ReadonlyArray<ComboboxOption<number>>,
): number | undefined => {
const numericMatch = inputValue.trim().match(/([0-9]+(?:\.[0-9]+)?)/);
if (!numericMatch) return undefined;
const parsedValue = Number(numericMatch[1]);
if (!Number.isFinite(parsedValue)) return undefined;
return options.reduce((nearest, option) =>
Math.abs(option.value - parsedValue) < Math.abs(nearest.value - parsedValue) ? option : nearest,
).value;
};
const AccessibilityTabPreview = observer(() => {
const {i18n} = useLingui();
const alwaysUnderlineLinks = Accessibility.alwaysUnderlineLinks;
@@ -308,7 +296,7 @@ export const AccessibilityTtsTabContent: React.FC = observer(() => {
value={selectedTtsRate}
options={ttsRateOptions}
onChange={handleRateChange}
autoSelectValueFromInput={resolveTtsRateInput}
autoSelectValueFromInput={resolveNearestNumericOption}
controlWidth="small"
dataFlx="user.accessibility-tab.accessibility-tts-tab-content.select.tts-rate"
data-flx="user.accessibility-tab.accessibility-tts-tab-content.compact-combobox-row.rate-change"
@@ -13,7 +13,6 @@ import {I18N_WEBLATE_DOMAIN, I18N_WEBLATE_URL} from '@app/features/app/config/I1
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import * as EmojiUtils from '@app/features/expressions/utils/EmojiUtils';
import Spellcheck from '@app/features/messaging/state/Spellcheck';
import type {SpellcheckEngine} from '@app/features/platform/types/Electron';
import {Button} from '@app/features/ui/button/Button';
import {Combobox, type ComboboxFilterOption, type ComboboxOption} from '@app/features/ui/components/form/FormCombobox';
import {Switch} from '@app/features/ui/components/form/FormSwitch';
@@ -25,6 +24,7 @@ import * as UserSettingsCommands from '@app/features/user/commands/UserSettingsC
import styles from '@app/features/user/components/modals/tabs/LanguageTab.module.css';
import UserSettings from '@app/features/user/state/UserSettings';
import * as LocaleUtils from '@app/features/user/utils/LocaleUtils';
import type {SpellcheckEngine} from '@app/types/electron.d';
import {TimeFormatTypes} from '@fluxer/constants/src/UserConstants';
import {getFormattedTime} from '@fluxer/date_utils/src/DateFormatting';
import {localeUses12Hour} from '@fluxer/date_utils/src/DateHourCycle';
@@ -10,6 +10,7 @@ import {Notifications} from '@app/features/user/components/modals/tabs/notificat
import {Sounds} from '@app/features/user/components/modals/tabs/notifications_tab/NotificationsTabSounds';
import {TextToSpeech} from '@app/features/user/components/modals/tabs/notifications_tab/TextToSpeech';
import {useSoundSettings} from '@app/features/user/components/modals/tabs/notifications_tab/useSoundSettings';
import inlineStyles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
@@ -23,7 +24,19 @@ const TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR = msg({
message: 'Text-to-speech notifications',
comment: 'Notifications settings: section heading for text-to-speech notification behavior.',
});
const NotificationsTab: React.FC = observer(() => {
const CONTROL_SPEECH_COMMANDS_AND_NARRATION_FOR_INCOMING_CONTENT_DESCRIPTOR = msg({
message: 'Control speech commands and narration for incoming content.',
comment: 'Description text in the inline.',
});
const INLINE_MENTION_PREFERENCE_DESCRIPTOR = msg({
message: 'Mention preference',
comment: 'Short label in the inline. Keep it concise.',
});
const INLINE_TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR = msg({
message: 'Text-to-speech notifications',
comment: 'Short label in the inline. Keep it concise.',
});
const NotificationsSections = observer(({inline}: {inline: boolean}) => {
const {i18n} = useLingui();
const browserNotificationsEnabled = Notification.browserNotificationsEnabled;
const unreadMessageBadgeEnabled = Notification.unreadMessageBadgeEnabled;
@@ -41,55 +54,70 @@ const NotificationsTab: React.FC = observer(() => {
handleSoundOverrideReset,
handleAllOverridesReset,
} = useSoundSettings();
const flx = inline ? 'user.notifications-tab.inline.notifications-inline-content' : 'user.notifications-tab';
return (
<SettingsTabContainer data-flx="user.notifications-tab.settings-tab-container">
<SettingsTabContent data-flx="user.notifications-tab.settings-tab-content">
<SettingsSection
id="notifications"
title={i18n._(GENERAL_DESCRIPTOR)}
data-flx="user.notifications-tab.notifications"
>
<Notifications
browserNotificationsEnabled={browserNotificationsEnabled}
unreadMessageBadgeEnabled={unreadMessageBadgeEnabled}
data-flx="user.notifications-tab.notifications--2"
/>
</SettingsSection>
<SettingsSection
id="mention-preference"
title={i18n._(MENTION_PREFERENCE_SECTION_TITLE_DESCRIPTOR)}
data-flx="user.notifications-tab.mention-preference"
>
<MentionPreferenceTabContent data-flx="user.notifications-tab.mention-preference-tab-content" />
</SettingsSection>
<SettingsSection id="sounds" title={i18n._(SOUNDS_DESCRIPTOR)} data-flx="user.notifications-tab.sounds">
<Sounds
soundSettings={soundSettings}
soundTypeLabels={soundTypeLabels}
customSounds={customSounds}
isSoundEnabled={Sound.isSoundTypeEnabled}
onToggleAllSounds={handleToggleAllSounds}
onToggleSound={handleToggleSound}
onPreviewSound={handlePreviewSound}
onUploadClick={handleUploadClick}
onCustomSoundDelete={handleCustomSoundDelete}
onMasterVolumeChange={handleMasterVolumeChange}
onSoundOverrideChange={handleSoundOverrideChange}
onSoundOverrideReset={handleSoundOverrideReset}
onAllOverridesReset={handleAllOverridesReset}
data-flx="user.notifications-tab.sounds--2"
/>
</SettingsSection>
<SettingsSection
id="text-to-speech"
title={i18n._(TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR)}
data-flx="user.notifications-tab.text-to-speech"
>
<TextToSpeech data-flx="user.notifications-tab.text-to-speech--2" />
</SettingsSection>
</SettingsTabContent>
</SettingsTabContainer>
<>
<SettingsSection id="notifications" title={i18n._(GENERAL_DESCRIPTOR)} data-flx={`${flx}.notifications`}>
<Notifications
browserNotificationsEnabled={browserNotificationsEnabled}
unreadMessageBadgeEnabled={unreadMessageBadgeEnabled}
data-flx={`${flx}.notifications--2`}
/>
</SettingsSection>
<SettingsSection
id="mention-preference"
title={i18n._(inline ? INLINE_MENTION_PREFERENCE_DESCRIPTOR : MENTION_PREFERENCE_SECTION_TITLE_DESCRIPTOR)}
data-flx={`${flx}.mention-preference`}
>
<MentionPreferenceTabContent data-flx={`${flx}.mention-preference-tab-content`} />
</SettingsSection>
<SettingsSection id="sounds" title={i18n._(SOUNDS_DESCRIPTOR)} data-flx={`${flx}.sounds`}>
<Sounds
soundSettings={soundSettings}
soundTypeLabels={soundTypeLabels}
customSounds={customSounds}
isSoundEnabled={Sound.isSoundTypeEnabled}
onToggleAllSounds={handleToggleAllSounds}
onToggleSound={handleToggleSound}
onPreviewSound={handlePreviewSound}
onUploadClick={handleUploadClick}
onCustomSoundDelete={handleCustomSoundDelete}
onMasterVolumeChange={handleMasterVolumeChange}
onSoundOverrideChange={handleSoundOverrideChange}
onSoundOverrideReset={handleSoundOverrideReset}
onAllOverridesReset={handleAllOverridesReset}
data-flx={`${flx}.sounds--2`}
/>
</SettingsSection>
<SettingsSection
id="text-to-speech"
title={i18n._(
inline ? INLINE_TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR : TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR,
)}
description={inline ? i18n._(CONTROL_SPEECH_COMMANDS_AND_NARRATION_FOR_INCOMING_CONTENT_DESCRIPTOR) : undefined}
data-flx={`${flx}.text-to-speech`}
>
<TextToSpeech data-flx={`${flx}.text-to-speech--2`} />
</SettingsSection>
</>
);
});
const NotificationsTab: React.FC = () => (
<SettingsTabContainer data-flx="user.notifications-tab.settings-tab-container">
<SettingsTabContent data-flx="user.notifications-tab.settings-tab-content">
<NotificationsSections inline={false} data-flx="user.notifications-tab.notifications-sections" />
</SettingsTabContent>
</SettingsTabContainer>
);
export const NotificationsInlineContent: React.FC = () => (
<div
className={inlineStyles.container}
data-flx="user.notifications-tab.inline.notifications-inline-content.container"
>
<NotificationsSections inline={true} data-flx="user.notifications-tab.inline.notifications-sections" />
</div>
);
export default NotificationsTab;
@@ -6,11 +6,11 @@ import {
AccessibilityScreenReaderTabContent,
AccessibilityTtsTabContent,
} from '@app/features/user/components/modals/tabs/AccessibilityTab';
import styles from '@app/features/user/components/modals/tabs/accessibility_tab/AccessibilityTabInline.module.css';
import {AnimationTabContent} from '@app/features/user/components/modals/tabs/accessibility_tab/AnimationTab';
import {KeyboardTabContent} from '@app/features/user/components/modals/tabs/accessibility_tab/KeyboardTab';
import {MotionTabContent} from '@app/features/user/components/modals/tabs/accessibility_tab/MotionTab';
import {VisualTabContent} from '@app/features/user/components/modals/tabs/accessibility_tab/VisualTab';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -3,7 +3,7 @@
import {StreamerModeGate} from '@app/features/streamer_mode/components/StreamerModeGate';
import StreamerMode from '@app/features/streamer_mode/state/StreamerMode';
import {AccountSecuritySections} from '@app/features/user/components/modals/tabs/account_security_tab/AccountSecuritySections';
import styles from '@app/features/user/components/modals/tabs/account_security_tab/AccountSecurityTabInline.module.css';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {
getAccountSectionForLegacySection,
getAccountSectionForNestedTab,
@@ -8,17 +8,13 @@ import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import {Switch} from '@app/features/ui/components/form/FormSwitch';
import MobileLayout from '@app/features/ui/state/MobileLayout';
import {
getCachedDesktopWindowBehaviorSettings,
getDesktopWindowBehaviorSettings,
relaunchDesktopApp,
setDesktopWindowBehaviorSettings,
} from '@app/features/ui/utils/DesktopWindowBehaviorUtils';
import {relaunchDesktopApp} from '@app/features/ui/utils/DesktopWindowBehaviorUtils';
import {useDesktopWindowBehaviorSettings} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedDesktopControls';
import type {DesktopWindowBehaviorSettings} from '@app/types/electron.d';
import {msg, ph} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
import {useCallback, useLayoutEffect, useState} from 'react';
import {useCallback} from 'react';
const ENABLE_TEXT_SELECTION_DESCRIPTOR = msg({
message: 'Enable text selection',
@@ -61,45 +57,6 @@ function hasDesktopMiddleClickAutoscrollPendingRestart(settings: DesktopWindowBe
return settings !== null && settings.middleClickAutoscroll !== settings.activeMiddleClickAutoscroll;
}
function useDesktopWindowBehaviorSettings() {
const cachedDesktopWindowBehavior = getCachedDesktopWindowBehaviorSettings();
const [desktopWindowBehavior, setDesktopWindowBehavior] = useState<DesktopWindowBehaviorSettings | null>(
cachedDesktopWindowBehavior,
);
const [desktopWindowBehaviorBusy, setDesktopWindowBehaviorBusy] = useState(cachedDesktopWindowBehavior === null);
useLayoutEffect(() => {
let mounted = true;
if (getCachedDesktopWindowBehaviorSettings() !== null) {
setDesktopWindowBehaviorBusy(false);
return () => {
mounted = false;
};
}
const initDesktopWindowBehavior = async () => {
const settings = await getDesktopWindowBehaviorSettings();
if (!mounted) return;
if (settings !== null) {
setDesktopWindowBehavior(settings);
}
setDesktopWindowBehaviorBusy(false);
};
void initDesktopWindowBehavior();
return () => {
mounted = false;
};
}, []);
const updateDesktopWindowBehavior = useCallback(async (settings: Partial<DesktopWindowBehaviorSettings>) => {
setDesktopWindowBehaviorBusy(true);
const nextSettings = await setDesktopWindowBehaviorSettings(settings);
if (nextSettings !== null) {
setDesktopWindowBehavior(nextSettings);
}
setDesktopWindowBehaviorBusy(false);
return nextSettings;
}, []);
return {desktopWindowBehavior, desktopWindowBehaviorBusy, updateDesktopWindowBehavior};
}
function useChromiumScrollingRestartModal() {
const {i18n} = useLingui();
return useCallback(() => {
@@ -76,7 +76,7 @@ const LATER_DESCRIPTOR = msg({
comment: 'Short confirmation button label in advanced settings.',
});
function useDesktopWindowBehaviorSettings() {
export function useDesktopWindowBehaviorSettings() {
const cachedDesktopWindowBehavior = getCachedDesktopWindowBehaviorSettings();
const [desktopWindowBehavior, setDesktopWindowBehavior] = useState<DesktopWindowBehaviorSettings | null>(
cachedDesktopWindowBehavior,
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -7,13 +7,13 @@ import {
HdrTabContent,
shouldShowHdrSettings,
} from '@app/features/user/components/modals/tabs/appearance_tab/AppearanceTabHdrTab';
import styles from '@app/features/user/components/modals/tabs/appearance_tab/AppearanceTabInline.module.css';
import {ChannelListTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/ChannelListTab';
import {InterfaceTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/InterfaceTab';
import {MessagesTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/MessagesTab';
import {AppZoomLevelTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/ScalingTab';
import {StreamerModeTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/StreamerModeTab';
import {ThemeTabContent} from '@app/features/user/components/modals/tabs/appearance_tab/theme/ThemeTabContent';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {shouldShowAppZoomLevel} from '@app/features/user/components/settings_utils/AppZoomLevelUtils';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
@@ -12,7 +12,7 @@ import Channels from '@app/features/channel/state/Channels';
import {getCachedNumberFormat} from '@app/features/i18n/utils/IntlCache';
import {Message as MessageModel} from '@app/features/messaging/models/MessagingMessage';
import {isNewMessageGroup} from '@app/features/messaging/utils/MessageGroupingUtils';
import type {ComboboxOption} from '@app/features/ui/components/form/FormCombobox';
import {type ComboboxOption, resolveNearestNumericOption} from '@app/features/ui/components/form/FormCombobox';
import {Switch} from '@app/features/ui/components/form/FormSwitch';
import {canResetSliderValue, SliderResetIconButton} from '@app/features/ui/components/slider/SliderResetIconButton';
import type {RadioOption} from '@app/features/ui/radio_group/RadioGroup';
@@ -104,18 +104,6 @@ const getNearestMessageGroupSpacing = (value: number): number => {
return nearest;
};
const resolveMessageGroupSpacingInput = (
inputValue: string,
options: ReadonlyArray<ComboboxOption<number>>,
): number | undefined => {
const numericMatch = inputValue.trim().match(/([0-9]+(?:\.[0-9]+)?)/);
if (!numericMatch) return undefined;
const parsedValue = Number(numericMatch[1]);
if (!Number.isFinite(parsedValue)) return undefined;
return options.reduce((nearest, option) =>
Math.abs(option.value - parsedValue) < Math.abs(nearest.value - parsedValue) ? option : nearest,
).value;
};
const MessagesPreview: React.FC = observer(() => {
const {i18n} = useLingui();
const {messageDisplayCompact} = UserSettings;
@@ -288,7 +276,7 @@ export const MessagesTabContent: React.FC = observer(() => {
value={selectedMessageGroupSpacing}
options={messageGroupSpacingOptions}
onChange={(value) => AccessibilityCommands.update(getMessageGroupSpacingPatch(messageDisplayCompact, value))}
autoSelectValueFromInput={resolveMessageGroupSpacingInput}
autoSelectValueFromInput={resolveNearestNumericOption}
controlWidth="small"
menuMinWidth={128}
aria-label={i18n._(SPACE_BETWEEN_MESSAGE_GROUPS_DESCRIPTOR)}
@@ -8,7 +8,7 @@ import Accessibility, {
} from '@app/features/accessibility/state/Accessibility';
import {APP_ZOOM_LEVEL_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import {getCachedNumberFormat} from '@app/features/i18n/utils/IntlCache';
import type {ComboboxOption} from '@app/features/ui/components/form/FormCombobox';
import {type ComboboxOption, resolveNearestNumericOption} from '@app/features/ui/components/form/FormCombobox';
import {Slider} from '@app/features/ui/components/Slider';
import {canResetSliderValue, SliderResetIconButton} from '@app/features/ui/components/slider/SliderResetIconButton';
import {formatRoundedPercentage, roundPercentage} from '@app/features/ui/utils/PercentageFormatting';
@@ -52,19 +52,6 @@ const getNearestFontSize = (value: number): number => {
return nearest;
};
const resolveFontSizeInput = (
inputValue: string,
options: ReadonlyArray<ComboboxOption<number>>,
): number | undefined => {
const numericMatch = inputValue.trim().match(/([0-9]+(?:\.[0-9]+)?)/);
if (!numericMatch) return undefined;
const parsedValue = Number(numericMatch[1]);
if (!Number.isFinite(parsedValue)) return undefined;
return options.reduce((nearest, option) =>
Math.abs(option.value - parsedValue) < Math.abs(nearest.value - parsedValue) ? option : nearest,
).value;
};
function canResetFontSize(): boolean {
return canResetSliderValue(Accessibility.fontSize, DEFAULT_FONT_SIZE);
}
@@ -104,7 +91,7 @@ export const FontSizeTabContent: React.FC = observer(() => {
value={selectedFontSize}
options={options}
onChange={(value) => AccessibilityCommands.update({fontSize: value})}
autoSelectValueFromInput={resolveFontSizeInput}
autoSelectValueFromInput={resolveNearestNumericOption}
controlWidth="small"
menuMinWidth={128}
aria-label={i18n._(CHAT_FONT_SIZE_DESCRIPTOR)}
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {SettingsSection} from '@app/features/app/components/dialogs/shared/SettingsSection';
import styles from '@app/features/user/components/modals/tabs/chat_settings_tab/ChatSettingsTabInline.module.css';
import {InputTabContent} from '@app/features/user/components/modals/tabs/chat_settings_tab/ChatSettingsTabInputTab';
import {DisplayTabContent} from '@app/features/user/components/modals/tabs/chat_settings_tab/DisplayTab';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -1,100 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {SettingsSection} from '@app/features/app/components/dialogs/shared/SettingsSection';
import {GENERAL_DESCRIPTOR, SOUNDS_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import Notification from '@app/features/ui/state/Notification';
import Sound from '@app/features/ui/state/Sound';
import {MentionPreferenceTabContent} from '@app/features/user/components/modals/tabs/notifications_tab/MentionPreferenceTab';
import {Notifications} from '@app/features/user/components/modals/tabs/notifications_tab/Notifications';
import styles from '@app/features/user/components/modals/tabs/notifications_tab/NotificationsTabInline.module.css';
import {Sounds} from '@app/features/user/components/modals/tabs/notifications_tab/NotificationsTabSounds';
import {TextToSpeech} from '@app/features/user/components/modals/tabs/notifications_tab/TextToSpeech';
import {useSoundSettings} from '@app/features/user/components/modals/tabs/notifications_tab/useSoundSettings';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite';
import type React from 'react';
const CONTROL_SPEECH_COMMANDS_AND_NARRATION_FOR_INCOMING_CONTENT_DESCRIPTOR = msg({
message: 'Control speech commands and narration for incoming content.',
comment: 'Description text in the inline.',
});
const MENTION_PREFERENCE_DESCRIPTOR = msg({
message: 'Mention preference',
comment: 'Short label in the inline. Keep it concise.',
});
const TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR = msg({
message: 'Text-to-speech notifications',
comment: 'Short label in the inline. Keep it concise.',
});
export const NotificationsInlineContent: React.FC = observer(() => {
const {i18n} = useLingui();
const browserNotificationsEnabled = Notification.browserNotificationsEnabled;
const unreadMessageBadgeEnabled = Notification.unreadMessageBadgeEnabled;
const soundSettings = Sound.settings;
const {
soundTypeLabels,
customSounds,
handleToggleAllSounds,
handleToggleSound,
handlePreviewSound,
handleUploadClick,
handleCustomSoundDelete,
handleMasterVolumeChange,
handleSoundOverrideChange,
handleSoundOverrideReset,
handleAllOverridesReset,
} = useSoundSettings();
return (
<div className={styles.container} data-flx="user.notifications-tab.inline.notifications-inline-content.container">
<SettingsSection
id="notifications"
title={i18n._(GENERAL_DESCRIPTOR)}
data-flx="user.notifications-tab.inline.notifications-inline-content.notifications"
>
<Notifications
browserNotificationsEnabled={browserNotificationsEnabled}
unreadMessageBadgeEnabled={unreadMessageBadgeEnabled}
data-flx="user.notifications-tab.inline.notifications-inline-content.notifications--2"
/>
</SettingsSection>
<SettingsSection
id="mention-preference"
title={i18n._(MENTION_PREFERENCE_DESCRIPTOR)}
data-flx="user.notifications-tab.inline.notifications-inline-content.mention-preference"
>
<MentionPreferenceTabContent data-flx="user.notifications-tab.inline.notifications-inline-content.mention-preference-tab-content" />
</SettingsSection>
<SettingsSection
id="sounds"
title={i18n._(SOUNDS_DESCRIPTOR)}
data-flx="user.notifications-tab.inline.notifications-inline-content.sounds"
>
<Sounds
soundSettings={soundSettings}
soundTypeLabels={soundTypeLabels}
customSounds={customSounds}
isSoundEnabled={Sound.isSoundTypeEnabled}
onToggleAllSounds={handleToggleAllSounds}
onToggleSound={handleToggleSound}
onPreviewSound={handlePreviewSound}
onUploadClick={handleUploadClick}
onCustomSoundDelete={handleCustomSoundDelete}
onMasterVolumeChange={handleMasterVolumeChange}
onSoundOverrideChange={handleSoundOverrideChange}
onSoundOverrideReset={handleSoundOverrideReset}
onAllOverridesReset={handleAllOverridesReset}
data-flx="user.notifications-tab.inline.notifications-inline-content.sounds--2"
/>
</SettingsSection>
<SettingsSection
id="text-to-speech"
title={i18n._(TEXT_TO_SPEECH_NOTIFICATIONS_DESCRIPTOR)}
description={i18n._(CONTROL_SPEECH_COMMANDS_AND_NARRATION_FOR_INCOMING_CONTENT_DESCRIPTOR)}
data-flx="user.notifications-tab.inline.notifications-inline-content.text-to-speech"
>
<TextToSpeech data-flx="user.notifications-tab.inline.notifications-inline-content.text-to-speech--2" />
</SettingsSection>
</div>
);
});
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -7,9 +7,9 @@ import {CommunicationTabContent as CommunicationTab} from '@app/features/user/co
import {ConnectionsTabContent as ConnectionsTab} from '@app/features/user/components/modals/tabs/privacy_safety_tab/ConnectionsTab';
import {DataDeletionTabContent as DataDeletionTab} from '@app/features/user/components/modals/tabs/privacy_safety_tab/DataDeletionTab';
import {DataExportTabContent as DataExportTab} from '@app/features/user/components/modals/tabs/privacy_safety_tab/DataExportTab';
import styles from '@app/features/user/components/modals/tabs/privacy_safety_tab/PrivacySafetyTabInline.module.css';
import {ProfilePrivacyTabContent as ProfilePrivacyTab} from '@app/features/user/components/modals/tabs/privacy_safety_tab/ProfilePrivacyTab';
import {SensitiveContentTabContent as SensitiveContentTab} from '@app/features/user/components/modals/tabs/privacy_safety_tab/SensitiveContentTab';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {useUserSettingsMutationController} from '@app/features/user/hooks/useUserSettingsMutationController';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
@@ -1,6 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.container {
display: flex;
flex-direction: column;
}
@@ -3,9 +3,9 @@
import {SettingsSection} from '@app/features/app/components/dialogs/shared/SettingsSection';
import {AUDIO_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import MediaPermission from '@app/features/permissions/system/state/MediaPermission';
import styles from '@app/features/user/components/modals/tabs/TabInline.module.css';
import {VideoTab} from '@app/features/user/components/modals/tabs/UserVideoTab';
import {VoiceTab} from '@app/features/user/components/modals/tabs/UserVoiceTab';
import styles from '@app/features/user/components/modals/tabs/voice_video_tab/VoiceVideoTabInline.module.css';
import Users from '@app/features/user/state/Users';
import VoiceSettings from '@app/features/voice/state/VoiceSettings';
import {MediaDeviceRefreshType, refreshMediaDeviceLists} from '@app/features/voice/utils/MediaDeviceRefresh';