2019-09-08 10:19:16 +02:00
|
|
|
import {SortedArray} from "../observable/index.js";
|
2020-04-20 21:35:53 +02:00
|
|
|
import {EventEmitter} from "../utils/EventEmitter.js";
|
2020-04-20 22:49:14 +02:00
|
|
|
import {LoadStatus} from "../matrix/SessionContainer.js";
|
|
|
|
import {SyncStatus} from "../matrix/Sync.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;
|
2019-10-13 08:29:23 +02:00
|
|
|
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-10-13 08:29:23 +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-13 08:29:23 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-12 21:16:48 +02:00
|
|
|
}
|
2019-07-29 22:39:56 +02:00
|
|
|
|
2020-04-20 21:26:39 +02:00
|
|
|
export class SessionPickerViewModel {
|
2020-04-20 22:49:14 +02:00
|
|
|
constructor({storageFactory, sessionInfoStorage, sessionCallback, createSessionContainer}) {
|
2019-10-12 21:16:48 +02:00
|
|
|
this._storageFactory = storageFactory;
|
2020-04-19 19:13:38 +02:00
|
|
|
this._sessionInfoStorage = sessionInfoStorage;
|
2019-07-29 22:39:56 +02:00
|
|
|
this._sessionCallback = sessionCallback;
|
2020-04-20 22:49:14 +02:00
|
|
|
this._createSessionContainer = createSessionContainer;
|
2019-10-13 08:16:08 +02:00
|
|
|
this._sessions = new SortedArray((s1, s2) => s1.id.localeCompare(s2.id));
|
2020-04-20 22:49:14 +02:00
|
|
|
this._loading = false;
|
2019-07-29 22:39:56 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 22:49:14 +02:00
|
|
|
// this loads all the sessions
|
2019-07-29 22:39:56 +02:00
|
|
|
async load() {
|
2020-04-19 19:13:38 +02:00
|
|
|
const sessions = await this._sessionInfoStorage.getAll();
|
2019-10-12 21:16:48 +02:00
|
|
|
this._sessions.setManyUnsorted(sessions.map(s => new SessionItemViewModel(s, this)));
|
2019-07-29 22:39:56 +02:00
|
|
|
}
|
|
|
|
|
2020-04-20 22:49:14 +02:00
|
|
|
// this is the loading of a single picked session
|
|
|
|
get loading() {
|
|
|
|
return this._loading;
|
|
|
|
}
|
|
|
|
|
|
|
|
get loadStatus() {
|
|
|
|
return this._sessionContainer && this._sessionContainer.loadStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
get loadError() {
|
|
|
|
if (this._sessionContainer) {
|
|
|
|
const error = this._sessionContainer.loadError;
|
|
|
|
if (error) {
|
|
|
|
return error.message;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
async pick(id) {
|
2019-10-12 22:18:08 +02:00
|
|
|
const sessionVM = this._sessions.array.find(s => s.id === id);
|
|
|
|
if (sessionVM) {
|
2020-04-20 22:49:14 +02:00
|
|
|
this._loading = true;
|
|
|
|
this.emit("change", "loading");
|
|
|
|
this._sessionContainer = this._createSessionContainer();
|
|
|
|
this._sessionContainer.startWithExistingSession(sessionVM.sessionInfo.id);
|
|
|
|
// TODO: allow to cancel here
|
|
|
|
const waitHandle = this._sessionContainer.loadStatus.waitFor(s => {
|
|
|
|
this.emit("change", "loadStatus");
|
|
|
|
// wait for initial sync, but not catchup sync
|
|
|
|
return (
|
|
|
|
s === LoadStatus.FirstSync &&
|
|
|
|
this._sessionContainer.sync.status === SyncStatus.CatchupSync
|
|
|
|
) || s === LoadStatus.Ready;
|
|
|
|
});
|
|
|
|
await waitHandle.promise;
|
|
|
|
this._sessionCallback(this._sessionContainer);
|
2019-07-29 22:39:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-14 18:29:35 +01:00
|
|
|
async _exportData(id) {
|
2020-04-19 19:13:38 +02:00
|
|
|
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}.`;
|
2020-04-20 22:49:14 +02:00
|
|
|
sessionInfo.id = this._createSessionContainer().createNewSessionId();
|
2019-12-14 18:29:35 +01:00
|
|
|
await this._storageFactory.import(sessionInfo.id, data.stores);
|
2020-04-19 19:13:38 +02:00
|
|
|
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);
|
2020-04-19 19:13:38 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-29 22:39:56 +02:00
|
|
|
get sessions() {
|
|
|
|
return this._sessions;
|
|
|
|
}
|
|
|
|
|
|
|
|
cancel() {
|
|
|
|
this._sessionCallback();
|
|
|
|
}
|
|
|
|
}
|