forked from mystiq/hydrogen-web
add delete button in session picker
This commit is contained in:
parent
d367037332
commit
2fa5fa7890
8 changed files with 112 additions and 13 deletions
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue