vector-im-hydrogen-web/src/domain/SessionPickerViewModel.js

250 lines
7.5 KiB
JavaScript
Raw Normal View History

import {SortedArray} from "../observable/index.js";
2020-04-20 21:35:53 +02:00
import {EventEmitter} from "../utils/EventEmitter.js";
import {LoadStatus} from "../matrix/SessionContainer.js";
import {SyncStatus} from "../matrix/Sync.js";
import {loadLabel} from "./common.js";
2019-10-12 21:16:48 +02:00
class SessionItemViewModel extends EventEmitter {
constructor(sessionInfo, pickerVM) {
super();
this._pickerVM = pickerVM;
this._sessionInfo = sessionInfo;
this._isDeleting = false;
2019-10-13 07:48:49 +02:00
this._isClearing = false;
2019-10-12 21:16:48 +02:00
this._error = null;
2019-12-14 18:29:35 +01:00
this._exportDataUrl = null;
2019-10-12 21:16:48 +02:00
}
get error() {
return this._error && this._error.message;
}
async delete() {
this._isDeleting = true;
this.emit("change", "isDeleting");
try {
await this._pickerVM.delete(this.id);
} catch(err) {
this._error = err;
console.error(err);
this.emit("change", "error");
} finally {
this._isDeleting = false;
this.emit("change", "isDeleting");
}
}
2019-10-13 07:48:49 +02:00
async clear() {
this._isClearing = true;
this.emit("change");
2019-10-13 07:48:49 +02:00
try {
await this._pickerVM.clear(this.id);
} catch(err) {
this._error = err;
console.error(err);
this.emit("change", "error");
} finally {
this._isClearing = false;
this.emit("change", "isClearing");
}
}
2019-10-12 21:16:48 +02:00
get isDeleting() {
return this._isDeleting;
}
2019-10-13 07:48:49 +02:00
get isClearing() {
return this._isClearing;
}
2019-10-12 21:16:48 +02:00
get id() {
return this._sessionInfo.id;
}
2019-12-14 18:29:35 +01:00
get label() {
const {userId, comment} = this._sessionInfo;
if (comment) {
return `${userId} (${comment})`;
} else {
return userId;
}
2019-10-12 21:16:48 +02:00
}
2019-10-12 22:18:08 +02:00
get sessionInfo() {
return this._sessionInfo;
2019-10-12 21:16:48 +02:00
}
2019-12-14 18:29:35 +01:00
get exportDataUrl() {
return this._exportDataUrl;
}
async export() {
try {
const data = await this._pickerVM._exportData(this._sessionInfo.id);
const json = JSON.stringify(data, undefined, 2);
const blob = new Blob([json], {type: "application/json"});
this._exportDataUrl = URL.createObjectURL(blob);
this.emit("change", "exportDataUrl");
} catch (err) {
alert(err.message);
console.error(err);
}
}
clearExport() {
if (this._exportDataUrl) {
URL.revokeObjectURL(this._exportDataUrl);
this._exportDataUrl = null;
this.emit("change", "exportDataUrl");
}
}
2019-10-12 21:16:48 +02:00
}
class LoadViewModel extends EventEmitter {
constructor({createSessionContainer, sessionCallback, sessionId}) {
super();
this._createSessionContainer = createSessionContainer;
this._sessionCallback = sessionCallback;
this._sessionId = sessionId;
this._loading = false;
}
async _start() {
try {
this._loading = true;
this.emit("change", "loading");
this._sessionContainer = this._createSessionContainer();
this._sessionContainer.startWithExistingSession(this._sessionId);
this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => {
this.emit("change", "loadStatus");
// wait for initial sync, but not catchup sync
const isCatchupSync = s === LoadStatus.FirstSync &&
this._sessionContainer.sync.status === SyncStatus.CatchupSync;
return isCatchupSync ||
s === LoadStatus.Error ||
s === LoadStatus.Ready;
});
try {
await this._waitHandle.promise;
} catch (err) {
// swallow AbortError
}
if (this._sessionContainer.loadStatus.get() !== LoadStatus.Error) {
this._sessionCallback(this._sessionContainer);
}
} catch (err) {
this._error = err;
} finally {
this._loading = false;
this.emit("change", "loading");
}
}
get loading() {
return this._loading;
}
goBack() {
if (this._sessionContainer) {
this._sessionContainer.stop();
this._sessionContainer = null;
if (this._waitHandle) {
this._waitHandle.dispose();
}
}
this._sessionCallback();
}
get loadLabel() {
const sc = this._sessionContainer;
return loadLabel(
sc && sc.loadStatus,
sc && sc.loadError || this._error);
}
}
export class SessionPickerViewModel extends EventEmitter {
constructor({storageFactory, sessionInfoStorage, sessionCallback, createSessionContainer}) {
super();
2019-10-12 21:16:48 +02:00
this._storageFactory = storageFactory;
this._sessionInfoStorage = sessionInfoStorage;
this._sessionCallback = sessionCallback;
this._createSessionContainer = createSessionContainer;
2019-10-13 08:16:08 +02:00
this._sessions = new SortedArray((s1, s2) => s1.id.localeCompare(s2.id));
this._loadViewModel = null;
this._error = null;
}
// this loads all the sessions
async load() {
const sessions = await this._sessionInfoStorage.getAll();
2019-10-12 21:16:48 +02:00
this._sessions.setManyUnsorted(sessions.map(s => new SessionItemViewModel(s, this)));
}
// for the loading of 1 picked session
get loadViewModel() {
return this._loadViewModel;
}
async pick(id) {
if (this._loadViewModel) {
return;
}
2019-10-12 22:18:08 +02:00
const sessionVM = this._sessions.array.find(s => s.id === id);
if (sessionVM) {
this._loadViewModel = new LoadViewModel({
createSessionContainer: this._createSessionContainer,
sessionCallback: sessionContainer => {
if (sessionContainer) {
// make parent view model move away
this._sessionCallback(sessionContainer);
} else {
// show list of session again
this._loadViewModel = null;
this.emit("change", "loadViewModel");
}
},
sessionId: sessionVM.id,
});
this._loadViewModel.start();
this.emit("change", "loadViewModel");
}
}
2019-12-14 18:29:35 +01:00
async _exportData(id) {
const sessionInfo = await this._sessionInfoStorage.get(id);
2019-12-14 18:29:35 +01:00
const stores = await this._storageFactory.export(id);
const data = {sessionInfo, stores};
return data;
}
2019-10-13 08:16:08 +02:00
async import(json) {
2019-12-14 18:29:35 +01:00
const data = JSON.parse(json);
const {sessionInfo} = data;
sessionInfo.comment = `Imported on ${new Date().toLocaleString()} from id ${sessionInfo.id}.`;
sessionInfo.id = this._createSessionContainer().createNewSessionId();
2019-12-14 18:29:35 +01:00
await this._storageFactory.import(sessionInfo.id, data.stores);
await this._sessionInfoStorage.add(sessionInfo);
2019-10-13 08:16:08 +02:00
this._sessions.set(new SessionItemViewModel(sessionInfo, this));
}
2019-10-12 21:16:48 +02:00
async delete(id) {
const idx = this._sessions.array.findIndex(s => s.id === id);
await this._sessionInfoStorage.delete(id);
2019-10-12 21:16:48 +02:00
await this._storageFactory.delete(id);
this._sessions.remove(idx);
}
2019-10-13 07:48:49 +02:00
async clear(id) {
await this._storageFactory.delete(id);
}
get sessions() {
return this._sessions;
}
cancel() {
this._sessionCallback();
}
}