2020-10-23 14:15:14 +00:00
|
|
|
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")
|
2020-11-08 11:19:56 +00:00
|
|
|
const {resolveMxc, extractLocalpart, extractDisplayName} = require("./functions.js")
|
2020-10-12 12:35:54 +00:00
|
|
|
|
2020-10-12 13:26:10 +00:00
|
|
|
class ActiveGroupMarker extends ElemJS {
|
2020-10-19 11:43:33 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2020-10-12 13:26:10 +00:00
|
|
|
}
|
2020-10-15 03:43:37 +00:00
|
|
|
|
2020-10-19 11:43:33 +00:00
|
|
|
const activeGroupMarker = new ActiveGroupMarker()
|
2020-10-12 13:26:10 +00:00
|
|
|
|
2020-11-25 06:54:09 +00:00
|
|
|
class GroupNotifier extends ElemJS {
|
|
|
|
constructor() {
|
|
|
|
super("div")
|
|
|
|
|
|
|
|
this.class("c-group__number")
|
|
|
|
this.state = {}
|
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
|
|
|
update(state) {
|
|
|
|
Object.assign(this.state, state)
|
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.state = {}
|
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let total = Object.values(this.state).reduce((a, c) => a + c, 0)
|
|
|
|
if (total > 0) {
|
|
|
|
this.text(total)
|
|
|
|
this.class("c-group__number--active")
|
|
|
|
} else {
|
|
|
|
this.removeClass("c-group__number--active")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 12:35:54 +00:00
|
|
|
class Group extends ElemJS {
|
2020-10-19 11:43:33 +00:00
|
|
|
constructor(key, data) {
|
|
|
|
super("div")
|
|
|
|
|
|
|
|
this.data = data
|
|
|
|
this.order = this.data.order
|
2020-11-25 06:54:09 +00:00
|
|
|
this.number = new GroupNotifier()
|
2020-10-19 11:43:33 +00:00
|
|
|
|
|
|
|
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")
|
|
|
|
),
|
2020-11-25 06:54:09 +00:00
|
|
|
this.number,
|
2020-10-19 11:43:33 +00:00
|
|
|
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)
|
|
|
|
}
|
2020-10-12 12:35:54 +00:00
|
|
|
}
|
|
|
|
|
2020-11-24 12:27:41 +00:00
|
|
|
class RoomNotifier extends ElemJS {
|
2020-11-25 06:54:09 +00:00
|
|
|
constructor(room) {
|
2020-11-24 12:27:41 +00:00
|
|
|
super("div")
|
|
|
|
|
2020-11-25 06:54:09 +00:00
|
|
|
this.class("c-room__number")
|
|
|
|
|
|
|
|
this.room = room
|
2020-11-24 12:27:41 +00:00
|
|
|
this.classes = [
|
|
|
|
"notifications",
|
|
|
|
"unreads",
|
|
|
|
"none"
|
|
|
|
]
|
|
|
|
this.state = {
|
|
|
|
notifications: 0,
|
|
|
|
unreads: 0
|
|
|
|
}
|
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {object} state
|
|
|
|
* @param {number} [state.notifications]
|
|
|
|
* @param {number} [state.unreads]
|
|
|
|
*/
|
|
|
|
update(state) {
|
|
|
|
Object.assign(this.state, state)
|
2020-11-25 06:54:09 +00:00
|
|
|
this.informGroup()
|
2020-11-24 12:27:41 +00:00
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
2020-11-25 06:54:09 +00:00
|
|
|
informGroup() {
|
|
|
|
this.room.getGroup().number.update({[this.room.id]: (
|
|
|
|
this.state.notifications || (this.state.unreads ? 1 : 0)
|
|
|
|
)})
|
|
|
|
}
|
|
|
|
|
2020-11-24 12:27:41 +00:00
|
|
|
render() {
|
|
|
|
const display = {
|
|
|
|
number: this.state.notifications || this.state.unreads,
|
|
|
|
kind: this.state.notifications ? "notifications" : "unreads"
|
|
|
|
}
|
|
|
|
// set number
|
|
|
|
if (display.number) {
|
|
|
|
this.text(display.number)
|
|
|
|
} else {
|
|
|
|
this.text("")
|
|
|
|
display.kind = "none"
|
|
|
|
}
|
|
|
|
// set class
|
|
|
|
this.classes.forEach(c => {
|
|
|
|
const name = "c-room__number--" + c
|
|
|
|
if (c === display.kind) {
|
|
|
|
this.class(name)
|
|
|
|
} else {
|
|
|
|
this.removeClass(name)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 12:35:54 +00:00
|
|
|
class Room extends ElemJS {
|
2020-10-19 11:43:33 +00:00
|
|
|
constructor(id, data) {
|
|
|
|
super("div")
|
|
|
|
|
|
|
|
this.id = id
|
|
|
|
this.data = data
|
2020-11-25 06:54:09 +00:00
|
|
|
this.number = new RoomNotifier(this)
|
2020-10-20 12:04:53 +00:00
|
|
|
this.timeline = new Timeline(this)
|
2020-10-19 11:43:33 +00:00
|
|
|
this.group = null
|
2020-10-19 13:05:16 +00:00
|
|
|
this.members = new SubscribeMapList(SubscribeValue)
|
2020-10-19 11:43:33 +00:00
|
|
|
|
|
|
|
this.class("c-room")
|
|
|
|
|
|
|
|
this.on("click", this.onClick.bind(this))
|
|
|
|
store.activeRoom.subscribe("changeSelf", this.render.bind(this))
|
|
|
|
|
|
|
|
this.render()
|
|
|
|
}
|
|
|
|
|
|
|
|
get order() {
|
2020-11-24 12:27:41 +00:00
|
|
|
let string = ""
|
|
|
|
if (this.number.state.notifications) {
|
|
|
|
string += "N"
|
|
|
|
} else if (this.number.state.unreads) {
|
|
|
|
string += "U"
|
|
|
|
} else {
|
|
|
|
string += "_"
|
|
|
|
}
|
2020-10-19 11:43:33 +00:00
|
|
|
if (this.group) {
|
2020-11-24 12:27:41 +00:00
|
|
|
string += this.name
|
2020-10-19 11:43:33 +00:00
|
|
|
} else {
|
2020-11-24 12:27:41 +00:00
|
|
|
string += (4000000000000 - this.timeline.latest) // good until 2065 :)
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
2020-11-24 12:27:41 +00:00
|
|
|
return string
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
|
|
|
|
2020-11-08 11:19:56 +00:00
|
|
|
getMemberName(mxid) {
|
|
|
|
if (this.members.has(mxid)) {
|
|
|
|
const state = this.members.get(mxid).value()
|
|
|
|
return extractDisplayName(state)
|
|
|
|
} else {
|
|
|
|
return extractLocalpart(mxid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-19 11:43:33 +00:00
|
|
|
getName() {
|
2020-10-21 09:10:58 +00:00
|
|
|
// if the room has a name
|
2020-10-19 11:43:33 +00:00
|
|
|
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
2020-10-21 09:10:58 +00:00
|
|
|
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
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
2020-10-21 09:10:58 +00:00
|
|
|
// if the room has no alias, use the names of its members ("heroes")
|
|
|
|
const users = this.data.summary["m.heroes"]
|
2020-10-22 07:56:27 +00:00
|
|
|
if (users && users.length) {
|
2020-11-08 11:19:56 +00:00
|
|
|
const usernames = users.map(mxid => this.getMemberName(mxid))
|
2020-10-22 07:56:27 +00:00
|
|
|
return usernames.join(", ")
|
|
|
|
}
|
|
|
|
// the room is empty
|
|
|
|
return "Empty room"
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getIcon() {
|
2020-11-05 03:23:40 +00:00
|
|
|
// if the room has a normal avatar
|
2020-10-19 11:43:33 +00:00
|
|
|
const avatar = this.data.state.events.find(e => e.type === "m.room.avatar")
|
|
|
|
if (avatar) {
|
2020-10-21 07:23:51 +00:00
|
|
|
const url = avatar.content.url || avatar.content.avatar_url
|
|
|
|
if (url) {
|
|
|
|
return resolveMxc(url, 32, "crop")
|
|
|
|
}
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
2020-11-05 03:23:40 +00:00
|
|
|
// 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")
|
|
|
|
}
|
|
|
|
}
|
2020-10-21 07:23:51 +00:00
|
|
|
return null
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()))
|
2020-11-24 12:27:41 +00:00
|
|
|
this.child(this.number)
|
2020-10-19 11:43:33 +00:00
|
|
|
// active
|
|
|
|
const active = store.activeRoom.value() === this
|
|
|
|
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
|
|
|
}
|
2020-10-12 12:35:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class Rooms extends ElemJS {
|
2020-10-19 11:43:33 +00:00
|
|
|
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))
|
2020-11-24 12:27:41 +00:00
|
|
|
store.notificationsChange.subscribe("changeSelf", this.sort.bind(this))
|
2020-10-19 11:43:33 +00:00
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-12 12:35:54 +00:00
|
|
|
}
|
2020-10-19 11:43:33 +00:00
|
|
|
const rooms = new Rooms()
|
2020-10-12 12:35:54 +00:00
|
|
|
|
|
|
|
class Groups extends ElemJS {
|
2020-10-19 11:43:33 +00:00
|
|
|
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) => {
|
2020-11-25 06:54:09 +00:00
|
|
|
item.value().number.clear()
|
2020-10-19 11:43:33 +00:00
|
|
|
this.child(item.value())
|
|
|
|
})
|
2020-11-25 06:54:09 +00:00
|
|
|
store.rooms.forEach((id, room) => {
|
|
|
|
room.value().number.informGroup() // update group notification number
|
|
|
|
})
|
2020-10-19 11:43:33 +00:00
|
|
|
}
|
2020-10-12 12:35:54 +00:00
|
|
|
}
|
2020-10-19 11:43:33 +00:00
|
|
|
const groups = new Groups()
|