// Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { JSX, ReactNode } from 'react'; import { isNumber } from 'lodash'; import { DialogType } from '../../types/Dialogs.std.ts'; import type { LocalizerType } from '../../types/Util.std.ts'; import { PRODUCTION_DOWNLOAD_URL, BETA_DOWNLOAD_URL, UNSUPPORTED_OS_URL, } from '../../types/support.std.ts'; import type { UpdatesStateType } from '../../state/ducks/updates.preload.ts'; import { isBeta } from '../../util/version.std.ts'; import { missingCaseError } from '../../util/missingCaseError.std.ts'; import { roundFractionForProgressBar } from '../../util/numbers.std.ts'; import { I18n } from '../I18n.dom.tsx'; import { formatFileSize } from '../../util/formatFileSize.std.ts'; import { AxoConfirmDialog } from '../../axo/AxoConfirmDialog.dom.tsx'; import { tw } from '../../axo/tw.dom.tsx'; import { TitlebarDragArea } from '../TitlebarDragArea.dom.tsx'; import { InstallScreenSignalLogo } from './InstallScreenSignalLogo.dom.tsx'; import { ProgressBar } from '../ProgressBar.dom.tsx'; export type PropsType = UpdatesStateType & Readonly<{ i18n: LocalizerType; forceCheck: () => void; startUpdate: () => void; currentVersion: string; OS: string; onClose?: () => void; }>; export function InstallScreenUpdateDialog({ i18n, dialogType, isCheckingForUpdates, downloadSize, downloadedSize, forceCheck, startUpdate, currentVersion, OS, onClose = () => null, }: PropsType): JSX.Element | null { let modal: ReactNode | undefined = undefined; let inlineElement: ReactNode | undefined = undefined; if (dialogType === DialogType.None) { if (isCheckingForUpdates) { inlineElement = (
{i18n('icu:InstallScreenUpdateDialog--checking-for-updates')}
); } else { modal = ( ); } } else if (dialogType === DialogType.UnsupportedOS) { modal = ; } else if (dialogType === DialogType.MASUpdate) { modal = ( ); } else if (dialogType === DialogType.DownloadedUpdate) { modal = ( ); } else if ( dialogType === DialogType.AutoUpdate || // Manual update with an action button dialogType === DialogType.DownloadReady || dialogType === DialogType.FullDownloadReady ) { modal = ( ); } else if (dialogType === DialogType.Downloading) { const fractionComplete = downloadSize ? roundFractionForProgressBar((downloadedSize || 0) / downloadSize) : null; inlineElement = (
{i18n('icu:InstallScreenUpdateDialog--updating-signal')}
{isNumber(fractionComplete) ? (
{i18n('icu:InstallScreenUpdateDialog--download-progress', { currentBytes: formatFileSize(downloadedSize ?? 0), totalBytes: formatFileSize(downloadSize ?? 1), percentage: fractionComplete, })}
) : undefined}
); } else if ( dialogType === DialogType.Cannot_Update || dialogType === DialogType.Cannot_Update_Require_Manual ) { modal = ( ); } else if (dialogType === DialogType.MacOS_Read_Only) { modal = ; } else { throw missingCaseError(dialogType); } return (
{modal} {inlineElement}
); } function CenteredElement({ children }: { children: ReactNode }): JSX.Element { return (
{children}
); } function UpdateRequiredModal(props: { isMas: boolean; i18n: LocalizerType; onClose: () => void; onAction: () => void; }): ReactNode { const { i18n } = props; return ( {props.isMas ? i18n( 'icu:InstallScreenUpdateDialog--update-required__action-update__mas' ) : i18n( 'icu:InstallScreenUpdateDialog--update-required__action-update' )} ); } function UpdateAvailableModal(props: { i18n: LocalizerType; onClose: () => void; onStartUpdate: () => void; downloadSize?: number; downloadReady: boolean; }): ReactNode { const { i18n, onStartUpdate } = props; return ( { event.preventDefault(); onStartUpdate(); }} > {props.downloadReady ? ( ({formatFileSize(props.downloadSize ?? 0)}) ), }} /> ) : ( i18n('icu:autoUpdateRestartButtonLabel') )} ); } function UpdateDownloadedModal(props: { i18n: LocalizerType; onClose: () => void; onStartUpdate: () => void; }): ReactNode { const { i18n, onStartUpdate } = props; return ( { event.preventDefault(); onStartUpdate(); }} > {i18n('icu:autoUpdateRestartButtonLabel')} ); } const learnMoreLink = (parts: Array) => ( {parts} ); function UnsupportedOSModal(props: { i18n: LocalizerType; OS: string; onClose: () => void; }): ReactNode { const { i18n } = props; return ( } /> ); } function CannotUpdateModal(props: { i18n: LocalizerType; onClose: () => void; currentVersion: string; needsManualUpdate: boolean; onStartUpdate: () => void; }): ReactNode { const { i18n, onStartUpdate } = props; const url = isBeta(props.currentVersion) ? BETA_DOWNLOAD_URL : PRODUCTION_DOWNLOAD_URL; return ( {url} ), }} /> } > {!props.needsManualUpdate && ( { event.preventDefault(); onStartUpdate(); }} > {i18n('icu:autoUpdateRetry')} )} ); } function CannotUpdateMacOsReadOnlyModal(props: { i18n: LocalizerType; onClose: () => void; }): ReactNode { const { i18n } = props; return ( Signal.app, folder: /Applications, }} i18n={i18n} id="icu:readOnlyVolume" /> } /> ); }