replace ad hoc buttons and label with SyncStatusBar
This commit is contained in:
parent
a4bc2dd2b0
commit
1917a528c7
6 changed files with 85 additions and 35 deletions
11
index.html
11
index.html
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
51
src/domain/session/SyncStatusViewModel.js
Normal file
51
src/domain/session/SyncStatusViewModel.js
Normal 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;
|
||||
}
|
||||
}
|
26
src/main.js
26
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}`);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Reference in a new issue