add delete button in session picker

This commit is contained in:
Bruno Windels 2019-10-12 21:16:48 +02:00
parent d367037332
commit 2fa5fa7890
8 changed files with 112 additions and 13 deletions

View File

@ -6,9 +6,9 @@ import SessionPickerViewModel from "./SessionPickerViewModel.js";
import EventEmitter from "../EventEmitter.js"; import EventEmitter from "../EventEmitter.js";
export default class BrawlViewModel extends EventEmitter { export default class BrawlViewModel extends EventEmitter {
constructor({createStorage, sessionStore, createHsApi, clock}) { constructor({storageFactory, sessionStore, createHsApi, clock}) {
super(); super();
this._createStorage = createStorage; this._storageFactory = storageFactory;
this._sessionStore = sessionStore; this._sessionStore = sessionStore;
this._createHsApi = createHsApi; this._createHsApi = createHsApi;
this._clock = clock; this._clock = clock;
@ -32,6 +32,7 @@ export default class BrawlViewModel extends EventEmitter {
this._clearSections(); this._clearSections();
this._sessionPickerViewModel = new SessionPickerViewModel({ this._sessionPickerViewModel = new SessionPickerViewModel({
sessionStore: this._sessionStore, sessionStore: this._sessionStore,
storageFactory: this._storageFactory,
sessionCallback: sessionInfo => this._onSessionPicked(sessionInfo) sessionCallback: sessionInfo => this._onSessionPicked(sessionInfo)
}); });
this.emit("change", "activeSection"); this.emit("change", "activeSection");
@ -122,7 +123,7 @@ export default class BrawlViewModel extends EventEmitter {
this._loading = true; this._loading = true;
this._loadingText = "Loading your conversations…"; this._loadingText = "Loading your conversations…";
const hsApi = this._createHsApi(sessionInfo.homeServer, sessionInfo.accessToken); const hsApi = this._createHsApi(sessionInfo.homeServer, sessionInfo.accessToken);
const storage = await this._createStorage(sessionInfo.id); const storage = await this._storageFactory.create(sessionInfo.id);
// no need to pass access token to session // no need to pass access token to session
const filteredSessionInfo = { const filteredSessionInfo = {
deviceId: sessionInfo.deviceId, deviceId: sessionInfo.deviceId,

View File

@ -1,7 +1,54 @@
import {SortedArray} from "../observable/index.js"; import {SortedArray} from "../observable/index.js";
import EventEmitter from "../EventEmitter.js";
class SessionItemViewModel extends EventEmitter {
constructor(sessionInfo, pickerVM) {
super();
this._pickerVM = pickerVM;
this._sessionInfo = sessionInfo;
this._isDeleting = false;
this._error = null;
}
get error() {
return this._error && this._error.message;
}
async delete() {
this._isDeleting = true;
this.emit("change", "isDeleting");
try {
await this._pickerVM.delete(this.id);
} catch(err) {
this._error = err;
console.error(err);
this.emit("change", "error");
} finally {
this._isDeleting = false;
this.emit("change", "isDeleting");
}
}
get isDeleting() {
return this._isDeleting;
}
get id() {
return this._sessionInfo.id;
}
get userId() {
return this._sessionInfo.userId;
}
get lastUsed() {
return this._sessionInfo.lastUsed;
}
}
export default class SessionPickerViewModel { export default class SessionPickerViewModel {
constructor({sessionStore, sessionCallback}) { constructor({storageFactory, sessionStore, sessionCallback}) {
this._storageFactory = storageFactory;
this._sessionStore = sessionStore; this._sessionStore = sessionStore;
this._sessionCallback = sessionCallback; this._sessionCallback = sessionCallback;
this._sessions = new SortedArray((s1, s2) => (s1.lastUsed || 0) - (s2.lastUsed || 0)); this._sessions = new SortedArray((s1, s2) => (s1.lastUsed || 0) - (s2.lastUsed || 0));
@ -9,7 +56,7 @@ export default class SessionPickerViewModel {
async load() { async load() {
const sessions = await this._sessionStore.getAll(); const sessions = await this._sessionStore.getAll();
this._sessions.setManyUnsorted(sessions); this._sessions.setManyUnsorted(sessions.map(s => new SessionItemViewModel(s, this)));
} }
pick(id) { pick(id) {
@ -19,6 +66,13 @@ export default class SessionPickerViewModel {
} }
} }
async delete(id) {
const idx = this._sessions.array.findIndex(s => s.id === id);
await this._sessionStore.delete(id);
await this._storageFactory.delete(id);
this._sessions.remove(idx);
}
get sessions() { get sessions() {
return this._sessions; return this._sessions;
} }

View File

@ -1,5 +1,5 @@
import HomeServerApi from "./matrix/hs-api.js"; import HomeServerApi from "./matrix/hs-api.js";
import createIdbStorage from "./matrix/storage/idb/create.js"; import StorageFactory from "./matrix/storage/idb/create.js";
import SessionsStore from "./matrix/sessions-store/localstorage/SessionsStore.js"; import SessionsStore from "./matrix/sessions-store/localstorage/SessionsStore.js";
import BrawlViewModel from "./domain/BrawlViewModel.js"; import BrawlViewModel from "./domain/BrawlViewModel.js";
import BrawlView from "./ui/web/BrawlView.js"; import BrawlView from "./ui/web/BrawlView.js";
@ -7,7 +7,7 @@ import BrawlView from "./ui/web/BrawlView.js";
export default async function main(container) { export default async function main(container) {
try { try {
const vm = new BrawlViewModel({ const vm = new BrawlViewModel({
createStorage: sessionId => createIdbStorage(`brawl_session_${sessionId}`), storageFactory: new StorageFactory(),
createHsApi: (homeServer, accessToken = null) => new HomeServerApi(homeServer, accessToken), createHsApi: (homeServer, accessToken = null) => new HomeServerApi(homeServer, accessToken),
sessionStore: new SessionsStore("brawl_sessions_v1"), sessionStore: new SessionsStore("brawl_sessions_v1"),
clock: Date //just for `now` fn clock: Date //just for `now` fn

View File

@ -42,4 +42,11 @@ export default class SessionsStore {
sessions.push(sessionInfo); sessions.push(sessionInfo);
localStorage.setItem(this._name, JSON.stringify(sessions)); localStorage.setItem(this._name, JSON.stringify(sessions));
} }
async delete(sessionId) {
let sessions = await this.getAll();
sessions = sessions.filter(s => s.id !== sessionId);
localStorage.setItem(this._name, JSON.stringify(sessions));
}
} }

View File

@ -1,11 +1,20 @@
import Storage from "./storage.js"; import Storage from "./storage.js";
import { openDatabase } from "./utils.js"; import { openDatabase, reqAsPromise } from "./utils.js";
export default async function createIdbStorage(databaseName) { export default class StorageFactory {
async create(sessionId) {
const databaseName = `brawl_session_${sessionId}`;
const db = await openDatabase(databaseName, createStores, 1); const db = await openDatabase(databaseName, createStores, 1);
return new Storage(db); return new Storage(db);
} }
delete(sessionId) {
const databaseName = `brawl_session_${sessionId}`;
const req = window.indexedDB.deleteDatabase(databaseName);
return reqAsPromise(req);
}
}
function createStores(db) { function createStores(db) {
db.createObjectStore("session", {keyPath: "key"}); db.createObjectStore("session", {keyPath: "key"});
// any way to make keys unique here? (just use put?) // any way to make keys unique here? (just use put?)

View File

@ -42,6 +42,15 @@ body {
background-color: grey; background-color: grey;
padding: 0.5em; padding: 0.5em;
cursor: pointer; cursor: pointer;
display: flex;
}
.SessionPickerView li span.userId {
flex: 1;
}
.SessionPickerView li span.error {
margin: 0 20px;
} }
.LoginView { .LoginView {

View File

@ -32,6 +32,8 @@ export default class TemplateView {
} }
update(value, prop) { update(value, prop) {
if (this._template) {
this._template.update(value); this._template.update(value);
} }
} }
}

View File

@ -2,8 +2,25 @@ import ListView from "../general/ListView.js";
import TemplateView from "../general/TemplateView.js"; import TemplateView from "../general/TemplateView.js";
class SessionPickerItem extends TemplateView { class SessionPickerItem extends TemplateView {
constructor(vm) {
super(vm, true);
this._onDeleteClick = this._onDeleteClick.bind(this);
}
_onDeleteClick(event) {
event.stopPropagation();
event.preventDefault();
this.viewModel.delete();
}
render(t) { render(t) {
return t.li([vm => vm.userId]); const deleteButton = t.button({
disabled: vm => vm.isDeleting,
onClick: event => this._onDeleteClick(event)
}, "Delete");
const userName = t.span({className: "userId"}, vm => vm.userId);
const errorMessage = t.if(vm => vm.error, t => t.span({className: "error"}, vm => vm.error));
return t.li([userName, errorMessage, deleteButton]);
} }
} }