mirror of https://github.com/keeweb/keeweb.git
settings
This commit is contained in:
parent
aa11e1a65e
commit
d5a61acd01
|
@ -10,25 +10,6 @@ import { WindowClass } from 'comp/browser/window-class';
|
|||
|
||||
const logger = new Logger('settings-manager');
|
||||
|
||||
const DesktopLocaleKeys = [
|
||||
'sysMenuAboutKeeWeb',
|
||||
'sysMenuServices',
|
||||
'sysMenuHide',
|
||||
'sysMenuHideOthers',
|
||||
'sysMenuUnhide',
|
||||
'sysMenuQuit',
|
||||
'sysMenuEdit',
|
||||
'sysMenuUndo',
|
||||
'sysMenuRedo',
|
||||
'sysMenuCut',
|
||||
'sysMenuCopy',
|
||||
'sysMenuPaste',
|
||||
'sysMenuSelectAll',
|
||||
'sysMenuWindow',
|
||||
'sysMenuMinimize',
|
||||
'sysMenuClose'
|
||||
];
|
||||
|
||||
const SettingsManager = {
|
||||
activeTheme: null as string | null,
|
||||
|
||||
|
@ -38,57 +19,68 @@ const SettingsManager = {
|
|||
'fr-FR': 'Français'
|
||||
} as Record<string, string>,
|
||||
|
||||
allThemes: {
|
||||
dark: 'setGenThemeDark',
|
||||
light: 'setGenThemeLight',
|
||||
sd: 'setGenThemeSd',
|
||||
sl: 'setGenThemeSl',
|
||||
fb: 'setGenThemeFb',
|
||||
bl: 'setGenThemeBl',
|
||||
db: 'setGenThemeDb',
|
||||
lb: 'setGenThemeLb',
|
||||
te: 'setGenThemeTe',
|
||||
lt: 'setGenThemeLt',
|
||||
dc: 'setGenThemeDc',
|
||||
hc: 'setGenThemeHc'
|
||||
} as Record<string, string>,
|
||||
get builtInThemes(): Record<string, string> {
|
||||
return {
|
||||
dark: Locale.setGenThemeDark,
|
||||
light: Locale.setGenThemeLight,
|
||||
sd: Locale.setGenThemeSd,
|
||||
sl: Locale.setGenThemeSl,
|
||||
fb: Locale.setGenThemeFb,
|
||||
bl: Locale.setGenThemeBl,
|
||||
db: Locale.setGenThemeDb,
|
||||
lb: Locale.setGenThemeLb,
|
||||
te: Locale.setGenThemeTe,
|
||||
lt: Locale.setGenThemeLt,
|
||||
dc: Locale.setGenThemeDc,
|
||||
hc: Locale.setGenThemeHc
|
||||
};
|
||||
},
|
||||
|
||||
get allThemes(): Record<string, string> {
|
||||
return {
|
||||
...this.builtInThemes,
|
||||
...this.customThemes
|
||||
};
|
||||
},
|
||||
|
||||
// changing something here? don't forget about desktop/app.js
|
||||
autoSwitchedThemes: [
|
||||
{
|
||||
name: 'setGenThemeDefault',
|
||||
dark: 'dark',
|
||||
light: 'light'
|
||||
},
|
||||
{
|
||||
name: 'setGenThemeSol',
|
||||
dark: 'sd',
|
||||
light: 'sl'
|
||||
},
|
||||
{
|
||||
name: 'setGenThemeBlue',
|
||||
dark: 'fb',
|
||||
light: 'bl'
|
||||
},
|
||||
{
|
||||
name: 'setGenThemeBrown',
|
||||
dark: 'db',
|
||||
light: 'lb'
|
||||
},
|
||||
{
|
||||
name: 'setGenThemeTerminal',
|
||||
dark: 'te',
|
||||
light: 'lt'
|
||||
},
|
||||
{
|
||||
name: 'setGenThemeHighContrast',
|
||||
dark: 'dc',
|
||||
light: 'hc'
|
||||
}
|
||||
],
|
||||
get autoSwitchedThemes(): { name: string; dark: string; light: string }[] {
|
||||
return [
|
||||
{
|
||||
name: Locale.setGenThemeDefault,
|
||||
dark: 'dark',
|
||||
light: 'light'
|
||||
},
|
||||
{
|
||||
name: Locale.setGenThemeSol,
|
||||
dark: 'sd',
|
||||
light: 'sl'
|
||||
},
|
||||
{
|
||||
name: Locale.setGenThemeBlue,
|
||||
dark: 'fb',
|
||||
light: 'bl'
|
||||
},
|
||||
{
|
||||
name: Locale.setGenThemeBrown,
|
||||
dark: 'db',
|
||||
light: 'lb'
|
||||
},
|
||||
{
|
||||
name: Locale.setGenThemeTerminal,
|
||||
dark: 'te',
|
||||
light: 'lt'
|
||||
},
|
||||
{
|
||||
name: Locale.setGenThemeHighContrast,
|
||||
dark: 'dc',
|
||||
light: 'hc'
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
customLocales: new Map<string, Record<string, string>>(),
|
||||
customThemeNames: new Map<string, string>(),
|
||||
customThemes: {} as Record<string, string>,
|
||||
|
||||
init(): void {
|
||||
Events.on('dark-mode-changed', () => this.darkModeChanged());
|
||||
|
@ -183,10 +175,7 @@ const SettingsManager = {
|
|||
Events.emit('locale-changed', loc);
|
||||
|
||||
if (Launcher) {
|
||||
const localeValuesForDesktopApp: Record<string, string> = {};
|
||||
for (const key of DesktopLocaleKeys) {
|
||||
localeValuesForDesktopApp[key] = Locale.get(key);
|
||||
}
|
||||
const localeValuesForDesktopApp = this.getDesktopAppLocaleValues();
|
||||
Launcher.ipcRenderer.invoke('set-locale', loc, localeValuesForDesktopApp).catch(noop);
|
||||
}
|
||||
},
|
||||
|
@ -197,6 +186,27 @@ const SettingsManager = {
|
|||
return 'en-US';
|
||||
}
|
||||
return language;
|
||||
},
|
||||
|
||||
getDesktopAppLocaleValues(): Record<string, string> {
|
||||
return {
|
||||
sysMenuAboutKeeWeb: Locale.sysMenuAboutKeeWeb.with('KeeWeb'),
|
||||
sysMenuServices: Locale.sysMenuServices,
|
||||
sysMenuHide: Locale.sysMenuHide.with('KeeWeb'),
|
||||
sysMenuHideOthers: Locale.sysMenuHideOthers,
|
||||
sysMenuUnhide: Locale.sysMenuUnhide,
|
||||
sysMenuQuit: Locale.sysMenuQuit.with('KeeWeb'),
|
||||
sysMenuEdit: Locale.sysMenuEdit,
|
||||
sysMenuUndo: Locale.sysMenuUndo,
|
||||
sysMenuRedo: Locale.sysMenuRedo,
|
||||
sysMenuCut: Locale.sysMenuCut,
|
||||
sysMenuCopy: Locale.sysMenuCopy,
|
||||
sysMenuPaste: Locale.sysMenuPaste,
|
||||
sysMenuSelectAll: Locale.sysMenuSelectAll,
|
||||
sysMenuWindow: Locale.sysMenuWindow,
|
||||
sysMenuMinimize: Locale.sysMenuMinimize,
|
||||
sysMenuClose: Locale.sysMenuClose
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ export type AppSettingsAutoUpdate = 'install' | 'check';
|
|||
export type AppSettingsRememberKeyFiles = 'path' | 'data';
|
||||
export type AppSettingsTitlebarStyle = 'default' | 'hidden' | 'hidden-inset';
|
||||
export type AppSettingsDeviceOwnerAuth = 'memory' | 'file';
|
||||
export type AppSettingsFontSize = 0 | 1 | 2;
|
||||
|
||||
class AppSettings extends Model {
|
||||
theme: string | null = null; // UI theme
|
||||
|
@ -31,7 +32,7 @@ class AppSettings extends Model {
|
|||
clipboardSeconds = 0; // number of seconds after which the clipboard will be cleared
|
||||
autoSave = true; // auto-save open files
|
||||
autoSaveInterval = 0; // interval between performing automatic sync, minutes, -1: on every change
|
||||
rememberKeyFiles: AppSettingsRememberKeyFiles = 'path'; // remember keyfiles selected on the Open screen
|
||||
rememberKeyFiles: AppSettingsRememberKeyFiles | null = 'path'; // remember keyfiles selected on the Open screen
|
||||
idleMinutes = 15; // app lock timeout after inactivity, minutes
|
||||
minimizeOnClose = false; // minimise the app instead of closing
|
||||
minimizeOnFieldCopy = false; // minimise the app on copy
|
||||
|
@ -51,7 +52,7 @@ class AppSettings extends Model {
|
|||
hideEmptyFields = false; // hide empty fields in entries
|
||||
skipHttpsWarning = false; // disable the non-HTTPS warning
|
||||
demoOpened = false; // hide the demo button inside the More... menu
|
||||
fontSize: 0 | 1 | 2 = 0; // font size
|
||||
fontSize: AppSettingsFontSize = 0; // font size
|
||||
tableViewColumns: string[] | null = null; // columns displayed in the table view
|
||||
generatorPresets: PasswordGeneratorAppSetting | null = null; // presets used in the password generator
|
||||
generatorHidePassword = false; // hide password in the generator
|
||||
|
@ -478,8 +479,13 @@ class AppSettings extends Model {
|
|||
}
|
||||
|
||||
private setRememberKeyFiles(value: unknown) {
|
||||
if (value === 'path' || value === 'data') {
|
||||
this.rememberKeyFiles = value;
|
||||
if (value) {
|
||||
if (value === 'path' || value === 'data') {
|
||||
this.rememberKeyFiles = value;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
this.rememberKeyFiles = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -41,7 +41,7 @@ export type MenuItemFilter =
|
|||
class MenuItem extends Model {
|
||||
readonly id: string;
|
||||
title?: string;
|
||||
locTitle?: LocaleKey;
|
||||
locTitle?: () => string;
|
||||
icon?: string;
|
||||
customIcon?: string;
|
||||
active = false;
|
||||
|
|
|
@ -44,7 +44,7 @@ class Menu extends Model {
|
|||
this.sections = [];
|
||||
|
||||
this.allItemsItem = new MenuItem({
|
||||
locTitle: 'menuAllItems',
|
||||
locTitle: () => Locale.menuAllItems,
|
||||
icon: 'th-large',
|
||||
active: true,
|
||||
shortcut: Keys.DOM_VK_A,
|
||||
|
@ -58,7 +58,7 @@ class Menu extends Model {
|
|||
this.groupsSection.grow = true;
|
||||
|
||||
this.colorsItem = new MenuItem({
|
||||
locTitle: 'menuColors',
|
||||
locTitle: () => Locale.menuColors,
|
||||
icon: 'bookmark',
|
||||
shortcut: Keys.DOM_VK_C,
|
||||
cls: 'menu__item-colors',
|
||||
|
@ -75,7 +75,7 @@ class Menu extends Model {
|
|||
|
||||
this.trashSection = new MenuSection(
|
||||
new MenuItem({
|
||||
locTitle: 'menuTrash',
|
||||
locTitle: () => Locale.menuTrash,
|
||||
icon: 'trash-alt',
|
||||
shortcut: Keys.DOM_VK_D,
|
||||
filter: { type: 'trash' },
|
||||
|
@ -103,44 +103,44 @@ class Menu extends Model {
|
|||
|
||||
this.generalSection = new MenuSection(
|
||||
new MenuItem({
|
||||
locTitle: 'menuSetGeneral',
|
||||
locTitle: () => Locale.menuSetGeneral,
|
||||
icon: 'cog',
|
||||
page: 'general',
|
||||
anchor: undefined,
|
||||
active: true
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'setGenAppearance',
|
||||
locTitle: () => Locale.setGenAppearance,
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'appearance'
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'setGenFunction',
|
||||
locTitle: () => Locale.setGenFunction,
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'function'
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'setGenAudit',
|
||||
locTitle: () => Locale.setGenAudit,
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'audit'
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'setGenLock',
|
||||
locTitle: () => Locale.setGenLock,
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'lock'
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'setGenStorage',
|
||||
locTitle: () => Locale.setGenStorage,
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'storage'
|
||||
}),
|
||||
new MenuItem({
|
||||
locTitle: 'advanced',
|
||||
locTitle: () => StringFormat.capFirst(Locale.advanced),
|
||||
icon: '0',
|
||||
page: 'general',
|
||||
anchor: 'advanced'
|
||||
|
@ -148,30 +148,46 @@ class Menu extends Model {
|
|||
);
|
||||
|
||||
this.shortcutsSection = new MenuSection(
|
||||
new MenuItem({ locTitle: 'shortcuts', icon: 'keyboard', page: 'shortcuts' })
|
||||
new MenuItem({
|
||||
locTitle: () => StringFormat.capFirst(Locale.shortcuts),
|
||||
icon: 'keyboard',
|
||||
page: 'shortcuts'
|
||||
})
|
||||
);
|
||||
if (Features.supportsBrowserExtensions) {
|
||||
this.browserSection = new MenuSection(
|
||||
new MenuItem({
|
||||
locTitle: 'menuSetBrowser',
|
||||
locTitle: () => Locale.menuSetBrowser,
|
||||
icon: Features.browserIcon,
|
||||
page: 'browser'
|
||||
})
|
||||
);
|
||||
}
|
||||
this.pluginsSection = new MenuSection(
|
||||
new MenuItem({ locTitle: 'plugins', icon: 'puzzle-piece', page: 'plugins' })
|
||||
new MenuItem({
|
||||
locTitle: () => StringFormat.capFirst(Locale.plugins),
|
||||
icon: 'puzzle-piece',
|
||||
page: 'plugins'
|
||||
})
|
||||
);
|
||||
if (Launcher) {
|
||||
this.devicesSection = new MenuSection(
|
||||
new MenuItem({ locTitle: 'menuSetDevices', icon: 'usb', page: 'devices' })
|
||||
new MenuItem({
|
||||
locTitle: () => Locale.menuSetDevices,
|
||||
icon: 'usb',
|
||||
page: 'devices'
|
||||
})
|
||||
);
|
||||
}
|
||||
this.aboutSection = new MenuSection(
|
||||
new MenuItem({ locTitle: 'menuSetAbout', icon: 'info', page: 'about' })
|
||||
new MenuItem({ locTitle: () => Locale.menuSetAbout, icon: 'info', page: 'about' })
|
||||
);
|
||||
this.helpSection = new MenuSection(
|
||||
new MenuItem({ locTitle: 'help', icon: 'question', page: 'help' })
|
||||
new MenuItem({
|
||||
locTitle: () => StringFormat.capFirst(Locale.help),
|
||||
icon: 'question',
|
||||
page: 'help'
|
||||
})
|
||||
);
|
||||
this.filesSection = new MenuSection();
|
||||
this.filesSection.scrollable = true;
|
||||
|
@ -299,7 +315,7 @@ class Menu extends Model {
|
|||
for (const section of menu) {
|
||||
for (const item of section.items) {
|
||||
if (item.locTitle) {
|
||||
item.title = StringFormat.capFirst(Locale.get(item.locTitle));
|
||||
item.title = item.locTitle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,9 +342,7 @@ class Plugin extends Model {
|
|||
el.addEventListener('load', () => {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
if (theme) {
|
||||
const locKey = this.getThemeLocaleKey(theme.name);
|
||||
SettingsManager.allThemes[theme.name] = locKey;
|
||||
SettingsManager.customThemeNames.set(locKey, theme.title);
|
||||
SettingsManager.customThemes[theme.name] = theme.title;
|
||||
for (const styleSheet of Array.from(document.styleSheets)) {
|
||||
const node = styleSheet.ownerNode as HTMLElement;
|
||||
if (node?.id === id) {
|
||||
|
@ -470,16 +468,12 @@ class Plugin extends Model {
|
|||
}
|
||||
}
|
||||
|
||||
getThemeLocaleKey(name: string): string {
|
||||
return `setGenThemeCustom_${name}`;
|
||||
}
|
||||
|
||||
removeTheme(theme: PluginManifestTheme): void {
|
||||
delete SettingsManager.allThemes[theme.name];
|
||||
if (AppSettings.theme === theme.name) {
|
||||
AppSettings.theme = SettingsManager.getDefaultTheme();
|
||||
}
|
||||
SettingsManager.customThemeNames.delete(this.getThemeLocaleKey(theme.name));
|
||||
delete SettingsManager.customThemes[theme.name];
|
||||
}
|
||||
|
||||
loadPluginSettings(): void {
|
||||
|
|
|
@ -15,6 +15,10 @@ class StorageCache extends StorageBase {
|
|||
return !Launcher;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return 'Cache';
|
||||
}
|
||||
|
||||
async save(id: string, data: ArrayBuffer): Promise<StorageSaveResult> {
|
||||
await this._io.save(id, data);
|
||||
return {};
|
||||
|
@ -38,6 +42,10 @@ class StorageCache extends StorageBase {
|
|||
watch: undefined;
|
||||
unwatch: undefined;
|
||||
getPathForName: undefined;
|
||||
getOpenConfig: undefined;
|
||||
getSettingsConfig: undefined;
|
||||
applyConfig: undefined;
|
||||
applySetting: undefined;
|
||||
}
|
||||
|
||||
export { StorageCache };
|
||||
|
|
|
@ -38,6 +38,10 @@ class StorageDropbox extends StorageBase {
|
|||
return AppSettings.dropbox;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return Locale.dropbox;
|
||||
}
|
||||
|
||||
private toFullPath(path: string) {
|
||||
const rootFolder = AppSettings.dropboxFolder;
|
||||
if (rootFolder) {
|
||||
|
@ -114,29 +118,29 @@ class StorageDropbox extends StorageBase {
|
|||
getOpenConfig(): StorageOpenConfig {
|
||||
const keyField: StorageConfigFieldText = {
|
||||
id: 'key',
|
||||
title: 'dropboxAppKey',
|
||||
desc: 'dropboxAppKeyDesc',
|
||||
title: Locale.dropboxAppKey,
|
||||
desc: Locale.dropboxAppKeyDesc,
|
||||
type: 'text',
|
||||
required: true,
|
||||
pattern: '\\w+'
|
||||
};
|
||||
const secretField: StorageConfigFieldPassword = {
|
||||
id: 'secret',
|
||||
title: 'dropboxAppSecret',
|
||||
desc: 'dropboxAppSecretDesc',
|
||||
title: Locale.dropboxAppSecret,
|
||||
desc: Locale.dropboxAppSecretDesc,
|
||||
type: 'password',
|
||||
required: true,
|
||||
pattern: '\\w+'
|
||||
};
|
||||
const folderField: StorageConfigFieldText = {
|
||||
id: 'folder',
|
||||
title: 'dropboxFolder',
|
||||
desc: 'dropboxFolderDesc',
|
||||
title: Locale.dropboxFolder,
|
||||
desc: Locale.dropboxFolderDesc,
|
||||
type: 'text',
|
||||
placeholder: 'dropboxFolderPlaceholder'
|
||||
placeholder: Locale.dropboxFolderPlaceholder
|
||||
};
|
||||
return {
|
||||
desc: 'dropboxSetupDesc',
|
||||
desc: Locale.dropboxSetupDesc,
|
||||
fields: [keyField, secretField, folderField]
|
||||
};
|
||||
}
|
||||
|
@ -146,15 +150,19 @@ class StorageDropbox extends StorageBase {
|
|||
const appKey = this.getKey();
|
||||
const linkField: StorageConfigFieldSelect = {
|
||||
id: 'link',
|
||||
title: 'dropboxLink',
|
||||
title: Locale.dropboxLink,
|
||||
type: 'select',
|
||||
value: 'custom',
|
||||
options: { app: 'dropboxLinkApp', full: 'dropboxLinkFull', custom: 'dropboxLinkCustom' }
|
||||
options: {
|
||||
app: Locale.dropboxLinkApp,
|
||||
full: Locale.dropboxLinkFull,
|
||||
custom: Locale.dropboxLinkCustom
|
||||
}
|
||||
};
|
||||
const keyField: StorageConfigFieldText = {
|
||||
id: 'key',
|
||||
title: 'dropboxAppKey',
|
||||
desc: 'dropboxAppKeyDesc',
|
||||
title: Locale.dropboxAppKey,
|
||||
desc: Locale.dropboxAppKeyDesc,
|
||||
type: 'text',
|
||||
required: true,
|
||||
pattern: '\\w+',
|
||||
|
@ -162,8 +170,8 @@ class StorageDropbox extends StorageBase {
|
|||
};
|
||||
const secretField: StorageConfigFieldPassword = {
|
||||
id: 'secret',
|
||||
title: 'dropboxAppSecret',
|
||||
desc: 'dropboxAppSecretDesc',
|
||||
title: Locale.dropboxAppSecret,
|
||||
desc: Locale.dropboxAppSecretDesc,
|
||||
type: 'password',
|
||||
required: true,
|
||||
pattern: '\\w+',
|
||||
|
@ -171,8 +179,8 @@ class StorageDropbox extends StorageBase {
|
|||
};
|
||||
const folderField: StorageConfigFieldText = {
|
||||
id: 'folder',
|
||||
title: 'dropboxFolder',
|
||||
desc: 'dropboxFolderSettingsDesc',
|
||||
title: Locale.dropboxFolder,
|
||||
desc: Locale.dropboxFolderSettingsDesc,
|
||||
type: 'text',
|
||||
value: AppSettings.dropboxFolder ?? ''
|
||||
};
|
||||
|
|
|
@ -18,6 +18,10 @@ class StorageFileCache extends StorageBase {
|
|||
return !!Launcher;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return 'File cache';
|
||||
}
|
||||
|
||||
private async init(): Promise<string> {
|
||||
if (this._path) {
|
||||
return Promise.resolve(this._path);
|
||||
|
@ -108,6 +112,10 @@ class StorageFileCache extends StorageBase {
|
|||
unwatch: undefined;
|
||||
watch: undefined;
|
||||
getPathForName: undefined;
|
||||
getOpenConfig: undefined;
|
||||
getSettingsConfig: undefined;
|
||||
applyConfig: undefined;
|
||||
applySetting: undefined;
|
||||
}
|
||||
|
||||
export { StorageFileCache };
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'storage/types';
|
||||
import * as fs from 'fs';
|
||||
import * as NodePath from 'path';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
interface StorageFileWatcherCallbackItem {
|
||||
file: string;
|
||||
|
@ -40,6 +41,10 @@ class StorageFile extends StorageBase {
|
|||
return !!Launcher;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return Locale.file;
|
||||
}
|
||||
|
||||
async load(path: string): Promise<StorageFileData> {
|
||||
this._logger.info('Load', path);
|
||||
const ts = this._logger.ts();
|
||||
|
@ -185,6 +190,10 @@ class StorageFile extends StorageBase {
|
|||
list: undefined;
|
||||
remove: undefined;
|
||||
getPathForName: undefined;
|
||||
getOpenConfig: undefined;
|
||||
getSettingsConfig: undefined;
|
||||
applyConfig: undefined;
|
||||
applySetting: undefined;
|
||||
}
|
||||
|
||||
export { StorageFile };
|
||||
|
|
|
@ -35,6 +35,10 @@ class StorageGDrive extends StorageBase {
|
|||
return AppSettings.gdrive;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return Locale.gdrive;
|
||||
}
|
||||
|
||||
getPathForName(fileName: string): string {
|
||||
return NewFileIdPrefix + fileName;
|
||||
}
|
||||
|
@ -398,6 +402,10 @@ class StorageGDrive extends StorageBase {
|
|||
mkdir: undefined;
|
||||
watch: undefined;
|
||||
unwatch: undefined;
|
||||
getOpenConfig: undefined;
|
||||
getSettingsConfig: undefined;
|
||||
applyConfig: undefined;
|
||||
applySetting: undefined;
|
||||
}
|
||||
|
||||
export { StorageGDrive };
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
StorageRevConflictError,
|
||||
StorageSaveResult
|
||||
} from 'storage/types';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
|
||||
|
||||
|
@ -31,6 +32,10 @@ class StorageOneDrive extends StorageBase {
|
|||
return AppSettings.onedrive;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return Locale.onedrive;
|
||||
}
|
||||
|
||||
getPathForName(fileName: string): string {
|
||||
return '/drive/root:/' + fileName + '.kdbx';
|
||||
}
|
||||
|
@ -323,6 +328,10 @@ class StorageOneDrive extends StorageBase {
|
|||
|
||||
watch: undefined;
|
||||
unwatch: undefined;
|
||||
getOpenConfig: undefined;
|
||||
getSettingsConfig: undefined;
|
||||
applyConfig: undefined;
|
||||
applySetting: undefined;
|
||||
}
|
||||
|
||||
export { StorageOneDrive };
|
||||
|
|
|
@ -32,6 +32,10 @@ class StorageWebDav extends StorageBase {
|
|||
return AppSettings.webdav;
|
||||
}
|
||||
|
||||
get locName(): string {
|
||||
return Locale.webdav;
|
||||
}
|
||||
|
||||
get needShowOpenConfig(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
@ -39,24 +43,24 @@ class StorageWebDav extends StorageBase {
|
|||
getOpenConfig(): StorageOpenConfig {
|
||||
const pathField: StorageConfigFieldText = {
|
||||
id: 'path',
|
||||
title: 'openUrl',
|
||||
desc: 'openUrlDesc',
|
||||
title: Locale.openUrl,
|
||||
desc: Locale.openUrlDesc,
|
||||
type: 'text',
|
||||
required: true,
|
||||
pattern: '^https://.+'
|
||||
};
|
||||
const userField: StorageConfigFieldText = {
|
||||
id: 'user',
|
||||
title: 'openUser',
|
||||
desc: 'openUserDesc',
|
||||
placeholder: 'openUserPlaceholder',
|
||||
title: Locale.openUser,
|
||||
desc: Locale.openUserDesc,
|
||||
placeholder: Locale.openUserPlaceholder,
|
||||
type: 'text'
|
||||
};
|
||||
const passwordField: StorageConfigFieldPassword = {
|
||||
id: 'password',
|
||||
title: 'openPass',
|
||||
desc: 'openPassDesc',
|
||||
placeholder: 'openPassPlaceholder',
|
||||
title: Locale.openPass,
|
||||
desc: Locale.openPassDesc,
|
||||
placeholder: Locale.openPassPlaceholder,
|
||||
type: 'password'
|
||||
};
|
||||
|
||||
|
@ -68,14 +72,14 @@ class StorageWebDav extends StorageBase {
|
|||
getSettingsConfig(): StorageSettingsConfig {
|
||||
const methodField: StorageConfigFieldSelect = {
|
||||
id: 'webdavSaveMethod',
|
||||
title: 'webdavSaveMethod',
|
||||
title: Locale.webdavSaveMethod,
|
||||
type: 'select',
|
||||
value: AppSettings.webdavSaveMethod,
|
||||
options: { default: 'webdavSaveMove', put: 'webdavSavePut' }
|
||||
options: { move: Locale.webdavSaveMove, put: Locale.webdavSavePut }
|
||||
};
|
||||
const reloadField: StorageConfigFieldCheckbox = {
|
||||
id: 'webdavStatReload',
|
||||
title: 'webdavStatReload',
|
||||
title: Locale.webdavStatReload,
|
||||
type: 'checkbox',
|
||||
value: AppSettings.webdavStatReload ? 'true' : null
|
||||
};
|
||||
|
@ -408,6 +412,7 @@ class StorageWebDav extends StorageBase {
|
|||
watch: undefined;
|
||||
unwatch: undefined;
|
||||
getPathForName: undefined;
|
||||
applyConfig: undefined;
|
||||
}
|
||||
|
||||
export { StorageWebDav };
|
||||
|
|
|
@ -85,9 +85,7 @@ abstract class StorageBase {
|
|||
}
|
||||
|
||||
abstract load(id: string, opts?: StorageFileOptions): Promise<StorageFileData>;
|
||||
|
||||
abstract stat(id: string, opts?: StorageFileOptions): Promise<StorageFileStat>;
|
||||
|
||||
abstract save(
|
||||
id: string,
|
||||
data: ArrayBuffer,
|
||||
|
@ -96,22 +94,22 @@ abstract class StorageBase {
|
|||
): Promise<StorageSaveResult>;
|
||||
|
||||
abstract remove?(id: string, opts?: StorageFileOptions): Promise<void>;
|
||||
|
||||
abstract list?(dir?: string): Promise<StorageListItem[]>;
|
||||
|
||||
abstract mkdir?(path: string): Promise<void>;
|
||||
|
||||
abstract watch?(path: string, callback: StorageFileWatcherCallback): void;
|
||||
|
||||
abstract unwatch?(path: string, callback: StorageFileWatcherCallback): void;
|
||||
|
||||
abstract getPathForName?(fileName: string): string;
|
||||
abstract getOpenConfig?(): StorageOpenConfig;
|
||||
abstract getSettingsConfig?(): StorageSettingsConfig;
|
||||
abstract applyConfig?(config: Record<string, string | null>): Promise<void>;
|
||||
abstract applySetting?(key: string, value: string | null): void;
|
||||
|
||||
protected getOAuthConfig(): StorageOAuthConfig {
|
||||
throw new Error('OAuth is not supported');
|
||||
}
|
||||
|
||||
abstract get enabled(): boolean;
|
||||
abstract get locName(): string;
|
||||
|
||||
get loggedIn(): boolean {
|
||||
return !!this.getStoredOAuthToken();
|
||||
|
@ -494,6 +492,8 @@ abstract class StorageBase {
|
|||
protected async oauthRevokeToken(url?: string, usePost?: boolean): Promise<void> {
|
||||
const token = this.getStoredOAuthToken();
|
||||
if (token) {
|
||||
this.deleteStoredToken();
|
||||
this._oauthToken = undefined;
|
||||
if (url) {
|
||||
await this.xhr({
|
||||
url: url.replace('{token}', token.accessToken),
|
||||
|
@ -501,8 +501,6 @@ abstract class StorageBase {
|
|||
method: usePost ? 'POST' : 'GET'
|
||||
});
|
||||
}
|
||||
this.deleteStoredToken();
|
||||
this._oauthToken = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -568,7 +566,7 @@ abstract class StorageBase {
|
|||
}
|
||||
|
||||
this._logger.info('OAuth code exchanged', response);
|
||||
this.oauthProcessReturn(response);
|
||||
this.oauthProcessReturn(response.data);
|
||||
}
|
||||
|
||||
private async oauthExchangeRefreshToken(): Promise<void> {
|
||||
|
@ -617,24 +615,6 @@ abstract class StorageBase {
|
|||
get needShowOpenConfig(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
getOpenConfig(): StorageOpenConfig {
|
||||
throw new Error('getOpenConfig is not implemented');
|
||||
}
|
||||
|
||||
getSettingsConfig(): StorageSettingsConfig {
|
||||
throw new Error('getSettingsConfig is not implemented');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
applyConfig(config: Record<string, string | null>): Promise<void> {
|
||||
throw new Error('applyConfig is not implemented');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
applySetting(key: string, value: string | null): void {
|
||||
throw new Error('applySetting is not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
export { StorageBase };
|
||||
|
|
|
@ -74,8 +74,7 @@ export interface StorageSaveResult {
|
|||
path?: string;
|
||||
}
|
||||
|
||||
export interface StorageConfigField {
|
||||
type: string;
|
||||
export interface StorageConfigFieldBase {
|
||||
id: string;
|
||||
title: string;
|
||||
desc?: string;
|
||||
|
@ -83,27 +82,33 @@ export interface StorageConfigField {
|
|||
value?: string | null;
|
||||
}
|
||||
|
||||
export interface StorageConfigFieldText extends StorageConfigField {
|
||||
export interface StorageConfigFieldText extends StorageConfigFieldBase {
|
||||
type: 'text';
|
||||
pattern?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface StorageConfigFieldPassword extends StorageConfigField {
|
||||
export interface StorageConfigFieldPassword extends StorageConfigFieldBase {
|
||||
type: 'password';
|
||||
pattern?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface StorageConfigFieldSelect extends StorageConfigField {
|
||||
export interface StorageConfigFieldSelect extends StorageConfigFieldBase {
|
||||
type: 'select';
|
||||
options: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface StorageConfigFieldCheckbox extends StorageConfigField {
|
||||
export interface StorageConfigFieldCheckbox extends StorageConfigFieldBase {
|
||||
type: 'checkbox';
|
||||
}
|
||||
|
||||
export type StorageConfigField =
|
||||
| StorageConfigFieldText
|
||||
| StorageConfigFieldPassword
|
||||
| StorageConfigFieldSelect
|
||||
| StorageConfigFieldCheckbox;
|
||||
|
||||
export interface StorageOpenConfig {
|
||||
desc?: string;
|
||||
fields: StorageConfigField[];
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
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';
|
||||
|
@ -10,14 +8,9 @@ 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 { minmax } from 'util/fn';
|
||||
import { NativeModules } from 'comp/launcher/native-modules';
|
||||
import template from 'templates/settings/settings-general.hbs';
|
||||
|
||||
|
@ -80,184 +73,6 @@ class SettingsGeneralView extends View {
|
|||
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,
|
||||
autoTypeTitleFilterEnabled: AppSettingsModel.autoTypeTitleFilterEnabled,
|
||||
fieldLabelDblClickAutoType: AppSettingsModel.fieldLabelDblClickAutoType,
|
||||
supportsTitleBarStyles: Features.supportsTitleBarStyles,
|
||||
supportsCustomTitleBarAndDraggableWindow:
|
||||
Features.supportsCustomTitleBarAndDraggableWindow,
|
||||
titlebarStyle: AppSettingsModel.titlebarStyle,
|
||||
storageProviders,
|
||||
showReloadApp: Features.isStandalone,
|
||||
hasDeviceOwnerAuth: Features.isDesktop && Features.isMac,
|
||||
deviceOwnerAuth: AppSettingsModel.deviceOwnerAuth,
|
||||
deviceOwnerAuthTimeout: AppSettingsModel.deviceOwnerAuthTimeoutMinutes,
|
||||
disableOfflineStorage: AppSettingsModel.disableOfflineStorage,
|
||||
shortLivedStorageToken: AppSettingsModel.shortLivedStorageToken
|
||||
});
|
||||
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 === '...') {
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import { View } from 'framework/views/view';
|
||||
import { Storage } from 'storage';
|
||||
import template from 'templates/settings/settings-prv.hbs';
|
||||
|
||||
class SettingsPrvView extends View {
|
||||
template = template;
|
||||
|
||||
events = {
|
||||
'change .settings__general-prv-field-sel': 'changeField',
|
||||
'input .settings__general-prv-field-txt': 'changeField',
|
||||
'change .settings__general-prv-field-check': 'changeCheckbox'
|
||||
};
|
||||
|
||||
render() {
|
||||
const storage = Storage[this.model.name];
|
||||
if (storage && storage.getSettingsConfig) {
|
||||
super.render(storage.getSettingsConfig());
|
||||
}
|
||||
}
|
||||
|
||||
changeField(e) {
|
||||
const id = e.target.dataset.id;
|
||||
const value = e.target.value;
|
||||
if (value && !e.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
const storage = Storage[this.model.name];
|
||||
storage.applySetting(id, value);
|
||||
if ($(e.target).is('select')) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
changeCheckbox(e) {
|
||||
const id = e.target.dataset.id;
|
||||
const value = !!e.target.checked;
|
||||
const storage = Storage[this.model.name];
|
||||
storage.applySetting(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
export { SettingsPrvView };
|
|
@ -11,6 +11,7 @@ import { OpenController } from 'comp/app/open-controller';
|
|||
import { OpenState } from 'models/ui/open-state';
|
||||
import { GeneratorState } from 'models/ui/generator-state';
|
||||
import { Alerts } from 'comp/ui/alerts';
|
||||
import { StorageBase } from 'storage/storage-base';
|
||||
|
||||
const logger = new Logger('open');
|
||||
|
||||
|
@ -18,7 +19,7 @@ export const OpenButtons: FunctionComponent = () => {
|
|||
const secondRowVisible = useModelField(OpenState, 'secondRowVisible');
|
||||
const storageInProgress = useModelField(OpenState, 'storageInProgress');
|
||||
|
||||
const storageProviders = [];
|
||||
const storageProviders: StorageBase[] = [];
|
||||
|
||||
if (AppSettings.canOpenStorage) {
|
||||
for (const prv of Object.values(Storage.getAll())) {
|
||||
|
|
|
@ -10,7 +10,16 @@ import {
|
|||
PasswordGeneratorPreset
|
||||
} from 'util/generators/password-generator';
|
||||
import { Locale } from 'util/locale';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
|
||||
const CharRangeTitles: Record<CharRange, string> = {
|
||||
upper: Locale.genPsUpper,
|
||||
lower: Locale.genPsLower,
|
||||
digits: Locale.genPsDigits,
|
||||
special: Locale.genPsSpecial,
|
||||
brackets: Locale.genPsBrackets,
|
||||
high: Locale.genPsHigh,
|
||||
ambiguous: Locale.genPsAmbiguous
|
||||
};
|
||||
|
||||
export const GeneratorPresetsPanel: FunctionComponent = () => {
|
||||
const backClicked = () => Workspace.showList();
|
||||
|
@ -58,7 +67,7 @@ export const GeneratorPresetsPanel: FunctionComponent = () => {
|
|||
const range = key as CharRange;
|
||||
return {
|
||||
name: range,
|
||||
title: Locale.get(`genPs${StringFormat.capFirst(range)}`) ?? '',
|
||||
title: CharRangeTitles[range] ?? '',
|
||||
enabled: !!selected[range],
|
||||
sample: rangeOverride[range] || CharRanges[range]
|
||||
};
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralAdvancedView } from 'views/settings/general/settings-general-advanced-view';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { Features } from 'util/features';
|
||||
|
||||
export const SettingsGeneralAdvanced: FunctionComponent = () => {
|
||||
return h(SettingsGeneralAdvancedView, null);
|
||||
return h(SettingsGeneralAdvancedView, {
|
||||
devTools: !!Launcher,
|
||||
showReloadApp: Features.isStandalone
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,48 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralAppearanceView } from 'views/settings/general/settings-general-appearance-view';
|
||||
import { SettingsManager } from 'comp/settings/settings-manager';
|
||||
import { Locale } from 'util/locale';
|
||||
import { ThemeWatcher } from 'comp/browser/theme-watcher';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
import { Features } from 'util/features';
|
||||
|
||||
export const SettingsGeneralAppearance: FunctionComponent = () => {
|
||||
return h(SettingsGeneralAppearanceView, null);
|
||||
const getAllThemes = () => {
|
||||
const themes: Record<string, string> = {};
|
||||
if (AppSettings.autoSwitchTheme) {
|
||||
const ignoredThemes = new Set<string>();
|
||||
for (const config of SettingsManager.autoSwitchedThemes) {
|
||||
ignoredThemes.add(config.dark);
|
||||
ignoredThemes.add(config.light);
|
||||
const activeTheme = ThemeWatcher.dark ? config.dark : config.light;
|
||||
themes[activeTheme] = config.name;
|
||||
}
|
||||
for (const [th, name] of Object.entries(SettingsManager.allThemes)) {
|
||||
if (!ignoredThemes.has(th)) {
|
||||
themes[th] = name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const [th, name] of Object.entries(SettingsManager.allThemes)) {
|
||||
themes[th] = name;
|
||||
}
|
||||
}
|
||||
return themes;
|
||||
};
|
||||
|
||||
return h(SettingsGeneralAppearanceView, {
|
||||
locales: SettingsManager.allLocales,
|
||||
activeLocale: Locale.localeName,
|
||||
themes: getAllThemes(),
|
||||
activeTheme: SettingsManager.activeTheme ?? '',
|
||||
autoSwitchTheme: AppSettings.autoSwitchTheme,
|
||||
fontSize: AppSettings.fontSize,
|
||||
supportsTitleBarStyles: Features.supportsTitleBarStyles,
|
||||
titlebarStyle: AppSettings.titlebarStyle,
|
||||
supportsCustomTitleBarAndDraggableWindow: Features.supportsCustomTitleBarAndDraggableWindow,
|
||||
expandGroups: AppSettings.expandGroups,
|
||||
canSetTableView: !Features.isMobile,
|
||||
tableView: AppSettings.tableView,
|
||||
colorfulIcons: AppSettings.colorfulIcons
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralAuditView } from 'views/settings/general/settings-general-audit-view';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
|
||||
export const SettingsGeneralAudit: FunctionComponent = () => {
|
||||
return h(SettingsGeneralAuditView, null);
|
||||
return h(SettingsGeneralAuditView, {
|
||||
auditPasswords: AppSettings.auditPasswords,
|
||||
auditPasswordEntropy: AppSettings.auditPasswordEntropy,
|
||||
excludePinsFromAudit: AppSettings.excludePinsFromAudit,
|
||||
checkPasswordsOnHIBP: AppSettings.checkPasswordsOnHIBP,
|
||||
auditPasswordAge: AppSettings.auditPasswordAge
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralFunctionView } from 'views/settings/general/settings-general-function-view';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
import { Features } from 'util/features';
|
||||
|
||||
export const SettingsGeneralFunction: FunctionComponent = () => {
|
||||
return h(SettingsGeneralFunctionView, null);
|
||||
return h(SettingsGeneralFunctionView, {
|
||||
canAutoSaveOnClose: !!Launcher,
|
||||
autoSave: AppSettings.autoSave,
|
||||
autoSaveInterval: AppSettings.autoSaveInterval,
|
||||
rememberKeyFiles: AppSettings.rememberKeyFiles,
|
||||
supportFiles: !!Launcher,
|
||||
canClearClipboard: !!Launcher,
|
||||
clipboardSeconds: AppSettings.clipboardSeconds,
|
||||
canMinimize: !!Launcher,
|
||||
minimizeOnClose: AppSettings.minimizeOnClose,
|
||||
minimizeOnFieldCopy: AppSettings.minimizeOnFieldCopy,
|
||||
canAutoType: !!Launcher,
|
||||
directAutotype: AppSettings.directAutotype,
|
||||
autoTypeTitleFilterEnabled: AppSettings.autoTypeTitleFilterEnabled,
|
||||
fieldLabelDblClickAutoType: AppSettings.fieldLabelDblClickAutoType,
|
||||
useMarkdown: AppSettings.useMarkdown,
|
||||
useGroupIconForEntries: AppSettings.useGroupIconForEntries,
|
||||
hasDeviceOwnerAuth: Features.isDesktop && Features.isMac,
|
||||
deviceOwnerAuth: AppSettings.deviceOwnerAuth,
|
||||
deviceOwnerAuthTimeout: AppSettings.deviceOwnerAuthTimeoutMinutes
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralLockView } from 'views/settings/general/settings-general-lock-view';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
|
||||
export const SettingsGeneralLock: FunctionComponent = () => {
|
||||
return h(SettingsGeneralLockView, null);
|
||||
return h(SettingsGeneralLockView, {
|
||||
idleMinutes: AppSettings.idleMinutes,
|
||||
canDetectMinimize: !!Launcher,
|
||||
lockOnMinimize: AppSettings.lockOnMinimize,
|
||||
lockOnCopy: AppSettings.lockOnCopy,
|
||||
canAutoType: !!Launcher,
|
||||
lockOnAutoType: AppSettings.lockOnAutoType,
|
||||
canDetectOsSleep: !!Launcher,
|
||||
lockOnOsLock: AppSettings.lockOnOsLock
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralStorageProviderView } from 'views/settings/general/settings-general-storage-provider-view';
|
||||
import { Storage } from 'storage';
|
||||
|
||||
export const SettingsGeneralStorageProvider: FunctionComponent<{ name: string }> = ({ name }) => {
|
||||
const storage = Storage.get(name);
|
||||
if (!storage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const config = storage.getSettingsConfig?.();
|
||||
if (!config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldChanged = (id: string, value: string | null) => {
|
||||
storage.applySetting?.(id, value);
|
||||
};
|
||||
|
||||
return h(SettingsGeneralStorageProviderView, {
|
||||
name,
|
||||
fields: config.fields,
|
||||
|
||||
fieldChanged
|
||||
});
|
||||
};
|
|
@ -1,6 +1,24 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralStorageView } from 'views/settings/general/settings-general-storage-view';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
import { Storage } from 'storage';
|
||||
|
||||
export const SettingsGeneralStorage: FunctionComponent = () => {
|
||||
return h(SettingsGeneralStorageView, null);
|
||||
const getStorageProviders = () => {
|
||||
const storageProviders = Object.values(Storage.getAll()).filter((prv) => !prv.system);
|
||||
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));
|
||||
return storageProviders.map((sp) => ({
|
||||
name: sp.name,
|
||||
locName: sp.locName,
|
||||
enabled: sp.enabled,
|
||||
hasConfig: !!sp.getSettingsConfig,
|
||||
loggedIn: sp.loggedIn
|
||||
}));
|
||||
};
|
||||
|
||||
return h(SettingsGeneralStorageView, {
|
||||
disableOfflineStorage: AppSettings.disableOfflineStorage,
|
||||
shortLivedStorageToken: AppSettings.shortLivedStorageToken,
|
||||
storageProviders: getStorageProviders()
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,85 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralUpdateView } from 'views/settings/general/settings-general-update-view';
|
||||
import { Updater } from 'comp/app/updater';
|
||||
import { Launcher } from 'comp/launcher';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
import { useModelWatcher } from 'util/ui/hooks';
|
||||
import { Locale } from 'util/locale';
|
||||
import { SemVer } from 'util/data/semver';
|
||||
import { RuntimeInfo } from 'const/runtime-info';
|
||||
import { DateFormat } from 'util/formatting/date-format';
|
||||
import { RuntimeData } from 'models/runtime-data';
|
||||
|
||||
export const SettingsGeneralUpdate: FunctionComponent = () => {
|
||||
return h(SettingsGeneralUpdateView, null);
|
||||
useModelWatcher(Updater);
|
||||
|
||||
const getUpdateInfo = () => {
|
||||
switch (Updater.status) {
|
||||
case 'checking':
|
||||
return Locale.setGenUpdateChecking + '...';
|
||||
case 'error': {
|
||||
let errMsg = Locale.setGenErrorChecking;
|
||||
if (Updater.updateError) {
|
||||
errMsg += ': ' + Updater.updateError;
|
||||
}
|
||||
if (RuntimeData.lastSuccessUpdateCheckDate && RuntimeData.lastUpdateVersion) {
|
||||
errMsg +=
|
||||
'. ' +
|
||||
Locale.setGenLastCheckSuccess.with(
|
||||
DateFormat.dtStr(RuntimeData.lastSuccessUpdateCheckDate)
|
||||
) +
|
||||
': ' +
|
||||
Locale.setGenLastCheckVer.with(RuntimeData.lastUpdateVersion);
|
||||
}
|
||||
return errMsg;
|
||||
}
|
||||
case 'ok': {
|
||||
if (!RuntimeData.lastUpdateVersion) {
|
||||
return Locale.setGenErrorChecking + ': no version';
|
||||
}
|
||||
let msg =
|
||||
Locale.setGenCheckedAt +
|
||||
' ' +
|
||||
(RuntimeData.lastUpdateCheckDate
|
||||
? DateFormat.dtStr(RuntimeData.lastUpdateCheckDate)
|
||||
: '') +
|
||||
': ';
|
||||
const cmp = SemVer.compareVersions(
|
||||
RuntimeInfo.version,
|
||||
RuntimeData.lastUpdateVersion
|
||||
);
|
||||
if (cmp >= 0) {
|
||||
msg += Locale.setGenLatestVer;
|
||||
} else {
|
||||
msg +=
|
||||
Locale.setGenNewVer.with(RuntimeData.lastUpdateVersion) +
|
||||
' ' +
|
||||
(RuntimeData.lastUpdateVersionReleaseDate
|
||||
? DateFormat.dStr(RuntimeData.lastUpdateVersionReleaseDate)
|
||||
: '');
|
||||
}
|
||||
switch (Updater.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;
|
||||
}
|
||||
};
|
||||
|
||||
return h(SettingsGeneralUpdateView, {
|
||||
updateWaitingReload: Updater.updateStatus === 'ready' && !Launcher,
|
||||
autoUpdate: AppSettings.autoUpdate,
|
||||
showUpdateBlock: Updater.enabled,
|
||||
updateInfo: getUpdateInfo(),
|
||||
updateInProgress: Updater.updateInProgress,
|
||||
updateReady: Updater.updateStatus === 'ready',
|
||||
updateFound: Updater.updateStatus === 'found'
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { FunctionComponent, h } from 'preact';
|
||||
import { SettingsGeneralView } from 'views/settings/settings-general-view';
|
||||
import { useModelWatcher } from 'util/ui/hooks';
|
||||
import { AppSettings } from 'models/app-settings';
|
||||
|
||||
export const SettingsGeneral: FunctionComponent = () => {
|
||||
useModelWatcher(AppSettings);
|
||||
|
||||
return h(SettingsGeneralView, null);
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ function withReplace(name: string): LocWithReplace {
|
|||
// prettier-ignore
|
||||
export const Locale = {
|
||||
set,
|
||||
get,
|
||||
// get,
|
||||
get localeName(): string { return activeLocaleName; },
|
||||
|
||||
// this code is generated using npm run generate-locale
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { LocWithReplace } from 'util/locale';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
|
||||
export const LocalizedWith: FunctionComponent<{ str: LocWithReplace }> = ({ str, children }) => {
|
||||
const [first, ...rest] = str.with('{}').split('{}');
|
||||
export const LocalizedWith: FunctionComponent<{ str: LocWithReplace; capitalize?: boolean }> = ({
|
||||
str,
|
||||
capitalize,
|
||||
children
|
||||
}) => {
|
||||
let val = str.with('{}');
|
||||
if (capitalize) {
|
||||
val = StringFormat.capFirst(val);
|
||||
}
|
||||
const [first, ...rest] = val.split('{}');
|
||||
return (
|
||||
<>
|
||||
{first}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { classes } from 'util/ui/classes';
|
|||
|
||||
interface StorageProvider {
|
||||
name: string;
|
||||
locName: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
|
@ -126,7 +127,7 @@ export const OpenButtonsView: FunctionComponent<{
|
|||
onClick={() => storageClicked(prv.name)}
|
||||
>
|
||||
{prv.icon ? <i class={`fa fa-${prv.icon} open__icon-i`} /> : null}
|
||||
<div class="open__icon-text">{Locale.get(prv.name)}</div>
|
||||
<div class="open__icon-text">{prv.locName}</div>
|
||||
</div>
|
||||
))}
|
||||
{showDemoInSecondRow ? (
|
||||
|
|
|
@ -1,5 +1,34 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
|
||||
export const SettingsGeneralAdvancedView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralAdvancedView: FunctionComponent<{
|
||||
devTools: boolean;
|
||||
showReloadApp: boolean;
|
||||
}> = ({ devTools, showReloadApp }) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="advanced">{Locale.advanced}</h2>
|
||||
<a class="settings__general-show-advanced">{Locale.setGenShowAdvanced}</a>
|
||||
<div class="settings__general-advanced hide">
|
||||
{devTools ? (
|
||||
<>
|
||||
<button class="btn-silent settings__general-dev-tools-link">
|
||||
{Locale.setGenDevTools}
|
||||
</button>
|
||||
<button class="btn-silent settings__general-try-beta-link">
|
||||
{Locale.setGenTryBeta}
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
{showReloadApp ? (
|
||||
<button class="btn-silent settings__general-reload-app-link">
|
||||
{Locale.setGenReloadApp}
|
||||
</button>
|
||||
) : null}
|
||||
<button class="btn-silent settings__general-show-logs-link">
|
||||
{Locale.setGenShowAppLogs}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,172 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { AppSettingsFontSize, AppSettingsTitlebarStyle } from 'models/app-settings';
|
||||
import { classes } from 'util/ui/classes';
|
||||
|
||||
export const SettingsGeneralAppearanceView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralAppearanceView: FunctionComponent<{
|
||||
locales: Record<string, string>;
|
||||
activeLocale: string;
|
||||
themes: Record<string, string>;
|
||||
activeTheme: string;
|
||||
autoSwitchTheme: boolean;
|
||||
fontSize: AppSettingsFontSize;
|
||||
supportsTitleBarStyles: boolean;
|
||||
titlebarStyle: AppSettingsTitlebarStyle;
|
||||
supportsCustomTitleBarAndDraggableWindow: boolean;
|
||||
expandGroups: boolean;
|
||||
canSetTableView: boolean;
|
||||
tableView: boolean;
|
||||
colorfulIcons: boolean;
|
||||
}> = ({
|
||||
locales,
|
||||
activeLocale,
|
||||
themes,
|
||||
activeTheme,
|
||||
autoSwitchTheme,
|
||||
fontSize,
|
||||
supportsTitleBarStyles,
|
||||
titlebarStyle,
|
||||
supportsCustomTitleBarAndDraggableWindow,
|
||||
expandGroups,
|
||||
canSetTableView,
|
||||
tableView,
|
||||
colorfulIcons
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="appearance">{Locale.setGenAppearance}</h2>
|
||||
<div>
|
||||
<label for="settings__general-locale">{Locale.setGenLocale}:</label>
|
||||
<select
|
||||
class="settings__general-locale settings__select input-base"
|
||||
id="settings__general-locale"
|
||||
>
|
||||
{Object.entries(locales).map(([key, name]) => (
|
||||
<option key={key} value={key} selected={key === activeLocale}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
<option value="...">({Locale.setGenLocOther})</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>{Locale.setGenTheme}:</label>
|
||||
<div class="settings__general-themes">
|
||||
{Object.entries(themes).map(([key, name]) => (
|
||||
<div
|
||||
key={key}
|
||||
class={classes({
|
||||
[`th-${key}`]: true,
|
||||
'settings__general-theme': true,
|
||||
'settings__general-theme--selected': key === activeTheme
|
||||
})}
|
||||
data-theme={key}
|
||||
>
|
||||
<div class="settings__general-theme-name">{name}</div>
|
||||
<button class="settings__general-theme-button">
|
||||
<i class="fa fa-ellipsis-h" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
class="settings__general-theme settings__general-theme-plugins"
|
||||
data-theme="..."
|
||||
>
|
||||
<div class="settings__general-theme-plugins-name">
|
||||
{Locale.setGenMoreThemes}
|
||||
</div>
|
||||
<i class="settings__general-theme-plugins-icon fa fa-puzzle-piece" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-auto-switch-theme"
|
||||
id="settings__general-auto-switch-theme"
|
||||
checked={autoSwitchTheme}
|
||||
/>
|
||||
<label for="settings__general-auto-switch-theme">
|
||||
{Locale.setGenAutoSwitchTheme}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="settings__general-font-size">{Locale.setGenFontSize}:</label>
|
||||
<select
|
||||
class="settings__general-font-size settings__select input-base"
|
||||
id="settings__general-font-size"
|
||||
>
|
||||
<option value="0" selected={fontSize === 0}>
|
||||
{Locale.setGenFontSizeNormal}
|
||||
</option>
|
||||
<option value="1" selected={fontSize === 1}>
|
||||
{Locale.setGenFontSizeLarge}
|
||||
</option>
|
||||
<option value="2" selected={fontSize === 2}>
|
||||
{Locale.setGenFontSizeLargest}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
{supportsTitleBarStyles ? (
|
||||
<>
|
||||
<div>
|
||||
<label for="settings__general-titlebar-style">
|
||||
{Locale.setGenTitlebarStyle}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__general-titlebar-style settings__select input-base"
|
||||
id="settings__general-titlebar-style"
|
||||
>
|
||||
<option value="default" selected={titlebarStyle === 'default'}>
|
||||
{Locale.setGenTitlebarStyleDefault}
|
||||
</option>
|
||||
<option value="hidden" selected={titlebarStyle === 'hidden'}>
|
||||
{Locale.setGenTitlebarStyleHidden}
|
||||
</option>
|
||||
{supportsCustomTitleBarAndDraggableWindow ? (
|
||||
<option
|
||||
value="hidden-inset"
|
||||
selected={titlebarStyle === 'hidden-inset'}
|
||||
>
|
||||
{Locale.setGenTitlebarStyleHiddenInset}
|
||||
</option>
|
||||
) : null}
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-expand"
|
||||
id="settings__general-expand"
|
||||
checked={expandGroups}
|
||||
/>
|
||||
<label for="settings__general-expand">{Locale.setGenShowSubgroups}</label>
|
||||
</div>
|
||||
{canSetTableView ? (
|
||||
<>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-table-view"
|
||||
id="settings__general-table-view"
|
||||
checked={tableView}
|
||||
/>
|
||||
<label for="settings__general-table-view">{Locale.setGenTableView}</label>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-colorful-icons"
|
||||
id="settings__general-colorful-icons"
|
||||
checked={colorfulIcons}
|
||||
/>
|
||||
<label for="settings__general-colorful-icons">{Locale.setGenColorfulIcons}</label>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,111 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { LocalizedWith } from 'views/components/localized-with';
|
||||
import { Links } from 'const/links';
|
||||
|
||||
export const SettingsGeneralAuditView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralAuditView: FunctionComponent<{
|
||||
auditPasswords: boolean;
|
||||
auditPasswordEntropy: boolean;
|
||||
excludePinsFromAudit: boolean;
|
||||
checkPasswordsOnHIBP: boolean;
|
||||
auditPasswordAge: number;
|
||||
}> = ({
|
||||
auditPasswords,
|
||||
auditPasswordEntropy,
|
||||
excludePinsFromAudit,
|
||||
checkPasswordsOnHIBP,
|
||||
auditPasswordAge
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="audit">{Locale.setGenAudit}</h2>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-audit-passwords"
|
||||
id="settings__general-audit-passwords"
|
||||
checked={auditPasswords}
|
||||
/>
|
||||
<label for="settings__general-audit-passwords">{Locale.setGenAuditPasswords}</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-audit-password-entropy"
|
||||
id="settings__general-audit-password-entropy"
|
||||
checked={auditPasswordEntropy}
|
||||
/>
|
||||
<label for="settings__general-audit-password-entropy">
|
||||
{Locale.setGenAuditPasswordEntropy}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-exclude-pins-from-audit"
|
||||
id="settings__general-exclude-pins-from-audit"
|
||||
checked={excludePinsFromAudit}
|
||||
/>
|
||||
<label for="settings__general-exclude-pins-from-audit">
|
||||
{Locale.setGenExcludePinsFromAudit}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-check-passwords-on-hibp"
|
||||
id="settings__general-check-passwords-on-hibp"
|
||||
checked={checkPasswordsOnHIBP}
|
||||
/>
|
||||
<label for="settings__general-check-passwords-on-hibp">
|
||||
<LocalizedWith str={Locale.setGenCheckPasswordsOnHIBP}>
|
||||
<a href={Links.HaveIBeenPwned} rel="noreferrer noopener" target="_blank">
|
||||
Have I Been Pwned
|
||||
</a>
|
||||
</LocalizedWith>
|
||||
</label>
|
||||
<i class="fa fa-info-circle info-btn settings__general-toggle-help-hibp" />
|
||||
<div class="settings__general-help-hibp hide">
|
||||
<LocalizedWith str={Locale.setGenHelpHIBP}>
|
||||
<a
|
||||
href={Links.HaveIBeenPwnedPrivacy}
|
||||
rel="noreferrer noopener"
|
||||
target="_blank"
|
||||
>
|
||||
{Locale.setGenHelpHIBPLink}
|
||||
</a>
|
||||
</LocalizedWith>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="settings__general-audit-password-age">
|
||||
{Locale.setGenAuditPasswordAge}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__select input-base settings__general-audit-password-age"
|
||||
id="settings__general-audit-password-age"
|
||||
value={auditPasswordAge}
|
||||
>
|
||||
<option value="0">{Locale.setGenAuditPasswordAgeOff}</option>
|
||||
<option value="1">{Locale.setGenAuditPasswordAgeOneYear}</option>
|
||||
<option value="2">
|
||||
<LocalizedWith str={Locale.setGenAuditPasswordAgeYears}>2</LocalizedWith>
|
||||
</option>
|
||||
<option value="3">
|
||||
<LocalizedWith str={Locale.setGenAuditPasswordAgeYears}>3</LocalizedWith>
|
||||
</option>
|
||||
<option value="5">
|
||||
<LocalizedWith str={Locale.setGenAuditPasswordAgeYears}>5</LocalizedWith>
|
||||
</option>
|
||||
<option value="10">
|
||||
<LocalizedWith str={Locale.setGenAuditPasswordAgeYears}>10</LocalizedWith>
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,288 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { LocalizedWith } from 'views/components/localized-with';
|
||||
import { AppSettingsDeviceOwnerAuth, AppSettingsRememberKeyFiles } from 'models/app-settings';
|
||||
import { StringFormat } from 'util/formatting/string-format';
|
||||
|
||||
export const SettingsGeneralFunctionView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralFunctionView: FunctionComponent<{
|
||||
canAutoSaveOnClose: boolean;
|
||||
autoSave: boolean;
|
||||
autoSaveInterval: number;
|
||||
rememberKeyFiles: AppSettingsRememberKeyFiles | null;
|
||||
supportFiles: boolean;
|
||||
canClearClipboard: boolean;
|
||||
clipboardSeconds: number;
|
||||
canMinimize: boolean;
|
||||
minimizeOnClose: boolean;
|
||||
minimizeOnFieldCopy: boolean;
|
||||
canAutoType: boolean;
|
||||
directAutotype: boolean;
|
||||
autoTypeTitleFilterEnabled: boolean;
|
||||
fieldLabelDblClickAutoType: boolean;
|
||||
useMarkdown: boolean;
|
||||
useGroupIconForEntries: boolean;
|
||||
hasDeviceOwnerAuth: boolean;
|
||||
deviceOwnerAuth: AppSettingsDeviceOwnerAuth | null;
|
||||
deviceOwnerAuthTimeout: number;
|
||||
}> = ({
|
||||
canAutoSaveOnClose,
|
||||
autoSave,
|
||||
autoSaveInterval,
|
||||
rememberKeyFiles,
|
||||
supportFiles,
|
||||
canClearClipboard,
|
||||
clipboardSeconds,
|
||||
canMinimize,
|
||||
minimizeOnClose,
|
||||
minimizeOnFieldCopy,
|
||||
canAutoType,
|
||||
directAutotype,
|
||||
autoTypeTitleFilterEnabled,
|
||||
fieldLabelDblClickAutoType,
|
||||
useMarkdown,
|
||||
useGroupIconForEntries,
|
||||
hasDeviceOwnerAuth,
|
||||
deviceOwnerAuth,
|
||||
deviceOwnerAuthTimeout
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="function">{Locale.setGenFunction}</h2>
|
||||
{canAutoSaveOnClose ? (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-auto-save"
|
||||
id="settings__general-auto-save"
|
||||
checked={autoSave}
|
||||
/>
|
||||
<label for="settings__general-auto-save">{Locale.setGenAutoSyncOnClose}</label>
|
||||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<label for="settings__general-auto-save-interval">
|
||||
{Locale.setGenAutoSyncTimer}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__select input-base settings__general-auto-save-interval"
|
||||
id="settings__general-auto-save-interval"
|
||||
value={autoSaveInterval}
|
||||
>
|
||||
<option value="0">{Locale.setGenAutoSyncTimerOff}</option>
|
||||
<option value="-1">{Locale.setGenAutoSyncTimerOnChange}</option>
|
||||
<option value="1">
|
||||
<LocalizedWith str={Locale.setGenAutoSyncTimerInterval}>1</LocalizedWith>
|
||||
</option>
|
||||
<option value="5">
|
||||
<LocalizedWith str={Locale.setGenAutoSyncTimerInterval}>5</LocalizedWith>
|
||||
</option>
|
||||
<option value="15">
|
||||
<LocalizedWith str={Locale.setGenAutoSyncTimerInterval}>15</LocalizedWith>
|
||||
</option>
|
||||
<option value="30">
|
||||
<LocalizedWith str={Locale.setGenAutoSyncTimerInterval}>30</LocalizedWith>
|
||||
</option>
|
||||
<option value="60">
|
||||
<LocalizedWith str={Locale.setGenAutoSyncTimerInterval}>60</LocalizedWith>
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="settings__general-remember-key-files">
|
||||
{Locale.setGenRememberKeyFiles}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__general-remember-key-files settings__select input-base"
|
||||
id="settings__general-remember-key-files"
|
||||
>
|
||||
<option value="" selected={!rememberKeyFiles}>
|
||||
{Locale.setGenNoRememberKeyFiles}
|
||||
</option>
|
||||
<option value="data" selected={rememberKeyFiles === 'data'}>
|
||||
{Locale.setGenRememberKeyFilesData}
|
||||
</option>
|
||||
{supportFiles ? (
|
||||
<option value="path" selected={rememberKeyFiles === 'path'}>
|
||||
{Locale.setGenRememberKeyFilesPath}
|
||||
</option>
|
||||
) : null}
|
||||
</select>
|
||||
</div>
|
||||
{canClearClipboard ? (
|
||||
<div>
|
||||
<label for="settings__general-clipboard">{Locale.setGenClearClip}:</label>
|
||||
<select
|
||||
class="settings__general-clipboard settings__select input-base"
|
||||
id="settings__general-clipboard"
|
||||
value={clipboardSeconds}
|
||||
>
|
||||
<option value="0">{Locale.setGenNoClear}</option>
|
||||
<option value="5">
|
||||
<LocalizedWith str={Locale.setGenClearSeconds}>5</LocalizedWith>
|
||||
</option>
|
||||
<option value="10">
|
||||
<LocalizedWith str={Locale.setGenClearSeconds}>10</LocalizedWith>
|
||||
</option>
|
||||
<option value="15">
|
||||
<LocalizedWith str={Locale.setGenClearSeconds}>15</LocalizedWith>
|
||||
</option>
|
||||
<option value="60">{Locale.setGenClearMinute}</option>
|
||||
</select>
|
||||
</div>
|
||||
) : null}
|
||||
{canMinimize ? (
|
||||
<>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-minimize"
|
||||
id="settings__general-minimize"
|
||||
checked={minimizeOnClose}
|
||||
/>
|
||||
<label for="settings__general-minimize">{Locale.setGenMinInstead}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-minimize-on-field-copy"
|
||||
id="settings__general-minimize-on-field-copy"
|
||||
checked={minimizeOnFieldCopy}
|
||||
/>
|
||||
<label for="settings__general-minimize-on-field-copy">
|
||||
{Locale.setGenMinOnFieldCopy}
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{canAutoType ? (
|
||||
<>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-direct-autotype"
|
||||
id="settings__general-direct-autotype"
|
||||
checked={directAutotype}
|
||||
/>
|
||||
<label for="settings__general-direct-autotype">
|
||||
{Locale.setGenDirectAutotype}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-autotype-title-filter"
|
||||
id="settings__general-autotype-title-filter"
|
||||
checked={autoTypeTitleFilterEnabled}
|
||||
/>
|
||||
<label for="settings__general-autotype-title-filter">
|
||||
{Locale.setGenAutoTypeTitleFilterEnabled}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-field-label-dblclick-autotype"
|
||||
id="settings__general-field-label-dblclick-autotype"
|
||||
checked={fieldLabelDblClickAutoType}
|
||||
/>
|
||||
<label for="settings__general-field-label-dblclick-autotype">
|
||||
{Locale.setGenFieldLabelDblClickAutoType}
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-use-markdown"
|
||||
id="settings__general-use-markdown"
|
||||
checked={useMarkdown}
|
||||
/>
|
||||
<label for="settings__general-use-markdown">{Locale.setGenUseMarkdown}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-use-group-icon-for-entries"
|
||||
id="settings__general-use-group-icon-for-entries"
|
||||
checked={useGroupIconForEntries}
|
||||
/>
|
||||
<label for="settings__general-use-group-icon-for-entries">
|
||||
{Locale.setGenUseGroupIconForEntries}
|
||||
</label>
|
||||
</div>
|
||||
{hasDeviceOwnerAuth ? (
|
||||
<>
|
||||
<div>
|
||||
<label for="settings__general-device-owner-auth">
|
||||
{Locale.setGenTouchId}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__general-device-owner-auth settings__select input-base"
|
||||
id="settings__general-device-owner-auth"
|
||||
>
|
||||
<option value="" selected={!deviceOwnerAuth}>
|
||||
{Locale.setGenTouchIdDisabled}
|
||||
</option>
|
||||
<option value="memory" selected={deviceOwnerAuth === 'memory'}>
|
||||
{Locale.setGenTouchIdMemory}
|
||||
</option>
|
||||
<option value="file" selected={deviceOwnerAuth === 'file'}>
|
||||
{Locale.setGenTouchIdFile}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
{deviceOwnerAuth ? (
|
||||
<>
|
||||
<label for="settings__general-device-owner-auth-timeout">
|
||||
{Locale.setGenTouchIdPass}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__general-device-owner-auth-timeout settings__select input-base"
|
||||
id="settings__general-device-owner-auth-timeout"
|
||||
value={deviceOwnerAuthTimeout}
|
||||
>
|
||||
<option value="1">{StringFormat.capFirst(Locale.oneMinute)}</option>
|
||||
<option value="5">
|
||||
<LocalizedWith str={Locale.minutes} capitalize={true}>
|
||||
5
|
||||
</LocalizedWith>
|
||||
</option>
|
||||
<option value="30">
|
||||
<LocalizedWith str={Locale.minutes} capitalize={true}>
|
||||
30
|
||||
</LocalizedWith>
|
||||
</option>
|
||||
<option value="60">{StringFormat.capFirst(Locale.oneHour)}</option>
|
||||
<option value="120">
|
||||
<LocalizedWith str={Locale.hours} capitalize={true}>
|
||||
2
|
||||
</LocalizedWith>
|
||||
</option>
|
||||
<option value="480">
|
||||
<LocalizedWith str={Locale.hours} capitalize={true}>
|
||||
8
|
||||
</LocalizedWith>
|
||||
</option>
|
||||
<option value="1440">{StringFormat.capFirst(Locale.oneDay)}</option>
|
||||
<option value="10080">
|
||||
{StringFormat.capFirst(Locale.oneWeek)}
|
||||
</option>
|
||||
{deviceOwnerAuth === 'file' ? (
|
||||
<option value="43200">
|
||||
{StringFormat.capFirst(Locale.oneMonth)}
|
||||
</option>
|
||||
) : null}
|
||||
{deviceOwnerAuth === 'file' ? (
|
||||
<option value="525600">
|
||||
{StringFormat.capFirst(Locale.oneYear)}
|
||||
</option>
|
||||
) : null}
|
||||
</select>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,114 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { LocalizedWith } from 'views/components/localized-with';
|
||||
|
||||
export const SettingsGeneralLockView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralLockView: FunctionComponent<{
|
||||
idleMinutes: number;
|
||||
canDetectMinimize: boolean;
|
||||
lockOnMinimize: boolean;
|
||||
lockOnCopy: boolean;
|
||||
canAutoType: boolean;
|
||||
lockOnAutoType: boolean;
|
||||
canDetectOsSleep: boolean;
|
||||
lockOnOsLock: boolean;
|
||||
}> = ({
|
||||
idleMinutes,
|
||||
canDetectMinimize,
|
||||
lockOnMinimize,
|
||||
lockOnCopy,
|
||||
canAutoType,
|
||||
lockOnAutoType,
|
||||
canDetectOsSleep,
|
||||
lockOnOsLock
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="lock">{Locale.setGenLock}</h2>
|
||||
<div>
|
||||
<label for="settings__general-idle-minutes">{Locale.setGenLockInactive}:</label>
|
||||
<select
|
||||
class="settings__general-idle-minutes settings__select input-base"
|
||||
id="settings__general-idle-minutes"
|
||||
value={idleMinutes}
|
||||
>
|
||||
<option value="0">{Locale.setGenNoAutoLock}</option>
|
||||
<option value="5">
|
||||
<LocalizedWith str={Locale.setGenLockMinutes}>5</LocalizedWith>
|
||||
</option>
|
||||
<option value="10">
|
||||
<LocalizedWith str={Locale.setGenLockMinutes}>10</LocalizedWith>
|
||||
</option>
|
||||
<option value="15">
|
||||
<LocalizedWith str={Locale.setGenLockMinutes}>15</LocalizedWith>
|
||||
</option>
|
||||
<option value="30">
|
||||
<LocalizedWith str={Locale.setGenLockMinutes}>30</LocalizedWith>
|
||||
</option>
|
||||
<option value="60">{Locale.setGenLockHour}</option>
|
||||
<option value="180">
|
||||
<LocalizedWith str={Locale.setGenLockHours}>3</LocalizedWith>
|
||||
</option>
|
||||
<option value="360">
|
||||
<LocalizedWith str={Locale.setGenLockHours}>6</LocalizedWith>
|
||||
</option>
|
||||
<option value="720">
|
||||
<LocalizedWith str={Locale.setGenLockHours}>12</LocalizedWith>
|
||||
</option>
|
||||
<option value="1440">{Locale.setGenLockDay}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{canDetectMinimize ? (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-lock-on-minimize"
|
||||
id="settings__general-lock-on-minimize"
|
||||
checked={lockOnMinimize}
|
||||
/>
|
||||
<label for="settings__general-lock-on-minimize">
|
||||
{Locale.setGenLockMinimize}
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-lock-on-copy"
|
||||
id="settings__general-lock-on-copy"
|
||||
checked={lockOnCopy}
|
||||
/>
|
||||
<label for="settings__general-lock-on-copy">{Locale.setGenLockCopy}</label>
|
||||
</div>
|
||||
|
||||
{canAutoType ? (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-lock-on-auto-type"
|
||||
id="settings__general-lock-on-auto-type"
|
||||
checked={lockOnAutoType}
|
||||
/>
|
||||
<label for="settings__general-lock-on-auto-type">
|
||||
{Locale.setGenLockAutoType}
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{canDetectOsSleep ? (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-lock-on-os-lock"
|
||||
id="settings__general-lock-on-os-lock"
|
||||
checked={lockOnOsLock}
|
||||
/>
|
||||
<label for="settings__general-lock-on-os-lock">
|
||||
{Locale.setGenLockOrSleep}
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { StorageConfigField } from 'storage/types';
|
||||
|
||||
export const SettingsGeneralStorageProviderView: FunctionComponent<{
|
||||
name: string;
|
||||
fields: StorageConfigField[];
|
||||
|
||||
fieldChanged: (id: string, value: string | null) => void;
|
||||
}> = ({ name, fields, fieldChanged }) => {
|
||||
const inputChanged = (e: Event, fieldId: string) => {
|
||||
const input = e.target as HTMLInputElement;
|
||||
if (!input?.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
fieldChanged(fieldId, input.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={`settings__general-prv settings__general-prv-${name}`}>
|
||||
<div class="settings__general-prv-fields">
|
||||
{fields.map((field) =>
|
||||
field.type === 'select' ? (
|
||||
<div>
|
||||
<label for={`settings__general-prv-field-sel-${field.id}`}>
|
||||
{field.title}:
|
||||
</label>
|
||||
<select
|
||||
class="settings__select input-base settings__general-prv-field settings__general-prv-field-sel"
|
||||
id={`settings__general-prv-field-sel-${field.id}`}
|
||||
data-id={field.id}
|
||||
value={field.value || ''}
|
||||
onChange={(e) =>
|
||||
fieldChanged(field.id, (e.target as HTMLSelectElement).value)
|
||||
}
|
||||
>
|
||||
{Object.entries(field.options).map(([fieldValue, fieldTitle]) => (
|
||||
<option key={fieldValue} value={fieldValue}>
|
||||
{fieldTitle}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
) : field.type === 'checkbox' ? (
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="input-base settings__general-prv-field settings__input settings__general-prv-field-check"
|
||||
id={`settings__general-prv-field-check-${field.id}`}
|
||||
checked={!!field.value}
|
||||
value={field.value || ''}
|
||||
data-id={field.id}
|
||||
onClick={(e) =>
|
||||
fieldChanged(
|
||||
field.id,
|
||||
(e.target as HTMLInputElement).checked ? 'true' : null
|
||||
)
|
||||
}
|
||||
/>
|
||||
<label for={`settings__general-prv-field-check-${field.id}`}>
|
||||
{field.title}
|
||||
</label>
|
||||
{field.desc ? (
|
||||
<div class="settings__general-prv-field-desc muted-color">
|
||||
{field.desc}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<label for={`settings__general-prv-field-txt-${field.id}`}>
|
||||
{field.title}:
|
||||
</label>
|
||||
{field.desc ? (
|
||||
<div class="settings__general-prv-field-desc muted-color">
|
||||
{field.desc}
|
||||
</div>
|
||||
) : null}
|
||||
<input
|
||||
type={field.type}
|
||||
class="input-base settings__general-prv-field settings__input settings__general-prv-field-txt"
|
||||
id={`settings__general-prv-field-txt-${field.id}`}
|
||||
autocomplete="off"
|
||||
value={field.value || ''}
|
||||
data-id={field.id}
|
||||
placeholder={field.placeholder}
|
||||
required={field.required}
|
||||
pattern={field.pattern}
|
||||
onInput={(e) => inputChanged(e, field.id)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,5 +1,73 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { SettingsGeneralStorageProvider } from 'ui/settings/general/settings-general-storage-provider';
|
||||
|
||||
export const SettingsGeneralStorageView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
interface SettingsGeneralStorageProviderItem {
|
||||
name: string;
|
||||
locName: string;
|
||||
enabled: boolean;
|
||||
loggedIn: boolean;
|
||||
hasConfig: boolean;
|
||||
}
|
||||
|
||||
export const SettingsGeneralStorageView: FunctionComponent<{
|
||||
disableOfflineStorage: boolean;
|
||||
shortLivedStorageToken: boolean;
|
||||
storageProviders: SettingsGeneralStorageProviderItem[];
|
||||
}> = ({ disableOfflineStorage, shortLivedStorageToken, storageProviders }) => {
|
||||
return (
|
||||
<>
|
||||
<h2 id="storage">{Locale.setGenStorage}</h2>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-disable-offline-storage"
|
||||
id="settings__general-disable-offline-storage"
|
||||
checked={disableOfflineStorage}
|
||||
/>
|
||||
<label for="settings__general-disable-offline-storage">
|
||||
{Locale.setGenDisableOfflineStorage}
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="settings__input input-base settings__general-short-lived-storage-token"
|
||||
id="settings__general-short-lived-storage-token"
|
||||
checked={shortLivedStorageToken}
|
||||
/>
|
||||
<label for="settings__general-short-lived-storage-token">
|
||||
{Locale.setGenShortLivedStorageToken}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{storageProviders.map((prv) => (
|
||||
<div key={prv.name}>
|
||||
<h4 class="settings__general-storage-header">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`settings__general-prv-check-${prv.name}`}
|
||||
class="settings__general-prv-check"
|
||||
data-storage={prv.name}
|
||||
checked={prv.enabled}
|
||||
/>
|
||||
<label for={`settings__general-prv-check-${prv.name}`}>{prv.locName}</label>
|
||||
</h4>
|
||||
{prv.enabled && prv.hasConfig ? (
|
||||
<div class={`settings__general-prv-wrap settings__general-${prv.name}}`}>
|
||||
<SettingsGeneralStorageProvider name={prv.name} />
|
||||
</div>
|
||||
) : null}
|
||||
{prv.loggedIn ? (
|
||||
<button
|
||||
class="btn-silent settings__general-prv-logout"
|
||||
data-storage={prv.name}
|
||||
>
|
||||
{Locale.setGenStorageLogout}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,86 @@
|
|||
import { FunctionComponent } from 'preact';
|
||||
import { Locale } from 'util/locale';
|
||||
import { AppSettingsAutoUpdate } from 'models/app-settings';
|
||||
import { Links } from 'const/links';
|
||||
|
||||
export const SettingsGeneralUpdateView: FunctionComponent = () => {
|
||||
return <></>;
|
||||
export const SettingsGeneralUpdateView: FunctionComponent<{
|
||||
updateWaitingReload: boolean;
|
||||
autoUpdate: AppSettingsAutoUpdate | null;
|
||||
showUpdateBlock: boolean;
|
||||
updateInfo: string;
|
||||
updateInProgress: boolean;
|
||||
updateReady: boolean;
|
||||
updateFound: boolean;
|
||||
}> = ({
|
||||
updateWaitingReload,
|
||||
autoUpdate,
|
||||
showUpdateBlock,
|
||||
updateInfo,
|
||||
updateInProgress,
|
||||
updateReady,
|
||||
updateFound
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{updateWaitingReload ? (
|
||||
<>
|
||||
<h2 class="action-color">{Locale.setGenUpdate}</h2>
|
||||
<div>
|
||||
{Locale.setGenNewVersion}.{' '}
|
||||
<a href={Links.ReleaseNotes} target="_blank" rel="noreferrer">
|
||||
{Locale.setGenReleaseNotes}
|
||||
</a>
|
||||
</div>
|
||||
<div class="settings__general-update-buttons">
|
||||
<button class="settings__general-restart-btn">
|
||||
{Locale.setGenReloadToUpdate}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{showUpdateBlock ? (
|
||||
<>
|
||||
<h2>{Locale.setGenUpdate}</h2>
|
||||
<div>
|
||||
<select class="settings__general-auto-update settings__select input-base">
|
||||
<option value="install" selected={autoUpdate === 'install'}>
|
||||
{Locale.setGenUpdateAuto}
|
||||
</option>
|
||||
<option value="check" selected={autoUpdate === 'check'}>
|
||||
{Locale.setGenUpdateCheck}
|
||||
</option>
|
||||
<option value="" selected={!autoUpdate}>
|
||||
{Locale.setGenNoUpdate}
|
||||
</option>
|
||||
</select>
|
||||
<div>{updateInfo}</div>
|
||||
<a href={Links.ReleaseNotes} target="_blank" rel="noreferrer">
|
||||
{Locale.setGenReleaseNotes}
|
||||
</a>
|
||||
</div>
|
||||
<div class="settings__general-update-buttons">
|
||||
{updateInProgress ? (
|
||||
<button class="settings__general-update-btn btn-silent" disabled>
|
||||
{Locale.setGenUpdateChecking}
|
||||
</button>
|
||||
) : (
|
||||
<button class="settings__general-update-btn btn-silent">
|
||||
{Locale.setGenCheckUpdate}
|
||||
</button>
|
||||
)}
|
||||
{updateReady ? (
|
||||
<button class="settings__general-restart-btn">
|
||||
{Locale.setGenRestartToUpdate}
|
||||
</button>
|
||||
) : null}
|
||||
{updateFound ? (
|
||||
<button class="settings__general-update-found-btn">
|
||||
{Locale.setGenDownloadAndRestart}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,345 +0,0 @@
|
|||
{{#if updateWaitingReload}}
|
||||
<h2 class="action-color">{{res 'setGenUpdate'}}</h2>
|
||||
<div>{{res 'setGenNewVersion'}}. <a href="{{releaseNotesLink}}" target="_blank">{{res 'setGenReleaseNotes'}}</a></div>
|
||||
<div class="settings__general-update-buttons">
|
||||
<button class="settings__general-restart-btn">{{res 'setGenReloadToUpdate'}}</button>
|
||||
</div>
|
||||
{{else if updateManual}}
|
||||
<h2 class="action-color">{{res 'setGenUpdate'}}</h2>
|
||||
<div>{{res 'setGenUpdateManual'}}</div>
|
||||
<div class="settings__general-update-buttons">
|
||||
<button class="settings__general-download-update-btn">{{res 'setGenDownloadUpdate'}}</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if showUpdateBlock}}
|
||||
<h2>{{res 'setGenUpdate'}}</h2>
|
||||
<div>
|
||||
<select class="settings__general-auto-update settings__select input-base">
|
||||
<option value="install" {{#ifeq autoUpdate 'install'}}selected{{/ifeq}}>{{res 'setGenUpdateAuto'}}</option>
|
||||
<option value="check" {{#ifeq autoUpdate 'check'}}selected{{/ifeq}}>{{res 'setGenUpdateCheck'}}</option>
|
||||
<option value="" {{#unless autoUpdate}}selected{{/unless}}>{{res 'setGenNoUpdate'}}</option>
|
||||
</select>
|
||||
<div>{{updateInfo}}</div>
|
||||
<a href="{{releaseNotesLink}}" target="_blank">{{res 'setGenReleaseNotes'}}</a>
|
||||
</div>
|
||||
<div class="settings__general-update-buttons">
|
||||
{{#if updateInProgress}}
|
||||
<button class="settings__general-update-btn btn-silent" disabled>{{res 'setGenUpdateChecking'}}</button>
|
||||
{{else}}
|
||||
<button class="settings__general-update-btn btn-silent">{{res 'setGenCheckUpdate'}}</button>
|
||||
{{/if}}
|
||||
{{#if updateReady}}<button class="settings__general-restart-btn">{{res 'setGenRestartToUpdate'}}</button>{{/if}}
|
||||
{{#if updateFound}}<button class="settings__general-update-found-btn">{{res 'setGenDownloadAndRestart'}}</button>{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h2 id="appearance">{{res 'setGenAppearance'}}</h2>
|
||||
{{#if locales}}
|
||||
<div>
|
||||
<label for="settings__general-locale">{{res 'setGenLocale'}}:</label>
|
||||
<select class="settings__general-locale settings__select input-base" id="settings__general-locale">
|
||||
{{#each locales as |name key|}}
|
||||
<option value="{{key}}" {{#ifeq key ../activeLocale}}selected{{/ifeq}}>{{name}}</option>
|
||||
{{/each}}
|
||||
<option value="...">({{res 'setGenLocOther'}})</option>
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<label>{{res 'setGenTheme'}}:</label>
|
||||
<div class="settings__general-themes">
|
||||
{{#each themes as |name key|}}
|
||||
<div class="th-{{key}} settings__general-theme
|
||||
{{~#ifeq key ../activeTheme}} settings__general-theme--selected{{/ifeq~}}"
|
||||
data-theme="{{key}}"
|
||||
>
|
||||
<div class="settings__general-theme-name">{{name}}</div>
|
||||
<button class="settings__general-theme-button"><i class="fa fa-ellipsis-h"></i></button>
|
||||
</div>
|
||||
{{/each}}
|
||||
<div class="settings__general-theme settings__general-theme-plugins" data-theme="...">
|
||||
<div class="settings__general-theme-plugins-name">{{res 'setGenMoreThemes'}}</div>
|
||||
<i class="settings__general-theme-plugins-icon fa fa-puzzle-piece"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-auto-switch-theme" id="settings__general-auto-switch-theme" {{#if autoSwitchTheme}}checked{{/if}} />
|
||||
<label for="settings__general-auto-switch-theme">{{res 'setGenAutoSwitchTheme'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="settings__general-font-size">{{res 'setGenFontSize'}}:</label>
|
||||
<select class="settings__general-font-size settings__select input-base" id="settings__general-font-size">
|
||||
<option value="0" {{#ifeq fontSize 0}}selected{{/ifeq}}>{{res 'setGenFontSizeNormal'}}</option>
|
||||
<option value="1" {{#ifeq fontSize 1}}selected{{/ifeq}}>{{res 'setGenFontSizeLarge'}}</option>
|
||||
<option value="2" {{#ifeq fontSize 2}}selected{{/ifeq}}>{{res 'setGenFontSizeLargest'}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{#if supportsTitleBarStyles}}
|
||||
<div>
|
||||
<label for="settings__general-titlebar-style">{{res 'setGenTitlebarStyle'}}:</label>
|
||||
<select class="settings__general-titlebar-style settings__select input-base" id="settings__general-titlebar-style">
|
||||
<option value="default" {{#ifeq titlebarStyle 'default'}}selected{{/ifeq}}>{{res 'setGenTitlebarStyleDefault'}}</option>
|
||||
<option value="hidden" {{#ifeq titlebarStyle 'hidden'}}selected{{/ifeq}}>{{res 'setGenTitlebarStyleHidden'}}</option>
|
||||
{{#if supportsCustomTitleBarAndDraggableWindow}}
|
||||
<option value="hidden-inset" {{#ifeq titlebarStyle 'hidden-inset'}}selected{{/ifeq}}>{{res 'setGenTitlebarStyleHiddenInset'}}</option>
|
||||
{{/if}}
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-expand" id="settings__general-expand" {{#if expandGroups}}checked{{/if}} />
|
||||
<label for="settings__general-expand">{{res 'setGenShowSubgroups'}}</label>
|
||||
</div>
|
||||
{{#if canSetTableView}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-table-view" id="settings__general-table-view" {{#if tableView}}checked{{/if}} />
|
||||
<label for="settings__general-table-view">{{res 'setGenTableView'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-colorful-icons" id="settings__general-colorful-icons" {{#if colorfulIcons}}checked{{/if}} />
|
||||
<label for="settings__general-colorful-icons">{{res 'setGenColorfulIcons'}}</label>
|
||||
</div>
|
||||
|
||||
<h2 id="function">{{res 'setGenFunction'}}</h2>
|
||||
{{#if canAutoSaveOnClose}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-auto-save" id="settings__general-auto-save"
|
||||
{{#if autoSave}}checked{{/if}} />
|
||||
<label for="settings__general-auto-save">{{res 'setGenAutoSyncOnClose'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<label for="settings__general-auto-save-interval">{{res 'setGenAutoSyncTimer'}}:</label>
|
||||
<select class="settings__select input-base settings__general-auto-save-interval"
|
||||
id="settings__general-auto-save-interval">
|
||||
<option value="0" {{#ifeq autoSaveInterval 0}}selected{{/ifeq}}>{{res 'setGenAutoSyncTimerOff'}}</option>
|
||||
<option value="-1" {{#ifeq autoSaveInterval -1}}selected{{/ifeq}}>{{res 'setGenAutoSyncTimerOnChange'}}</option>
|
||||
<option value="1" {{#ifeq autoSaveInterval 1}}selected{{/ifeq}}>{{#res 'setGenAutoSyncTimerInterval'}}
|
||||
1{{/res}}</option>
|
||||
<option value="5" {{#ifeq autoSaveInterval 5}}selected{{/ifeq}}>{{#res 'setGenAutoSyncTimerInterval'}}
|
||||
5{{/res}}</option>
|
||||
<option value="15" {{#ifeq autoSaveInterval 15}}selected{{/ifeq}}>{{#res 'setGenAutoSyncTimerInterval'}}
|
||||
15{{/res}}</option>
|
||||
<option value="30" {{#ifeq autoSaveInterval 30}}selected{{/ifeq}}>{{#res 'setGenAutoSyncTimerInterval'}}
|
||||
30{{/res}}</option>
|
||||
<option value="60" {{#ifeq autoSaveInterval 60}}selected{{/ifeq}}>{{#res 'setGenAutoSyncTimerInterval'}}
|
||||
60{{/res}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="settings__general-remember-key-files">{{res 'setGenRememberKeyFiles'}}:</label>
|
||||
<select class="settings__general-remember-key-files settings__select input-base" id="settings__general-remember-key-files">
|
||||
<option value="" {{#unless rememberKeyFiles}}selected{{/unless}}>{{res 'setGenNoRememberKeyFiles'}}</option>
|
||||
<option value="data" {{#ifeq rememberKeyFiles 'data'}}selected{{/ifeq}}>{{res 'setGenRememberKeyFilesData'}}</option>
|
||||
{{#if supportFiles}}<option value="path" {{#ifeq rememberKeyFiles 'path'}}selected{{/ifeq}}>{{res 'setGenRememberKeyFilesPath'}}</option>{{/if}}
|
||||
</select>
|
||||
</div>
|
||||
{{#if canClearClipboard}}
|
||||
<div>
|
||||
<label for="settings__general-clipboard">{{res 'setGenClearClip'}}:</label>
|
||||
<select class="settings__general-clipboard settings__select input-base" id="settings__general-clipboard">
|
||||
<option value="0" {{#unless clipboardSeconds}}selected{{/unless}}>{{res 'setGenNoClear'}}</option>
|
||||
<option value="5" {{#ifeq clipboardSeconds 5}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}5{{/res}}</option>
|
||||
<option value="10" {{#ifeq clipboardSeconds 10}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}10{{/res}}</option>
|
||||
<option value="15" {{#ifeq clipboardSeconds 15}}selected{{/ifeq}}>{{#res 'setGenClearSeconds'}}15{{/res}}</option>
|
||||
<option value="60" {{#ifeq clipboardSeconds 60}}selected{{/ifeq}}>{{res 'setGenClearMinute'}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if canMinimize}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-minimize" id="settings__general-minimize"
|
||||
{{#if minimizeOnClose}}checked{{/if}} />
|
||||
<label for="settings__general-minimize">{{res 'setGenMinInstead'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-minimize-on-field-copy" id="settings__general-minimize-on-field-copy"
|
||||
{{#if minimizeOnFieldCopy}}checked{{/if}} />
|
||||
<label for="settings__general-minimize-on-field-copy">{{res 'setGenMinOnFieldCopy'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if canAutoType}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-direct-autotype"
|
||||
id="settings__general-direct-autotype" {{#if directAutotype}}checked{{/if}} />
|
||||
<label for="settings__general-direct-autotype">{{res 'setGenDirectAutotype'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-autotype-title-filter"
|
||||
id="settings__general-autotype-title-filter" {{#if autoTypeTitleFilterEnabled}}checked{{/if}} />
|
||||
<label for="settings__general-autotype-title-filter">{{res 'setGenAutoTypeTitleFilterEnabled'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-field-label-dblclick-autotype"
|
||||
id="settings__general-field-label-dblclick-autotype" {{#if fieldLabelDblClickAutoType}}checked{{/if}} />
|
||||
<label for="settings__general-field-label-dblclick-autotype">{{res 'setGenFieldLabelDblClickAutoType'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-use-markdown" id="settings__general-use-markdown" {{#if useMarkdown}}checked{{/if}} />
|
||||
<label for="settings__general-use-markdown">{{res 'setGenUseMarkdown'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-use-group-icon-for-entries"
|
||||
id="settings__general-use-group-icon-for-entries" {{#if useGroupIconForEntries}}checked{{/if}} />
|
||||
<label for="settings__general-use-group-icon-for-entries">{{res 'setGenUseGroupIconForEntries'}}</label>
|
||||
</div>
|
||||
{{#if hasDeviceOwnerAuth}}
|
||||
<div>
|
||||
<label for="settings__general-device-owner-auth">{{res 'setGenTouchId'}}:</label>
|
||||
<select class="settings__general-device-owner-auth settings__select input-base" id="settings__general-device-owner-auth">
|
||||
<option value="" {{#unless deviceOwnerAuth}}selected{{/unless}}>{{res 'setGenTouchIdDisabled'}}</option>
|
||||
<option value="memory" {{#ifeq deviceOwnerAuth 'memory'}}selected{{/ifeq}}>{{res 'setGenTouchIdMemory'}}</option>
|
||||
<option value="file" {{#ifeq deviceOwnerAuth 'file'}}selected{{/ifeq}}>{{res 'setGenTouchIdFile'}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{#if deviceOwnerAuth}}
|
||||
<label for="settings__general-device-owner-auth-timeout">{{res 'setGenTouchIdPass'}}:</label>
|
||||
<select class="settings__general-device-owner-auth-timeout settings__select input-base" id="settings__general-device-owner-auth-timeout">
|
||||
<option value="1" {{#ifeq deviceOwnerAuthTimeout 1}}selected{{/ifeq}}>{{Res 'oneMinute'}}</option>
|
||||
<option value="5" {{#ifeq deviceOwnerAuthTimeout 5}}selected{{/ifeq}}>{{#Res 'minutes'}}5{{/Res}}</option>
|
||||
<option value="30" {{#ifeq deviceOwnerAuthTimeout 30}}selected{{/ifeq}}>{{#Res 'minutes'}}30{{/Res}}</option>
|
||||
<option value="60" {{#ifeq deviceOwnerAuthTimeout 60}}selected{{/ifeq}}>{{Res 'oneHour'}}</option>
|
||||
<option value="120" {{#ifeq deviceOwnerAuthTimeout 120}}selected{{/ifeq}}>{{#Res 'hours'}}2{{/Res}}</option>
|
||||
<option value="480" {{#ifeq deviceOwnerAuthTimeout 480}}selected{{/ifeq}}>{{#Res 'hours'}}8{{/Res}}</option>
|
||||
<option value="1440" {{#ifeq deviceOwnerAuthTimeout 1440}}selected{{/ifeq}}>{{Res 'oneDay'}}</option>
|
||||
<option value="10080" {{#ifeq deviceOwnerAuthTimeout 10080}}selected{{/ifeq}}>{{Res 'oneWeek'}}</option>
|
||||
{{#ifeq deviceOwnerAuth 'file'}}
|
||||
<option value="43200" {{#ifeq deviceOwnerAuthTimeout 43200}}selected{{/ifeq}}>{{Res 'oneMonth'}}</option>
|
||||
<option value="525600" {{#ifeq deviceOwnerAuthTimeout 525600}}selected{{/ifeq}}>{{Res 'oneYear'}}</option>
|
||||
{{/ifeq}}
|
||||
</select>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<h2 id="audit">{{res 'setGenAudit'}}</h2>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-audit-passwords"
|
||||
id="settings__general-audit-passwords" {{#if auditPasswords}}checked{{/if}} />
|
||||
<label for="settings__general-audit-passwords">{{res 'setGenAuditPasswords'}}</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-audit-password-entropy"
|
||||
id="settings__general-audit-password-entropy" {{#if auditPasswordEntropy}}checked{{/if}} />
|
||||
<label for="settings__general-audit-password-entropy">{{res 'setGenAuditPasswordEntropy'}}</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-exclude-pins-from-audit"
|
||||
id="settings__general-exclude-pins-from-audit" {{#if excludePinsFromAudit}}checked{{/if}} />
|
||||
<label for="settings__general-exclude-pins-from-audit">{{res 'setGenExcludePinsFromAudit'}}</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-check-passwords-on-hibp"
|
||||
id="settings__general-check-passwords-on-hibp" {{#if checkPasswordsOnHIBP}}checked{{/if}} />
|
||||
<label for="settings__general-check-passwords-on-hibp">
|
||||
{{~#res 'setGenCheckPasswordsOnHIBP'~}}
|
||||
<a href="{{hibpLink}}" rel="noreferrer noopener" target="_blank">Have I Been Pwned</a>
|
||||
{{~/res~}}
|
||||
</label>
|
||||
<i class="fa fa-info-circle info-btn settings__general-toggle-help-hibp"></i>
|
||||
<div class="settings__general-help-hibp hide">
|
||||
{{~#res 'setGenHelpHIBP'~}}
|
||||
<a href="{{hibpPrivacyLink}}" rel="noreferrer noopener" target="_blank">{{res 'setGenHelpHIBPLink'}}</a>
|
||||
{{~/res~}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="settings__general-audit-password-age">{{res 'setGenAuditPasswordAge'}}:</label>
|
||||
<select class="settings__select input-base settings__general-audit-password-age"
|
||||
id="settings__general-audit-password-age">
|
||||
<option value="0" {{#ifeq auditPasswordAge 0}}selected{{/ifeq}}>{{res 'setGenAuditPasswordAgeOff'}}</option>
|
||||
<option value="1" {{#ifeq auditPasswordAge 1}}selected{{/ifeq}}>{{res 'setGenAuditPasswordAgeOneYear'}}</option>
|
||||
<option value="2" {{#ifeq auditPasswordAge 2}}selected{{/ifeq}}>{{#res 'setGenAuditPasswordAgeYears'}}
|
||||
2{{/res}}</option>
|
||||
<option value="3" {{#ifeq auditPasswordAge 3}}selected{{/ifeq}}>{{#res 'setGenAuditPasswordAgeYears'}}
|
||||
3{{/res}}</option>
|
||||
<option value="5" {{#ifeq auditPasswordAge 5}}selected{{/ifeq}}>{{#res 'setGenAuditPasswordAgeYears'}}
|
||||
5{{/res}}</option>
|
||||
<option value="10" {{#ifeq auditPasswordAge 10}}selected{{/ifeq}}>{{#res 'setGenAuditPasswordAgeYears'}}
|
||||
10{{/res}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h2 id="lock">{{res 'setGenLock'}}</h2>
|
||||
<div>
|
||||
<label for="settings__general-idle-minutes">{{res 'setGenLockInactive'}}:</label>
|
||||
<select class="settings__general-idle-minutes settings__select input-base" id="settings__general-idle-minutes">
|
||||
<option value="0" {{#cmp idleMinutes 0 '<='}}selected{{/cmp}}>{{res 'setGenNoAutoLock'}}</option>
|
||||
<option value="5" {{#ifeq idleMinutes 5}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}5{{/res}}</option>
|
||||
<option value="10" {{#ifeq idleMinutes 10}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}10{{/res}}</option>
|
||||
<option value="15" {{#ifeq idleMinutes 15}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}15{{/res}}</option>
|
||||
<option value="30" {{#ifeq idleMinutes 30}}selected{{/ifeq}}>{{#res 'setGenLockMinutes'}}30{{/res}}</option>
|
||||
<option value="60" {{#ifeq idleMinutes 60}}selected{{/ifeq}}>{{res 'setGenLockHour'}}</option>
|
||||
<option value="180" {{#ifeq idleMinutes 180}}selected{{/ifeq}}>{{#res 'setGenLockHours'}}3{{/res}}</option>
|
||||
<option value="360" {{#ifeq idleMinutes 360}}selected{{/ifeq}}>{{#res 'setGenLockHours'}}6{{/res}}</option>
|
||||
<option value="720" {{#ifeq idleMinutes 720}}selected{{/ifeq}}>{{#res 'setGenLockHours'}}12{{/res}}</option>
|
||||
<option value="1440" {{#ifeq idleMinutes 1440}}selected{{/ifeq}}>{{res 'setGenLockDay'}}</option>
|
||||
</select>
|
||||
</div>
|
||||
{{#if canDetectMinimize}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-lock-on-minimize" id="settings__general-lock-on-minimize"
|
||||
{{#if lockOnMinimize}}checked{{/if}} />
|
||||
<label for="settings__general-lock-on-minimize">{{res 'setGenLockMinimize'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-lock-on-copy" id="settings__general-lock-on-copy"
|
||||
{{#if lockOnCopy}}checked{{/if}} />
|
||||
<label for="settings__general-lock-on-copy">{{res 'setGenLockCopy'}}</label>
|
||||
</div>
|
||||
{{#if canAutoType}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-lock-on-auto-type" id="settings__general-lock-on-auto-type"
|
||||
{{#if lockOnAutoType}}checked{{/if}} />
|
||||
<label for="settings__general-lock-on-auto-type">{{res 'setGenLockAutoType'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if canDetectOsSleep}}
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-lock-on-os-lock" id="settings__general-lock-on-os-lock"
|
||||
{{#if lockOnOsLock}}checked{{/if}} />
|
||||
<label for="settings__general-lock-on-os-lock">{{res 'setGenLockOrSleep'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h2 id="storage">{{res 'setGenStorage'}}</h2>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-disable-offline-storage" id="settings__general-disable-offline-storage"
|
||||
{{#if disableOfflineStorage}}checked{{/if}} />
|
||||
<label for="settings__general-disable-offline-storage">{{res 'setGenDisableOfflineStorage'}}</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-short-lived-storage-token" id="settings__general-short-lived-storage-token"
|
||||
{{#if shortLivedStorageToken}}checked{{/if}} />
|
||||
<label for="settings__general-short-lived-storage-token">{{res 'setGenShortLivedStorageToken'}}</label>
|
||||
</div>
|
||||
|
||||
{{#each storageProviders as |prv|}}
|
||||
<h4 class="settings__general-storage-header"><input
|
||||
type="checkbox" id="settings__general-prv-check-{{prv.name}}" class="settings__general-prv-check"
|
||||
data-storage="{{prv.name}}" {{#if prv.enabled}}checked{{/if}}
|
||||
/><label for="settings__general-prv-check-{{prv.name}}">{{res prv.name}}</label></h4>
|
||||
<div class="settings__general-prv-wrap settings__general-{{prv.name}} {{#ifeq prv.enabled false}}hide{{/ifeq}}"></div>
|
||||
{{#if prv.loggedIn}}<button class="btn-silent settings__general-prv-logout"
|
||||
data-storage="{{prv.name}}">{{res 'setGenStorageLogout'}}</button>{{/if}}
|
||||
{{/each}}
|
||||
|
||||
<h2 id="advanced">{{res 'advanced'}}</h2>
|
||||
<a class="settings__general-show-advanced">{{res 'setGenShowAdvanced'}}</a>
|
||||
<div class="settings__general-advanced hide">
|
||||
{{#if devTools}}
|
||||
<button class="btn-silent settings__general-dev-tools-link">{{res 'setGenDevTools'}}</button>
|
||||
<button class="btn-silent settings__general-try-beta-link">{{res 'setGenTryBeta'}}</button>
|
||||
{{/if}}
|
||||
{{#if showReloadApp}}
|
||||
<button class="btn-silent settings__general-reload-app-link">{{res 'setGenReloadApp'}}</button>
|
||||
{{/if}}
|
||||
<button class="btn-silent settings__general-show-logs-link">{{res 'setGenShowAppLogs'}}</button>
|
||||
</div>
|
|
@ -1,43 +0,0 @@
|
|||
<div class="settings__general-prv settings__general-prv-{{name}}">
|
||||
{{#if desc}}<div class="settings__general-prv-desc">{{res desc}}</div>{{/if}}
|
||||
<div class="settings__general-prv-fields">
|
||||
{{#each fields as |field ix|}}
|
||||
{{#ifeq type 'select'}}
|
||||
<div>
|
||||
<label for="settings__general-prv-field-sel-{{id}}">{{res title}}:</label>
|
||||
<select
|
||||
class="settings__select input-base settings__general-prv-field settings__general-prv-field-sel"
|
||||
id="settings__general-prv-field-sel-{{id}}"
|
||||
data-id="{{id}}">
|
||||
{{#each options as |title val|}}
|
||||
<option value="{{val}}" {{#ifeq ../value val}}selected{{/ifeq}}>{{res title}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
{{else ifeq type 'checkbox'}}
|
||||
<input type="checkbox"
|
||||
class="input-base settings__general-prv-field settings__input settings__general-prv-field-check"
|
||||
id="settings__general-prv-field-check-{{id}}"
|
||||
{{#if value}}checked{{/if}}
|
||||
value="{{value}}"
|
||||
data-id="{{id}}"
|
||||
/>
|
||||
<label for="settings__general-prv-field-check-{{id}}">{{res title}}</label>
|
||||
{{#if desc}}<div class="settings__general-prv-field-desc muted-color">{{res desc}}</div>{{/if}}
|
||||
{{else}}
|
||||
<label for="settings__general-prv-field-txt-{{id}}">{{res title}}:</label>
|
||||
{{#if desc}}<div class="settings__general-prv-field-desc muted-color">{{res desc}}</div>{{/if}}
|
||||
<input type="{{type}}"
|
||||
class="input-base settings__general-prv-field settings__input settings__general-prv-field-txt"
|
||||
id="settings__general-prv-field-txt-{{id}}"
|
||||
autocomplete="off"
|
||||
value="{{value}}"
|
||||
data-id="{{id}}"
|
||||
{{#if placeholder}}placeholder="{{res placeholder}}"{{/if}}
|
||||
{{#if required}}required{{/if}}
|
||||
{{#if pattern}}pattern="{{pattern}}"{{/if}}
|
||||
/>
|
||||
{{/ifeq}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
|
@ -4,12 +4,12 @@ import { expect } from 'chai';
|
|||
describe('Locale', () => {
|
||||
it('returns simple locale strings', () => {
|
||||
expect(Locale.name).to.eql('name');
|
||||
expect(Locale.get('name')).to.eql('name');
|
||||
// expect(Locale.get('name')).to.eql('name');
|
||||
});
|
||||
|
||||
it('returns replaced locale strings', () => {
|
||||
expect(Locale.minutes.with('3')).to.eql('3 minutes');
|
||||
expect(Locale.get('minutes')).to.eql('{} minutes');
|
||||
// expect(Locale.get('minutes')).to.eql('{} minutes');
|
||||
});
|
||||
|
||||
it('sets a custom locale', () => {
|
||||
|
|
Loading…
Reference in New Issue