Display unread/notification counters on rooms
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
e90a2c7da8
commit
229e6903fd
7 changed files with 171 additions and 32 deletions
|
@ -56,12 +56,65 @@ class Group extends ElemJS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RoomNotifier extends ElemJS {
|
||||||
|
constructor() {
|
||||||
|
super("div")
|
||||||
|
|
||||||
|
this.classes = [
|
||||||
|
"notifications",
|
||||||
|
"unreads",
|
||||||
|
"none"
|
||||||
|
]
|
||||||
|
|
||||||
|
this.class("c-room__number")
|
||||||
|
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)
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Room extends ElemJS {
|
class Room extends ElemJS {
|
||||||
constructor(id, data) {
|
constructor(id, data) {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.id = id
|
this.id = id
|
||||||
this.data = data
|
this.data = data
|
||||||
|
this.number = new RoomNotifier()
|
||||||
this.timeline = new Timeline(this)
|
this.timeline = new Timeline(this)
|
||||||
this.group = null
|
this.group = null
|
||||||
this.members = new SubscribeMapList(SubscribeValue)
|
this.members = new SubscribeMapList(SubscribeValue)
|
||||||
|
@ -75,22 +128,21 @@ class Room extends ElemJS {
|
||||||
}
|
}
|
||||||
|
|
||||||
get order() {
|
get order() {
|
||||||
if (this.group) {
|
let string = ""
|
||||||
let chars = 36
|
if (this.number.state.notifications) {
|
||||||
let total = 0
|
string += "N"
|
||||||
const name = this.getName()
|
} else if (this.number.state.unreads) {
|
||||||
for (let i = 0; i < name.length; i++) {
|
string += "U"
|
||||||
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 {
|
} else {
|
||||||
return -this.timeline.latest
|
string += "_"
|
||||||
}
|
}
|
||||||
|
if (this.group) {
|
||||||
|
string += this.name
|
||||||
|
} else {
|
||||||
|
string += (4000000000000 - this.timeline.latest) // good until 2065 :)
|
||||||
|
}
|
||||||
|
console.log(string)
|
||||||
|
return string
|
||||||
}
|
}
|
||||||
|
|
||||||
getMemberName(mxid) {
|
getMemberName(mxid) {
|
||||||
|
@ -174,6 +226,7 @@ class Room extends ElemJS {
|
||||||
this.child(ejs("div").class("c-room__icon", "c-room__icon--no-icon"))
|
this.child(ejs("div").class("c-room__icon", "c-room__icon--no-icon"))
|
||||||
}
|
}
|
||||||
this.child(ejs("div").class("c-room__name").text(this.getName()))
|
this.child(ejs("div").class("c-room__name").text(this.getName()))
|
||||||
|
this.child(this.number)
|
||||||
// active
|
// active
|
||||||
const active = store.activeRoom.value() === this
|
const active = store.activeRoom.value() === this
|
||||||
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
||||||
|
@ -193,6 +246,7 @@ class Rooms extends ElemJS {
|
||||||
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
||||||
store.directs.subscribe("changeItem", this.render.bind(this))
|
store.directs.subscribe("changeItem", this.render.bind(this))
|
||||||
store.newEvents.subscribe("changeSelf", this.sort.bind(this))
|
store.newEvents.subscribe("changeSelf", this.sort.bind(this))
|
||||||
|
store.notificationsChange.subscribe("changeSelf", this.sort.bind(this))
|
||||||
|
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ const store = {
|
||||||
directs: new SubscribeSet(),
|
directs: new SubscribeSet(),
|
||||||
activeGroup: new SubscribeValue(),
|
activeGroup: new SubscribeValue(),
|
||||||
activeRoom: new SubscribeValue(),
|
activeRoom: new SubscribeValue(),
|
||||||
newEvents: new Subscribable()
|
newEvents: new Subscribable(),
|
||||||
|
notificationsChange: new Subscribable()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.store = store
|
window.store = store
|
||||||
|
|
|
@ -1,40 +1,54 @@
|
||||||
const {Subscribable} = require("./subscribable.js")
|
const {Subscribable} = require("./subscribable.js")
|
||||||
const {SubscribeValue} = require("./subscribe_value.js")
|
|
||||||
|
|
||||||
class SubscribeMap extends Subscribable {
|
class SubscribeMap extends Subscribable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
Object.assign(this.events, {
|
Object.assign(this.events, {
|
||||||
addItem: [],
|
addItem: [],
|
||||||
|
editItem: [],
|
||||||
|
deleteItem: [],
|
||||||
changeItem: [],
|
changeItem: [],
|
||||||
removeItem: []
|
askSet: []
|
||||||
})
|
})
|
||||||
this.map = new Map()
|
Object.assign(this.eventDeps, {
|
||||||
|
addItem: ["changeItem"],
|
||||||
|
editItem: ["changeItem"],
|
||||||
|
deleteItem: ["changeItem"],
|
||||||
|
changeItem: [],
|
||||||
|
askSet: []
|
||||||
|
})
|
||||||
|
this.backing = new Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
has(key) {
|
has(key) {
|
||||||
return this.map.has(key) && this.map.get(key).exists()
|
return this.backing.has(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
forEach(f) {
|
||||||
if (this.map.has(key)) {
|
for (const key of this.backing.keys()) {
|
||||||
return this.map.get(key)
|
f(key, this.backing.get(key))
|
||||||
} else {
|
|
||||||
this.map.set(key, new SubscribeValue())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
askSet(key, value) {
|
||||||
|
this.broadcast("askSet", key, value)
|
||||||
|
}
|
||||||
|
|
||||||
set(key, value) {
|
set(key, value) {
|
||||||
let s
|
const existed = this.backing.has(key)
|
||||||
if (this.map.has(key)) {
|
this.backing.set(key, value)
|
||||||
s = this.map.get(key).set(value)
|
if (existed) {
|
||||||
this.broadcast("changeItem", key)
|
|
||||||
} else {
|
|
||||||
s = new SubscribeValue().set(value)
|
|
||||||
this.map.set(key, s)
|
|
||||||
this.broadcast("addItem", key)
|
this.broadcast("addItem", key)
|
||||||
|
} else {
|
||||||
|
this.broadcast("editItem", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
if (this.backing.has(key)) {
|
||||||
|
this.backing.delete(key)
|
||||||
|
this.broadcast("deleteItem", key)
|
||||||
}
|
}
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,15 @@ class SubscribeMapList extends Subscribable {
|
||||||
}
|
}
|
||||||
|
|
||||||
sort() {
|
sort() {
|
||||||
|
const key = this.list[0]
|
||||||
|
if (typeof this.map.get(key).value().order === "number") {
|
||||||
|
this.sortByNumber()
|
||||||
|
} else {
|
||||||
|
this.sortByString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByNumber() {
|
||||||
this.list.sort((a, b) => {
|
this.list.sort((a, b) => {
|
||||||
const orderA = this.map.get(a).value().order
|
const orderA = this.map.get(a).value().order
|
||||||
const orderB = this.map.get(b).value().order
|
const orderB = this.map.get(b).value().order
|
||||||
|
@ -62,6 +71,20 @@ class SubscribeMapList extends Subscribable {
|
||||||
this.broadcast("changeItem")
|
this.broadcast("changeItem")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortByString() {
|
||||||
|
this.list.sort((a, b) => {
|
||||||
|
let r
|
||||||
|
const orderA = this.map.get(a).value().order
|
||||||
|
const orderB = this.map.get(b).value().order
|
||||||
|
if (orderA < orderB) r = -1
|
||||||
|
else if (orderA > orderB) r = 1
|
||||||
|
else r = 0
|
||||||
|
console.log("comparing", orderA, orderB, r)
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
this.broadcast("changeItem")
|
||||||
|
}
|
||||||
|
|
||||||
_add(key, value, start) {
|
_add(key, value, start) {
|
||||||
let s
|
let s
|
||||||
if (this.map.has(key)) {
|
if (this.map.has(key)) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ function sync() {
|
||||||
function manageSync(root) {
|
function manageSync(root) {
|
||||||
try {
|
try {
|
||||||
let newEvents = false
|
let newEvents = false
|
||||||
|
let notificationsChange = false
|
||||||
|
|
||||||
// set up directs
|
// set up directs
|
||||||
if (root.account_data) {
|
if (root.account_data) {
|
||||||
|
@ -66,6 +67,14 @@ function manageSync(root) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.ephemeral) timeline.updateEphemeral(data.ephemeral.events)
|
if (data.ephemeral) timeline.updateEphemeral(data.ephemeral.events)
|
||||||
|
if (data.unread_notifications) {
|
||||||
|
timeline.updateNotificationCount(data.unread_notifications.notification_count)
|
||||||
|
notificationsChange = true
|
||||||
|
}
|
||||||
|
if (data["org.matrix.msc2654.unread_count"] != undefined) {
|
||||||
|
timeline.updateUnreadCount(data["org.matrix.msc2654.unread_count"])
|
||||||
|
notificationsChange = true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +118,7 @@ function manageSync(root) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newEvents) store.newEvents.broadcast("changeSelf")
|
if (newEvents) store.newEvents.broadcast("changeSelf")
|
||||||
|
if (notificationsChange) store.notificationsChange.broadcast("changeSelf")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(root)
|
console.error(root)
|
||||||
throw e
|
throw e
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const {ElemJS, ejs} = require("./basic.js")
|
const {ElemJS, ejs} = require("./basic.js")
|
||||||
const {Subscribable} = require("./store/subscribable.js")
|
const {Subscribable} = require("./store/subscribable.js")
|
||||||
const {SubscribeValue} = require("./store/subscribe_value.js")
|
const {SubscribeValue} = require("./store/subscribe_value.js")
|
||||||
|
const {SubscribeMap} = require("./store/subscribe_map.js")
|
||||||
const {store} = require("./store/store.js")
|
const {store} = require("./store/store.js")
|
||||||
const {Anchor} = require("./anchor.js")
|
const {Anchor} = require("./anchor.js")
|
||||||
const {Sender} = require("./sender.js")
|
const {Sender} = require("./sender.js")
|
||||||
|
@ -197,6 +198,7 @@ class Timeline extends Subscribable {
|
||||||
this.pending = new Set()
|
this.pending = new Set()
|
||||||
this.pendingEdits = []
|
this.pendingEdits = []
|
||||||
this.typing = new SubscribeValue().set([])
|
this.typing = new SubscribeValue().set([])
|
||||||
|
this.userReads = new SubscribeMap()
|
||||||
this.from = null
|
this.from = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,8 +282,24 @@ class Timeline extends Subscribable {
|
||||||
if (eventData.type === "m.typing") {
|
if (eventData.type === "m.typing") {
|
||||||
this.typing.set(eventData.content.user_ids)
|
this.typing.set(eventData.content.user_ids)
|
||||||
}
|
}
|
||||||
|
if (eventData.type === "m.receipt") {
|
||||||
|
for (const eventID of Object.keys(eventData.content)) {
|
||||||
|
for (const user of Object.keys(eventData.content[eventID]["m.read"])) {
|
||||||
|
this.userReads.set(user, eventID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// console.log("Updated read receipts:", this.userReads)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUnreadCount(count) {
|
||||||
|
this.room.number.update({unreads: count})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNotificationCount(count) {
|
||||||
|
this.room.number.update({notifications: count})
|
||||||
|
}
|
||||||
|
|
||||||
removeEvent(id) {
|
removeEvent(id) {
|
||||||
if (!this.map.has(id)) throw new Error(`Tried to delete event ID ${id} which does not exist`)
|
if (!this.map.has(id)) throw new Error(`Tried to delete event ID ${id} which does not exist`)
|
||||||
|
|
|
@ -43,3 +43,22 @@ $icon-padding: 8px
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
|
flex: 1
|
||||||
|
|
||||||
|
&__number
|
||||||
|
flex-shrink: 0
|
||||||
|
line-height: 1
|
||||||
|
padding: 4px 5px
|
||||||
|
border-radius: 5px
|
||||||
|
font-size: 14px
|
||||||
|
|
||||||
|
&--none
|
||||||
|
display: none
|
||||||
|
|
||||||
|
&--unreads
|
||||||
|
background-color: #ddd
|
||||||
|
color: #111
|
||||||
|
|
||||||
|
&--notifications
|
||||||
|
background-color: #ffac4b
|
||||||
|
color: #000
|
||||||
|
|
Loading…
Reference in a new issue