const {q, ElemJS, ejs} = require("./basic.js") const {store} = require("./store/store.js") const {SubscribeMapList} = require("./store/subscribe_map_list.js") const {SubscribeValue} = require("./store/subscribe_value.js") const {Timeline} = require("./timeline.js") const lsm = require("./lsm.js") const {resolveMxc} = require("./functions.js") class ActiveGroupMarker extends ElemJS { constructor() { super(q("#c-group-marker")) store.activeGroup.subscribe("changeSelf", this.render.bind(this)) } render() { if (store.activeGroup.exists()) { const group = store.activeGroup.value() this.style("opacity", 1) this.style("transform", `translateY(${group.element.offsetTop}px)`) } else { this.style("opacity", 0) } } } const activeGroupMarker = new ActiveGroupMarker() class Group extends ElemJS { constructor(key, data) { super("div") this.data = data this.order = this.data.order this.class("c-group") this.child( (this.data.icon ? ejs("img").class("c-group__icon").attribute("src", this.data.icon) : ejs("div").class("c-group__icon") ), ejs("div").class("c-group__name").text(this.data.name) ) this.on("click", this.onClick.bind(this)) store.activeGroup.subscribe("changeSelf", this.render.bind(this)) } render() { const active = store.activeGroup.value() === this this.element.classList[active ? "add" : "remove"]("c-group--active") } onClick() { store.activeGroup.set(this) } } class Room extends ElemJS { constructor(id, data) { super("div") this.id = id this.data = data this.timeline = new Timeline(this) this.group = null this.members = new SubscribeMapList(SubscribeValue) this.class("c-room") this.on("click", this.onClick.bind(this)) store.activeRoom.subscribe("changeSelf", this.render.bind(this)) this.render() } get order() { if (this.group) { let chars = 36 let total = 0 const name = this.getName() for (let i = 0; i < name.length; i++) { const c = name[i] let d = 0 if (c >= "A" && c <= "Z") d = c.charCodeAt(0) - 65 + 10 else if (c >= "a" && c <= "z") d = c.charCodeAt(0) - 97 + 10 else if (c >= "0" && c <= "9") d = +c total += d * chars ** (-i) } return total } else { return -this.timeline.latest } } getName() { // if the room has a name let name = this.data.state.events.find(e => e.type === "m.room.name") if (name && name.content.name) { return name.content.name } // if the room has no name, use its canonical alias let canonicalAlias = this.data.state.events.find(e => e.type === "m.room.canonical_alias") if (canonicalAlias && canonicalAlias.content.alias) { return canonicalAlias.content.alias } // if the room has no alias, use the names of its members ("heroes") const users = this.data.summary["m.heroes"] if (users && users.length) { const usernames = users.map(u => { // if the member is in the room, use their display name if (this.members.has(u)) { const displayname = this.members.get(u).value().content.displayname if (displayname) { return displayname } } // we don't have the member, so extract the localpart from the mxid const match = u.match(/^@([^:]+):/) if (match) { return match[1] } // localpart extraction failed, use the whole mxid return u }) return usernames.join(", ") } // the room is empty return "Empty room" } getIcon() { // if the room has a normal avatar const avatar = this.data.state.events.find(e => e.type === "m.room.avatar") if (avatar) { const url = avatar.content.url || avatar.content.avatar_url if (url) { return resolveMxc(url, 32, "crop") } } // if the room has no avatar set, use a member's avatar const users = this.data.summary["m.heroes"] if (users && users[0] && this.members.has(users[0])) { // console.log(users[0], this.members.get(users[0])) const userAvatar = this.members.get(users[0]).value().content.avatar_url if (userAvatar) { return resolveMxc(userAvatar, 32, "crop") } } return null } isDirect() { return store.directs.has(this.id) } setGroup(id) { this.group = id } getGroup() { if (this.group) { return store.groups.get(this.group).value() } else { return this.isDirect() ? store.groups.get("directs").value() : store.groups.get("channels").value() } } onClick() { store.activeRoom.set(this) } render() { this.clearChildren() // data const icon = this.getIcon() if (icon) { this.child(ejs("img").class("c-room__icon").attribute("src", icon)) } else { this.child(ejs("div").class("c-room__icon", "c-room__icon--no-icon")) } this.child(ejs("div").class("c-room__name").text(this.getName())) // active const active = store.activeRoom.value() === this this.element.classList[active ? "add" : "remove"]("c-room--active") } } class Rooms extends ElemJS { constructor() { super(q("#c-rooms")) this.roomData = [] this.rooms = [] store.rooms.subscribe("askAdd", this.askAdd.bind(this)) store.rooms.subscribe("addItem", this.addItem.bind(this)) // store.rooms.subscribe("changeItem", this.render.bind(this)) store.activeGroup.subscribe("changeSelf", this.render.bind(this)) store.directs.subscribe("changeItem", this.render.bind(this)) store.newEvents.subscribe("changeSelf", this.sort.bind(this)) this.render() } sort() { store.rooms.sort() this.render() } askAdd(event, {key, data}) { const room = new Room(key, data) store.rooms.addEnd(key, room) } addItem(event, key) { const room = store.rooms.get(key).value() if (room.getGroup() === store.activeGroup.value()) { this.child(room) } } render() { this.clearChildren() let first = null // set room list store.rooms.forEach((id, room) => { if (room.value().getGroup() === store.activeGroup.value()) { if (!first) first = room.value() this.child(room.value()) } }) // if needed, change the active room to be an item in the room list if (!store.activeRoom.exists() || store.activeRoom.value().getGroup() !== store.activeGroup.value()) { if (first) { store.activeRoom.set(first) } else { store.activeRoom.delete() } } } } const rooms = new Rooms() class Groups extends ElemJS { constructor() { super(q("#c-groups-list")) store.groups.subscribe("askAdd", this.askAdd.bind(this)) store.groups.subscribe("changeItem", this.render.bind(this)) } askAdd(event, {key, data}) { const group = new Group(key, data) store.groups.addEnd(key, group) store.groups.sort() } render() { this.clearChildren() store.groups.forEach((key, item) => { this.child(item.value()) }) } } const groups = new Groups()