diff --git a/src/js/Timeline.js b/src/js/Timeline.js index f6d7b3e..d78092e 100644 --- a/src/js/Timeline.js +++ b/src/js/Timeline.js @@ -43,6 +43,14 @@ class Event extends ElemJS { this.update(data) } + // predicates + + canGroup() { + return this.data.type === "m.room.message" + } + + // operations + setGroup(group) { this.group = group } @@ -59,7 +67,17 @@ class Event extends ElemJS { render() { this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending") - this.text(this.data.content.body) + if (this.data.type === "m.room.message") { + this.text(this.data.content.body) + } else if (this.data.type === "m.room.member") { + if (this.data.content.membership === "join") { + this.child(ejs("i").text("joined the room")) + } else { + this.child(ejs("i").text("left the room")) + } + } else { + this.child(ejs("i").text(`Unsupported event type ${this.data.type}`)) + } } } @@ -69,13 +87,19 @@ class Sender { this.sender.subscribe("changeSelf", this.update.bind(this)) this.name = new ElemJS("div").class("c-message-group__name") this.avatar = new ElemJS("div").class("c-message-group__avatar") + this.displayingGoodData = false this.update() } update() { if (this.sender.exists()) { // name - this.name.text(this.sender.value().content.displayname) + if (this.sender.value().content.displayname) { + this.name.text(this.sender.value().content.displayname) + this.displayingGoodData = true + } else if (!this.displayingGoodData) { + this.name.text(this.sender.value().state_key) + } // avatar this.avatar.clearChildren() @@ -181,7 +205,7 @@ class ReactiveTimeline extends ElemJS { } class Timeline extends Subscribable { - constructor(id) { + constructor(room) { super() Object.assign(this.events, { beforeChange: [], @@ -191,25 +215,49 @@ class Timeline extends Subscribable { beforeChange: [], afterChange: [] }) - this.id = id + this.room = room + this.id = this.room.id this.list = [] this.map = new Map() - this.reactiveTimeline = new ReactiveTimeline(id, []) + this.reactiveTimeline = new ReactiveTimeline(this.id, []) this.latest = 0 this.pending = new Set() } + updateStateEvents(events) { + for (const eventData of events) { + let id = eventData.event_id + if (eventData.type === "m.room.member") { + // update members + if (eventData.membership !== "leave") { + this.room.members.get(eventData.state_key).set(eventData) + } + } + } + } + updateEvents(events) { this.broadcast("beforeChange") + // handle state events + this.updateStateEvents(events) for (const eventData of events) { + // set variables this.latest = Math.max(this.latest, eventData.origin_server_ts) let id = eventData.event_id + // handle local echoes if (eventData.sender === lsm.get("mx_user_id") && eventData.content && this.pending.has(eventData.content["chat.carbon.message.pending_id"])) { id = eventData.content["chat.carbon.message.pending_id"] } + // handle timeline events if (this.map.has(id)) { + // update existing event this.map.get(id).update(eventData) } else { + // skip displaying events that we don't know how to + if (eventData.type === "m.reaction") { + continue + } + // add new event const event = new Event(eventData) this.map.set(id, event) this.reactiveTimeline.addEvent(event) @@ -238,6 +286,7 @@ class Timeline extends Subscribable { "chat.carbon.message.pending_id": id } const fakeEvent = { + type: "m.room.message", origin_server_ts: Date.now(), event_id: id, sender: lsm.get("mx_user_id"), diff --git a/src/js/room-picker.js b/src/js/room-picker.js index 87e9320..8e696de 100644 --- a/src/js/room-picker.js +++ b/src/js/room-picker.js @@ -62,7 +62,7 @@ class Room extends ElemJS { this.id = id this.data = data - this.timeline = new Timeline(this.id) + this.timeline = new Timeline(this) this.group = null this.members = new SubscribeMapList(SubscribeValue) diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js index b374d62..bbd6b11 100644 --- a/src/js/sync/sync.js +++ b/src/js/sync/sync.js @@ -47,19 +47,15 @@ function manageSync(root) { } // set up rooms - Object.entries(root.rooms.join).forEach(([id, room]) => { + Object.entries(root.rooms.join).forEach(([id, data]) => { if (!store.rooms.has(id)) { - store.rooms.askAdd(id, room) + store.rooms.askAdd(id, data) } - const storeRoom = store.rooms.get(id).value() - room.state.events.forEach(event => { - if (event.type === "m.room.member") { - storeRoom.members.get(event.state_key).set(event) - } - }) - const timeline = storeRoom.timeline - if (room.timeline.events.length) newEvents = true - timeline.updateEvents(room.timeline.events) + const room = store.rooms.get(id).value() + const timeline = room.timeline + if (data.timeline.events.length) newEvents = true + timeline.updateStateEvents(data.state.events) + timeline.updateEvents(data.timeline.events) }) // set up groups @@ -90,6 +86,7 @@ function manageSync(root) { store.rooms.get(groupRoom.room_id).value().setGroup(id) } }) + store.newEvents.broadcast("changeSelf") // trigger a room list update }) } })