mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-22 19:14:52 +01:00
show timeline when clicking room in roomlist
This commit is contained in:
parent
5cafb92fee
commit
6b4ed65a57
14
src/main.js
14
src/main.js
@ -2,8 +2,8 @@ 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 ListView from "./ui/web/ListView.js";
|
||||
import RoomTile from "./ui/web/RoomTile.js";
|
||||
import SessionView from "./ui/web/SessionView.js";
|
||||
import SessionViewModel from "./ui/viewmodels/SessionViewModel.js";
|
||||
|
||||
const HOST = "localhost";
|
||||
const HOMESERVER = `http://${HOST}:8008`;
|
||||
@ -34,10 +34,10 @@ async function login(username, password, homeserver) {
|
||||
return {sessionId, loginData};
|
||||
}
|
||||
|
||||
function showRooms(container, rooms) {
|
||||
const sortedRooms = rooms.sortValues((a, b) => a.name.localeCompare(b.name));
|
||||
const listView = new ListView(sortedRooms, (room) => new RoomTile(room));
|
||||
container.appendChild(listView.mount());
|
||||
function showSession(container, session) {
|
||||
const vm = new SessionViewModel(session);
|
||||
const view = new SessionView(vm);
|
||||
container.appendChild(view.mount());
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@ -54,7 +54,7 @@ export default async function main(label, button, container) {
|
||||
await session.setLoginData(loginData);
|
||||
}
|
||||
await session.load();
|
||||
showRooms(container, session.rooms);
|
||||
showSession(container, session);
|
||||
const hsApi = new HomeServerApi(HOMESERVER, session.accessToken);
|
||||
console.log("session loaded");
|
||||
if (!session.syncToken) {
|
||||
|
@ -21,23 +21,27 @@ export default class RoomPersister {
|
||||
|
||||
// }
|
||||
|
||||
async persistSync(roomResponse, txn) {
|
||||
persistSync(roomResponse, txn) {
|
||||
let nextKey = this._lastSortKey;
|
||||
const timeline = roomResponse.timeline;
|
||||
const entries = [];
|
||||
// is limited true for initial sync???? or do we need to handle that as a special case?
|
||||
// I suppose it will, yes
|
||||
if (timeline.limited) {
|
||||
nextKey = nextKey.nextKeyWithGap();
|
||||
txn.roomTimeline.appendGap(this._roomId, nextKey, {prev_batch: timeline.prev_batch});
|
||||
}
|
||||
// const startOfChunkSortKey = nextKey;
|
||||
|
||||
if (timeline.events) {
|
||||
for(const event of timeline.events) {
|
||||
nextKey = nextKey.nextKey();
|
||||
txn.roomTimeline.appendEvent(this._roomId, nextKey, event);
|
||||
entries.push(this._createGapEntry(nextKey, timeline.prev_batch));
|
||||
}
|
||||
// const startOfChunkSortKey = nextKey;
|
||||
if (timeline.events) {
|
||||
for(const event of timeline.events) {
|
||||
nextKey = nextKey.nextKey();
|
||||
entries.push(this._createEventEntry(nextKey, event));
|
||||
}
|
||||
}
|
||||
// write to store
|
||||
for(const entry of entries) {
|
||||
txn.roomTimeline.append(entry);
|
||||
}
|
||||
// right thing to do? if the txn fails, not sure we'll continue anyways ...
|
||||
// only advance the key once the transaction has
|
||||
// succeeded
|
||||
@ -55,13 +59,30 @@ export default class RoomPersister {
|
||||
}
|
||||
|
||||
if (timeline.events) {
|
||||
if (state.events) {
|
||||
for (const event of timeline.events) {
|
||||
if (typeof event.state_key === "string") {
|
||||
txn.roomState.setStateEvent(this._roomId, event);
|
||||
}
|
||||
for (const event of timeline.events) {
|
||||
if (typeof event.state_key === "string") {
|
||||
txn.roomState.setStateEvent(this._roomId, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
_createGapEntry(sortKey, prevBatch) {
|
||||
return {
|
||||
roomId: this._roomId,
|
||||
sortKey: sortKey.buffer,
|
||||
event: null,
|
||||
gap: {prev_batch: prevBatch}
|
||||
};
|
||||
}
|
||||
|
||||
_createEventEntry(sortKey, event) {
|
||||
return {
|
||||
roomId: this._roomId,
|
||||
sortKey: sortKey.buffer,
|
||||
event: event,
|
||||
gap: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import EventEmitter from "../../EventEmitter.js";
|
||||
import RoomSummary from "./summary.js";
|
||||
import RoomPersister from "./persister.js";
|
||||
import EventEmitter from "../../EventEmitter.js";
|
||||
import Timeline from "./timeline.js";
|
||||
|
||||
export default class Room extends EventEmitter {
|
||||
constructor(roomId, storage, emitCollectionChange) {
|
||||
@ -10,19 +11,23 @@ export default class Room extends EventEmitter {
|
||||
this._summary = new RoomSummary(roomId);
|
||||
this._persister = new RoomPersister(roomId);
|
||||
this._emitCollectionChange = emitCollectionChange;
|
||||
this._timeline = null;
|
||||
}
|
||||
|
||||
persistSync(roomResponse, membership, txn) {
|
||||
const changed = this._summary.applySync(roomResponse, membership, txn);
|
||||
this._persister.persistSync(roomResponse, txn);
|
||||
return changed;
|
||||
const summaryChanged = this._summary.applySync(roomResponse, membership, txn);
|
||||
const newTimelineEntries = this._persister.persistSync(roomResponse, txn);
|
||||
return {summaryChanged, newTimelineEntries};
|
||||
}
|
||||
|
||||
emitSync(changed) {
|
||||
if (changed) {
|
||||
emitSync({summaryChanged, newTimelineEntries}) {
|
||||
if (summaryChanged) {
|
||||
this.emit("change");
|
||||
(this._emitCollectionChange)(this);
|
||||
}
|
||||
if (this._timeline) {
|
||||
this._timeline.appendLiveEntries(newTimelineEntries);
|
||||
}
|
||||
}
|
||||
|
||||
load(summary, txn) {
|
||||
@ -37,4 +42,18 @@ export default class Room extends EventEmitter {
|
||||
get id() {
|
||||
return this._roomId;
|
||||
}
|
||||
|
||||
async openTimeline() {
|
||||
if (this._timeline) {
|
||||
throw new Error("not dealing with load race here for now");
|
||||
}
|
||||
this._timeline = new Timeline({
|
||||
roomId: this.id,
|
||||
storage: this._storage,
|
||||
closeCallback: () => this._timeline = null,
|
||||
});
|
||||
await this._timeline.load();
|
||||
return this._timeline;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ export default class RoomSummary {
|
||||
this._membership = membership;
|
||||
changed = true;
|
||||
}
|
||||
// state comes before timeline
|
||||
if (roomResponse.state) {
|
||||
changed = roomResponse.state.events.reduce((changed, e) => {
|
||||
return this._processEvent(e) || changed;
|
||||
|
36
src/matrix/room/timeline.js
Normal file
36
src/matrix/room/timeline.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { ObservableArray } from "../../observable/index.js";
|
||||
|
||||
export default class Timeline {
|
||||
constructor({roomId, storage, closeCallback}) {
|
||||
this._roomId = roomId;
|
||||
this._storage = storage;
|
||||
this._closeCallback = closeCallback;
|
||||
this._entriesList = new ObservableArray();
|
||||
}
|
||||
|
||||
/** @package */
|
||||
async load() {
|
||||
const txn = await this._storage.readTxn([this._storage.storeNames.roomTimeline]);
|
||||
const entries = await txn.roomTimeline.lastEvents(this._roomId, 100);
|
||||
for (const entry of entries) {
|
||||
this._entriesList.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/** @package */
|
||||
appendLiveEntries(newEntries) {
|
||||
for (const entry of newEntries) {
|
||||
this._entriesList.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
get entries() {
|
||||
return this._entriesList;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
close() {
|
||||
this._closeCallback();
|
||||
}
|
||||
}
|
@ -18,20 +18,26 @@ export default class RoomTimelineStore {
|
||||
return this._timelineStore.selectLimit(range, amount);
|
||||
}
|
||||
|
||||
async eventsBefore(roomId, sortKey, amount) {
|
||||
const range = IDBKeyRange.bound([roomId, SortKey.minKey.buffer], [roomId, sortKey.buffer], false, true);
|
||||
const events = await this._timelineStore.selectLimitReverse(range, amount);
|
||||
events.reverse(); // because we fetched them backwards
|
||||
return events;
|
||||
}
|
||||
async eventsBefore(roomId, sortKey, amount) {
|
||||
const range = IDBKeyRange.bound([roomId, SortKey.minKey.buffer], [roomId, sortKey.buffer], false, true);
|
||||
const events = await this._timelineStore.selectLimitReverse(range, amount);
|
||||
events.reverse(); // because we fetched them backwards
|
||||
return events;
|
||||
}
|
||||
|
||||
// entry should have roomId, sortKey, event & gap keys
|
||||
append(entry) {
|
||||
this._timelineStore.add(entry);
|
||||
}
|
||||
// should this happen as part of a transaction that stores all synced in changes?
|
||||
// e.g.:
|
||||
// - timeline events for all rooms
|
||||
// - latest sync token
|
||||
// - new members
|
||||
// - new room state
|
||||
// - updated/new account data
|
||||
|
||||
|
||||
// should this happen as part of a transaction that stores all synced in changes?
|
||||
// e.g.:
|
||||
// - timeline events for all rooms
|
||||
// - latest sync token
|
||||
// - new members
|
||||
// - new room state
|
||||
// - updated/new account data
|
||||
|
||||
appendGap(roomId, sortKey, gap) {
|
||||
this._timelineStore.add({
|
||||
|
@ -3,6 +3,7 @@ import FilteredMap from "./map/FilteredMap.js";
|
||||
import MappedMap from "./map/MappedMap.js";
|
||||
import BaseObservableMap from "./map/BaseObservableMap.js";
|
||||
// re-export "root" (of chain) collections
|
||||
export { default as ObservableArray} from "./list/ObservableArray.js";
|
||||
export { default as ObservableMap } from "./map/ObservableMap.js";
|
||||
|
||||
// avoid circular dependency between these classes
|
||||
|
21
src/observable/list/ObservableArray.js
Normal file
21
src/observable/list/ObservableArray.js
Normal file
@ -0,0 +1,21 @@
|
||||
import BaseObservableList from "./BaseObservableList.js";
|
||||
|
||||
export default class ObservableArray extends BaseObservableList {
|
||||
constructor() {
|
||||
super();
|
||||
this._items = [];
|
||||
}
|
||||
|
||||
append(item) {
|
||||
this._items.push(item);
|
||||
this.emitAdd(this._items.length - 1, item);
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._items.length;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._items.values();
|
||||
}
|
||||
}
|
@ -55,6 +55,6 @@ export default class MappedMap extends BaseObservableMap {
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this._mappedValues.entries()[Symbol.iterator];
|
||||
return this._mappedValues.entries();
|
||||
}
|
||||
}
|
||||
|
24
src/ui/viewmodels/RoomTileViewModel.js
Normal file
24
src/ui/viewmodels/RoomTileViewModel.js
Normal file
@ -0,0 +1,24 @@
|
||||
export default class RoomTileViewModel {
|
||||
// we use callbacks to parent VM instead of emit because
|
||||
// it would be annoying to keep track of subscriptions in
|
||||
// parent for all RoomTileViewModels
|
||||
// emitUpdate is ObservableMap/ObservableList update mechanism
|
||||
constructor({room, emitUpdate, emitOpen}) {
|
||||
this._room = room;
|
||||
this._emitUpdate = emitUpdate;
|
||||
this._emitOpen = emitOpen;
|
||||
}
|
||||
|
||||
open() {
|
||||
this._emitOpen(this._room);
|
||||
}
|
||||
|
||||
compare(other) {
|
||||
// sort by name for now
|
||||
return this._room.name.localeCompare(other._room.name);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._room.name;
|
||||
}
|
||||
}
|
36
src/ui/viewmodels/RoomViewModel.js
Normal file
36
src/ui/viewmodels/RoomViewModel.js
Normal file
@ -0,0 +1,36 @@
|
||||
import EventEmitter from "../../EventEmitter.js";
|
||||
|
||||
export default class RoomViewModel extends EventEmitter {
|
||||
constructor(room) {
|
||||
super();
|
||||
this._room = room;
|
||||
this._timeline = null;
|
||||
this._onRoomChange = this._onRoomChange.bind(this);
|
||||
}
|
||||
|
||||
async enable() {
|
||||
this._room.on("change", this._onRoomChange);
|
||||
this._timeline = await this._room.openTimeline();
|
||||
this.emit("change", "timelineEntries");
|
||||
}
|
||||
|
||||
disable() {
|
||||
if (this._timeline) {
|
||||
this._timeline.close();
|
||||
}
|
||||
}
|
||||
|
||||
// room doesn't tell us yet which fields changed,
|
||||
// so emit all fields originating from summary
|
||||
_onRoomChange() {
|
||||
this.emit("change", "name");
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._room.name;
|
||||
}
|
||||
|
||||
get timelineEntries() {
|
||||
return this._timeline && this._timeline.entries;
|
||||
}
|
||||
}
|
37
src/ui/viewmodels/SessionViewModel.js
Normal file
37
src/ui/viewmodels/SessionViewModel.js
Normal file
@ -0,0 +1,37 @@
|
||||
import EventEmitter from "../../EventEmitter.js";
|
||||
import RoomTileViewModel from "./RoomTileViewModel.js";
|
||||
import RoomViewModel from "./RoomViewModel.js";
|
||||
|
||||
export default class SessionViewModel extends EventEmitter {
|
||||
constructor(session) {
|
||||
super();
|
||||
this._session = session;
|
||||
this._currentRoomViewModel = null;
|
||||
const roomTileVMs = this._session.rooms.mapValues((room, emitUpdate) => {
|
||||
return new RoomTileViewModel({
|
||||
room,
|
||||
emitUpdate,
|
||||
emitOpen: room => this._openRoom(room)
|
||||
});
|
||||
});
|
||||
this._roomList = roomTileVMs.sortValues((a, b) => a.compare(b));
|
||||
}
|
||||
|
||||
get roomList() {
|
||||
return this._roomList;
|
||||
}
|
||||
|
||||
get currentRoom() {
|
||||
return this._currentRoomViewModel;
|
||||
}
|
||||
|
||||
_openRoom(room) {
|
||||
if (this._currentRoomViewModel) {
|
||||
this._currentRoomViewModel.disable();
|
||||
}
|
||||
this._currentRoomViewModel = new RoomViewModel(room);
|
||||
this._currentRoomViewModel.enable();
|
||||
this.emit("change", "currentRoom");
|
||||
}
|
||||
}
|
||||
|
@ -19,34 +19,57 @@ function insertAt(parentNode, idx, childNode) {
|
||||
}
|
||||
|
||||
export default class ListView {
|
||||
constructor(collection, childCreator) {
|
||||
this._collection = collection;
|
||||
constructor({list, onItemClick}, childCreator) {
|
||||
this._onItemClick = onItemClick;
|
||||
this._list = list;
|
||||
this._root = null;
|
||||
this._subscription = null;
|
||||
this._childCreator = childCreator;
|
||||
this._childInstances = null;
|
||||
this._onClick = this._onClick.bind(this);
|
||||
}
|
||||
|
||||
root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
update() {}
|
||||
update(attributes) {
|
||||
if (attributes.hasOwnProperty("list")) {
|
||||
if (this._subscription) {
|
||||
this._unloadList();
|
||||
while (this._root.lastChild) {
|
||||
this._root.lastChild.remove();
|
||||
}
|
||||
}
|
||||
this._list = attributes.list;
|
||||
this._loadList();
|
||||
}
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._subscription = this._collection.subscribe(this);
|
||||
this._root = html.ul({className: "ListView"});
|
||||
this._childInstances = [];
|
||||
for (let item of this._collection) {
|
||||
const child = this._childCreator(item);
|
||||
this._childInstances.push(child);
|
||||
const childDomNode = child.mount();
|
||||
this._root.appendChild(childDomNode);
|
||||
this._loadList();
|
||||
if (this._onItemClick) {
|
||||
this._root.addEventListener("click", this._onClick);
|
||||
}
|
||||
return this._root;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this._unloadList();
|
||||
}
|
||||
|
||||
_onClick(event) {
|
||||
let childNode = event.target;
|
||||
while (childNode.parentNode !== this._root) {
|
||||
childNode = childNode.parentNode;
|
||||
}
|
||||
const index = Array.prototype.indexOf.call(this._root.childNodes, childNode);
|
||||
const childView = this._childInstances[index];
|
||||
this._onItemClick(childView);
|
||||
}
|
||||
|
||||
_unloadList() {
|
||||
this._subscription = this._subscription();
|
||||
for (let child of this._childInstances) {
|
||||
child.unmount();
|
||||
@ -54,6 +77,20 @@ export default class ListView {
|
||||
this._childInstances = null;
|
||||
}
|
||||
|
||||
_loadList() {
|
||||
if (!this._list) {
|
||||
return;
|
||||
}
|
||||
this._subscription = this._list.subscribe(this);
|
||||
this._childInstances = [];
|
||||
for (let item of this._list) {
|
||||
const child = this._childCreator(item);
|
||||
this._childInstances.push(child);
|
||||
const childDomNode = child.mount();
|
||||
this._root.appendChild(childDomNode);
|
||||
}
|
||||
}
|
||||
|
||||
onAdd(idx, value) {
|
||||
const child = this._childCreator(value);
|
||||
this._childInstances.splice(idx, 0, child);
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { li } from "./html.js";
|
||||
|
||||
export default class RoomTile {
|
||||
constructor(room) {
|
||||
this._room = room;
|
||||
constructor(viewModel) {
|
||||
this._viewModel = viewModel;
|
||||
this._root = null;
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._root = li(null, this._room.name);
|
||||
this._root = li(null, this._viewModel.name);
|
||||
return this._root;
|
||||
}
|
||||
|
||||
@ -16,7 +16,11 @@ export default class RoomTile {
|
||||
|
||||
update() {
|
||||
// no data-binding yet
|
||||
this._root.innerText = this._room.name;
|
||||
this._root.innerText = this._viewModel.name;
|
||||
}
|
||||
|
||||
clicked() {
|
||||
this._viewModel.open();
|
||||
}
|
||||
|
||||
root() {
|
||||
|
49
src/ui/web/RoomView.js
Normal file
49
src/ui/web/RoomView.js
Normal file
@ -0,0 +1,49 @@
|
||||
import TimelineTile from "./TimelineTile.js";
|
||||
import ListView from "./ListView.js";
|
||||
import * as html from "./html.js";
|
||||
|
||||
export default class RoomView {
|
||||
constructor(viewModel) {
|
||||
this._viewModel = viewModel;
|
||||
this._root = null;
|
||||
this._timelineList = null;
|
||||
this._nameLabel = null;
|
||||
this._onViewModelUpdate = this._onViewModelUpdate.bind(this);
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._viewModel.on("change", this._onViewModelUpdate);
|
||||
this._nameLabel = html.h2(null, this._viewModel.name);
|
||||
this._timelineList = new ListView({
|
||||
list: this._viewModel.timelineEntries
|
||||
}, entry => new TimelineTile(entry));
|
||||
this._timelineList.mount();
|
||||
|
||||
this._root = html.div({className: "RoomView"}, [
|
||||
this._nameLabel,
|
||||
this._timelineList.root()
|
||||
]);
|
||||
|
||||
return this._root;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this._timelineList.unmount();
|
||||
this._viewModel.off("change", this._onViewModelUpdate);
|
||||
}
|
||||
|
||||
root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
_onViewModelUpdate(prop) {
|
||||
if (prop === "name") {
|
||||
this._nameLabel.innerText = this._viewModel.name;
|
||||
}
|
||||
else if (prop === "timelineEntries") {
|
||||
this._timelineList.update({list: this._viewModel.timelineEntries});
|
||||
}
|
||||
}
|
||||
|
||||
update() {}
|
||||
}
|
67
src/ui/web/SessionView.js
Normal file
67
src/ui/web/SessionView.js
Normal file
@ -0,0 +1,67 @@
|
||||
import ListView from "./ListView.js";
|
||||
import RoomTile from "./RoomTile.js";
|
||||
import RoomView from "./RoomView.js";
|
||||
import { div } from "./html.js";
|
||||
|
||||
export default class SessionView {
|
||||
constructor(viewModel) {
|
||||
this._viewModel = viewModel;
|
||||
this._roomList = null;
|
||||
this._currentRoom = null;
|
||||
this._root = null;
|
||||
this._onViewModelChange = this._onViewModelChange.bind(this);
|
||||
}
|
||||
|
||||
root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._viewModel.on("change", this._onViewModelChange);
|
||||
|
||||
this._root = div({className: "SessionView"});
|
||||
this._roomList = new ListView(
|
||||
{
|
||||
list: this._viewModel.roomList,
|
||||
onItemClick: roomTile => roomTile.clicked()
|
||||
},
|
||||
(room) => new RoomTile(room)
|
||||
);
|
||||
this._roomList.mount();
|
||||
this._root.appendChild(this._roomList.root());
|
||||
|
||||
this._updateCurrentRoom();
|
||||
return this._root;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this._roomList.unmount();
|
||||
if (this._room) {
|
||||
this._room.unmount();
|
||||
}
|
||||
|
||||
this._viewModel.off("change", this._onViewModelChange);
|
||||
}
|
||||
|
||||
_onViewModelChange(prop) {
|
||||
if (prop === "currentRoom") {
|
||||
this._updateCurrentRoom();
|
||||
}
|
||||
}
|
||||
|
||||
// changing viewModel not supported for now
|
||||
update() {}
|
||||
|
||||
_updateCurrentRoom() {
|
||||
if (this._currentRoom) {
|
||||
this._currentRoom.root().remove();
|
||||
this._currentRoom.unmount();
|
||||
this._currentRoom = null;
|
||||
}
|
||||
if (this._viewModel.currentRoom) {
|
||||
this._currentRoom = new RoomView(this._viewModel.currentRoom);
|
||||
this._currentRoom.mount();
|
||||
this.root().appendChild(this._currentRoom.root());
|
||||
}
|
||||
}
|
||||
}
|
56
src/ui/web/TimelineTile.js
Normal file
56
src/ui/web/TimelineTile.js
Normal file
@ -0,0 +1,56 @@
|
||||
import * as html from "./html.js";
|
||||
|
||||
function tileText(event) {
|
||||
const content = event.content;
|
||||
switch (event.type) {
|
||||
case "m.room.message": {
|
||||
const msgtype = content.msgtype;
|
||||
switch (msgtype) {
|
||||
case "m.text":
|
||||
return content.body;
|
||||
default:
|
||||
return `unsupported msgtype: ${msgtype}`;
|
||||
}
|
||||
}
|
||||
case "m.room.name":
|
||||
return `changed the room name to "${content.name}"`;
|
||||
case "m.room.member":
|
||||
return `changed membership to ${content.membership}`;
|
||||
default:
|
||||
return `unsupported event type: ${event.type}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default class TimelineTile {
|
||||
constructor(entry) {
|
||||
this._entry = entry;
|
||||
this._root = null;
|
||||
}
|
||||
|
||||
root() {
|
||||
return this._root;
|
||||
}
|
||||
|
||||
mount() {
|
||||
let children;
|
||||
if (this._entry.gap) {
|
||||
children = [
|
||||
html.strong(null, "Gap"),
|
||||
" with prev_batch ",
|
||||
html.strong(null, this._entry.gap.prev_batch)
|
||||
];
|
||||
} else if (this._entry.event) {
|
||||
const event = this._entry.event;
|
||||
children = [
|
||||
html.strong(null, event.sender),
|
||||
`: ${tileText(event)}`,
|
||||
];
|
||||
}
|
||||
this._root = html.li(null, children);
|
||||
return this._root;
|
||||
}
|
||||
|
||||
unmount() {}
|
||||
|
||||
update() {}
|
||||
}
|
@ -47,3 +47,4 @@ export function section(... params) { return el("section", ... params); }
|
||||
export function main(... params) { return el("main", ... params); }
|
||||
export function article(... params) { return el("article", ... params); }
|
||||
export function aside(... params) { return el("aside", ... params); }
|
||||
export function pre(... params) { return el("pre", ... params); }
|
||||
|
Loading…
Reference in New Issue
Block a user