replace ad hoc buttons and label with SyncStatusBar

This commit is contained in:
Bruno Windels 2019-06-16 10:54:16 +02:00
parent a4bc2dd2b0
commit 1917a528c7
6 changed files with 85 additions and 35 deletions

View file

@ -52,18 +52,9 @@
</style> </style>
</head> </head>
<body> <body>
<p id="syncstatus"></p>
<div><button id="stopsync">stop syncing</button></div>
<div id="container"></div>
<script type="module"> <script type="module">
import main from "./src/main.js"; import main from "./src/main.js";
const label = document.getElementById("syncstatus"); main(document.body);
const button = document.getElementById("stopsync");
const container = document.getElementById("container");
//import("./src/main.js").then(main => {
main(label, button, container);
//});
</script> </script>
</body> </body>
</html> </html>

View file

@ -3,11 +3,11 @@ export default class EventEmitter {
this._handlersByName = {}; this._handlersByName = {};
} }
emit(name, value) { emit(name, ...values) {
const handlers = this._handlersByName[name]; const handlers = this._handlersByName[name];
if (handlers) { if (handlers) {
for(const h of handlers) { for(const h of handlers) {
h(value); h(...values);
} }
} }
} }
@ -15,6 +15,7 @@ export default class EventEmitter {
on(name, callback) { on(name, callback) {
let handlers = this._handlersByName[name]; let handlers = this._handlersByName[name];
if (!handlers) { if (!handlers) {
this.onFirstSubscriptionAdded(name);
this._handlersByName[name] = handlers = new Set(); this._handlersByName[name] = handlers = new Set();
} }
handlers.add(callback); handlers.add(callback);
@ -26,9 +27,14 @@ export default class EventEmitter {
handlers.delete(callback); handlers.delete(callback);
if (handlers.length === 0) { if (handlers.length === 0) {
delete this._handlersByName[name]; delete this._handlersByName[name];
this.onLastSubscriptionRemoved(name);
} }
} }
} }
onFirstSubscriptionAdded(name) {}
onLastSubscriptionRemoved(name) {}
} }
//#ifdef TESTS //#ifdef TESTS
export function tests() { export function tests() {

View file

@ -1,11 +1,13 @@
import EventEmitter from "../../EventEmitter.js"; import EventEmitter from "../../EventEmitter.js";
import RoomTileViewModel from "./roomlist/RoomTileViewModel.js"; import RoomTileViewModel from "./roomlist/RoomTileViewModel.js";
import RoomViewModel from "./room/RoomViewModel.js"; import RoomViewModel from "./room/RoomViewModel.js";
import SyncStatusViewModel from "./SyncStatusViewModel.js";
export default class SessionViewModel extends EventEmitter { export default class SessionViewModel extends EventEmitter {
constructor(session) { constructor(session, sync) {
super(); super();
this._session = session; this._session = session;
this._syncStatusViewModel = new SyncStatusViewModel(sync);
this._currentRoomViewModel = null; this._currentRoomViewModel = null;
const roomTileVMs = this._session.rooms.mapValues((room, emitUpdate) => { const roomTileVMs = this._session.rooms.mapValues((room, emitUpdate) => {
return new RoomTileViewModel({ return new RoomTileViewModel({
@ -17,6 +19,10 @@ export default class SessionViewModel extends EventEmitter {
this._roomList = roomTileVMs.sortValues((a, b) => a.compare(b)); this._roomList = roomTileVMs.sortValues((a, b) => a.compare(b));
} }
get syncStatusViewModel() {
return this._syncStatusViewModel;
}
get roomList() { get roomList() {
return this._roomList; return this._roomList;
} }

View file

@ -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;
}
}

View file

@ -2,7 +2,7 @@ import HomeServerApi from "./matrix/hs-api.js";
import Session from "./matrix/session.js"; import Session from "./matrix/session.js";
import createIdbStorage from "./matrix/storage/idb/create.js"; import createIdbStorage from "./matrix/storage/idb/create.js";
import Sync from "./matrix/sync.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"; import SessionViewModel from "./domain/session/SessionViewModel.js";
const HOST = "localhost"; const HOST = "localhost";
@ -44,15 +44,14 @@ async function login(username, password, homeserver) {
return storeSessionInfo(loginData); return storeSessionInfo(loginData);
} }
function showSession(container, session) { function showSession(container, session, sync) {
const vm = new SessionViewModel(session); const vm = new SessionViewModel(session, sync);
const view = new SessionView(vm); const view = new SessionView(vm);
view.mount(); container.appendChild(view.mount());
container.appendChild(view.root());
} }
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
export default async function main(label, button, container) { export default async function main(container) {
try { try {
let sessionInfo = getSessionInfo(USER_ID); let sessionInfo = getSessionInfo(USER_ID);
if (!sessionInfo) { if (!sessionInfo) {
@ -67,26 +66,17 @@ export default async function main(label, button, container) {
}}); }});
await session.load(); await session.load();
console.log("session loaded"); console.log("session loaded");
const sync = new Sync(hsApi, session, storage);
const needsInitialSync = !session.syncToken; const needsInitialSync = !session.syncToken;
if (needsInitialSync) { if (needsInitialSync) {
console.log("session needs initial sync"); console.log("session needs initial sync");
} else { } else {
showSession(container, session); showSession(container, session, sync);
} }
const sync = new Sync(hsApi, session, storage);
await sync.start(); await sync.start();
if (needsInitialSync) { 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) { } catch(err) {
console.error(`${err.message}:\n${err.stack}`); console.error(`${err.message}:\n${err.stack}`);
} }

View file

@ -32,12 +32,18 @@ export default class Sync extends EventEmitter {
this._isSyncing = false; this._isSyncing = false;
this._currentRequest = null; this._currentRequest = null;
} }
get isSyncing() {
return this._isSyncing;
}
// returns when initial sync is done // returns when initial sync is done
async start() { async start() {
if (this._isSyncing) { if (this._isSyncing) {
return; return;
} }
this._isSyncing = true; this._isSyncing = true;
this.emit("status", "started");
let syncToken = this._session.syncToken; let syncToken = this._session.syncToken;
// do initial sync if needed // do initial sync if needed
if (!syncToken) { if (!syncToken) {
@ -56,12 +62,12 @@ export default class Sync extends EventEmitter {
} catch (err) { } catch (err) {
this._isSyncing = false; this._isSyncing = false;
if (!(err instanceof RequestAbortError)) { if (!(err instanceof RequestAbortError)) {
console.warn("stopping sync because of error"); console.warn("stopping sync because of error", err.message);
this.emit("error", err); this.emit("status", "error", err);
} }
} }
} }
this.emit("stopped"); this.emit("status", "stopped");
} }
async _syncRequest(syncToken, timeout) { async _syncRequest(syncToken, timeout) {