Compare commits
No commits in common. "9f6c955b6304f673e950faba0065aa3b42edc2f0" and "c87b6dcaa72fcd3a49d8efd60317aa7406d462c7" have entirely different histories.
9f6c955b63
...
c87b6dcaa7
5 changed files with 3 additions and 69 deletions
|
@ -55,6 +55,7 @@ early in development. These important features still need to be
|
||||||
implemented:
|
implemented:
|
||||||
|
|
||||||
- Unreads
|
- Unreads
|
||||||
|
- Typing indicators
|
||||||
- Emojis
|
- Emojis
|
||||||
- Reactions
|
- Reactions
|
||||||
- Encryption
|
- Encryption
|
||||||
|
|
|
@ -5,85 +5,24 @@ const {chat} = require("./chat.js")
|
||||||
|
|
||||||
const input = q("#c-chat-textarea")
|
const input = q("#c-chat-textarea")
|
||||||
|
|
||||||
class TypingManager {
|
|
||||||
constructor() {
|
|
||||||
this.time = 20000 // how long to appear to type for
|
|
||||||
this.margin = 5000 // how long before the end of the timeout to send the request again
|
|
||||||
/** The room that we're typing in. We can semantically only type in one room at a time. */
|
|
||||||
this.typingRoom = null
|
|
||||||
this.timeout = null
|
|
||||||
}
|
|
||||||
|
|
||||||
request(id, typing) {
|
|
||||||
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/rooms/${id}/typing/${lsm.get("mx_user_id")}`)
|
|
||||||
url.searchParams.set("access_token", lsm.get("access_token"))
|
|
||||||
const body = {typing}
|
|
||||||
if (typing) body.timeout = this.time
|
|
||||||
fetch(url.toString(), {
|
|
||||||
method: "PUT",
|
|
||||||
body: JSON.stringify(body)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule(id) {
|
|
||||||
this.request(id, true)
|
|
||||||
this.timeout = setTimeout(() => {
|
|
||||||
this.schedule(id)
|
|
||||||
}, this.time - this.margin)
|
|
||||||
}
|
|
||||||
|
|
||||||
update(id) {
|
|
||||||
if (id) { // typing somewhere
|
|
||||||
if (this.typingRoom === id) return // already typing, don't do anything
|
|
||||||
// state
|
|
||||||
this.typingRoom = id
|
|
||||||
// mark and schedule
|
|
||||||
this.schedule(id)
|
|
||||||
// add self to typing list now instead of waiting a round trip
|
|
||||||
const typing = store.rooms.get(id).value().timeline.typing
|
|
||||||
typing.edit(list => list.concat(lsm.get("mx_user_id")))
|
|
||||||
} else { // stopped typing
|
|
||||||
if (this.typingRoom) {
|
|
||||||
clearTimeout(this.timeout)
|
|
||||||
this.request(this.typingRoom, false)
|
|
||||||
}
|
|
||||||
this.typingRoom = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const typingManager = new TypingManager()
|
|
||||||
|
|
||||||
store.activeRoom.subscribe("changeSelf", () => {
|
store.activeRoom.subscribe("changeSelf", () => {
|
||||||
// stop typing. you semantically can't type in a room you're not in.
|
|
||||||
typingManager.update(null)
|
|
||||||
// focus input box
|
|
||||||
if (store.activeRoom.exists()) {
|
if (store.activeRoom.exists()) {
|
||||||
input.focus()
|
input.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
input.addEventListener("keydown", event => {
|
input.addEventListener("keydown", event => {
|
||||||
if (!store.activeRoom.exists()) return
|
|
||||||
// send message?
|
|
||||||
if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) {
|
if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const body = input.value
|
const body = input.value
|
||||||
send(input.value)
|
send(input.value)
|
||||||
input.value = ""
|
input.value = ""
|
||||||
fixHeight()
|
fixHeight()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
input.addEventListener("input", () => {
|
input.addEventListener("input", () => {
|
||||||
fixHeight()
|
fixHeight()
|
||||||
// set typing
|
|
||||||
if (input.value) {
|
|
||||||
typingManager.update(store.activeRoom.value().id)
|
|
||||||
} else {
|
|
||||||
typingManager.update(null)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function fixHeight() {
|
function fixHeight() {
|
||||||
|
|
|
@ -30,7 +30,7 @@ class SubscribeValue extends Subscribable {
|
||||||
|
|
||||||
edit(f) {
|
edit(f) {
|
||||||
if (this.exists()) {
|
if (this.exists()) {
|
||||||
this.data = f(this.data)
|
f(this.data)
|
||||||
this.set(this.data)
|
this.set(this.data)
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Tried to edit a SubscribeValue that had no value")
|
throw new Error("Tried to edit a SubscribeValue that had no value")
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const {ElemJS, ejs, q} = require("./basic")
|
const {ElemJS, ejs, q} = require("./basic")
|
||||||
const {store} = require("./store/store")
|
const {store} = require("./store/store")
|
||||||
const lsm = require("./lsm")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of typing users to display all names for.
|
* Maximum number of typing users to display all names for.
|
||||||
|
@ -27,7 +26,6 @@ class Typing extends ElemJS {
|
||||||
changeRoom() {
|
changeRoom() {
|
||||||
if (this.typingUnsubscribe) {
|
if (this.typingUnsubscribe) {
|
||||||
this.typingUnsubscribe()
|
this.typingUnsubscribe()
|
||||||
this.typingUnsubscribe = null
|
|
||||||
}
|
}
|
||||||
if (!store.activeRoom.exists()) return
|
if (!store.activeRoom.exists()) return
|
||||||
const room = store.activeRoom.value()
|
const room = store.activeRoom.value()
|
||||||
|
@ -38,11 +36,8 @@ class Typing extends ElemJS {
|
||||||
render() {
|
render() {
|
||||||
if (!store.activeRoom.exists()) return
|
if (!store.activeRoom.exists()) return
|
||||||
const room = store.activeRoom.value()
|
const room = store.activeRoom.value()
|
||||||
let users = [...room.timeline.typing.value()]
|
const users = room.timeline.typing.value()
|
||||||
// don't show own typing status
|
|
||||||
users = users.filter(u => u !== lsm.get("mx_user_id"))
|
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
// nobody is typing
|
|
||||||
this.removeClass("c-typing--typing")
|
this.removeClass("c-typing--typing")
|
||||||
} else {
|
} else {
|
||||||
let message = ""
|
let message = ""
|
||||||
|
|
|
@ -92,7 +92,6 @@
|
||||||
background-color: c.$darkest
|
background-color: c.$darkest
|
||||||
padding: 8px
|
padding: 8px
|
||||||
border: 1px solid c.$divider
|
border: 1px solid c.$divider
|
||||||
white-space: pre-wrap
|
|
||||||
|
|
||||||
code
|
code
|
||||||
background-color: c.$darker
|
background-color: c.$darker
|
||||||
|
|
Loading…
Reference in a new issue