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>
</head>
<body>
<p id="syncstatus"></p>
<div><button id="stopsync">stop syncing</button></div>
<div id="container"></div>
<script type="module">
import main from "./src/main.js";
const label = document.getElementById("syncstatus");
const button = document.getElementById("stopsync");
const container = document.getElementById("container");
//import("./src/main.js").then(main => {
main(label, button, container);
//});
main(document.body);
</script>
</body>
</html>

View file

@ -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
//#endif

View file

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

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 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}`);
}

View file

@ -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) {