// 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"
/>
}
/>
);
}