add backupWriteStatus so binding can take multiple fields into account

This commit is contained in:
Bruno Windels 2022-01-31 16:25:08 +01:00
parent 6541aacf98
commit 06a1421e97
2 changed files with 68 additions and 17 deletions

View file

@ -18,7 +18,8 @@ import {ViewModel} from "../../ViewModel.js";
import {KeyType} from "../../../matrix/ssss/index"; import {KeyType} from "../../../matrix/ssss/index";
import {createEnum} from "../../../utils/enum"; import {createEnum} from "../../../utils/enum";
export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending", "NewVersionAvailable"); export const Status = createEnum("Enabled", "SetupKey", "SetupPhrase", "Pending", "NewVersionAvailable", "Error", "Cancelled");
export const BackupWriteStatus = createEnum("Writing", "Stopped", "Done", "Pending");
export class KeyBackupViewModel extends ViewModel { export class KeyBackupViewModel extends ViewModel {
constructor(options) { constructor(options) {
@ -30,7 +31,11 @@ export class KeyBackupViewModel extends ViewModel {
this._status = undefined; this._status = undefined;
this._backupOperation = this._session.keyBackup.flatMap(keyBackup => keyBackup.operationInProgress); this._backupOperation = this._session.keyBackup.flatMap(keyBackup => keyBackup.operationInProgress);
this._progress = this._backupOperation.flatMap(op => op.progress); this._progress = this._backupOperation.flatMap(op => op.progress);
this.track(this._backupOperation.subscribe(() => this.emitChange("isBackingUp"))); this.track(this._backupOperation.subscribe(() => {
// see if needsNewKey might be set
this._reevaluateStatus();
this.emitChange("isBackingUp");
}));
this.track(this._progress.subscribe(() => this.emitChange("backupPercentage"))); this.track(this._progress.subscribe(() => this.emitChange("backupPercentage")));
this._reevaluateStatus(); this._reevaluateStatus();
this.track(this._session.keyBackup.subscribe(() => { this.track(this._session.keyBackup.subscribe(() => {
@ -47,7 +52,7 @@ export class KeyBackupViewModel extends ViewModel {
let status; let status;
const keyBackup = this._session.keyBackup.get(); const keyBackup = this._session.keyBackup.get();
if (keyBackup) { if (keyBackup) {
status = keyBackup.needsNewKey.get() ? Status.NewVersionAvailable : Status.Enabled; status = keyBackup.needsNewKey ? Status.NewVersionAvailable : Status.Enabled;
} else { } else {
status = this.showPhraseSetup() ? Status.SetupPhrase : Status.SetupKey; status = this.showPhraseSetup() ? Status.SetupPhrase : Status.SetupKey;
} /* TODO: bring back "waiting to get online" } /* TODO: bring back "waiting to get online"
@ -83,6 +88,27 @@ export class KeyBackupViewModel extends ViewModel {
return this._session.keyBackup.get()?.version; return this._session.keyBackup.get()?.version;
} }
get backupWriteStatus() {
const keyBackup = this._session.keyBackup.get();
if (!keyBackup) {
return BackupWriteStatus.Pending;
} else if (keyBackup.hasStopped) {
return BackupWriteStatus.Stopped;
}
const operation = keyBackup.operationInProgress.get();
if (operation) {
return BackupWriteStatus.Writing;
} else if (keyBackup.hasBackedUpAllKeys) {
return BackupWriteStatus.Done;
} else {
return BackupWriteStatus.Pending;
}
}
get backupError() {
return this._session.keyBackup.get()?.error?.message;
}
get status() { get status() {
return this._status; return this._status;
} }
@ -171,7 +197,11 @@ export class KeyBackupViewModel extends ViewModel {
} }
cancelBackup() { cancelBackup() {
this._session.keyBackup.get()?.operationInProgress.get()?.abort(); this._backupOperation.get()?.abort();
}
startBackup() {
this._session.keyBackup.get()?.flush();
} }
} }

View file

@ -22,22 +22,36 @@ export class KeyBackupSettingsView extends TemplateView {
t.map(vm => vm.status, (status, t, vm) => { t.map(vm => vm.status, (status, t, vm) => {
switch (status) { switch (status) {
case "Enabled": return renderEnabled(t, vm); case "Enabled": return renderEnabled(t, vm);
case "NewVersionAvailable": return renderNewVersionAvailable(t, vm);
case "SetupKey": return renderEnableFromKey(t, vm); case "SetupKey": return renderEnableFromKey(t, vm);
case "SetupPhrase": return renderEnableFromPhrase(t, vm); case "SetupPhrase": return renderEnableFromPhrase(t, vm);
case "NewVersionAvailable": return t.p(vm.i18n`A new backup version has been created. Disable key backup and enable it again with the new key.`);
case "Pending": return t.p(vm.i18n`Waiting to go online…`); case "Pending": return t.p(vm.i18n`Waiting to go online…`);
} }
}), }),
t.map(vm => vm.isBackingUp, (backingUp, t, vm) => { t.map(vm => vm.backupWriteStatus, (status, t, vm) => {
if (backingUp) { switch (status) {
const progress = t.progress({ case "Writing": {
min: 0, const progress = t.progress({
max: 100, min: 0,
value: vm => vm.backupPercentage, max: 100,
}); value: vm => vm.backupPercentage,
return t.div([`Backup in progress `, progress, " ", vm => vm.backupInProgressLabel]); });
} else { return t.div([`Backup in progress `, progress, " ", vm => vm.backupInProgressLabel]);
return t.p("All keys are backed up."); }
case "Stopped": {
let label;
const error = vm.backupError;
if (error) {
label = `Backup has stopped because of an error: ${vm.backupError}`;
} else {
label = `Backup has stopped`;
}
return t.p(label, " ", t.button({onClick: () => vm.startBackup()}, `Backup now`));
}
case "Done":
return t.p(`All keys are backed up.`);
default:
return null;
} }
}) })
]); ]);
@ -46,7 +60,7 @@ export class KeyBackupSettingsView extends TemplateView {
function renderEnabled(t, vm) { function renderEnabled(t, vm) {
const items = [ const items = [
t.p([vm.i18n`Session backup is enabled, using backup version ${vm.backupVersion}. `, t.button({onClick: () => vm.disable()}, vm.i18n`Disable`)]) t.p([vm.i18n`Key backup is enabled, using backup version ${vm.backupVersion}. `, t.button({onClick: () => vm.disable()}, vm.i18n`Disable`)])
]; ];
if (vm.dehydratedDeviceId) { if (vm.dehydratedDeviceId) {
items.push(t.p(vm.i18n`A dehydrated device id was set up with id ${vm.dehydratedDeviceId} which you can use during your next login with your secret storage key.`)); items.push(t.p(vm.i18n`A dehydrated device id was set up with id ${vm.dehydratedDeviceId} which you can use during your next login with your secret storage key.`));
@ -54,6 +68,13 @@ function renderEnabled(t, vm) {
return t.div(items); return t.div(items);
} }
function renderNewVersionAvailable(t, vm) {
const items = [
t.p([vm.i18n`A new backup version has been created from another device. Disable key backup and enable it again with the new key.`, t.button({onClick: () => vm.disable()}, vm.i18n`Disable`)])
];
return t.div(items);
}
function renderEnableFromKey(t, vm) { function renderEnableFromKey(t, vm) {
const useASecurityPhrase = t.button({className: "link", onClick: () => vm.showPhraseSetup()}, vm.i18n`use a security phrase`); const useASecurityPhrase = t.button({className: "link", onClick: () => vm.showPhraseSetup()}, vm.i18n`use a security phrase`);
return t.div([ return t.div([
@ -101,7 +122,7 @@ function renderEnableFieldRow(t, vm, label, callback) {
function renderError(t) { function renderError(t) {
return t.if(vm => vm.error, (t, vm) => { return t.if(vm => vm.error, (t, vm) => {
return t.div([ return t.div([
t.p({className: "error"}, vm => vm.i18n`Could not enable session backup: ${vm.error}.`), t.p({className: "error"}, vm => vm.i18n`Could not enable key backup: ${vm.error}.`),
t.p(vm.i18n`Try double checking that you did not mix up your security key, security phrase and login password as explained above.`) t.p(vm.i18n`Try double checking that you did not mix up your security key, security phrase and login password as explained above.`)
]) ])
}); });