mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
refactor(app): merge duplicated components and stores (#3383)
This commit is contained in:
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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
|
||||
+4
-15
@@ -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"
|
||||
/>
|
||||
);
|
||||
});
|
||||
File renamed without changes.
@@ -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:';
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user