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>
|
</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>
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
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 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}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Reference in a new issue