diff --git a/build/index.html b/build/index.html
index 1fb6d0f..1304e78 100644
--- a/build/index.html
+++ b/build/index.html
@@ -5,8 +5,8 @@
-
-
+
+
Carbon
diff --git a/build/static/Timeline.js b/build/static/Timeline.js
index f412366..0adce82 100644
--- a/build/static/Timeline.js
+++ b/build/static/Timeline.js
@@ -1,7 +1,11 @@
import {ElemJS, ejs} from "./basic.js"
import {Subscribable} from "./store/Subscribable.js"
+import {store} from "./store/store.js"
import {Anchor} from "./Anchor.js"
import * as lsm from "./lsm.js"
+import {resolveMxc} from "./functions.js"
+
+const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", second: "numeric", day: "numeric", month: "short", year: "numeric"})
let sentIndex = 0
@@ -59,6 +63,35 @@ class Event extends ElemJS {
}
}
+class Sender {
+ constructor(roomID, mxid) {
+ this.sender = store.rooms.get(roomID).value().members.get(mxid)
+ 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.update()
+ }
+
+ update() {
+ if (this.sender.exists()) {
+ // name
+ this.name.text(this.sender.value().content.displayname)
+
+ // avatar
+ this.avatar.clearChildren()
+ if (this.sender.value().content.avatar_url) {
+ this.avatar.child(
+ ejs("img").class("c-message-group__icon").attribute("src", resolveMxc(this.sender.value().content.avatar_url, 32, "crop"))
+ )
+ } else {
+ this.avatar.child(
+ ejs("div").class("c-message-group__icon")
+ )
+ }
+ }
+ }
+}
+
class EventGroup extends ElemJS {
constructor(reactive, list) {
super("div")
@@ -69,14 +102,13 @@ class EventGroup extends ElemJS {
sender: list[0].data.sender,
origin_server_ts: list[0].data.origin_server_ts
}
+ this.sender = new Sender(this.reactive.id, this.data.sender)
this.child(
- ejs("div").class("c-message-group__avatar").child(
- ejs("div").class("c-message-group__icon")
- ),
+ this.sender.avatar,
this.messages = ejs("div").class("c-message-group__messages").child(
ejs("div").class("c-message-group__intro").child(
- ejs("div").class("c-message-group__name").text(this.data.sender),
- ejs("div").class("c-message-group__date").text(this.data.origin_server_ts)
+ this.sender.name,
+ ejs("div").class("c-message-group__date").text(dateFormatter.format(this.data.origin_server_ts))
),
...this.list
)
@@ -102,9 +134,10 @@ class EventGroup extends ElemJS {
}
class ReactiveTimeline extends ElemJS {
- constructor(list) {
+ constructor(id, list) {
super("div")
this.class("c-event-groups")
+ this.id = id
this.list = list
this.render()
}
@@ -161,7 +194,7 @@ class Timeline extends Subscribable {
this.id = id
this.list = []
this.map = new Map()
- this.reactiveTimeline = new ReactiveTimeline([])
+ this.reactiveTimeline = new ReactiveTimeline(id, [])
this.latest = 0
this.pending = new Set()
}
diff --git a/build/static/room-picker.js b/build/static/room-picker.js
index 3de2ab1..e93f72d 100644
--- a/build/static/room-picker.js
+++ b/build/static/room-picker.js
@@ -1,16 +1,10 @@
import {q, ElemJS, ejs} from "./basic.js"
import {store} from "./store/store.js"
+import {SubscribeMapList} from "./store/SubscribeMapList.js"
+import {SubscribeValue} from "./store/SubscribeValue.js"
import {Timeline} from "./Timeline.js"
import * as lsm from "./lsm.js"
-
-function resolveMxc(url, size, method) {
- const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
- if (size && method) {
- return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
- } else {
- return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
- }
-}
+import {resolveMxc} from "./functions.js"
class ActiveGroupMarker extends ElemJS {
constructor() {
@@ -70,6 +64,7 @@ class Room extends ElemJS {
this.data = data
this.timeline = new Timeline(this.id)
this.group = null
+ this.members = new SubscribeMapList(SubscribeValue)
this.class("c-room")
diff --git a/build/static/sync/sync.js b/build/static/sync/sync.js
index e5a8389..54c2652 100644
--- a/build/static/sync/sync.js
+++ b/build/static/sync/sync.js
@@ -1,18 +1,9 @@
import {store} from "../store/store.js"
import * as lsm from "../lsm.js"
+import {resolveMxc} from "../functions.js"
let lastBatch = null
-function resolveMxc(url, size, method) {
- const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
- if (size && method) {
- return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
- } else {
- return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
- }
-}
-
-
function sync() {
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
url.searchParams.append("access_token", lsm.get("access_token"))
@@ -60,7 +51,13 @@ function manageSync(root) {
if (!store.rooms.has(id)) {
store.rooms.askAdd(id, room)
}
- const timeline = store.rooms.get(id).value().timeline
+ 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)
})
diff --git a/spec.js b/spec.js
index 0ebbc17..38bb96e 100644
--- a/spec.js
+++ b/spec.js
@@ -79,6 +79,11 @@ module.exports = [
source: "/js/chat.js",
target: "/static/chat.js"
},
+ {
+ type: "js",
+ source: "/js/functions.js",
+ target: "/static/functions.js"
+ },
{
type: "file",
source: "/assets/fonts/whitney-500.woff",
diff --git a/src/js/Timeline.js b/src/js/Timeline.js
index f412366..0adce82 100644
--- a/src/js/Timeline.js
+++ b/src/js/Timeline.js
@@ -1,7 +1,11 @@
import {ElemJS, ejs} from "./basic.js"
import {Subscribable} from "./store/Subscribable.js"
+import {store} from "./store/store.js"
import {Anchor} from "./Anchor.js"
import * as lsm from "./lsm.js"
+import {resolveMxc} from "./functions.js"
+
+const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", second: "numeric", day: "numeric", month: "short", year: "numeric"})
let sentIndex = 0
@@ -59,6 +63,35 @@ class Event extends ElemJS {
}
}
+class Sender {
+ constructor(roomID, mxid) {
+ this.sender = store.rooms.get(roomID).value().members.get(mxid)
+ 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.update()
+ }
+
+ update() {
+ if (this.sender.exists()) {
+ // name
+ this.name.text(this.sender.value().content.displayname)
+
+ // avatar
+ this.avatar.clearChildren()
+ if (this.sender.value().content.avatar_url) {
+ this.avatar.child(
+ ejs("img").class("c-message-group__icon").attribute("src", resolveMxc(this.sender.value().content.avatar_url, 32, "crop"))
+ )
+ } else {
+ this.avatar.child(
+ ejs("div").class("c-message-group__icon")
+ )
+ }
+ }
+ }
+}
+
class EventGroup extends ElemJS {
constructor(reactive, list) {
super("div")
@@ -69,14 +102,13 @@ class EventGroup extends ElemJS {
sender: list[0].data.sender,
origin_server_ts: list[0].data.origin_server_ts
}
+ this.sender = new Sender(this.reactive.id, this.data.sender)
this.child(
- ejs("div").class("c-message-group__avatar").child(
- ejs("div").class("c-message-group__icon")
- ),
+ this.sender.avatar,
this.messages = ejs("div").class("c-message-group__messages").child(
ejs("div").class("c-message-group__intro").child(
- ejs("div").class("c-message-group__name").text(this.data.sender),
- ejs("div").class("c-message-group__date").text(this.data.origin_server_ts)
+ this.sender.name,
+ ejs("div").class("c-message-group__date").text(dateFormatter.format(this.data.origin_server_ts))
),
...this.list
)
@@ -102,9 +134,10 @@ class EventGroup extends ElemJS {
}
class ReactiveTimeline extends ElemJS {
- constructor(list) {
+ constructor(id, list) {
super("div")
this.class("c-event-groups")
+ this.id = id
this.list = list
this.render()
}
@@ -161,7 +194,7 @@ class Timeline extends Subscribable {
this.id = id
this.list = []
this.map = new Map()
- this.reactiveTimeline = new ReactiveTimeline([])
+ this.reactiveTimeline = new ReactiveTimeline(id, [])
this.latest = 0
this.pending = new Set()
}
diff --git a/src/js/functions.js b/src/js/functions.js
new file mode 100644
index 0000000..da5da62
--- /dev/null
+++ b/src/js/functions.js
@@ -0,0 +1,12 @@
+import * as lsm from "./lsm.js"
+
+function resolveMxc(url, size, method) {
+ const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
+ if (size && method) {
+ return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
+ } else {
+ return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
+ }
+}
+
+export {resolveMxc}
diff --git a/src/js/room-picker.js b/src/js/room-picker.js
index 3de2ab1..e93f72d 100644
--- a/src/js/room-picker.js
+++ b/src/js/room-picker.js
@@ -1,16 +1,10 @@
import {q, ElemJS, ejs} from "./basic.js"
import {store} from "./store/store.js"
+import {SubscribeMapList} from "./store/SubscribeMapList.js"
+import {SubscribeValue} from "./store/SubscribeValue.js"
import {Timeline} from "./Timeline.js"
import * as lsm from "./lsm.js"
-
-function resolveMxc(url, size, method) {
- const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
- if (size && method) {
- return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
- } else {
- return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
- }
-}
+import {resolveMxc} from "./functions.js"
class ActiveGroupMarker extends ElemJS {
constructor() {
@@ -70,6 +64,7 @@ class Room extends ElemJS {
this.data = data
this.timeline = new Timeline(this.id)
this.group = null
+ this.members = new SubscribeMapList(SubscribeValue)
this.class("c-room")
diff --git a/src/js/sync/sync.js b/src/js/sync/sync.js
index e5a8389..54c2652 100644
--- a/src/js/sync/sync.js
+++ b/src/js/sync/sync.js
@@ -1,18 +1,9 @@
import {store} from "../store/store.js"
import * as lsm from "../lsm.js"
+import {resolveMxc} from "../functions.js"
let lastBatch = null
-function resolveMxc(url, size, method) {
- const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
- if (size && method) {
- return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
- } else {
- return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
- }
-}
-
-
function sync() {
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
url.searchParams.append("access_token", lsm.get("access_token"))
@@ -60,7 +51,13 @@ function manageSync(root) {
if (!store.rooms.has(id)) {
store.rooms.askAdd(id, room)
}
- const timeline = store.rooms.get(id).value().timeline
+ 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)
})