keeweb/app/scripts/views/settings/settings-general-view.js

558 lines
21 KiB
JavaScript

import { Events } from 'framework/events';
import { View } from 'framework/views/view';
import { AutoType } from 'auto-type';
import { Storage } from 'storage';
import { RuntimeInfo } from 'const/runtime-info';
import { Updater } from 'comp/app/updater';
import { Launcher } from 'comp/launcher';
import { SettingsManager } from 'comp/settings/settings-manager';
import { Alerts } from 'comp/ui/alerts';
import { Links } from 'const/links';
import { AppSettingsModel } from 'models/app-settings-model';
import { UpdateModel } from 'models/update-model';
import { SemVer } from 'util/data/semver';
import { Features } from 'util/features';
import { DateFormat } from 'comp/i18n/date-format';
import { Locale } from 'util/locale';
import { SettingsLogsView } from 'views/settings/settings-logs-view';
import { SettingsPrvView } from 'views/settings/settings-prv-view';
import { mapObject, minmax } from 'util/fn';
import { ThemeWatcher } from 'comp/browser/theme-watcher';
import template from 'templates/settings/settings-general.hbs';
class SettingsGeneralView extends View {
template = template;
events = {
'click .settings__general-theme': 'changeTheme',
'click .settings__general-auto-switch-theme': 'changeAuthSwitchTheme',
'change .settings__general-locale': 'changeLocale',
'change .settings__general-font-size': 'changeFontSize',
'change .settings__general-expand': 'changeExpandGroups',
'change .settings__general-auto-update': 'changeAutoUpdate',
'change .settings__general-idle-minutes': 'changeIdleMinutes',
'change .settings__general-clipboard': 'changeClipboard',
'change .settings__general-auto-save': 'changeAutoSave',
'change .settings__general-auto-save-interval': 'changeAutoSaveInterval',
'change .settings__general-remember-key-files': 'changeRememberKeyFiles',
'change .settings__general-minimize': 'changeMinimize',
'change .settings__general-minimize-on-field-copy': 'changeMinimizeOnFieldCopy',
'change .settings__general-audit-passwords': 'changeAuditPasswords',
'change .settings__general-audit-password-entropy': 'changeAuditPasswordEntropy',
'change .settings__general-exclude-pins-from-audit': 'changeExcludePinsFromAudit',
'change .settings__general-check-passwords-on-hibp': 'changeCheckPasswordsOnHIBP',
'click .settings__general-toggle-help-hibp': 'clickToggleHelpHIBP',
'change .settings__general-audit-password-age': 'changeAuditPasswordAge',
'change .settings__general-lock-on-minimize': 'changeLockOnMinimize',
'change .settings__general-lock-on-copy': 'changeLockOnCopy',
'change .settings__general-lock-on-auto-type': 'changeLockOnAutoType',
'change .settings__general-lock-on-os-lock': 'changeLockOnOsLock',
'change .settings__general-table-view': 'changeTableView',
'change .settings__general-colorful-icons': 'changeColorfulIcons',
'change .settings__general-use-markdown': 'changeUseMarkdown',
'change .settings__general-use-group-icon-for-entries': 'changeUseGroupIconForEntries',
'change .settings__general-direct-autotype': 'changeDirectAutotype',
'change .settings__general-field-label-dblclick-autotype':
'changeFieldLabelDblClickAutoType',
'change .settings__general-use-legacy-autotype': 'changeUseLegacyAutoType',
'change .settings__general-device-owner-auth': 'changeDeviceOwnerAuth',
'change .settings__general-device-owner-auth-timeout': 'changeDeviceOwnerAuthTimeout',
'change .settings__general-titlebar-style': 'changeTitlebarStyle',
'click .settings__general-update-btn': 'checkUpdate',
'click .settings__general-restart-btn': 'installUpdateAndRestart',
'click .settings__general-download-update-btn': 'downloadUpdate',
'click .settings__general-update-found-btn': 'installFoundUpdate',
'change .settings__general-disable-offline-storage': 'changeDisableOfflineStorage',
'change .settings__general-prv-check': 'changeStorageEnabled',
'click .settings__general-prv-logout': 'logoutFromStorage',
'click .settings__general-show-advanced': 'showAdvancedSettings',
'click .settings__general-dev-tools-link': 'openDevTools',
'click .settings__general-try-beta-link': 'tryBeta',
'click .settings__general-show-logs-link': 'showLogs',
'click .settings__general-reload-app-link': 'reloadApp'
};
constructor(model, options) {
super(model, options);
this.listenTo(UpdateModel, 'change', this.render);
this.listenTo(Events, 'theme-applied', this.render);
}
render() {
const updateReady = UpdateModel.updateStatus === 'ready';
const updateFound = UpdateModel.updateStatus === 'found';
const updateManual = UpdateModel.updateManual;
const storageProviders = this.getStorageProviders();
super.render({
themes: this.getAllThemes(),
autoSwitchTheme: AppSettingsModel.autoSwitchTheme,
activeTheme: SettingsManager.activeTheme,
locales: SettingsManager.allLocales,
activeLocale: SettingsManager.activeLocale,
fontSize: AppSettingsModel.fontSize,
expandGroups: AppSettingsModel.expandGroups,
canClearClipboard: !!Launcher,
clipboardSeconds: AppSettingsModel.clipboardSeconds,
rememberKeyFiles: AppSettingsModel.rememberKeyFiles,
supportFiles: !!Launcher,
autoSave: AppSettingsModel.autoSave,
autoSaveInterval: AppSettingsModel.autoSaveInterval,
idleMinutes: AppSettingsModel.idleMinutes,
minimizeOnClose: AppSettingsModel.minimizeOnClose,
minimizeOnFieldCopy: AppSettingsModel.minimizeOnFieldCopy,
devTools: Launcher && Launcher.devTools,
canAutoUpdate: Updater.enabled,
canAutoSaveOnClose: !!Launcher,
canMinimize: !!Launcher,
canDetectMinimize: !!Launcher,
canDetectOsSleep: Launcher && Launcher.canDetectOsSleep(),
canAutoType: AutoType.enabled,
auditPasswords: AppSettingsModel.auditPasswords,
auditPasswordEntropy: AppSettingsModel.auditPasswordEntropy,
excludePinsFromAudit: AppSettingsModel.excludePinsFromAudit,
checkPasswordsOnHIBP: AppSettingsModel.checkPasswordsOnHIBP,
auditPasswordAge: AppSettingsModel.auditPasswordAge,
hibpLink: Links.HaveIBeenPwned,
hibpPrivacyLink: Links.HaveIBeenPwnedPrivacy,
lockOnMinimize: Launcher && AppSettingsModel.lockOnMinimize,
lockOnCopy: AppSettingsModel.lockOnCopy,
lockOnAutoType: AppSettingsModel.lockOnAutoType,
lockOnOsLock: AppSettingsModel.lockOnOsLock,
tableView: AppSettingsModel.tableView,
canSetTableView: !Features.isMobile,
autoUpdate: Updater.getAutoUpdateType(),
updateInProgress: Updater.updateInProgress(),
updateInfo: this.getUpdateInfo(),
updateWaitingReload: updateReady && !Launcher,
showUpdateBlock: Updater.enabled && !updateManual,
updateReady,
updateFound,
updateManual,
releaseNotesLink: Links.ReleaseNotes,
colorfulIcons: AppSettingsModel.colorfulIcons,
useMarkdown: AppSettingsModel.useMarkdown,
useGroupIconForEntries: AppSettingsModel.useGroupIconForEntries,
directAutotype: AppSettingsModel.directAutotype,
fieldLabelDblClickAutoType: AppSettingsModel.fieldLabelDblClickAutoType,
useLegacyAutoType: AppSettingsModel.useLegacyAutoType,
supportsTitleBarStyles: Features.supportsTitleBarStyles(),
titlebarStyle: AppSettingsModel.titlebarStyle,
storageProviders,
showReloadApp: Features.isStandalone,
hasDeviceOwnerAuth: Features.isDesktop && Features.isMac,
deviceOwnerAuth: AppSettingsModel.deviceOwnerAuth,
deviceOwnerAuthTimeout: AppSettingsModel.deviceOwnerAuthTimeoutMinutes,
disableOfflineStorage: AppSettingsModel.disableOfflineStorage
});
this.renderProviderViews(storageProviders);
}
renderProviderViews(storageProviders) {
storageProviders.forEach(function (prv) {
if (this.views[prv.name]) {
this.views[prv.name].remove();
}
if (prv.hasConfig) {
const prvView = new SettingsPrvView(prv, {
parent: this.$el.find('.settings__general-' + prv.name)[0]
});
this.views[prv.name] = prvView;
prvView.render();
}
}, this);
}
getUpdateInfo() {
switch (UpdateModel.status) {
case 'checking':
return Locale.setGenUpdateChecking + '...';
case 'error': {
let errMsg = Locale.setGenErrorChecking;
if (UpdateModel.lastError) {
errMsg += ': ' + UpdateModel.lastError;
}
if (UpdateModel.lastSuccessCheckDate) {
errMsg +=
'. ' +
Locale.setGenLastCheckSuccess.replace(
'{}',
DateFormat.dtStr(UpdateModel.lastSuccessCheckDate)
) +
': ' +
Locale.setGenLastCheckVer.replace('{}', UpdateModel.lastVersion);
}
return errMsg;
}
case 'ok': {
let msg =
Locale.setGenCheckedAt +
' ' +
DateFormat.dtStr(UpdateModel.lastCheckDate) +
': ';
const cmp = SemVer.compareVersions(RuntimeInfo.version, UpdateModel.lastVersion);
if (cmp >= 0) {
msg += Locale.setGenLatestVer;
} else {
msg +=
Locale.setGenNewVer.replace('{}', UpdateModel.lastVersion) +
' ' +
DateFormat.dStr(UpdateModel.lastVersionReleaseDate);
}
switch (UpdateModel.updateStatus) {
case 'downloading':
return msg + '. ' + Locale.setGenDownloadingUpdate;
case 'extracting':
return msg + '. ' + Locale.setGenExtractingUpdate;
case 'error':
return msg + '. ' + Locale.setGenCheckErr;
}
return msg;
}
default:
return Locale.setGenNeverChecked;
}
}
getStorageProviders() {
const storageProviders = [];
Object.keys(Storage).forEach((name) => {
const prv = Storage[name];
if (!prv.system) {
storageProviders.push(prv);
}
});
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));
return storageProviders.map((sp) => ({
name: sp.name,
enabled: sp.enabled,
hasConfig: !!sp.getSettingsConfig,
loggedIn: sp.loggedIn
}));
}
getAllThemes() {
const { autoSwitchTheme } = AppSettingsModel;
if (autoSwitchTheme) {
const themes = {};
const ignoredThemes = {};
for (const config of SettingsManager.autoSwitchedThemes) {
ignoredThemes[config.dark] = true;
ignoredThemes[config.light] = true;
const activeTheme = ThemeWatcher.dark ? config.dark : config.light;
themes[activeTheme] = Locale[config.name];
}
for (const [th, name] of Object.entries(SettingsManager.allThemes)) {
if (!ignoredThemes[th]) {
themes[th] = Locale[name];
}
}
return themes;
} else {
return mapObject(SettingsManager.allThemes, (theme) => Locale[theme]);
}
}
changeTheme(e) {
const theme = e.target.closest('.settings__general-theme').dataset.theme;
if (theme === '...') {
this.goToPlugins();
} else {
const changedInSettings = AppSettingsModel.theme !== theme;
if (changedInSettings) {
AppSettingsModel.theme = theme;
} else {
SettingsManager.setTheme(theme);
}
}
}
changeAuthSwitchTheme(e) {
const autoSwitchTheme = e.target.checked;
AppSettingsModel.autoSwitchTheme = autoSwitchTheme;
SettingsManager.darkModeChanged();
this.render();
}
changeLocale(e) {
const locale = e.target.value;
if (locale === '...') {
e.target.value = AppSettingsModel.locale || 'en-US';
this.goToPlugins();
} else {
AppSettingsModel.locale = locale;
}
}
goToPlugins() {
this.appModel.menu.select({
item: this.appModel.menu.pluginsSection.items[0]
});
}
changeFontSize(e) {
const fontSize = +e.target.value;
AppSettingsModel.fontSize = fontSize;
}
changeTitlebarStyle(e) {
const titlebarStyle = e.target.value;
AppSettingsModel.titlebarStyle = titlebarStyle;
}
changeClipboard(e) {
const clipboardSeconds = +e.target.value;
AppSettingsModel.clipboardSeconds = clipboardSeconds;
}
changeIdleMinutes(e) {
const idleMinutes = +e.target.value;
AppSettingsModel.idleMinutes = idleMinutes;
}
changeAutoUpdate(e) {
const autoUpdate = e.target.value || false;
AppSettingsModel.autoUpdate = autoUpdate;
if (autoUpdate) {
Updater.scheduleNextCheck();
}
}
checkUpdate() {
Updater.check(true);
}
changeAutoSave(e) {
const autoSave = e.target.checked || false;
AppSettingsModel.autoSave = autoSave;
}
changeAutoSaveInterval(e) {
const autoSaveInterval = Number(e.target.value) || 0;
AppSettingsModel.autoSaveInterval = autoSaveInterval;
}
changeRememberKeyFiles(e) {
const rememberKeyFiles = e.target.value || false;
AppSettingsModel.rememberKeyFiles = rememberKeyFiles;
this.appModel.clearStoredKeyFiles();
}
changeMinimize(e) {
const minimizeOnClose = e.target.checked || false;
AppSettingsModel.minimizeOnClose = minimizeOnClose;
}
changeMinimizeOnFieldCopy(e) {
const minimizeOnFieldCopy = e.target.checked || false;
AppSettingsModel.minimizeOnFieldCopy = minimizeOnFieldCopy;
}
changeAuditPasswords(e) {
const auditPasswords = e.target.checked || false;
AppSettingsModel.auditPasswords = auditPasswords;
}
changeAuditPasswordEntropy(e) {
const auditPasswordEntropy = e.target.checked || false;
AppSettingsModel.auditPasswordEntropy = auditPasswordEntropy;
}
changeExcludePinsFromAudit(e) {
const excludePinsFromAudit = e.target.checked || false;
AppSettingsModel.excludePinsFromAudit = excludePinsFromAudit;
}
changeCheckPasswordsOnHIBP(e) {
if (e.target.closest('a')) {
return;
}
const checkPasswordsOnHIBP = e.target.checked || false;
AppSettingsModel.checkPasswordsOnHIBP = checkPasswordsOnHIBP;
}
clickToggleHelpHIBP() {
this.el.querySelector('.settings__general-help-hibp').classList.toggle('hide');
}
changeAuditPasswordAge(e) {
const auditPasswordAge = e.target.value | 0;
AppSettingsModel.auditPasswordAge = auditPasswordAge;
}
changeLockOnMinimize(e) {
const lockOnMinimize = e.target.checked || false;
AppSettingsModel.lockOnMinimize = lockOnMinimize;
}
changeLockOnCopy(e) {
const lockOnCopy = e.target.checked || false;
AppSettingsModel.lockOnCopy = lockOnCopy;
}
changeLockOnAutoType(e) {
const lockOnAutoType = e.target.checked || false;
AppSettingsModel.lockOnAutoType = lockOnAutoType;
}
changeLockOnOsLock(e) {
const lockOnOsLock = e.target.checked || false;
AppSettingsModel.lockOnOsLock = lockOnOsLock;
}
changeTableView(e) {
const tableView = e.target.checked || false;
AppSettingsModel.tableView = tableView;
Events.emit('refresh');
}
changeColorfulIcons(e) {
const colorfulIcons = e.target.checked || false;
AppSettingsModel.colorfulIcons = colorfulIcons;
Events.emit('refresh');
}
changeUseMarkdown(e) {
const useMarkdown = e.target.checked || false;
AppSettingsModel.useMarkdown = useMarkdown;
Events.emit('refresh');
}
changeUseGroupIconForEntries(e) {
const useGroupIconForEntries = e.target.checked || false;
AppSettingsModel.useGroupIconForEntries = useGroupIconForEntries;
}
changeDirectAutotype(e) {
const directAutotype = e.target.checked || false;
AppSettingsModel.directAutotype = directAutotype;
}
changeFieldLabelDblClickAutoType(e) {
const fieldLabelDblClickAutoType = e.target.checked || false;
AppSettingsModel.fieldLabelDblClickAutoType = fieldLabelDblClickAutoType;
Events.emit('refresh');
}
changeUseLegacyAutoType(e) {
const useLegacyAutoType = e.target.checked || false;
AppSettingsModel.useLegacyAutoType = useLegacyAutoType;
Events.emit('refresh');
}
changeDeviceOwnerAuth(e) {
const deviceOwnerAuth = e.target.value || null;
let deviceOwnerAuthTimeoutMinutes = AppSettingsModel.deviceOwnerAuthTimeoutMinutes | 0;
if (deviceOwnerAuth) {
const timeouts = { memory: [30, 10080], file: [30, 525600] };
const [tMin, tMax] = timeouts[deviceOwnerAuth] || [0, 0];
deviceOwnerAuthTimeoutMinutes = minmax(deviceOwnerAuthTimeoutMinutes, tMin, tMax);
}
AppSettingsModel.set({ deviceOwnerAuth, deviceOwnerAuthTimeoutMinutes });
this.render();
this.appModel.checkEncryptedPasswordsStorage();
}
changeDeviceOwnerAuthTimeout(e) {
const deviceOwnerAuthTimeout = e.target.value | 0;
AppSettingsModel.deviceOwnerAuthTimeoutMinutes = deviceOwnerAuthTimeout;
}
installUpdateAndRestart() {
if (Launcher) {
Updater.installAndRestart();
} else {
window.location.reload();
}
}
downloadUpdate() {
Launcher.openLink(Links.Desktop);
}
installFoundUpdate() {
Updater.update(true, () => {
Updater.installAndRestart();
});
}
changeExpandGroups(e) {
const expand = e.target.checked;
AppSettingsModel.expandGroups = expand;
Events.emit('refresh');
}
changeDisableOfflineStorage(e) {
const disableOfflineStorage = e.target.checked;
AppSettingsModel.disableOfflineStorage = disableOfflineStorage;
if (disableOfflineStorage) {
this.appModel.deleteAllCachedFiles();
}
}
changeStorageEnabled(e) {
const storage = Storage[$(e.target).data('storage')];
if (storage) {
storage.setEnabled(e.target.checked);
AppSettingsModel[storage.name] = storage.enabled;
this.$el
.find('.settings__general-' + storage.name)
.toggleClass('hide', !e.target.checked);
}
}
logoutFromStorage(e) {
const storage = Storage[$(e.target).data('storage')];
if (storage) {
storage.logout();
$(e.target).remove();
}
}
showAdvancedSettings() {
this.$el
.find('.settings__general-show-advanced, .settings__general-advanced')
.toggleClass('hide');
this.scrollToBottom();
}
openDevTools() {
if (Launcher) {
Launcher.openDevTools();
}
}
tryBeta() {
if (this.appModel.files.hasUnsavedFiles()) {
Alerts.info({
header: Locale.setGenTryBetaWarning,
body: Locale.setGenTryBetaWarningBody
});
} else {
location.href = Links.BetaWebApp;
}
}
showLogs() {
if (this.views.logView) {
this.views.logView.remove();
}
this.views.logView = new SettingsLogsView();
this.views.logView.render();
this.scrollToBottom();
}
reloadApp() {
location.reload();
}
scrollToBottom() {
this.$el.closest('.scroller').scrollTop(this.$el.height());
}
}
export { SettingsGeneralView };