forked from OctoWoW/OctoLauncher
Fixed tweaks and mods, added localization, added antivirus walkthrough
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useState, type ReactElement } from 'react';
|
||||
import cls from 'classnames';
|
||||
import log from 'electron-log/renderer';
|
||||
|
||||
import { type UpdaterStatus, type ModsStatus } from '~main/types';
|
||||
import { formatFileSize } from '~common/utils';
|
||||
import { api } from '~renderer/utils/api';
|
||||
import { useT } from '~renderer/i18n';
|
||||
|
||||
import Button from './styled/Button';
|
||||
import DialogButton from './styled/DialogButton';
|
||||
@@ -20,40 +22,43 @@ const formatDuration = (seconds: number) => {
|
||||
return minRem ? `${h}h ${minRem}m` : `${h}h`;
|
||||
};
|
||||
|
||||
const formatPercent = (progress: number) => `${(progress * 100).toFixed(1)}%`;
|
||||
|
||||
const ProgressDetails = ({ status }: { status: UpdaterStatus }) => {
|
||||
const { bytesDone, bytesTotal, bytesPerSecond, etaSeconds, progress } = status;
|
||||
const t = useT();
|
||||
const { bytesDone, bytesTotal, bytesPerSecond, etaSeconds, progress } =
|
||||
status;
|
||||
if (bytesTotal === undefined || bytesDone === undefined) return null;
|
||||
|
||||
const pct = progress !== undefined && progress >= 0
|
||||
? `${(progress * 100).toFixed(1)}%`
|
||||
: '—';
|
||||
const pct =
|
||||
progress !== undefined && progress >= 0 ? formatPercent(progress) : '—';
|
||||
|
||||
return (
|
||||
<p className="s1 text-blueGray">
|
||||
<span className="tw-color">{pct}</span>
|
||||
<span> · {formatFileSize(bytesDone)} / {formatFileSize(bytesTotal)}</span>
|
||||
<span>
|
||||
{' '}
|
||||
· {formatFileSize(bytesDone)} / {formatFileSize(bytesTotal)}
|
||||
</span>
|
||||
{bytesPerSecond !== undefined && bytesPerSecond > 0 && (
|
||||
<span> · {formatFileSize(bytesPerSecond)}/s</span>
|
||||
)}
|
||||
<span>
|
||||
{' · '}
|
||||
{etaSeconds !== undefined
|
||||
? `~${formatDuration(etaSeconds)} remaining`
|
||||
: 'calculating…'}
|
||||
? `~${formatDuration(etaSeconds)} ${t('launch.remaining')}`
|
||||
: t('launch.calculating')}
|
||||
</span>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchPanel = () => {
|
||||
const t = useT();
|
||||
const [status, setStatus] = useState<UpdaterStatus>({ state: 'verifying' });
|
||||
api.updater.observe.useSubscription(undefined, {
|
||||
onData: data => {
|
||||
console.log({ data });
|
||||
setStatus(data);
|
||||
},
|
||||
onError: err => console.log({ err }),
|
||||
onStarted: () => console.log('Started')
|
||||
onData: setStatus,
|
||||
onError: err => log.error('Updater subscription error:', err)
|
||||
});
|
||||
|
||||
const { data: pref } = api.preferences.get.useQuery();
|
||||
@@ -72,23 +77,25 @@ const LaunchPanel = () => {
|
||||
UpdaterStatus['state'],
|
||||
{ button: ReactElement; helperText?: ReactElement }
|
||||
> = {
|
||||
verifying: { button: <Button disabled>Verifying</Button> },
|
||||
verifying: { button: <Button disabled>{t('launch.verifying')}</Button> },
|
||||
serverUnreachable: {
|
||||
button: pref?.version ? (
|
||||
<Button onClick={() => start.mutateAsync()}>Play</Button>
|
||||
<Button onClick={() => start.mutateAsync()}>{t('launch.play')}</Button>
|
||||
) : (
|
||||
<Button onClick={() => verify.mutateAsync()}>Retry</Button>
|
||||
<Button onClick={() => verify.mutateAsync()}>
|
||||
{t('launch.retry')}
|
||||
</Button>
|
||||
),
|
||||
helperText: (
|
||||
<div className="-mb-2">
|
||||
<p>
|
||||
<span className="text-orange">Error: </span> Failed to reach update
|
||||
server
|
||||
<span className="text-orange">{t('launch.errorLabel')}</span>{' '}
|
||||
{t('launch.serverFail')}
|
||||
</p>
|
||||
<p className="s1 text-blueGray">
|
||||
{pref?.version
|
||||
? `You can launch local version ${pref?.version}`
|
||||
: 'Please try again later'}
|
||||
? t('launch.localVersion', { version: pref.version })
|
||||
: t('launch.tryLater')}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
@@ -101,39 +108,44 @@ const LaunchPanel = () => {
|
||||
>
|
||||
{open => (
|
||||
<Button primary onClick={open}>
|
||||
Install
|
||||
{t('launch.install')}
|
||||
</Button>
|
||||
)}
|
||||
</DialogButton>
|
||||
)
|
||||
},
|
||||
updateAvailable: {
|
||||
button: <Button onClick={() => update.mutateAsync()}>Update</Button>,
|
||||
button: (
|
||||
<Button onClick={() => update.mutateAsync()}>
|
||||
{t('launch.update')}
|
||||
</Button>
|
||||
),
|
||||
helperText: (
|
||||
<div className="-mb-2 flex flex-col gap-1">
|
||||
<p>Update available!</p>
|
||||
<p>{t('launch.updateAvailable')}</p>
|
||||
<p className="s1 text-blueGray">
|
||||
{status.progress !== undefined &&
|
||||
status.bytesDone !== undefined &&
|
||||
status.bytesTotal !== undefined && (
|
||||
<>
|
||||
<span className="tw-color">
|
||||
{(status.progress * 100).toFixed(1)}%
|
||||
{formatPercent(status.progress)}
|
||||
</span>
|
||||
<span>
|
||||
{' '}
|
||||
· {formatFileSize(status.bytesDone)} /{' '}
|
||||
{formatFileSize(status.bytesTotal)} on disk ·{' '}
|
||||
{formatFileSize(status.bytesTotal)} {t('launch.onDisk')} ·{' '}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span className="break-all">{status.message}</span> remaining
|
||||
<span className="break-all">{status.message}</span>{' '}
|
||||
{t('launch.remaining')}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
updating: {
|
||||
button: <Button disabled>Updating</Button>,
|
||||
button: <Button disabled>{t('launch.updating')}</Button>,
|
||||
helperText: (
|
||||
<div className="-mb-2 flex flex-col gap-1">
|
||||
{status.message && (
|
||||
@@ -150,35 +162,41 @@ const LaunchPanel = () => {
|
||||
onClick={() => applyMods.mutateAsync()}
|
||||
disabled={applyMods.isLoading || modsStatus?.state === 'busy'}
|
||||
>
|
||||
{modsStatus?.state === 'busy' ? 'Applying' : 'Update'}
|
||||
{modsStatus?.state === 'busy'
|
||||
? t('launch.applying')
|
||||
: t('launch.update')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button primary onClick={() => start.mutateAsync()}>
|
||||
Play
|
||||
{t('launch.play')}
|
||||
</Button>
|
||||
),
|
||||
helperText: (
|
||||
<div className="-mb-2">
|
||||
{modsStatus?.dirty ? (
|
||||
<p>Mods changed — apply before playing</p>
|
||||
<p>{t('launch.modsChanged')}</p>
|
||||
) : (
|
||||
<p>Everything up to date!</p>
|
||||
<p>{t('launch.upToDate')}</p>
|
||||
)}
|
||||
<p className="s1 text-blueGray">Version: {pref?.version}</p>
|
||||
<p className="s1 text-blueGray">
|
||||
{t('launch.version', { version: pref?.version ?? '' })}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
failed: {
|
||||
button: <Button onClick={() => verify.mutateAsync()}>Retry</Button>,
|
||||
button: (
|
||||
<Button onClick={() => verify.mutateAsync()}>
|
||||
{t('launch.retry')}
|
||||
</Button>
|
||||
),
|
||||
helperText: (
|
||||
<div className="-mb-2">
|
||||
<p>
|
||||
<span className="text-orange">Error: </span>
|
||||
<span className="text-orange">{t('launch.errorLabel')}</span>{' '}
|
||||
{status.message}
|
||||
</p>
|
||||
<p className="s1 text-blueGray">
|
||||
Verify your game data by clicking Retry.
|
||||
</p>
|
||||
<p className="s1 text-blueGray">{t('launch.verifyHint')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -191,6 +209,9 @@ const LaunchPanel = () => {
|
||||
(status.message && (
|
||||
<p className="s1 -mb-2 text-blueGray">{status.message}</p>
|
||||
))}
|
||||
{start.data && !start.data.ok && start.data.error && (
|
||||
<p className="s1 -mb-2 text-orange">{start.data.error}</p>
|
||||
)}
|
||||
<div className="tw-loading-wrapper">
|
||||
{status.progress !== undefined && (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user