You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vizality/renderer/src/builtins/updater/components/Settings.jsx

357 lines
12 KiB

import { SwitchItem, TextInput, Category, FormTitle } from '@vizality/components/settings';
import { Confirm, Button, FormNotice } from '@vizality/components';
import { openModal, closeModal } from '@vizality/modal';
import { Repositories } from '@vizality/constants';
import React, { useEffect, memo } from 'react';
import { Messages } from '@vizality/i18n';
import { joinClassNames } from '@vizality/util/dom';
import { getModule } from '@vizality/webpack';
import moment from 'moment';
import Update from './Update';
import Icons from './Icons';
export default memo(({ getSetting, toggleSetting, updateSetting }) => {
useEffect(() => {
const updateToasts = [
'vizality-updater-update-complete',
'vizality-updater-update-available'
];
updateToasts.forEach(t => vizality.api.notifications.closeToast(t));
});
// eslint-disable-next-line consistent-this
const _this = vizality.manager.builtins.get('updater');
// @todo: Make this be in its own store
const awaitingReload = getSetting('awaitingReload', false);
const updating = getSetting('updating', false);
const checking = getSetting('checking', false);
const disabled = getSetting('disabled', false);
const paused = getSetting('paused', false);
const failed = getSetting('failed', false);
const updates = getSetting('updates', []);
const disabledAddons = getSetting('addonsDisabled', []);
const checkingProgress = getSetting('checkingProgress', [ 0, 0 ]);
const last = moment(getSetting('lastCheck', false)).calendar();
let icon, title;
if (disabled) {
icon = <Icons.Update color='#f04747'/>;
title = Messages.VIZALITY_UPDATES_DISABLED;
} else if (paused) {
icon = <Icons.Paused/>;
title = Messages.VIZALITY_UPDATES_PAUSED;
} else if (checking) {
icon = <Icons.Update color='#7289da' animated/>;
title = Messages.VIZALITY_UPDATES_CHECKING;
} else if (updating) {
icon = <Icons.Update color='#7289da' animated/>;
title = Messages.VIZALITY_UPDATES_UPDATING;
} else if (failed) {
icon = <Icons.Error/>;
title = Messages.VIZALITY_UPDATES_FAILED;
} else if (updates.length > 0) {
icon = <Icons.Update/>;
title = Messages.VIZALITY_UPDATES_AVAILABLE;
} else {
icon = <Icons.UpToDate/>;
title = Messages.VIZALITY_UPDATES_UP_TO_DATE;
}
/**
* Gets a valid interval value in minutes based on user's input.
* @param {number} minutes Interval value
* @returns {void}
*/
const _getValidInterval = minutes => {
if (Number(minutes)) {
if (Number(minutes) >= 10) {
/**
* This is to make sure the user doesn't input a value that goes over setInterval's
* max of 2,147,483,647, because if this happens, it will trigger the updates immediately
* and infinitely, using max computer resources and freezing the app.
*/
if (Number(minutes) >= 34000) {
return 34000;
}
return Math.ceil(Number(minutes));
}
/**
* If it's set to less than 10, set it to 10.
*/
return 10;
}
/**
* If all else fails, set it to the default 15 minutes.
*/
return 15;
};
/**
* Gets a valid concurrency limit based on user's input.
* @param {number} limit Concurrency limit
* @returns {void}
*/
const _getValidConcurrency = limit => {
if (Number(limit)) {
if (Number(limit) >= 1) {
return Math.ceil(Number(limit));
}
/**
* If it's set to less than 1, set it to 1.
*/
return 1;
}
/**
* If all else fails, set it to the default 2 concurrency limit.
*/
return 2;
};
const _renderFormNotice = (title, body) => {
const { marginBottom20 } = getModule('marginBottom20');
return <FormNotice
className={joinClassNames('vz-updater-form-notice', marginBottom20)}
imageData={{
width: 60,
height: 60,
src: '/assets/0694f38cb0b10cc3b5b89366a0893768.svg'
}}
type={FormNotice.Types.WARNING}
title={title}
body={body}
/>;
};
// --- PARTS
const renderReload = () => {
const body = <>
<p>{Messages.VIZALITY_UPDATES_AWAITING_RELOAD_DESC}</p>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.YELLOW}
look={Button.Looks.INVERTED}
onClick={() => location.reload()}
>
{Messages.ERRORS_RELOAD}
</Button>
</>;
return _renderFormNotice(Messages.VIZALITY_UPDATES_AWAITING_RELOAD_TITLE, body);
};
// @todo Add to modals AI
const _confirm = (title, content, confirm, callback) => {
const { colorStandard } = getModule('colorStandard');
const { spacing } = getModule('spacing', 'message');
const { size16 } = getModule('size16');
openModal(() => props => (
<Confirm
{...props}
header={title}
confirmText={confirm}
cancelText={Messages.CANCEL}
onConfirm={callback}
onCancel={closeModal}
>
<div className={joinClassNames(colorStandard, spacing, size16)}>
{content}
</div>
</Confirm>
));
};
// --- PROMPTS
const confirmSkipUpdate = callback => {
_confirm(
Messages.VIZALITY_UPDATES_SKIP_MODAL_TITLE,
Messages.VIZALITY_UPDATES_SKIP_MODAL,
Messages.VIZALITY_UPDATES_SKIP,
callback
);
};
const confirmPauseUpdates = () => {
_confirm(
Messages.VIZALITY_UPDATES_PAUSE,
Messages.VIZALITY_UPDATES_PAUSE_MODAL,
Messages.VIZALITY_UPDATES_PAUSE,
() => updateSetting('paused', true)
);
};
const confirmDisableUpdates = (all, callback) => {
_confirm(
Messages.VIZALITY_UPDATES_DISABLE,
all ? Messages.VIZALITY_UPDATES_DISABLE_MODAL_ALL : Messages.VIZALITY_UPDATES_DISABLE_MODAL,
Messages.VIZALITY_UPDATES_DISABLE,
callback
);
};
return (
<>
{awaitingReload && renderReload()}
<div className='vz-updater-top-section'>
<div className='vz-updater-top-section-header'>
<div className='vz-updater-top-section-icon'>
{icon}
</div>
<div className='vz-updater-top-section-status'>
<h3 className='vz-updater-top-section-title'>
{title}
</h3>
{!disabled && !updating && (!checking || checkingProgress[1] > 0) && <div className='vz-updater-top-section-subtitle'>
{paused
? Messages.VIZALITY_UPDATES_PAUSED_RESUME
: checking
? Messages.VIZALITY_UPDATES_CHECKING_STATUS.format({
checked: checkingProgress[0],
total: checkingProgress[1]
})
: Messages.VIZALITY_UPDATES_LAST_CHECKED.format({ date: last })}
</div>}
</div>
<div className='vz-updater-top-section-about'>
<div className='vz-updater-top-section-about-column'>
<span className='vz-updater-top-section-about-title'>
{Messages.VIZALITY_UPDATES_UPSTREAM}
</span>
<span className='vz-updater-top-section-about-title'>
{Messages.VIZALITY_UPDATES_REVISION}
</span>
<span className='vz-updater-top-section-about-title'>
{Messages.VIZALITY_UPDATES_BRANCH}
</span>
</div>
<div className='vz-updater-top-section-about-column'>
<span className='vz-updater-top-section-about-value'>
{vizality.git.upstream.replace(Repositories.VIZALITY, Messages.VIZALITY_UPDATES_UPSTREAM_OFFICIAL)}
</span>
<span className='vz-updater-top-section-about-value'>
{vizality.git.revision.substring(0, 7)}
</span>
<span className='vz-updater-top-section-about-value'>
{vizality.git.branch}
</span>
</div>
</div>
</div>
<div className='vz-updater-top-section-footer'>
{disabled || paused
? <Button
size={Button.Sizes.SMALL}
color={Button.Colors.GREEN}
onClick={() => {
updateSetting('paused', false);
updateSetting('disabled', false);
}}
>
{disabled ? Messages.VIZALITY_UPDATES_ENABLE : Messages.VIZALITY_UPDATES_RESUME}
</Button>
: (!checking && !updating && <>
{updates.length > 0 && <Button
size={Button.Sizes.SMALL}
color={failed ? Button.Colors.RED : Button.Colors.GREEN}
onClick={() => failed ? _this.confirmForce() : _this.updateAll()}
>
{failed ? Messages.VIZALITY_UPDATES_FORCE : Messages.VIZALITY_UPDATES_UPDATE}
</Button>}
<Button
size={Button.Sizes.SMALL}
onClick={() => _this.checkForUpdates(true)}
>
{Messages.VIZALITY_UPDATES_CHECK}
</Button>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.YELLOW}
onClick={() => confirmPauseUpdates()}
>
{Messages.VIZALITY_UPDATES_PAUSE}
</Button>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.RED}
onClick={() => confirmDisableUpdates(true, () => updateSetting('disabled', true))}
>
{Messages.VIZALITY_UPDATES_DISABLE}
</Button>
</>)}
</div>
</div>
{!disabled && !paused && !checking && updates.length > 0 && <div className='vz-updater-updates'>
<FormTitle className='vz-updater-updates-title'>
Pending Updates
</FormTitle>
{updates.map(update => {
return (
<Update
{...update}
key={update.updateId}
updating={updating}
onUpdate={() => _this.update(update)}
onSkip={() => confirmSkipUpdate(() => _this.skipUpdate(update, update.commits[0].id))}
onDisable={() => confirmDisableUpdates(false, () => _this.disableUpdates(update))}
/>
);
})}
</div>}
{disabledAddons.length > 0 && (
<Category title={Messages.VIZALITY_UPDATES_DISABLED_SECTION} description={Messages.VIZALITY_UPDATES_DISABLED_SECTION_DESC}>
{disabledAddons.map(update => {
return (
<Update
{...update}
disabled={true}
key={update.updateId}
updating={updating}
onEnableUpdates={() => _this.enableUpdates(update)}
/>
);
})}
</Category>
)}
<div className='vz-updater-options'>
<FormTitle className='vz-updater-options-title'>
{Messages.OPTIONS}
</FormTitle>
<SwitchItem
value={getSetting('automatic', false)}
disabled={disabled}
onChange={() => toggleSetting('automatic')}
note={Messages.VIZALITY_UPDATES_OPTS_AUTO_DESC}
>
{Messages.VIZALITY_UPDATES_OPTS_AUTO}
</SwitchItem>
<SwitchItem
value={getSetting('checkForUpdatesOnStartup', false)}
disabled={disabled}
onChange={() => toggleSetting('checkForUpdatesOnStartup')}
note={Messages.VIZALITY_UPDATES_OPTS_STARTUP_CHECK_DESC}
>
{Messages.VIZALITY_UPDATES_OPTS_STARTUP_CHECK}
</SwitchItem>
<TextInput
note={Messages.VIZALITY_UPDATES_OPTS_INTERVAL_DESC.format()}
disabled={disabled}
onChange={val => updateSetting('interval', _getValidInterval(val))}
defaultValue={getSetting('interval', 15)}
required={true}
>
{Messages.VIZALITY_UPDATES_OPTS_INTERVAL}
</TextInput>
<TextInput
note={Messages.VIZALITY_UPDATES_OPTS_CONCURRENCY_DESC.format()}
disabled={disabled}
onChange={val => updateSetting('concurrency', _getValidConcurrency(val))}
defaultValue={getSetting('concurrency', 2)}
required={true}
>
{Messages.VIZALITY_UPDATES_OPTS_CONCURRENCY}
</TextInput>
</div>
</>
);
});