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

@@ -19,7 +19,7 @@ import KeybindsTab from '@app/features/user/components/modals/tabs/KeybindsTab';
import LanguageTab from '@app/features/user/components/modals/tabs/LanguageTab';
import LinkedAccountsTab from '@app/features/user/components/modals/tabs/LinkedAccountsTab';
import MyProfileTab from '@app/features/user/components/modals/tabs/MyProfileTab';
import {NotificationsInlineContent} from '@app/features/user/components/modals/tabs/notifications_tab/NotificationsTabInline';
import {NotificationsInlineContent} from '@app/features/user/components/modals/tabs/NotificationsTab';
import {PlutoniumInlineTab} from '@app/features/user/components/modals/tabs/PlutoniumTab';
import {PrivacyDashboardContent} from '@app/features/user/components/modals/tabs/privacy_safety_tab/PrivacySafetyTabInline';
import {VoiceVideoInlineContent} from '@app/features/user/components/modals/tabs/voice_video_tab/VoiceVideoTabInline';
@@ -6,6 +6,7 @@ import styles from '@app/features/app/components/dialogs/components/FeatureCompa
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {Limits} from '@app/features/app/utils/UserLimits';
import {COMMUNITIES_DESCRIPTOR} from '@app/features/i18n/utils/CommonMessageDescriptors';
import {formatNumber} from '@app/features/i18n/utils/IntlCache';
import {formatFileSize} from '@app/features/messaging/utils/FileUtils';
import {getPremiumProductName} from '@app/features/premium/utils/PremiumUtils';
import {
@@ -17,7 +18,6 @@ import {
} from '@fluxer/constants/src/LimitTierPerks';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {formatNumber} from '@pkgs/number_utils/src/NumberFormatting';
import {observer} from 'mobx-react-lite';
import {useMemo} from 'react';
@@ -8,13 +8,14 @@ import {
MobileSettingsDangerItem,
MobileSettingsList,
} from '@app/features/app/components/dialogs/shared/MobileSettingsComponents';
import {usePreservedScrollerPosition} from '@app/features/app/components/dialogs/shared/UsePreservedScrollerPosition';
import Authentication from '@app/features/auth/state/Authentication';
import {GuildDeleteModal} from '@app/features/guild/components/modals/GuildDeleteModal';
import type {Guild} from '@app/features/guild/models/Guild';
import {isStockCommunityGuild} from '@app/features/guild/utils/GuildCommunityUtils';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import {Scroller, type ScrollerHandle} from '@app/features/ui/components/Scroller';
import {Scroller} from '@app/features/ui/components/Scroller';
import userSettingsStyles from '@app/features/user/components/modals/UserSettingsModal.module.css';
import type {
GuildSettingsTab,
@@ -30,7 +31,7 @@ import {TrashIcon} from '@phosphor-icons/react';
import {AnimatePresence, motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {type UIEvent, useCallback, useEffect, useRef} from 'react';
import {useCallback} from 'react';
const DELETE_COMMUNITY_DESCRIPTOR = msg({
message: 'Delete community',
@@ -104,19 +105,7 @@ export const MobileGuildSettingsView: React.FC<MobileGuildSettingsViewProps> = o
);
const showMobileList = mobileNav.isRootView;
const showMobileContent = !mobileNav.isRootView;
const listScrollPositionRef = useRef(0);
const listScrollerRef = useRef<ScrollerHandle | null>(null);
const handleListScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
listScrollPositionRef.current = event.currentTarget.scrollTop;
}, []);
useEffect(() => {
if (!showMobileList) return;
const scroller = listScrollerRef.current;
if (!scroller) return;
const target = listScrollPositionRef.current;
if (target === 0) return;
scroller.scrollTo({to: target, animate: false});
}, [showMobileList]);
const {scrollerRef: listScrollerRef, handleScroll: handleListScroll} = usePreservedScrollerPosition(showMobileList);
const dangerAction =
guild.isOwner(Authentication.currentUserId) && !isStockCommunityGuild(guild.id) ? (
<MobileSettingsDangerItem
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import styles from '@app/features/app/components/dialogs/components/PricingCard.module.css';
import {formatNumber} from '@app/features/i18n/utils/IntlCache';
import {Button} from '@app/features/ui/button/Button';
import * as LocaleUtils from '@app/features/user/utils/LocaleUtils';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {formatNumber} from '@pkgs/number_utils/src/NumberFormatting';
import {clsx} from 'clsx';
import {observer} from 'mobx-react-lite';
import {useCallback} from 'react';
@@ -1,11 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import Accessibility from '@app/features/accessibility/state/Accessibility';
import guildStyles from '@app/features/app/components/layout/GuildsLayout.module.css';
import styles from '@app/features/app/components/layout/sidebar_nav/AddGuildButton.module.css';
import {useContextMenuHoverState} from '@app/features/app/hooks/useContextMenuHoverState';
import {useHover} from '@app/features/app/hooks/useHover';
import {useMergeRefs} from '@app/features/app/hooks/useMergeRefs';
import {GuildListActionButton} from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton';
import styles from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton.module.css';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import {AddGuildModal, type AddGuildModalView} from '@app/features/guild/components/modals/AddGuildModal';
import {
@@ -17,17 +13,12 @@ import {MenuItem} from '@app/features/ui/action_menu/MenuItem';
import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
import {modal} from '@app/features/ui/commands/ModalCommands';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {TooltipWithKeybind} from '@app/features/ui/keybind_hint/KeybindHint';
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {HouseIcon, LinkIcon, PlusIcon} from '@phosphor-icons/react';
import {clsx} from 'clsx';
import {motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useRef} from 'react';
const CREATE_OR_JOIN_A_COMMUNITY_DESCRIPTOR = msg({
message: 'Create or join a community',
@@ -35,12 +26,6 @@ const CREATE_OR_JOIN_A_COMMUNITY_DESCRIPTOR = msg({
});
export const AddGuildButton = observer(() => {
const {i18n} = useLingui();
const [hoverRef, isHovering] = useHover();
const buttonRef = useRef<HTMLButtonElement | null>(null);
const iconRef = useRef<HTMLDivElement | null>(null);
const itemRef = useRef<HTMLElement | null>(null);
const contextMenuOpen = useContextMenuHoverState(itemRef);
const mergedButtonRef = useMergeRefs([hoverRef, buttonRef, itemRef]);
const buttonLabel = i18n._(CREATE_OR_JOIN_A_COMMUNITY_DESCRIPTOR);
const handleAddGuild = (view?: AddGuildModalView) => {
ModalCommands.push(
@@ -91,61 +76,27 @@ export const AddGuildButton = observer(() => {
</MenuGroup>
));
};
const shouldShowHoverState = isHovering || contextMenuOpen;
if (RuntimeConfig.singleCommunityEnabled) {
return null;
}
return (
<div
className={clsx(guildStyles.createGuildButton, contextMenuOpen && guildStyles.contextMenuHover)}
data-flx="app.sidebar-nav.add-guild-button.div"
>
<Tooltip
position="right"
size="large"
text={() => (
<TooltipWithKeybind
label={buttonLabel}
action="nav_add_guild"
data-flx="app.sidebar-nav.add-guild-button.tooltip-with-keybind"
/>
)}
data-flx="app.sidebar-nav.add-guild-button.tooltip"
>
<FocusRing
offset={-2}
focusTarget={buttonRef}
ringTarget={iconRef}
data-flx="app.sidebar-nav.add-guild-button.focus-ring"
>
<button
type="button"
aria-label={buttonLabel}
aria-haspopup="dialog"
data-guild-list-focus-item="true"
onClick={() => handleAddGuild()}
onContextMenu={handleContextMenu}
className={styles.button}
ref={mergedButtonRef}
data-flx="app.sidebar-nav.add-guild-button.button.add-guild"
>
<motion.div
ref={iconRef}
className={guildStyles.createGuildButtonIcon}
animate={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
initial={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.07, ease: 'easeOut'}}
data-flx="app.sidebar-nav.add-guild-button.div--2"
>
<PlusIcon
weight="bold"
className={styles.iconText}
data-flx="app.sidebar-nav.add-guild-button.icon-text"
/>
</motion.div>
</button>
</FocusRing>
</Tooltip>
</div>
<GuildListActionButton
label={buttonLabel}
tooltip={() => (
<TooltipWithKeybind
label={buttonLabel}
action="nav_add_guild"
data-flx="app.sidebar-nav.add-guild-button.tooltip-with-keybind"
/>
)}
icon={
<PlusIcon weight="bold" className={styles.iconText} data-flx="app.sidebar-nav.add-guild-button.icon-text" />
}
onClick={() => handleAddGuild()}
onContextMenu={handleContextMenu}
hasDialogPopup
buttonDataFlx="app.sidebar-nav.add-guild-button.button.add-guild"
data-flx="app.sidebar-nav.add-guild-button"
/>
);
});
@@ -1,22 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.button {
position: relative;
display: flex;
width: 100%;
justify-content: center;
border: none;
background-color: transparent;
padding: 0;
}
.iconText {
height: 1.25rem;
width: 1.25rem;
color: var(--text-primary);
}
.menuIcon {
height: 1rem;
width: 1rem;
}
@@ -1,28 +1,19 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import Accessibility from '@app/features/accessibility/state/Accessibility';
import guildStyles from '@app/features/app/components/layout/GuildsLayout.module.css';
import styles from '@app/features/app/components/layout/sidebar_nav/DownloadButton.module.css';
import {GuildListActionButton} from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton';
import styles from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton.module.css';
import {DESKTOP_DOWNLOAD_URL} from '@app/features/app/config/I18nDisplayConstants';
import {useContextMenuHoverState} from '@app/features/app/hooks/useContextMenuHoverState';
import {useHover} from '@app/features/app/hooks/useHover';
import {useMergeRefs} from '@app/features/app/hooks/useMergeRefs';
import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import HiddenGuildListButtons from '@app/features/guild/state/HiddenGuildListButtons';
import {openExternalUrlWithWarning} from '@app/features/messaging/utils/ExternalLinkUtils';
import {MenuGroup} from '@app/features/ui/action_menu/MenuGroup';
import {MenuItem} from '@app/features/ui/action_menu/MenuItem';
import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {DownloadSimpleIcon, EyeSlashIcon} from '@phosphor-icons/react';
import {clsx} from 'clsx';
import {motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useRef} from 'react';
const DOWNLOAD_DESCRIPTOR = msg({
message: 'Download {productName}',
@@ -30,12 +21,6 @@ const DOWNLOAD_DESCRIPTOR = msg({
});
export const DownloadButton = observer(() => {
const {i18n} = useLingui();
const [hoverRef, isHovering] = useHover();
const buttonRef = useRef<HTMLButtonElement | null>(null);
const iconRef = useRef<HTMLDivElement | null>(null);
const itemRef = useRef<HTMLElement | null>(null);
const contextMenuOpen = useContextMenuHoverState(itemRef);
const mergedButtonRef = useMergeRefs([hoverRef, buttonRef, itemRef]);
if (HiddenGuildListButtons.downloadButtonHidden) {
return null;
}
@@ -65,51 +50,21 @@ export const DownloadButton = observer(() => {
</MenuGroup>
));
};
const shouldShowHoverState = isHovering || contextMenuOpen;
return (
<div
className={clsx(guildStyles.createGuildButton, contextMenuOpen && guildStyles.contextMenuHover)}
data-flx="app.sidebar-nav.download-button.div"
>
<Tooltip
position="right"
size="large"
text={() => i18n._(DOWNLOAD_DESCRIPTOR, {productName: RuntimeConfig.productName})}
data-flx="app.sidebar-nav.download-button.tooltip"
>
<FocusRing
offset={-2}
focusTarget={buttonRef}
ringTarget={iconRef}
data-flx="app.sidebar-nav.download-button.focus-ring"
>
<button
type="button"
aria-label={i18n._(DOWNLOAD_DESCRIPTOR, {productName: RuntimeConfig.productName})}
data-guild-list-focus-item="true"
onClick={handleDownload}
onContextMenu={handleContextMenu}
className={styles.button}
ref={mergedButtonRef}
data-flx="app.sidebar-nav.download-button.button.download"
>
<motion.div
ref={iconRef}
className={guildStyles.createGuildButtonIcon}
animate={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
initial={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.07, ease: 'easeOut'}}
data-flx="app.sidebar-nav.download-button.div--2"
>
<DownloadSimpleIcon
weight="bold"
className={styles.iconText}
data-flx="app.sidebar-nav.download-button.icon-text"
/>
</motion.div>
</button>
</FocusRing>
</Tooltip>
</div>
<GuildListActionButton
label={i18n._(DOWNLOAD_DESCRIPTOR, {productName: RuntimeConfig.productName})}
tooltip={() => i18n._(DOWNLOAD_DESCRIPTOR, {productName: RuntimeConfig.productName})}
icon={
<DownloadSimpleIcon
weight="bold"
className={styles.iconText}
data-flx="app.sidebar-nav.download-button.icon-text"
/>
}
onClick={handleDownload}
onContextMenu={handleContextMenu}
buttonDataFlx="app.sidebar-nav.download-button.button.download"
data-flx="app.sidebar-nav.download-button"
/>
);
});
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import Accessibility from '@app/features/accessibility/state/Accessibility';
import guildStyles from '@app/features/app/components/layout/GuildsLayout.module.css';
import styles from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton.module.css';
import {useContextMenuHoverState} from '@app/features/app/hooks/useContextMenuHoverState';
import {useHover} from '@app/features/app/hooks/useHover';
import {useMergeRefs} from '@app/features/app/hooks/useMergeRefs';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
import {clsx} from 'clsx';
import {motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useRef} from 'react';
interface GuildListActionButtonProps {
label: string;
tooltip: () => React.ReactNode;
icon: React.ReactNode;
onClick: () => void;
onContextMenu: (event: React.MouseEvent) => void;
hasDialogPopup?: boolean;
buttonDataFlx: string;
'data-flx': string;
}
export const GuildListActionButton = observer(
({
label,
tooltip,
icon,
onClick,
onContextMenu,
hasDialogPopup = false,
buttonDataFlx,
'data-flx': dataFlx,
}: GuildListActionButtonProps) => {
const [hoverRef, isHovering] = useHover();
const buttonRef = useRef<HTMLButtonElement | null>(null);
const iconRef = useRef<HTMLDivElement | null>(null);
const itemRef = useRef<HTMLElement | null>(null);
const contextMenuOpen = useContextMenuHoverState(itemRef);
const mergedButtonRef = useMergeRefs([hoverRef, buttonRef, itemRef]);
const shouldShowHoverState = isHovering || contextMenuOpen;
return (
<div
className={clsx(guildStyles.createGuildButton, contextMenuOpen && guildStyles.contextMenuHover)}
data-flx={`${dataFlx}.div`}
>
<Tooltip position="right" size="large" text={tooltip} data-flx={`${dataFlx}.tooltip`}>
<FocusRing offset={-2} focusTarget={buttonRef} ringTarget={iconRef} data-flx={`${dataFlx}.focus-ring`}>
<button
type="button"
aria-label={label}
aria-haspopup={hasDialogPopup ? 'dialog' : undefined}
data-guild-list-focus-item="true"
onClick={onClick}
onContextMenu={onContextMenu}
className={styles.button}
ref={mergedButtonRef}
data-flx={buttonDataFlx}
>
<motion.div
ref={iconRef}
className={guildStyles.createGuildButtonIcon}
animate={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
initial={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.07, ease: 'easeOut'}}
data-flx={`${dataFlx}.div--2`}
>
{icon}
</motion.div>
</button>
</FocusRing>
</Tooltip>
</div>
);
},
);
@@ -6,6 +6,7 @@ import type {SidebarVoiceRow} from '@app/features/app/hooks/useSidebarVoiceSumma
import {GuildBadge} from '@app/features/guild/components/GuildBadge';
import type {Guild} from '@app/features/guild/models/Guild';
import type {GuildCounts} from '@app/features/guild/state/GuildCount';
import {formatNumber} from '@app/features/i18n/utils/IntlCache';
import type {KeyCombo} from '@app/features/input/state/InputKeybind';
import {AvatarStack} from '@app/features/ui/avatars/AvatarStack';
import {KeybindHint} from '@app/features/ui/keybind_hint/KeybindHint';
@@ -14,7 +15,6 @@ import {GuildFeatures} from '@fluxer/constants/src/GuildConstants';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {BellSlashIcon, MonitorPlayIcon, SpeakerHighIcon} from '@phosphor-icons/react';
import {formatNumber} from '@pkgs/number_utils/src/NumberFormatting';
import type React from 'react';
const INVITES_PAUSED_RAID_TOOLTIP_DESCRIPTOR = msg({
@@ -1,22 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.button {
position: relative;
display: flex;
width: 100%;
justify-content: center;
border: none;
background-color: transparent;
padding: 0;
}
.iconText {
height: 1.25rem;
width: 1.25rem;
color: var(--text-primary);
}
.menuIcon {
height: 1rem;
width: 1rem;
}
@@ -1,28 +1,19 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {Routes} from '@app/app/Routes';
import Accessibility from '@app/features/accessibility/state/Accessibility';
import guildStyles from '@app/features/app/components/layout/GuildsLayout.module.css';
import styles from '@app/features/app/components/layout/sidebar_nav/HelpButton.module.css';
import {useContextMenuHoverState} from '@app/features/app/hooks/useContextMenuHoverState';
import {useHover} from '@app/features/app/hooks/useHover';
import {useMergeRefs} from '@app/features/app/hooks/useMergeRefs';
import {GuildListActionButton} from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton';
import styles from '@app/features/app/components/layout/sidebar_nav/GuildListActionButton.module.css';
import HiddenGuildListButtons from '@app/features/guild/state/HiddenGuildListButtons';
import {openExternalUrlWithWarning} from '@app/features/messaging/utils/ExternalLinkUtils';
import {MenuGroup} from '@app/features/ui/action_menu/MenuGroup';
import {MenuItem} from '@app/features/ui/action_menu/MenuItem';
import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {TooltipWithKeybind} from '@app/features/ui/keybind_hint/KeybindHint';
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
import {EyeSlashIcon, QuestionMarkIcon} from '@phosphor-icons/react';
import {clsx} from 'clsx';
import {motion} from 'framer-motion';
import {observer} from 'mobx-react-lite';
import type React from 'react';
import {useRef} from 'react';
const HELP_CENTER_DESCRIPTOR = msg({
message: 'Help center',
@@ -30,12 +21,6 @@ const HELP_CENTER_DESCRIPTOR = msg({
});
export const HelpButton = observer(() => {
const {i18n} = useLingui();
const [hoverRef, isHovering] = useHover();
const buttonRef = useRef<HTMLButtonElement | null>(null);
const iconRef = useRef<HTMLDivElement | null>(null);
const itemRef = useRef<HTMLElement | null>(null);
const contextMenuOpen = useContextMenuHoverState(itemRef);
const mergedButtonRef = useMergeRefs([hoverRef, buttonRef, itemRef]);
const helpUrl = Routes.help();
if (HiddenGuildListButtons.helpButtonHidden || !helpUrl) {
return null;
@@ -66,57 +51,23 @@ export const HelpButton = observer(() => {
</MenuGroup>
));
};
const shouldShowHoverState = isHovering || contextMenuOpen;
return (
<div
className={clsx(guildStyles.createGuildButton, contextMenuOpen && guildStyles.contextMenuHover)}
data-flx="app.sidebar-nav.help-button.div"
>
<Tooltip
position="right"
size="large"
text={() => (
<TooltipWithKeybind
label={i18n._(HELP_CENTER_DESCRIPTOR)}
action="misc_help"
data-flx="app.sidebar-nav.help-button.tooltip-with-keybind"
/>
)}
data-flx="app.sidebar-nav.help-button.tooltip"
>
<FocusRing
offset={-2}
focusTarget={buttonRef}
ringTarget={iconRef}
data-flx="app.sidebar-nav.help-button.focus-ring"
>
<button
type="button"
aria-label={i18n._(HELP_CENTER_DESCRIPTOR)}
data-guild-list-focus-item="true"
onClick={handleHelp}
onContextMenu={handleContextMenu}
className={styles.button}
ref={mergedButtonRef}
data-flx="app.sidebar-nav.help-button.button.help"
>
<motion.div
ref={iconRef}
className={guildStyles.createGuildButtonIcon}
animate={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
initial={{borderRadius: shouldShowHoverState ? '30%' : '50%'}}
transition={{duration: Accessibility.useReducedMotion ? 0 : 0.07, ease: 'easeOut'}}
data-flx="app.sidebar-nav.help-button.div--2"
>
<QuestionMarkIcon
weight="bold"
className={styles.iconText}
data-flx="app.sidebar-nav.help-button.icon-text"
/>
</motion.div>
</button>
</FocusRing>
</Tooltip>
</div>
<GuildListActionButton
label={i18n._(HELP_CENTER_DESCRIPTOR)}
tooltip={() => (
<TooltipWithKeybind
label={i18n._(HELP_CENTER_DESCRIPTOR)}
action="misc_help"
data-flx="app.sidebar-nav.help-button.tooltip-with-keybind"
/>
)}
icon={
<QuestionMarkIcon weight="bold" className={styles.iconText} data-flx="app.sidebar-nav.help-button.icon-text" />
}
onClick={handleHelp}
onContextMenu={handleContextMenu}
buttonDataFlx="app.sidebar-nav.help-button.button.help"
data-flx="app.sidebar-nav.help-button"
/>
);
});
@@ -7,7 +7,7 @@ export type KeyboardShortcutEvent = Pick<KeyboardEvent, 'key' | 'ctrlKey' | 'alt
Partial<Pick<KeyboardEvent, 'code'>>;
export type KeyboardShortcutPressEvent = KeyboardShortcutEvent & Pick<KeyboardEvent, 'repeat'>;
const normalizeKeyboardShortcutKey = (key: string): string => {
export const normalizeKeyboardShortcutKey = (key: string): string => {
if (key === ' ') return 'space';
if (key === 'Break') return 'pause';
return key.toLowerCase();
@@ -7,6 +7,7 @@ import {
keyboardEventReleasesComboModifier,
keyboardEventStartsComboPress,
keyboardEventTriggerMatchesCombo,
normalizeKeyboardShortcutKey,
shouldAllowLocalShortcutForChannelTextarea,
} from '@app/features/app/keybindings/KeybindEventUtils';
import {
@@ -105,11 +106,6 @@ import type {I18n} from '@lingui/core';
import CombokeysImport from 'combokeys';
import {autorun, compareStructural, computed, reaction} from 'mobx';
const normalizeKeyboardShortcutKey = (key: string): string => {
if (key === ' ') return 'space';
if (key === 'Break') return 'pause';
return key.toLowerCase();
};
const ROUTE_ALLOWED_ACTIONS = new Set<KeybindCommand>(['system_open_theme_studio_popout']);
const GAMEPAD_POLL_INTERVAL_MS = 50;
const PORTAL_SOURCE_ID_PREFIX = 'portal:';
+34 -2
View File
@@ -4,7 +4,6 @@ import Config from '@app/features/app/config/Config';
import {DESKTOP_DOWNLOAD_URL} from '@app/features/app/config/I18nDisplayConstants';
import {WORKER_NAVIGATION_CACHE_PREFIX} from '@app/features/platform/service_worker/WorkerCacheCleanup';
import {getProtectedCacheStorage} from '@app/features/platform/state/ProtectedWebStorage';
import type {UpdaterContext, UpdaterDownloadOption, UpdaterEvent} from '@app/features/platform/types/Electron';
import {Logger} from '@app/features/platform/utils/AppLogger';
import {getClientInfo} from '@app/features/platform/utils/ClientInfo';
import {flushPendingPersistWrites} from '@app/features/platform/utils/MobXPersistence';
@@ -27,9 +26,42 @@ import {
type UpdaterMachineSnapshot,
} from '@app/features/updater/state/UpdaterStateMachine';
import {buildLinuxManualUpdateOptions} from '@app/features/updater/utils/LinuxManualUpdateOptions';
import type {UpdaterEvent as NativeUpdaterEvent} from '@app/types/electron.d';
import type {UpdaterEvent as NativeUpdaterEvent, UpdaterDownloadOption} from '@app/types/electron.d';
import {makeAutoObservable, runInAction} from 'mobx';
type UpdaterContext = NonNullable<NativeUpdaterEvent['context']>;
type UpdaterEvent =
| {
type: 'checking';
context: UpdaterContext;
}
| {
type: 'available';
context: UpdaterContext;
version: string | null;
downloadUrl?: string;
downloadOptions?: Array<UpdaterDownloadOption>;
}
| {
type: 'not-available';
context: UpdaterContext;
}
| {
type: 'downloaded';
context: UpdaterContext;
}
| {
type: 'error';
context: UpdaterContext;
message: string;
}
| {
type: 'unsupported';
context: UpdaterContext;
reason: 'platform' | 'unpackaged' | 'managed-package';
downloadUrl?: string;
};
const logger = new Logger('Updater');
const CHECK_INTERVAL_MS = 5 * 60 * 1000;
const MIN_CHECK_INTERVAL_MS = 60 * 1000;