150 lines
4.1 KiB
JavaScript
150 lines
4.1 KiB
JavaScript
const {ElemJS, ejs, q} = require("./basic.js")
|
|
const {store} = require("./store/store.js")
|
|
const lsm = require("./lsm.js")
|
|
|
|
function markFullyRead(roomID, eventID) {
|
|
return fetch(`${lsm.get("domain")}/_matrix/client/r0/rooms/${roomID}/read_markers?access_token=${lsm.get("access_token")}`, {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
"m.fully_read": eventID,
|
|
"m.read": eventID
|
|
})
|
|
})
|
|
}
|
|
|
|
class ReadBanner extends ElemJS {
|
|
constructor() {
|
|
super(q("#c-chat-banner"))
|
|
|
|
this.newMessages = ejs("span")
|
|
this.child(
|
|
ejs("div").class("c-chat-banner__inner").child(
|
|
ejs("button").class("c-chat-banner__part").on("click", this.jumpTo.bind(this)).child(
|
|
ejs("div").class("c-chat-banner__part-inner")
|
|
.child(this.newMessages)
|
|
.addText(" new messages")
|
|
),
|
|
ejs("button").class("c-chat-banner__part", "c-chat-banner__last").on("click", this.markRead.bind(this)).child(
|
|
ejs("div").class("c-chat-banner__part-inner").text("Mark as read")
|
|
)
|
|
)
|
|
)
|
|
|
|
store.activeRoom.subscribe("changeSelf", this.render.bind(this))
|
|
store.notificationsChange.subscribe("changeSelf", this.render.bind(this))
|
|
this.render()
|
|
}
|
|
|
|
async jumpTo() {
|
|
if (!store.activeRoom.exists()) return
|
|
const timeline = store.activeRoom.value().timeline
|
|
const readMarker = timeline.readMarker
|
|
while (true) {
|
|
if (readMarker.attached) {
|
|
readMarker.element.scrollIntoView({behavior: "smooth", block: "center"})
|
|
return
|
|
} else {
|
|
q("#c-chat-messages").scrollTo({
|
|
top: 0,
|
|
left: 0,
|
|
behavior: "smooth"
|
|
})
|
|
await new Promise(resolve => {
|
|
const unsubscribe = timeline.subscribe("afterScrollbackLoad", () => {
|
|
unsubscribe()
|
|
resolve()
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
markRead() {
|
|
if (!store.activeRoom.exists()) return
|
|
const timeline = store.activeRoom.value().timeline
|
|
markFullyRead(timeline.id, timeline.latestEventID)
|
|
}
|
|
|
|
render() {
|
|
let count = 0
|
|
if (store.activeRoom.exists()) {
|
|
count = store.activeRoom.value().number.state.unreads
|
|
}
|
|
if (count !== 0) {
|
|
this.newMessages.text(count)
|
|
this.class("c-chat-banner--active")
|
|
} else {
|
|
this.removeClass("c-chat-banner--active")
|
|
}
|
|
}
|
|
}
|
|
const readBanner = new ReadBanner()
|
|
|
|
class ReadMarker extends ElemJS {
|
|
constructor(timeline) {
|
|
super("div")
|
|
|
|
this.class("c-read-marker")
|
|
this.loadingIcon = ejs("div")
|
|
.class("c-read-marker__loading", "loading-icon")
|
|
.style("display", "none")
|
|
this.child(
|
|
ejs("div").class("c-read-marker__inner").child(
|
|
ejs("div").class("c-read-marker__text").child(this.loadingIcon).addText("New")
|
|
)
|
|
)
|
|
|
|
let processing = false
|
|
const observer = new IntersectionObserver(entries => {
|
|
const entry = entries[0]
|
|
if (!entry.isIntersecting) return
|
|
if (processing) return
|
|
processing = true
|
|
this.loadingIcon.style("display", "")
|
|
markFullyRead(this.timeline.id, this.timeline.latestEventID).then(() => {
|
|
this.loadingIcon.style("display", "none")
|
|
processing = false
|
|
})
|
|
}, {
|
|
root: document.getElementById("c-chat-messages"),
|
|
rootMargin: "-80px 0px 0px 0px", // marker must be this distance inside the top of the screen to be counted as read
|
|
threshold: 0.01
|
|
})
|
|
observer.observe(this.element)
|
|
|
|
this.attached = false
|
|
this.timeline = timeline
|
|
this.timeline.userReads.get(lsm.get("mx_user_id")).subscribe("changeSelf", (_, eventID) => {
|
|
// read marker updated, attach to it
|
|
const event = this.timeline.map.get(eventID)
|
|
this.attach(event)
|
|
})
|
|
this.timeline.subscribe("afterChange", () => {
|
|
// timeline has new events, attach to last read one
|
|
const eventID = this.timeline.userReads.get(lsm.get("mx_user_id")).value()
|
|
const event = this.timeline.map.get(eventID)
|
|
this.attach(event)
|
|
})
|
|
}
|
|
|
|
attach(event) {
|
|
if (event && event.data.origin_server_ts !== this.timeline.latest) {
|
|
this.class("c-read-marker--attached")
|
|
event.element.insertAdjacentElement("beforeend", this.element)
|
|
this.attached = true
|
|
} else {
|
|
this.removeClass("c-read-marker--attached")
|
|
this.attached = false
|
|
}
|
|
if (store.activeRoom.value() === this.timeline.room) {
|
|
readBanner.render()
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
ReadMarker,
|
|
readBanner,
|
|
markFullyRead
|
|
}
|