This commit is contained in:
antelle 2021-07-04 16:01:34 +02:00
parent 8df3899920
commit 48c4f28d5d
No known key found for this signature in database
GPG Key ID: 63C9777AAB7C563C
18 changed files with 452 additions and 300 deletions

View File

@ -668,6 +668,20 @@ class FileController {
this.backupFile(file, data).catch(noop);
}
}
deleteAllCachedFiles(): void {
for (const fileInfo of FileManager.fileInfos) {
if (fileInfo.storage && !fileInfo.modified) {
Storage.cache.remove(fileInfo.id).catch(noop);
}
}
}
deleteAllStoredTokens(): void {
for (const storage of Object.values(Storage.getAll())) {
storage.deleteStoredToken();
}
}
}
const instance = new FileController();

View File

@ -64,7 +64,7 @@ class Updater extends Model {
);
}
private scheduleNextCheck() {
scheduleNextCheck(): void {
if (this._nextCheckTimeout) {
clearTimeout(this._nextCheckTimeout);
this._nextCheckTimeout = undefined;
@ -89,7 +89,7 @@ class Updater extends Model {
logger.info(`Next update check will happen in ${Math.round(timeDiff / 1000)}s`);
}
private async check(startedByUser: boolean) {
async check(startedByUser: boolean) {
if (!this.enabled || this.updateInProgress) {
return;
}
@ -176,6 +176,11 @@ class Updater extends Model {
}
}
async updateAndRestart() {
await this.update();
await this.installAndRestart();
}
private async update() {
if (!this.enabled) {
logger.info('Updater is disabled');

View File

@ -181,25 +181,6 @@ class AppModel {
);
}
deleteAllCachedFiles() {
for (const fileInfo of this.fileInfos) {
if (fileInfo.storage && !fileInfo.modified) {
Storage.cache.remove(fileInfo.id);
}
}
}
clearStoredKeyFiles() {
for (const fileInfo of this.fileInfos) {
fileInfo.set({
keyFileName: null,
keyFilePath: null,
keyFileHash: null
});
}
this.fileInfos.save();
}
unsetKeyFile(fileId) {
const fileInfo = this.fileInfos.get(fileId);
fileInfo.set({

View File

@ -69,4 +69,12 @@ export class FileInfo extends Model {
}
return new FileInfo(rec as { id: string; name: string });
}
clearKeyFile(): void {
this.batchSet(() => {
this.keyFileName = undefined;
this.keyFilePath = undefined;
this.keyFileHash = undefined;
});
}
}

View File

@ -188,6 +188,13 @@ class FileManager extends Model<FileManagerEvents> {
return this.files.some((f) => !f.readOnly);
}
clearStoredKeyFiles(): void {
for (const fileInfo of this.fileInfos) {
fileInfo.clearKeyFile();
}
this.saveFileInfosDelayed();
}
private fileClosed(file: File) {
if (file.storage === 'file' && file.path) {
Storage.file.unwatch(file.path);

View File

@ -130,7 +130,7 @@ abstract class StorageBase {
}
}
private deleteStoredToken(): void {
deleteStoredToken(): void {
RuntimeData.delete(`${this.name}OAuthToken`);
}

View File

@ -1,219 +0,0 @@
import { Events } from 'framework/events';
import { View } from 'framework/views/view';
import { Storage } from 'storage';
import { Updater } from 'comp/app/updater';
import { Launcher } from 'comp/launcher';
import { Links } from 'const/links';
import { AppSettingsModel } from 'models/app-settings-model';
import { minmax } from 'util/fn';
import { NativeModules } from 'comp/launcher/native-modules';
class SettingsGeneralView extends View {
changeClipboard(e) {
const clipboardSeconds = +e.target.value;
AppSettingsModel.clipboardSeconds = clipboardSeconds;
}
changeIdleMinutes(e) {
const idleMinutes = +e.target.value;
AppSettingsModel.idleMinutes = idleMinutes;
}
changeAutoUpdate(e) {
const autoUpdate = e.target.value || false;
AppSettingsModel.autoUpdate = autoUpdate;
if (autoUpdate) {
Updater.scheduleNextCheck();
}
}
checkUpdate() {
Updater.check(true);
}
changeAutoSave(e) {
const autoSave = e.target.checked || false;
AppSettingsModel.autoSave = autoSave;
}
changeAutoSaveInterval(e) {
const autoSaveInterval = e.target.value | 0;
AppSettingsModel.autoSaveInterval = autoSaveInterval;
}
changeRememberKeyFiles(e) {
const rememberKeyFiles = e.target.value || false;
AppSettingsModel.rememberKeyFiles = rememberKeyFiles;
this.appModel.clearStoredKeyFiles();
}
changeMinimize(e) {
const minimizeOnClose = e.target.checked || false;
AppSettingsModel.minimizeOnClose = minimizeOnClose;
}
changeMinimizeOnFieldCopy(e) {
const minimizeOnFieldCopy = e.target.checked || false;
AppSettingsModel.minimizeOnFieldCopy = minimizeOnFieldCopy;
}
changeAuditPasswords(e) {
const auditPasswords = e.target.checked || false;
AppSettingsModel.auditPasswords = auditPasswords;
}
changeAuditPasswordEntropy(e) {
const auditPasswordEntropy = e.target.checked || false;
AppSettingsModel.auditPasswordEntropy = auditPasswordEntropy;
}
changeExcludePinsFromAudit(e) {
const excludePinsFromAudit = e.target.checked || false;
AppSettingsModel.excludePinsFromAudit = excludePinsFromAudit;
}
changeCheckPasswordsOnHIBP(e) {
if (e.target.closest('a')) {
return;
}
const checkPasswordsOnHIBP = e.target.checked || false;
AppSettingsModel.checkPasswordsOnHIBP = checkPasswordsOnHIBP;
}
clickToggleHelpHIBP() {
this.el.querySelector('.settings__general-help-hibp').classList.toggle('hide');
}
changeAuditPasswordAge(e) {
const auditPasswordAge = e.target.value | 0;
AppSettingsModel.auditPasswordAge = auditPasswordAge;
}
changeLockOnMinimize(e) {
const lockOnMinimize = e.target.checked || false;
AppSettingsModel.lockOnMinimize = lockOnMinimize;
}
changeLockOnCopy(e) {
const lockOnCopy = e.target.checked || false;
AppSettingsModel.lockOnCopy = lockOnCopy;
}
changeLockOnAutoType(e) {
const lockOnAutoType = e.target.checked || false;
AppSettingsModel.lockOnAutoType = lockOnAutoType;
}
changeLockOnOsLock(e) {
const lockOnOsLock = e.target.checked || false;
AppSettingsModel.lockOnOsLock = lockOnOsLock;
}
changeUseMarkdown(e) {
const useMarkdown = e.target.checked || false;
AppSettingsModel.useMarkdown = useMarkdown;
Events.emit('refresh');
}
changeUseGroupIconForEntries(e) {
const useGroupIconForEntries = e.target.checked || false;
AppSettingsModel.useGroupIconForEntries = useGroupIconForEntries;
}
changeDirectAutotype(e) {
const directAutotype = e.target.checked || false;
AppSettingsModel.directAutotype = directAutotype;
}
changeAutoTypeTitleFilter(e) {
const autoTypeTitleFilterEnabled = e.target.checked || false;
AppSettingsModel.autoTypeTitleFilterEnabled = autoTypeTitleFilterEnabled;
}
changeFieldLabelDblClickAutoType(e) {
const fieldLabelDblClickAutoType = e.target.checked || false;
AppSettingsModel.fieldLabelDblClickAutoType = fieldLabelDblClickAutoType;
Events.emit('refresh');
}
changeDeviceOwnerAuth(e) {
const deviceOwnerAuth = e.target.value || null;
let deviceOwnerAuthTimeoutMinutes = AppSettingsModel.deviceOwnerAuthTimeoutMinutes | 0;
if (deviceOwnerAuth) {
const timeouts = { memory: [30, 10080], file: [30, 525600] };
const [tMin, tMax] = timeouts[deviceOwnerAuth] || [0, 0];
deviceOwnerAuthTimeoutMinutes = minmax(deviceOwnerAuthTimeoutMinutes, tMin, tMax);
}
AppSettingsModel.set({ deviceOwnerAuth, deviceOwnerAuthTimeoutMinutes });
this.render();
this.appModel.checkEncryptedPasswordsStorage();
if (!deviceOwnerAuth) {
NativeModules.hardwareCryptoDeleteKey().catch(() => {});
}
}
changeDeviceOwnerAuthTimeout(e) {
const deviceOwnerAuthTimeout = e.target.value | 0;
AppSettingsModel.deviceOwnerAuthTimeoutMinutes = deviceOwnerAuthTimeout;
}
installUpdateAndRestart() {
if (Launcher) {
Updater.installAndRestart();
} else {
window.location.reload();
}
}
downloadUpdate() {
Launcher.openLink(Links.Desktop);
}
installFoundUpdate() {
Updater.update(true, () => {
Updater.installAndRestart();
});
}
changeDisableOfflineStorage(e) {
const disableOfflineStorage = e.target.checked;
AppSettingsModel.disableOfflineStorage = disableOfflineStorage;
if (disableOfflineStorage) {
this.appModel.deleteAllCachedFiles();
}
}
changeShortLivedStorageToken(e) {
const shortLivedStorageToken = e.target.checked;
AppSettingsModel.shortLivedStorageToken = shortLivedStorageToken;
if (shortLivedStorageToken) {
for (const storage of Object.values(Storage)) {
storage.deleteStoredToken();
}
}
}
changeStorageEnabled(e) {
const storage = Storage[$(e.target).data('storage')];
if (storage) {
storage.setEnabled(e.target.checked);
AppSettingsModel[storage.name] = storage.enabled;
this.$el
.find('.settings__general-' + storage.name)
.toggleClass('hide', !e.target.checked);
}
}
logoutFromStorage(e) {
const storage = Storage[$(e.target).data('storage')];
if (storage) {
storage.logout();
$(e.target).remove();
}
}
}
export { SettingsGeneralView };

View File

@ -1,13 +1,48 @@
import { FunctionComponent, h } from 'preact';
import { SettingsGeneralAuditView } from 'views/settings/general/settings-general-audit-view';
import { AppSettings } from 'models/app-settings';
import { useState } from 'preact/hooks';
export const SettingsGeneralAudit: FunctionComponent = () => {
const [showAboutHIBP, setShowAboutHIBP] = useState(false);
const auditPasswordsChanged = () => {
AppSettings.auditPasswords = !AppSettings.auditPasswords;
};
const auditPasswordEntropyChanged = () => {
AppSettings.auditPasswordEntropy = !AppSettings.auditPasswordEntropy;
};
const excludePinsFromAuditChanged = () => {
AppSettings.excludePinsFromAudit = !AppSettings.excludePinsFromAudit;
};
const checkPasswordsOnHIBPChanged = () => {
AppSettings.checkPasswordsOnHIBP = !AppSettings.checkPasswordsOnHIBP;
};
const showAboutHIBPChanged = () => {
setShowAboutHIBP(!showAboutHIBP);
};
const auditPasswordAgeChanged = (age: number) => {
AppSettings.auditPasswordAge = age;
};
return h(SettingsGeneralAuditView, {
auditPasswords: AppSettings.auditPasswords,
auditPasswordEntropy: AppSettings.auditPasswordEntropy,
excludePinsFromAudit: AppSettings.excludePinsFromAudit,
checkPasswordsOnHIBP: AppSettings.checkPasswordsOnHIBP,
auditPasswordAge: AppSettings.auditPasswordAge
showAboutHIBP,
auditPasswordAge: AppSettings.auditPasswordAge,
auditPasswordsChanged,
auditPasswordEntropyChanged,
excludePinsFromAuditChanged,
checkPasswordsOnHIBPChanged,
showAboutHIBPChanged,
auditPasswordAgeChanged
});
};

View File

@ -1,10 +1,83 @@
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 {
AppSettings,
AppSettingsDeviceOwnerAuth,
AppSettingsRememberKeyFiles
} from 'models/app-settings';
import { Features } from 'util/features';
import { FileManager } from 'models/file-manager';
import { minmax } from 'util/fn';
export const SettingsGeneralFunction: FunctionComponent = () => {
const autoSaveChanged = () => {
AppSettings.autoSave = !AppSettings.autoSave;
};
const autoSaveIntervalChanged = (interval: number) => {
AppSettings.autoSaveInterval = interval;
};
const rememberKeyFilesChanged = (rememberKeyFiles: AppSettingsRememberKeyFiles | null) => {
AppSettings.rememberKeyFiles = rememberKeyFiles;
FileManager.clearStoredKeyFiles();
};
const clipboardSecondsChanged = (seconds: number) => {
AppSettings.clipboardSeconds = seconds;
};
const minimizeOnCloseChanged = () => {
AppSettings.minimizeOnClose = !AppSettings.minimizeOnClose;
};
const minimizeOnFieldCopyChanged = () => {
AppSettings.minimizeOnFieldCopy = !AppSettings.minimizeOnFieldCopy;
};
const directAutoTypeChanged = () => {
AppSettings.directAutotype = !AppSettings.directAutotype;
};
const autoTypeTitleFilterEnabledChanged = () => {
AppSettings.autoTypeTitleFilterEnabled = !AppSettings.autoTypeTitleFilterEnabled;
};
const fieldLabelDblClickAutoTypeChanged = () => {
AppSettings.fieldLabelDblClickAutoType = !AppSettings.fieldLabelDblClickAutoType;
};
const useMarkdownChanged = () => {
AppSettings.useMarkdown = !AppSettings.useMarkdown;
};
const useGroupIconForEntriesChanged = () => {
AppSettings.useGroupIconForEntries = !AppSettings.useGroupIconForEntries;
};
const deviceOwnerAuthChanged = (deviceOwnerAuth: AppSettingsDeviceOwnerAuth | null) => {
let deviceOwnerAuthTimeoutMinutes = AppSettings.deviceOwnerAuthTimeoutMinutes | 0;
if (deviceOwnerAuth) {
const timeouts = { memory: [30, 10080], file: [30, 525600] };
const [tMin, tMax] = timeouts[deviceOwnerAuth] || [0, 0];
deviceOwnerAuthTimeoutMinutes = minmax(deviceOwnerAuthTimeoutMinutes, tMin, tMax);
}
AppSettings.deviceOwnerAuth = deviceOwnerAuth;
AppSettings.deviceOwnerAuthTimeoutMinutes = deviceOwnerAuthTimeoutMinutes;
// TODO: device owner auth
// Workspace.checkEncryptedPasswordsStorage();
// if (!deviceOwnerAuth) {
// NativeModules.hardwareCryptoDeleteKey().catch(() => {});
// }
};
const deviceOwnerAuthTimeoutChanged = (timeout: number) => {
AppSettings.deviceOwnerAuthTimeoutMinutes = timeout;
};
return h(SettingsGeneralFunctionView, {
canAutoSaveOnClose: !!Launcher,
autoSave: AppSettings.autoSave,
@ -24,6 +97,20 @@ export const SettingsGeneralFunction: FunctionComponent = () => {
useGroupIconForEntries: AppSettings.useGroupIconForEntries,
hasDeviceOwnerAuth: Features.isDesktop && Features.isMac,
deviceOwnerAuth: AppSettings.deviceOwnerAuth,
deviceOwnerAuthTimeout: AppSettings.deviceOwnerAuthTimeoutMinutes
deviceOwnerAuthTimeout: AppSettings.deviceOwnerAuthTimeoutMinutes,
autoSaveChanged,
autoSaveIntervalChanged,
rememberKeyFilesChanged,
clipboardSecondsChanged,
minimizeOnCloseChanged,
minimizeOnFieldCopyChanged,
directAutoTypeChanged,
autoTypeTitleFilterEnabledChanged,
fieldLabelDblClickAutoTypeChanged,
useMarkdownChanged,
useGroupIconForEntriesChanged,
deviceOwnerAuthChanged,
deviceOwnerAuthTimeoutChanged
});
};

View File

@ -4,6 +4,26 @@ import { AppSettings } from 'models/app-settings';
import { Launcher } from 'comp/launcher';
export const SettingsGeneralLock: FunctionComponent = () => {
const idleMinutesChanged = (idleMinutes: number) => {
AppSettings.idleMinutes = idleMinutes;
};
const lockOnMinimizeChanged = () => {
AppSettings.lockOnMinimize = !AppSettings.lockOnMinimize;
};
const lockOnCopyChanged = () => {
AppSettings.lockOnCopy = !AppSettings.lockOnCopy;
};
const lockOnAutoTypeChanged = () => {
AppSettings.lockOnAutoType = !AppSettings.lockOnAutoType;
};
const lockOnOsLockChanged = () => {
AppSettings.lockOnOsLock = !AppSettings.lockOnOsLock;
};
return h(SettingsGeneralLockView, {
idleMinutes: AppSettings.idleMinutes,
canDetectMinimize: !!Launcher,
@ -12,6 +32,12 @@ export const SettingsGeneralLock: FunctionComponent = () => {
canAutoType: !!Launcher,
lockOnAutoType: AppSettings.lockOnAutoType,
canDetectOsSleep: !!Launcher,
lockOnOsLock: AppSettings.lockOnOsLock
lockOnOsLock: AppSettings.lockOnOsLock,
idleMinutesChanged,
lockOnMinimizeChanged,
lockOnCopyChanged,
lockOnAutoTypeChanged,
lockOnOsLockChanged
});
};

View File

@ -1,9 +1,14 @@
import { FunctionComponent, h } from 'preact';
import { SettingsGeneralStorageView } from 'views/settings/general/settings-general-storage-view';
import { AppSettings } from 'models/app-settings';
import { AppSettings, AppSettingsFieldName } from 'models/app-settings';
import { Storage } from 'storage';
import { FileController } from 'comp/app/file-controller';
import { useModelWatcher } from 'util/ui/hooks';
import { RuntimeData } from 'models/runtime-data';
export const SettingsGeneralStorage: FunctionComponent = () => {
useModelWatcher(RuntimeData);
const getStorageProviders = () => {
const storageProviders = Object.values(Storage.getAll()).filter((prv) => !prv.system);
storageProviders.sort((x, y) => (x.uipos || Infinity) - (y.uipos || Infinity));
@ -16,9 +21,41 @@ export const SettingsGeneralStorage: FunctionComponent = () => {
}));
};
const disableOfflineStorageChanged = () => {
AppSettings.disableOfflineStorage = !AppSettings.disableOfflineStorage;
if (AppSettings.disableOfflineStorage) {
FileController.deleteAllCachedFiles();
}
};
const shortLivedStorageTokenChanged = () => {
AppSettings.shortLivedStorageToken = !AppSettings.shortLivedStorageToken;
if (AppSettings.shortLivedStorageToken) {
FileController.deleteAllStoredTokens();
}
};
const storageEnabledChanged = (storageName: string) => {
const storage = Storage.get(storageName);
if (!storage) {
return;
}
AppSettings.set(storage.name as AppSettingsFieldName, !storage.enabled);
};
const logoutFromStorage = (storageName: string) => {
Storage.get(storageName)?.logout();
};
return h(SettingsGeneralStorageView, {
disableOfflineStorage: AppSettings.disableOfflineStorage,
shortLivedStorageToken: AppSettings.shortLivedStorageToken,
storageProviders: getStorageProviders()
storageProviders: getStorageProviders(),
disableOfflineStorageChanged,
shortLivedStorageTokenChanged,
storageEnabledChanged,
logoutFromStorage
});
};

View File

@ -2,16 +2,18 @@ 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 { AppSettings, AppSettingsAutoUpdate } 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';
import { noop } from 'util/fn';
export const SettingsGeneralUpdate: FunctionComponent = () => {
useModelWatcher(Updater);
useModelWatcher(RuntimeData);
const getUpdateInfo = () => {
switch (Updater.status) {
@ -73,6 +75,29 @@ export const SettingsGeneralUpdate: FunctionComponent = () => {
}
};
const installUpdateClicked = () => {
if (Launcher) {
Updater.installAndRestart().catch(noop);
} else {
window.location.reload();
}
};
const checkUpdateClicked = () => {
Updater.check(true).catch(noop);
};
const downloadAndInstallUpdateClicked = () => {
Updater.updateAndRestart().catch(noop);
};
const autoUpdateChanged = (autoUpdate: AppSettingsAutoUpdate | null) => {
AppSettings.autoUpdate = autoUpdate;
if (autoUpdate) {
Updater.scheduleNextCheck();
}
};
return h(SettingsGeneralUpdateView, {
updateWaitingReload: Updater.updateStatus === 'ready' && !Launcher,
autoUpdate: AppSettings.autoUpdate,
@ -80,6 +105,11 @@ export const SettingsGeneralUpdate: FunctionComponent = () => {
updateInfo: getUpdateInfo(),
updateInProgress: Updater.updateInProgress,
updateReady: Updater.updateStatus === 'ready',
updateFound: Updater.updateStatus === 'found'
updateFound: Updater.updateStatus === 'found',
installUpdateClicked,
checkUpdateClicked,
downloadAndInstallUpdateClicked,
autoUpdateChanged
});
};

View File

@ -9,13 +9,36 @@ export const SettingsGeneralAuditView: FunctionComponent<{
excludePinsFromAudit: boolean;
checkPasswordsOnHIBP: boolean;
auditPasswordAge: number;
showAboutHIBP: boolean;
auditPasswordsChanged: () => void;
auditPasswordEntropyChanged: () => void;
excludePinsFromAuditChanged: () => void;
checkPasswordsOnHIBPChanged: () => void;
showAboutHIBPChanged: () => void;
auditPasswordAgeChanged: (age: number) => void;
}> = ({
auditPasswords,
auditPasswordEntropy,
excludePinsFromAudit,
checkPasswordsOnHIBP,
auditPasswordAge
auditPasswordAge,
showAboutHIBP,
auditPasswordsChanged,
auditPasswordEntropyChanged,
excludePinsFromAuditChanged,
checkPasswordsOnHIBPChanged,
showAboutHIBPChanged,
auditPasswordAgeChanged
}) => {
const checkPasswordsOnHIBPClickedInternal = (e: Event) => {
if ((e.target as HTMLElement).closest('a')) {
return;
}
checkPasswordsOnHIBPChanged();
};
return (
<>
<h2 id="audit">{Locale.setGenAudit}</h2>
@ -25,6 +48,7 @@ export const SettingsGeneralAuditView: FunctionComponent<{
class="settings__input input-base settings__general-audit-passwords"
id="settings__general-audit-passwords"
checked={auditPasswords}
onClick={auditPasswordsChanged}
/>
<label for="settings__general-audit-passwords">{Locale.setGenAuditPasswords}</label>
</div>
@ -35,6 +59,7 @@ export const SettingsGeneralAuditView: FunctionComponent<{
class="settings__input input-base settings__general-audit-password-entropy"
id="settings__general-audit-password-entropy"
checked={auditPasswordEntropy}
onClick={auditPasswordEntropyChanged}
/>
<label for="settings__general-audit-password-entropy">
{Locale.setGenAuditPasswordEntropy}
@ -47,6 +72,7 @@ export const SettingsGeneralAuditView: FunctionComponent<{
class="settings__input input-base settings__general-exclude-pins-from-audit"
id="settings__general-exclude-pins-from-audit"
checked={excludePinsFromAudit}
onClick={excludePinsFromAuditChanged}
/>
<label for="settings__general-exclude-pins-from-audit">
{Locale.setGenExcludePinsFromAudit}
@ -59,6 +85,7 @@ export const SettingsGeneralAuditView: FunctionComponent<{
class="settings__input input-base settings__general-check-passwords-on-hibp"
id="settings__general-check-passwords-on-hibp"
checked={checkPasswordsOnHIBP}
onClick={checkPasswordsOnHIBPClickedInternal}
/>
<label for="settings__general-check-passwords-on-hibp">
<LocalizedWith str={Locale.setGenCheckPasswordsOnHIBP}>
@ -67,18 +94,23 @@ export const SettingsGeneralAuditView: FunctionComponent<{
</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>
<i
class="fa fa-info-circle info-btn settings__general-toggle-help-hibp"
onClick={showAboutHIBPChanged}
/>
{showAboutHIBP ? (
<div class="settings__general-help-hibp">
<LocalizedWith str={Locale.setGenHelpHIBP}>
<a
href={Links.HaveIBeenPwnedPrivacy}
rel="noreferrer noopener"
target="_blank"
>
{Locale.setGenHelpHIBPLink}
</a>
</LocalizedWith>
</div>
) : null}
</div>
<div>
@ -89,6 +121,9 @@ export const SettingsGeneralAuditView: FunctionComponent<{
class="settings__select input-base settings__general-audit-password-age"
id="settings__general-audit-password-age"
value={auditPasswordAge}
onChange={(e) =>
auditPasswordAgeChanged(+(e.target as HTMLSelectElement).value)
}
>
<option value="0">{Locale.setGenAuditPasswordAgeOff}</option>
<option value="1">{Locale.setGenAuditPasswordAgeOneYear}</option>

View File

@ -24,6 +24,20 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
hasDeviceOwnerAuth: boolean;
deviceOwnerAuth: AppSettingsDeviceOwnerAuth | null;
deviceOwnerAuthTimeout: number;
autoSaveChanged: () => void;
autoSaveIntervalChanged: (interval: number) => void;
rememberKeyFilesChanged: (rememberKeyFiles: AppSettingsRememberKeyFiles | null) => void;
clipboardSecondsChanged: (seconds: number) => void;
minimizeOnCloseChanged: () => void;
minimizeOnFieldCopyChanged: () => void;
directAutoTypeChanged: () => void;
autoTypeTitleFilterEnabledChanged: () => void;
fieldLabelDblClickAutoTypeChanged: () => void;
useMarkdownChanged: () => void;
useGroupIconForEntriesChanged: () => void;
deviceOwnerAuthChanged: (deviceOwnerAuth: AppSettingsDeviceOwnerAuth | null) => void;
deviceOwnerAuthTimeoutChanged: (timeout: number) => void;
}> = ({
canAutoSaveOnClose,
autoSave,
@ -43,7 +57,21 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
useGroupIconForEntries,
hasDeviceOwnerAuth,
deviceOwnerAuth,
deviceOwnerAuthTimeout
deviceOwnerAuthTimeout,
autoSaveChanged,
autoSaveIntervalChanged,
rememberKeyFilesChanged,
clipboardSecondsChanged,
minimizeOnCloseChanged,
minimizeOnFieldCopyChanged,
directAutoTypeChanged,
autoTypeTitleFilterEnabledChanged,
fieldLabelDblClickAutoTypeChanged,
useMarkdownChanged,
useGroupIconForEntriesChanged,
deviceOwnerAuthChanged,
deviceOwnerAuthTimeoutChanged
}) => {
return (
<>
@ -55,6 +83,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-auto-save"
id="settings__general-auto-save"
checked={autoSave}
onClick={autoSaveChanged}
/>
<label for="settings__general-auto-save">{Locale.setGenAutoSyncOnClose}</label>
</div>
@ -67,6 +96,9 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__select input-base settings__general-auto-save-interval"
id="settings__general-auto-save-interval"
value={autoSaveInterval}
onChange={(e) =>
autoSaveIntervalChanged(+(e.target as HTMLSelectElement).value)
}
>
<option value="0">{Locale.setGenAutoSyncTimerOff}</option>
<option value="-1">{Locale.setGenAutoSyncTimerOnChange}</option>
@ -94,17 +126,16 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
<select
class="settings__general-remember-key-files settings__select input-base"
id="settings__general-remember-key-files"
value={rememberKeyFiles || ''}
onChange={(e) => {
const val = (e.target as HTMLSelectElement).value || null;
rememberKeyFilesChanged(val as AppSettingsRememberKeyFiles | null);
}}
>
<option value="" selected={!rememberKeyFiles}>
{Locale.setGenNoRememberKeyFiles}
</option>
<option value="data" selected={rememberKeyFiles === 'data'}>
{Locale.setGenRememberKeyFilesData}
</option>
<option value="">{Locale.setGenNoRememberKeyFiles}</option>
<option value="data">{Locale.setGenRememberKeyFilesData}</option>
{supportFiles ? (
<option value="path" selected={rememberKeyFiles === 'path'}>
{Locale.setGenRememberKeyFilesPath}
</option>
<option value="path">{Locale.setGenRememberKeyFilesPath}</option>
) : null}
</select>
</div>
@ -115,6 +146,9 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__general-clipboard settings__select input-base"
id="settings__general-clipboard"
value={clipboardSeconds}
onChange={(e) =>
clipboardSecondsChanged(+(e.target as HTMLSelectElement).value)
}
>
<option value="0">{Locale.setGenNoClear}</option>
<option value="5">
@ -138,6 +172,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-minimize"
id="settings__general-minimize"
checked={minimizeOnClose}
onClick={minimizeOnCloseChanged}
/>
<label for="settings__general-minimize">{Locale.setGenMinInstead}</label>
</div>
@ -147,6 +182,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-minimize-on-field-copy"
id="settings__general-minimize-on-field-copy"
checked={minimizeOnFieldCopy}
onClick={minimizeOnFieldCopyChanged}
/>
<label for="settings__general-minimize-on-field-copy">
{Locale.setGenMinOnFieldCopy}
@ -162,6 +198,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-direct-autotype"
id="settings__general-direct-autotype"
checked={directAutotype}
onClick={directAutoTypeChanged}
/>
<label for="settings__general-direct-autotype">
{Locale.setGenDirectAutotype}
@ -173,6 +210,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-autotype-title-filter"
id="settings__general-autotype-title-filter"
checked={autoTypeTitleFilterEnabled}
onClick={autoTypeTitleFilterEnabledChanged}
/>
<label for="settings__general-autotype-title-filter">
{Locale.setGenAutoTypeTitleFilterEnabled}
@ -184,6 +222,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-field-label-dblclick-autotype"
id="settings__general-field-label-dblclick-autotype"
checked={fieldLabelDblClickAutoType}
onClick={fieldLabelDblClickAutoTypeChanged}
/>
<label for="settings__general-field-label-dblclick-autotype">
{Locale.setGenFieldLabelDblClickAutoType}
@ -197,6 +236,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-use-markdown"
id="settings__general-use-markdown"
checked={useMarkdown}
onClick={useMarkdownChanged}
/>
<label for="settings__general-use-markdown">{Locale.setGenUseMarkdown}</label>
</div>
@ -206,6 +246,7 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__input input-base settings__general-use-group-icon-for-entries"
id="settings__general-use-group-icon-for-entries"
checked={useGroupIconForEntries}
onClick={useGroupIconForEntriesChanged}
/>
<label for="settings__general-use-group-icon-for-entries">
{Locale.setGenUseGroupIconForEntries}
@ -220,16 +261,15 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
<select
class="settings__general-device-owner-auth settings__select input-base"
id="settings__general-device-owner-auth"
value={deviceOwnerAuth || ''}
onChange={(e) => {
const val = (e.target as HTMLSelectElement).value || null;
deviceOwnerAuthChanged(val as AppSettingsDeviceOwnerAuth | null);
}}
>
<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>
<option value="">{Locale.setGenTouchIdDisabled}</option>
<option value="memory">{Locale.setGenTouchIdMemory}</option>
<option value="file">{Locale.setGenTouchIdFile}</option>
</select>
</div>
{deviceOwnerAuth ? (
@ -241,6 +281,11 @@ export const SettingsGeneralFunctionView: FunctionComponent<{
class="settings__general-device-owner-auth-timeout settings__select input-base"
id="settings__general-device-owner-auth-timeout"
value={deviceOwnerAuthTimeout}
onChange={(e) =>
deviceOwnerAuthTimeoutChanged(
+(e.target as HTMLSelectElement).value
)
}
>
<option value="1">{StringFormat.capFirst(Locale.oneMinute)}</option>
<option value="5">

View File

@ -11,6 +11,12 @@ export const SettingsGeneralLockView: FunctionComponent<{
lockOnAutoType: boolean;
canDetectOsSleep: boolean;
lockOnOsLock: boolean;
idleMinutesChanged: (idleMinutes: number) => void;
lockOnMinimizeChanged: () => void;
lockOnCopyChanged: () => void;
lockOnAutoTypeChanged: () => void;
lockOnOsLockChanged: () => void;
}> = ({
idleMinutes,
canDetectMinimize,
@ -19,7 +25,13 @@ export const SettingsGeneralLockView: FunctionComponent<{
canAutoType,
lockOnAutoType,
canDetectOsSleep,
lockOnOsLock
lockOnOsLock,
idleMinutesChanged,
lockOnMinimizeChanged,
lockOnCopyChanged,
lockOnAutoTypeChanged,
lockOnOsLockChanged
}) => {
return (
<>
@ -30,6 +42,7 @@ export const SettingsGeneralLockView: FunctionComponent<{
class="settings__general-idle-minutes settings__select input-base"
id="settings__general-idle-minutes"
value={idleMinutes}
onChange={(e) => idleMinutesChanged(+(e.target as HTMLSelectElement).value)}
>
<option value="0">{Locale.setGenNoAutoLock}</option>
<option value="5">
@ -65,6 +78,7 @@ export const SettingsGeneralLockView: FunctionComponent<{
class="settings__input input-base settings__general-lock-on-minimize"
id="settings__general-lock-on-minimize"
checked={lockOnMinimize}
onClick={lockOnMinimizeChanged}
/>
<label for="settings__general-lock-on-minimize">
{Locale.setGenLockMinimize}
@ -78,6 +92,7 @@ export const SettingsGeneralLockView: FunctionComponent<{
class="settings__input input-base settings__general-lock-on-copy"
id="settings__general-lock-on-copy"
checked={lockOnCopy}
onClick={lockOnCopyChanged}
/>
<label for="settings__general-lock-on-copy">{Locale.setGenLockCopy}</label>
</div>
@ -89,6 +104,7 @@ export const SettingsGeneralLockView: FunctionComponent<{
class="settings__input input-base settings__general-lock-on-auto-type"
id="settings__general-lock-on-auto-type"
checked={lockOnAutoType}
onClick={lockOnAutoTypeChanged}
/>
<label for="settings__general-lock-on-auto-type">
{Locale.setGenLockAutoType}
@ -103,6 +119,7 @@ export const SettingsGeneralLockView: FunctionComponent<{
class="settings__input input-base settings__general-lock-on-os-lock"
id="settings__general-lock-on-os-lock"
checked={lockOnOsLock}
onClick={lockOnOsLockChanged}
/>
<label for="settings__general-lock-on-os-lock">
{Locale.setGenLockOrSleep}

View File

@ -14,7 +14,21 @@ export const SettingsGeneralStorageView: FunctionComponent<{
disableOfflineStorage: boolean;
shortLivedStorageToken: boolean;
storageProviders: SettingsGeneralStorageProviderItem[];
}> = ({ disableOfflineStorage, shortLivedStorageToken, storageProviders }) => {
disableOfflineStorageChanged: () => void;
shortLivedStorageTokenChanged: () => void;
storageEnabledChanged: (storageName: string) => void;
logoutFromStorage: (storageName: string) => void;
}> = ({
disableOfflineStorage,
shortLivedStorageToken,
storageProviders,
disableOfflineStorageChanged,
shortLivedStorageTokenChanged,
storageEnabledChanged,
logoutFromStorage
}) => {
return (
<>
<h2 id="storage">{Locale.setGenStorage}</h2>
@ -24,6 +38,7 @@ export const SettingsGeneralStorageView: FunctionComponent<{
class="settings__input input-base settings__general-disable-offline-storage"
id="settings__general-disable-offline-storage"
checked={disableOfflineStorage}
onClick={disableOfflineStorageChanged}
/>
<label for="settings__general-disable-offline-storage">
{Locale.setGenDisableOfflineStorage}
@ -35,6 +50,7 @@ export const SettingsGeneralStorageView: FunctionComponent<{
class="settings__input input-base settings__general-short-lived-storage-token"
id="settings__general-short-lived-storage-token"
checked={shortLivedStorageToken}
onClick={shortLivedStorageTokenChanged}
/>
<label for="settings__general-short-lived-storage-token">
{Locale.setGenShortLivedStorageToken}
@ -48,8 +64,8 @@ export const SettingsGeneralStorageView: FunctionComponent<{
type="checkbox"
id={`settings__general-prv-check-${prv.name}`}
class="settings__general-prv-check"
data-storage={prv.name}
checked={prv.enabled}
onClick={() => storageEnabledChanged(prv.name)}
/>
<label for={`settings__general-prv-check-${prv.name}`}>{prv.locName}</label>
</h4>
@ -61,7 +77,7 @@ export const SettingsGeneralStorageView: FunctionComponent<{
{prv.loggedIn ? (
<button
class="btn-silent settings__general-prv-logout"
data-storage={prv.name}
onClick={() => logoutFromStorage(prv.name)}
>
{Locale.setGenStorageLogout}
</button>

View File

@ -11,6 +11,11 @@ export const SettingsGeneralUpdateView: FunctionComponent<{
updateInProgress: boolean;
updateReady: boolean;
updateFound: boolean;
installUpdateClicked: () => void;
checkUpdateClicked: () => void;
downloadAndInstallUpdateClicked: () => void;
autoUpdateChanged: (autoUpdate: AppSettingsAutoUpdate | null) => void;
}> = ({
updateWaitingReload,
autoUpdate,
@ -18,7 +23,12 @@ export const SettingsGeneralUpdateView: FunctionComponent<{
updateInfo,
updateInProgress,
updateReady,
updateFound
updateFound,
installUpdateClicked,
checkUpdateClicked,
downloadAndInstallUpdateClicked,
autoUpdateChanged
}) => {
return (
<>
@ -32,7 +42,10 @@ export const SettingsGeneralUpdateView: FunctionComponent<{
</a>
</div>
<div class="settings__general-update-buttons">
<button class="settings__general-restart-btn">
<button
class="settings__general-restart-btn"
onClick={installUpdateClicked}
>
{Locale.setGenReloadToUpdate}
</button>
</div>
@ -42,16 +55,17 @@ export const SettingsGeneralUpdateView: FunctionComponent<{
<>
<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
class="settings__general-auto-update settings__select input-base"
value={autoUpdate || ''}
onChange={(e) => {
const val = (e.target as HTMLSelectElement).value || null;
autoUpdateChanged(val as AppSettingsAutoUpdate | null);
}}
>
<option value="install">{Locale.setGenUpdateAuto}</option>
<option value="check">{Locale.setGenUpdateCheck}</option>
<option value="">{Locale.setGenNoUpdate}</option>
</select>
<div>{updateInfo}</div>
<a href={Links.ReleaseNotes} target="_blank" rel="noreferrer">
@ -64,17 +78,26 @@ export const SettingsGeneralUpdateView: FunctionComponent<{
{Locale.setGenUpdateChecking}
</button>
) : (
<button class="settings__general-update-btn btn-silent">
<button
class="settings__general-update-btn btn-silent"
onClick={checkUpdateClicked}
>
{Locale.setGenCheckUpdate}
</button>
)}
{updateReady ? (
<button class="settings__general-restart-btn">
<button
class="settings__general-restart-btn"
onClick={installUpdateClicked}
>
{Locale.setGenRestartToUpdate}
</button>
) : null}
{updateFound ? (
<button class="settings__general-update-found-btn">
<button
class="settings__general-update-found-btn"
onClick={downloadAndInstallUpdateClicked}
>
{Locale.setGenDownloadAndRestart}
</button>
) : null}

View File

@ -18,6 +18,11 @@ export const SettingsGeneralView: FunctionComponent<{
if (selectedMenuAnchor) {
const el = document.getElementById(selectedMenuAnchor);
el?.scrollIntoView();
} else {
const scroller = contentRef.current.closest('.scroller');
if (scroller) {
scroller.scrollTop = 0;
}
}
}, [selectedMenuAnchor]);