mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(desktop): update from the splash only after the user clicks (#3280)
This commit is contained in:
1 parent
f1373a63c3
commit
7937d5f802
90 files changed
+6888
-11255
No files matched your search
@@ -559,29 +559,11 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.updateIconButtonDisabled {
|
||||
composes: updateIconButton;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.updateIcon {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.updateProgress {
|
||||
position: absolute;
|
||||
bottom: -0.4rem;
|
||||
right: -0.6rem;
|
||||
background: var(--background-tertiary);
|
||||
color: var(--channel-header-text-primary);
|
||||
font-size: 0.65rem;
|
||||
padding: 0 0.25rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 0.0625rem solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.updateIconError {
|
||||
color: var(--status-danger);
|
||||
}
|
||||
|
||||
+30
-180
@@ -1,59 +1,21 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import Updater, {
|
||||
DOWNLOADING_UPDATE_DESCRIPTOR as DOWNLOADING_UPDATE_STATUS_DESCRIPTOR,
|
||||
} from '@app/features/app/state/Updater';
|
||||
import Updater from '@app/features/app/state/Updater';
|
||||
import styles from '@app/features/channel/components/ChannelHeader.module.css';
|
||||
import {getCachedNumberFormat} from '@app/features/i18n/utils/IntlCache';
|
||||
import {Platform} from '@app/features/platform/types/Platform';
|
||||
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
|
||||
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
import {useLingui} from '@lingui/react/macro';
|
||||
import {ArrowClockwiseIcon, DownloadSimpleIcon} from '@phosphor-icons/react';
|
||||
import {ArrowClockwiseIcon} from '@phosphor-icons/react';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import {useCallback, useMemo} from 'react';
|
||||
import {useCallback} from 'react';
|
||||
|
||||
const INSTALLING_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Installing desktop update {version}…',
|
||||
const UPDATE_AVAILABLE_DESCRIPTOR = msg({
|
||||
message: 'Update available',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon while a Fluxer Desktop update is being installed. version is the target version. Trailing horizontal ellipsis is intentional.',
|
||||
});
|
||||
const INSTALLING_UPDATE_2_DESCRIPTOR = msg({
|
||||
message: 'Installing desktop update…',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon while a Fluxer Desktop update is being installed when version is not known. Trailing horizontal ellipsis is intentional.',
|
||||
});
|
||||
const DOWNLOADING_UPDATE_AT_S_DESCRIPTOR = msg({
|
||||
message: 'Downloading desktop update: {percent}% ({formatBytes} / {formatBytes2} at {formatBytes3}/s)',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon while downloading an update. Shows percent, bytes downloaded, total bytes, and bytes per second.',
|
||||
});
|
||||
const DOWNLOADING_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Downloading desktop update {version}: {percent}%',
|
||||
comment: 'Tooltip on the channel header updater icon while downloading a versioned update without throughput info.',
|
||||
});
|
||||
const DOWNLOADING_UPDATE_2_DESCRIPTOR = msg({
|
||||
message: 'Downloading desktop update: {percent}%',
|
||||
comment: 'Tooltip on the channel header updater icon while downloading an update with only percent available.',
|
||||
});
|
||||
const CLICK_TO_DOWNLOAD_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Download desktop update {version} ({formatBytes})',
|
||||
comment: 'Tooltip on the channel header updater icon prompting download of a versioned update with known size.',
|
||||
});
|
||||
const CLICK_TO_DOWNLOAD_UPDATE_2_DESCRIPTOR = msg({
|
||||
message: 'Download desktop update ({formatBytes})',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon prompting download of an update of known size but unknown version.',
|
||||
});
|
||||
const CLICK_TO_DOWNLOAD_UPDATE_3_DESCRIPTOR = msg({
|
||||
message: 'Download desktop update {version}',
|
||||
comment: 'Tooltip on the channel header updater icon prompting download of a versioned update with unknown size.',
|
||||
});
|
||||
const CLICK_TO_DOWNLOAD_UPDATE_4_DESCRIPTOR = msg({
|
||||
message: 'Download desktop update',
|
||||
comment: 'Tooltip on the channel header updater icon prompting download of an update with no version or size info.',
|
||||
'Tooltip and accessible label on the channel header updater icon in the desktop app. Clicking it closes the window and opens the updater, which downloads and installs the update.',
|
||||
});
|
||||
const CHOOSE_LINUX_PACKAGE_DESCRIPTOR = msg({
|
||||
message: 'Desktop update {version} available. Choose a Linux package.',
|
||||
@@ -64,21 +26,6 @@ const CHOOSE_LINUX_PACKAGE_2_DESCRIPTOR = msg({
|
||||
comment:
|
||||
'Tooltip on the updater icon for a Linux desktop update that requires choosing a package format and no version is known.',
|
||||
});
|
||||
const CLICK_TO_RESTART_AND_INSTALL_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Restart {productName} to install desktop update {version}',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon prompting a restart to apply a downloaded versioned update. productName is the app name.',
|
||||
});
|
||||
const CLICK_TO_RESTART_AND_INSTALL_UPDATE_2_DESCRIPTOR = msg({
|
||||
message: 'Restart {productName} to install desktop update',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon prompting a restart to apply a downloaded update of unknown version. productName is the app name.',
|
||||
});
|
||||
const CLICK_TO_RELOAD_FOR_MODULE_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Reload {productName} to use the updated modules',
|
||||
comment:
|
||||
'Tooltip on the channel header updater icon prompting a reload to apply freshly downloaded desktop modules. productName is the app name.',
|
||||
});
|
||||
const CLICK_TO_RELOAD_AND_UPDATE_DESCRIPTOR = msg({
|
||||
message: 'Reload {productName} to use web update {version}',
|
||||
comment:
|
||||
@@ -90,151 +37,54 @@ const CLICK_TO_RELOAD_AND_UPDATE_2_DESCRIPTOR = msg({
|
||||
'Tooltip on the channel header updater icon prompting a web reload to apply an update of unknown version. productName is the app name.',
|
||||
});
|
||||
|
||||
function formatBytes(locale: string, bytes: number): string {
|
||||
const units = ['byte', 'kilobyte', 'megabyte', 'gigabyte'] as const;
|
||||
let value = Number.isFinite(bytes) && bytes > 0 ? bytes : 0;
|
||||
let unitIndex = 0;
|
||||
while (value >= 1024 && unitIndex < units.length - 1) {
|
||||
value /= 1024;
|
||||
unitIndex++;
|
||||
}
|
||||
return getCachedNumberFormat(locale, {
|
||||
style: 'unit',
|
||||
unit: units[unitIndex],
|
||||
unitDisplay: 'narrow',
|
||||
maximumFractionDigits: value >= 100 || unitIndex === 0 ? 0 : 1,
|
||||
}).format(value);
|
||||
}
|
||||
type UpdaterAffordance = 'desktop' | 'manual' | 'web' | null;
|
||||
|
||||
function resolveUpdaterVisibility(): {
|
||||
readonly hasActionableNativeUpdate: boolean;
|
||||
readonly hasActionableModuleUpdate: boolean;
|
||||
readonly hasActionableWebUpdate: boolean;
|
||||
} {
|
||||
const hasActionableNativeUpdate =
|
||||
Platform.isElectron &&
|
||||
(Updater.nativeUpdateReady ||
|
||||
Updater.nativeAwaitingDownload ||
|
||||
Updater.nativeManualUpdateAvailable ||
|
||||
Updater.nativeDownloadInFlight);
|
||||
const hasActionableModuleUpdate = Platform.isElectron && Updater.moduleUpdateReady && !hasActionableNativeUpdate;
|
||||
const hasActionableWebUpdate =
|
||||
!!Updater.updateInfo.web.available && !hasActionableNativeUpdate && !hasActionableModuleUpdate;
|
||||
return {hasActionableNativeUpdate, hasActionableModuleUpdate, hasActionableWebUpdate};
|
||||
function resolveUpdaterAffordance(): UpdaterAffordance {
|
||||
if (Platform.isElectron && Updater.desktopUpdateAvailable) return 'desktop';
|
||||
if (Platform.isElectron && Updater.nativeManualUpdateAvailable) return 'manual';
|
||||
if (Updater.updateInfo.web.available) return 'web';
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isUpdaterIconVisible(): boolean {
|
||||
const {hasActionableNativeUpdate, hasActionableModuleUpdate, hasActionableWebUpdate} = resolveUpdaterVisibility();
|
||||
return hasActionableNativeUpdate || hasActionableModuleUpdate || hasActionableWebUpdate;
|
||||
return resolveUpdaterAffordance() != null;
|
||||
}
|
||||
|
||||
export const UpdaterIcon = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
const store = Updater;
|
||||
const {hasActionableNativeUpdate, hasActionableModuleUpdate, hasActionableWebUpdate} = resolveUpdaterVisibility();
|
||||
const tooltip = useMemo(() => {
|
||||
const version = store.displayVersion;
|
||||
if (hasActionableModuleUpdate) {
|
||||
return i18n._(CLICK_TO_RELOAD_FOR_MODULE_UPDATE_DESCRIPTOR, {productName: PRODUCT_NAME});
|
||||
}
|
||||
if (Platform.isElectron && store.updateInfo.native.installing) {
|
||||
return version ? i18n._(INSTALLING_UPDATE_DESCRIPTOR, {version}) : i18n._(INSTALLING_UPDATE_2_DESCRIPTOR);
|
||||
}
|
||||
if (Platform.isElectron && store.nativeDownloadInFlight) {
|
||||
if (!store.nativeDownloadProgressSupported) {
|
||||
return i18n._(DOWNLOADING_UPDATE_STATUS_DESCRIPTOR);
|
||||
}
|
||||
const progress = store.downloadProgress;
|
||||
const percent = getCachedNumberFormat(i18n.locale).format(progress ? Math.round(progress.percent) : 0);
|
||||
if (progress && progress.total > 0 && progress.bytesPerSecond > 0) {
|
||||
return i18n._(DOWNLOADING_UPDATE_AT_S_DESCRIPTOR, {
|
||||
percent,
|
||||
formatBytes: formatBytes(i18n.locale, progress.transferred),
|
||||
formatBytes2: formatBytes(i18n.locale, progress.total),
|
||||
formatBytes3: formatBytes(i18n.locale, progress.bytesPerSecond),
|
||||
});
|
||||
}
|
||||
return version
|
||||
? i18n._(DOWNLOADING_UPDATE_DESCRIPTOR, {version, percent})
|
||||
: i18n._(DOWNLOADING_UPDATE_2_DESCRIPTOR, {percent});
|
||||
}
|
||||
if (Platform.isElectron && (store.nativeAwaitingDownload || store.nativeManualUpdateAvailable)) {
|
||||
if (store.nativeManualUpdateAvailable && store.nativeManualDownloadOptions.length > 0) {
|
||||
return version ? i18n._(CHOOSE_LINUX_PACKAGE_DESCRIPTOR, {version}) : i18n._(CHOOSE_LINUX_PACKAGE_2_DESCRIPTOR);
|
||||
}
|
||||
const size = store.updateInfo.native.downloadSize;
|
||||
if (version && size && size > 0) {
|
||||
return i18n._(CLICK_TO_DOWNLOAD_UPDATE_DESCRIPTOR, {version, formatBytes: formatBytes(i18n.locale, size)});
|
||||
}
|
||||
if (size && size > 0) {
|
||||
return i18n._(CLICK_TO_DOWNLOAD_UPDATE_2_DESCRIPTOR, {formatBytes: formatBytes(i18n.locale, size)});
|
||||
}
|
||||
return version
|
||||
? i18n._(CLICK_TO_DOWNLOAD_UPDATE_3_DESCRIPTOR, {version})
|
||||
: i18n._(CLICK_TO_DOWNLOAD_UPDATE_4_DESCRIPTOR);
|
||||
}
|
||||
if (Platform.isElectron && store.nativeUpdateReady) {
|
||||
return version
|
||||
? i18n._(CLICK_TO_RESTART_AND_INSTALL_UPDATE_DESCRIPTOR, {version, productName: PRODUCT_NAME})
|
||||
: i18n._(CLICK_TO_RESTART_AND_INSTALL_UPDATE_2_DESCRIPTOR, {productName: PRODUCT_NAME});
|
||||
}
|
||||
return version
|
||||
? i18n._(CLICK_TO_RELOAD_AND_UPDATE_DESCRIPTOR, {version, productName: PRODUCT_NAME})
|
||||
: i18n._(CLICK_TO_RELOAD_AND_UPDATE_2_DESCRIPTOR, {productName: PRODUCT_NAME});
|
||||
}, [
|
||||
hasActionableModuleUpdate,
|
||||
store.displayVersion,
|
||||
store.downloadProgress,
|
||||
store.nativeAwaitingDownload,
|
||||
store.nativeDownloadInFlight,
|
||||
store.nativeDownloadProgressSupported,
|
||||
store.nativeManualDownloadOptions,
|
||||
store.nativeManualUpdateAvailable,
|
||||
store.nativeUpdateReady,
|
||||
store.updateInfo.native.downloadSize,
|
||||
store.updateInfo.native.installing,
|
||||
i18n.locale,
|
||||
]);
|
||||
const handleClick = useCallback(() => {
|
||||
void store.applyUpdate();
|
||||
}, [store]);
|
||||
if (!hasActionableNativeUpdate && !hasActionableModuleUpdate && !hasActionableWebUpdate) {
|
||||
void Updater.applyUpdate();
|
||||
}, []);
|
||||
const affordance = resolveUpdaterAffordance();
|
||||
if (affordance == null) {
|
||||
return null;
|
||||
}
|
||||
const isInstalling = Platform.isElectron && store.updateInfo.native.installing;
|
||||
const isDownloading = Platform.isElectron && store.nativeDownloadInFlight;
|
||||
const buttonClass = isInstalling ? styles.updateIconButtonDisabled : styles.updateIconButton;
|
||||
const Icon = hasActionableWebUpdate || hasActionableModuleUpdate ? ArrowClockwiseIcon : DownloadSimpleIcon;
|
||||
const percentLabel =
|
||||
isDownloading && store.nativeDownloadProgressSupported
|
||||
? getCachedNumberFormat(i18n.locale, {style: 'percent', maximumFractionDigits: 0}).format(
|
||||
(store.downloadProgress?.percent ?? 0) / 100,
|
||||
)
|
||||
: null;
|
||||
const version = Updater.displayVersion;
|
||||
let tooltip: string;
|
||||
if (affordance === 'manual' && Updater.nativeManualDownloadOptions.length > 0) {
|
||||
tooltip = version ? i18n._(CHOOSE_LINUX_PACKAGE_DESCRIPTOR, {version}) : i18n._(CHOOSE_LINUX_PACKAGE_2_DESCRIPTOR);
|
||||
} else if (affordance !== 'web') {
|
||||
tooltip = i18n._(UPDATE_AVAILABLE_DESCRIPTOR);
|
||||
} else {
|
||||
tooltip = version
|
||||
? i18n._(CLICK_TO_RELOAD_AND_UPDATE_DESCRIPTOR, {version, productName: PRODUCT_NAME})
|
||||
: i18n._(CLICK_TO_RELOAD_AND_UPDATE_2_DESCRIPTOR, {productName: PRODUCT_NAME});
|
||||
}
|
||||
return (
|
||||
<Tooltip text={tooltip} position="bottom" data-flx="channel.channel-header-components.updater-icon.tooltip">
|
||||
<FocusRing offset={-2} data-flx="channel.channel-header-components.updater-icon.focus-ring">
|
||||
<button
|
||||
type="button"
|
||||
className={buttonClass}
|
||||
className={styles.updateIconButton}
|
||||
onClick={handleClick}
|
||||
aria-label={tooltip}
|
||||
aria-disabled={isInstalling || undefined}
|
||||
data-flx="channel.channel-header-components.updater-icon.button.click"
|
||||
>
|
||||
<Icon
|
||||
<ArrowClockwiseIcon
|
||||
weight="bold"
|
||||
className={styles.updateIcon}
|
||||
data-flx="channel.channel-header-components.updater-icon.update-icon"
|
||||
/>
|
||||
{percentLabel != null && (
|
||||
<span
|
||||
className={styles.updateProgress}
|
||||
data-flx="channel.channel-header-components.updater-icon.update-progress"
|
||||
>
|
||||
{percentLabel}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</FocusRing>
|
||||
</Tooltip>
|
||||
|
||||
-31
@@ -73,26 +73,6 @@ const MEDIA_LOADING_STATE_DESCRIPTOR = msg({
|
||||
message: 'Media loading state',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const ANY_UPDATER_READY_DESCRIPTOR = msg({
|
||||
message: 'Any updater ready',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const NATIVE_UPDATER_READY_DESCRIPTOR = msg({
|
||||
message: 'Native updater ready',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const NATIVE_UPDATE_PROGRESS_DESCRIPTOR = msg({
|
||||
message: 'Native update progress',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const WEB_UPDATER_READY_DESCRIPTOR = msg({
|
||||
message: 'Web updater ready',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const UPDATER_STATE_DESCRIPTOR = msg({
|
||||
message: 'Updater state',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
});
|
||||
const SHOW_MYSELF_TYPING_DESCRIPTOR = msg({
|
||||
message: 'Show myself typing',
|
||||
comment: 'Developer tools debug menu label. Internal-only surface for developers; translators may keep this terse.',
|
||||
@@ -297,7 +277,6 @@ const formatDeveloperOptionValue = <K extends keyof DeveloperOptionsState>(
|
||||
case 'mockMatureContentGateReason':
|
||||
case 'mockMatureMediaGateReason':
|
||||
case 'mockTitlebarPlatformOverride':
|
||||
case 'mockUpdaterState':
|
||||
return String(value).replace(/_/g, ' ');
|
||||
case 'premiumSinceOverride':
|
||||
case 'premiumUntilOverride':
|
||||
@@ -342,16 +321,6 @@ export const getDeveloperOptionLabel = (key: keyof DeveloperOptionsState): Messa
|
||||
return EMBED_SKELETONS_DESCRIPTOR;
|
||||
case 'forceMediaLoading':
|
||||
return MEDIA_LOADING_STATE_DESCRIPTOR;
|
||||
case 'forceUpdateReady':
|
||||
return ANY_UPDATER_READY_DESCRIPTOR;
|
||||
case 'forceNativeUpdateReady':
|
||||
return NATIVE_UPDATER_READY_DESCRIPTOR;
|
||||
case 'mockNativeUpdateProgress':
|
||||
return NATIVE_UPDATE_PROGRESS_DESCRIPTOR;
|
||||
case 'forceWebUpdateReady':
|
||||
return WEB_UPDATER_READY_DESCRIPTOR;
|
||||
case 'mockUpdaterState':
|
||||
return UPDATER_STATE_DESCRIPTOR;
|
||||
case 'showMyselfTyping':
|
||||
return SHOW_MYSELF_TYPING_DESCRIPTOR;
|
||||
case 'slowAttachmentUpload':
|
||||
|
||||
-5
@@ -12,11 +12,6 @@ export const DEFAULT_DEVELOPER_OPTIONS = {
|
||||
forceRenderPlaceholders: false,
|
||||
forceEmbedSkeletons: false,
|
||||
forceMediaLoading: false,
|
||||
forceUpdateReady: false,
|
||||
forceNativeUpdateReady: false,
|
||||
mockNativeUpdateProgress: null,
|
||||
forceWebUpdateReady: false,
|
||||
mockUpdaterState: 'none',
|
||||
showMyselfTyping: false,
|
||||
slowAttachmentUpload: false,
|
||||
slowMessageLoad: false,
|
||||
|
||||
Reference in new issue
Block a user