diff --git a/index.html b/index.html index 477d7c05..0ab41b2d 100644 --- a/index.html +++ b/index.html @@ -52,18 +52,9 @@ -

-
-
diff --git a/src/EventEmitter.js b/src/EventEmitter.js index 460d75a0..27a4e17d 100644 --- a/src/EventEmitter.js +++ b/src/EventEmitter.js @@ -3,11 +3,11 @@ export default class EventEmitter { this._handlersByName = {}; } - emit(name, value) { + emit(name, ...values) { const handlers = this._handlersByName[name]; if (handlers) { for(const h of handlers) { - h(value); + h(...values); } } } @@ -15,6 +15,7 @@ export default class EventEmitter { on(name, callback) { let handlers = this._handlersByName[name]; if (!handlers) { + this.onFirstSubscriptionAdded(name); this._handlersByName[name] = handlers = new Set(); } handlers.add(callback); @@ -26,9 +27,14 @@ export default class EventEmitter { handlers.delete(callback); if (handlers.length === 0) { delete this._handlersByName[name]; + this.onLastSubscriptionRemoved(name); } } } + + onFirstSubscriptionAdded(name) {} + + onLastSubscriptionRemoved(name) {} } //#ifdef TESTS export function tests() { @@ -66,4 +72,4 @@ export function tests() { } }; } -//#endif \ No newline at end of file +//#endif diff --git a/src/domain/session/SessionViewModel.js b/src/domain/session/SessionViewModel.js index 7bbcfe75..5339ffc9 100644 --- a/src/domain/session/SessionViewModel.js +++ b/src/domain/session/SessionViewModel.js @@ -1,11 +1,13 @@ import EventEmitter from "../../EventEmitter.js"; import RoomTileViewModel from "./roomlist/RoomTileViewModel.js"; import RoomViewModel from "./room/RoomViewModel.js"; +import SyncStatusViewModel from "./SyncStatusViewModel.js"; export default class SessionViewModel extends EventEmitter { - constructor(session) { + constructor(session, sync) { super(); this._session = session; + this._syncStatusViewModel = new SyncStatusViewModel(sync); this._currentRoomViewModel = null; const roomTileVMs = this._session.rooms.mapValues((room, emitUpdate) => { return new RoomTileViewModel({ @@ -17,6 +19,10 @@ export default class SessionViewModel extends EventEmitter { this._roomList = roomTileVMs.sortValues((a, b) => a.compare(b)); } + get syncStatusViewModel() { + return this._syncStatusViewModel; + } + get roomList() { return this._roomList; } diff --git a/src/domain/session/SyncStatusViewModel.js b/src/domain/session/SyncStatusViewModel.js new file mode 100644 index 00000000..ef227873 --- /dev/null +++ b/src/domain/session/SyncStatusViewModel.js @@ -0,0 +1,51 @@ +import EventEmitter from "../../EventEmitter.js"; + +export default class SyncStatusViewModel extends EventEmitter { + constructor(sync) { + super(); + this._sync = sync; + this._onStatus = this._onStatus.bind(this); + } + + _onStatus(status, err) { + if (status === "error") { + this._error = err; + } else if (status === "started") { + this._error = null; + } + this.emit("change"); + } + + onFirstSubscriptionAdded(name) { + if (name === "change") { + this._sync.on("status", this._onStatus); + } + } + + onLastSubscriptionRemoved(name) { + if (name === "change") { + this._sync.on("status", this._onStatus); + } + } + + trySync() { + this._sync.start(); + this.emit("change"); + } + + get status() { + if (!this.isSyncing) { + if (this._error) { + return `Error while syncing: ${this._error.message}`; + } else { + return "Sync stopped"; + } + } else { + return "Sync running"; + } + } + + get isSyncing() { + return this._sync.isSyncing; + } +} diff --git a/src/main.js b/src/main.js index 6ecef32c..6500420c 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,7 @@ import HomeServerApi from "./matrix/hs-api.js"; import Session from "./matrix/session.js"; import createIdbStorage from "./matrix/storage/idb/create.js"; import Sync from "./matrix/sync.js"; -import SessionView from "./ui/web/SessionView.js"; +import SessionView from "./ui/web/session/SessionView.js"; import SessionViewModel from "./domain/session/SessionViewModel.js"; const HOST = "localhost"; @@ -44,15 +44,14 @@ async function login(username, password, homeserver) { return storeSessionInfo(loginData); } -function showSession(container, session) { - const vm = new SessionViewModel(session); +function showSession(container, session, sync) { + const vm = new SessionViewModel(session, sync); const view = new SessionView(vm); - view.mount(); - container.appendChild(view.root()); + container.appendChild(view.mount()); } // eslint-disable-next-line no-unused-vars -export default async function main(label, button, container) { +export default async function main(container) { try { let sessionInfo = getSessionInfo(USER_ID); if (!sessionInfo) { @@ -67,26 +66,17 @@ export default async function main(label, button, container) { }}); await session.load(); console.log("session loaded"); + const sync = new Sync(hsApi, session, storage); const needsInitialSync = !session.syncToken; if (needsInitialSync) { console.log("session needs initial sync"); } else { - showSession(container, session); + showSession(container, session, sync); } - const sync = new Sync(hsApi, session, storage); await sync.start(); if (needsInitialSync) { - showSession(container, session); + showSession(container, session, sync); } - label.innerText = "sync running"; - button.addEventListener("click", () => sync.stop()); - sync.on("error", err => { - label.innerText = "sync error"; - console.error("sync error", err); - }); - sync.on("stopped", () => { - label.innerText = "sync stopped"; - }); } catch(err) { console.error(`${err.message}:\n${err.stack}`); } diff --git a/src/matrix/sync.js b/src/matrix/sync.js index 37425090..21e2e472 100644 --- a/src/matrix/sync.js +++ b/src/matrix/sync.js @@ -32,12 +32,18 @@ export default class Sync extends EventEmitter { this._isSyncing = false; this._currentRequest = null; } + + get isSyncing() { + return this._isSyncing; + } + // returns when initial sync is done async start() { if (this._isSyncing) { return; } this._isSyncing = true; + this.emit("status", "started"); let syncToken = this._session.syncToken; // do initial sync if needed if (!syncToken) { @@ -56,12 +62,12 @@ export default class Sync extends EventEmitter { } catch (err) { this._isSyncing = false; if (!(err instanceof RequestAbortError)) { - console.warn("stopping sync because of error"); - this.emit("error", err); + console.warn("stopping sync because of error", err.message); + this.emit("status", "error", err); } } } - this.emit("stopped"); + this.emit("status", "stopped"); } async _syncRequest(syncToken, timeout) {