Support Element's groups and list sorting
This commit is contained in:
parent
4b0b5c4b39
commit
1f9462b89d
18 changed files with 272 additions and 145 deletions
2
build.js
2
build.js
|
@ -49,7 +49,7 @@ function validate(filename, body, type) {
|
||||||
let match
|
let match
|
||||||
if (match = message.message.match(/Property “([\w-]+)” doesn't exist.$/)) {
|
if (match = message.message.match(/Property “([\w-]+)” doesn't exist.$/)) {
|
||||||
// allow these properties specifically
|
// allow these properties specifically
|
||||||
if (["scrollbar-width", "scrollbar-color"].includes(match[1])) {
|
if (["scrollbar-width", "scrollbar-color", "overflow-anchor"].includes(match[1])) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" type="text/css" href="static/main.css?static=da499eb39c">
|
<link rel="stylesheet" type="text/css" href="static/main.css?static=9aad8398d2">
|
||||||
<script type="module" src="static/groups.js?static=2cc7f0daf8"></script>
|
<script type="module" src="static/groups.js?static=2cc7f0daf8"></script>
|
||||||
<script type="module" src="static/chat-input.js?static=e8b21037fa"></script>
|
<script type="module" src="static/chat-input.js?static=e8b21037fa"></script>
|
||||||
<script type="module" src="static/room-picker.js?static=1d38378110"></script>
|
<script type="module" src="static/room-picker.js?static=7bc94b38d3"></script>
|
||||||
<script type="module" src="static/sync/sync.js?static=9e31c8a727"></script>
|
<script type="module" src="static/sync/sync.js?static=56e374b23d"></script>
|
||||||
<script type="module" src="static/chat.js?static=8a04bee48d"></script>
|
<script type="module" src="static/chat.js?static=8a04bee48d"></script>
|
||||||
<title>Carbon</title>
|
<title>Carbon</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Anchor extends ElemJS {
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll() {
|
scroll() {
|
||||||
console.log("anchor scrolled")
|
// console.log("anchor scrolled")
|
||||||
this.element.scrollIntoView({block: "start"})
|
this.element.scrollIntoView({block: "start"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class EventGroup extends ElemJS {
|
||||||
this.messages = ejs("div").class("c-message-group__messages").child(
|
this.messages = ejs("div").class("c-message-group__messages").child(
|
||||||
ejs("div").class("c-message-group__intro").child(
|
ejs("div").class("c-message-group__intro").child(
|
||||||
ejs("div").class("c-message-group__name").text(this.data.sender),
|
ejs("div").class("c-message-group__name").text(this.data.sender),
|
||||||
ejs("div").class("c-message-group__date").text("at 4:20 pm")
|
ejs("div").class("c-message-group__date").text(this.data.origin_server_ts)
|
||||||
),
|
),
|
||||||
...this.list
|
...this.list
|
||||||
)
|
)
|
||||||
|
@ -123,11 +123,13 @@ class Timeline extends Subscribable {
|
||||||
this.list = []
|
this.list = []
|
||||||
this.map = new Map()
|
this.map = new Map()
|
||||||
this.reactiveTimeline = new ReactiveTimeline([])
|
this.reactiveTimeline = new ReactiveTimeline([])
|
||||||
|
this.latest = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEvents(events) {
|
updateEvents(events) {
|
||||||
this.broadcast("beforeChange")
|
this.broadcast("beforeChange")
|
||||||
for (const eventData of events) {
|
for (const eventData of events) {
|
||||||
|
this.latest = Math.max(this.latest, eventData.origin_server_ts)
|
||||||
if (this.map.has(eventData.event_id)) {
|
if (this.map.has(eventData.event_id)) {
|
||||||
this.map.get(eventData.event_id).update(eventData)
|
this.map.get(eventData.event_id).update(eventData)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -71,7 +71,7 @@ body {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 24px;
|
font-size: 22px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.c-groups__display {
|
.c-groups__display {
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Group extends ElemJS {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.data = data
|
this.data = data
|
||||||
|
this.order = this.data.order
|
||||||
|
|
||||||
this.class("c-group")
|
this.class("c-group")
|
||||||
this.child(
|
this.child(
|
||||||
|
@ -68,6 +69,7 @@ class Room extends ElemJS {
|
||||||
this.id = id
|
this.id = id
|
||||||
this.data = data
|
this.data = data
|
||||||
this.timeline = new Timeline()
|
this.timeline = new Timeline()
|
||||||
|
this.group = null
|
||||||
|
|
||||||
this.class("c-room")
|
this.class("c-room")
|
||||||
|
|
||||||
|
@ -77,6 +79,25 @@ class Room extends ElemJS {
|
||||||
this.render()
|
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() {
|
getName() {
|
||||||
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
||||||
if (name) {
|
if (name) {
|
||||||
|
@ -102,8 +123,16 @@ class Room extends ElemJS {
|
||||||
return store.directs.has(this.id)
|
return store.directs.has(this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setGroup(id) {
|
||||||
|
this.group = id
|
||||||
|
}
|
||||||
|
|
||||||
getGroup() {
|
getGroup() {
|
||||||
return this.isDirect() ? store.groups.get("directs").value() : store.groups.get("channels").value()
|
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() {
|
onClick() {
|
||||||
|
@ -138,10 +167,16 @@ class Rooms extends ElemJS {
|
||||||
// store.rooms.subscribe("changeItem", this.render.bind(this))
|
// store.rooms.subscribe("changeItem", this.render.bind(this))
|
||||||
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))
|
||||||
|
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
store.rooms.sort()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
askAdd(event, {key, data}) {
|
askAdd(event, {key, data}) {
|
||||||
const room = new Room(key, data)
|
const room = new Room(key, data)
|
||||||
store.rooms.addEnd(key, room)
|
store.rooms.addEnd(key, room)
|
||||||
|
@ -181,74 +216,20 @@ class Groups extends ElemJS {
|
||||||
super(q("#c-groups-list"))
|
super(q("#c-groups-list"))
|
||||||
|
|
||||||
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
store.groups.subscribe("addItem", this.addItem.bind(this))
|
store.groups.subscribe("changeItem", this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
askAdd(event, {key, data}) {
|
askAdd(event, {key, data}) {
|
||||||
const group = new Group(key, data)
|
const group = new Group(key, data)
|
||||||
store.groups.addEnd(key, group)
|
store.groups.addEnd(key, group)
|
||||||
|
store.groups.sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
addItem(event, key) {
|
render() {
|
||||||
this.child(store.groups.get(key).value())
|
this.clearChildren()
|
||||||
|
store.groups.forEach((key, item) => {
|
||||||
|
this.child(item.value())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const groups = new Groups()
|
const groups = new Groups()
|
||||||
|
|
||||||
|
|
||||||
;[
|
|
||||||
{
|
|
||||||
id: "directs",
|
|
||||||
name: "Directs",
|
|
||||||
icon: "/static/directs.svg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "channels",
|
|
||||||
name: "Channels",
|
|
||||||
icon: "/static/channels.svg"
|
|
||||||
}/*,
|
|
||||||
{
|
|
||||||
id: "123",
|
|
||||||
name: "Fediverse Drama Museum"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "456",
|
|
||||||
name: "Epicord"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "789",
|
|
||||||
name: "Invidious"
|
|
||||||
}*/
|
|
||||||
].forEach(data => store.groups.askAdd(data.id, data))
|
|
||||||
|
|
||||||
/*
|
|
||||||
;[
|
|
||||||
{id: "001", name: "riley", group: store.groups.get("directs").value()},
|
|
||||||
{id: "002", name: "BadAtNames", group: store.groups.get("directs").value()},
|
|
||||||
{id: "003", name: "lynxano", group: store.groups.get("directs").value()},
|
|
||||||
{id: "004", name: "quarky", group: store.groups.get("directs").value()},
|
|
||||||
{id: "005", name: "lepton", group: store.groups.get("directs").value()},
|
|
||||||
{id: "006", name: "ash", group: store.groups.get("directs").value()},
|
|
||||||
{id: "007", name: "mewmew", group: store.groups.get("directs").value()},
|
|
||||||
{id: "008", name: "Toniob", group: store.groups.get("directs").value()},
|
|
||||||
{id: "009", name: "cockandball", group: store.groups.get("directs").value()},
|
|
||||||
{id: "010", name: "Carbon brainstorming", group: store.groups.get("channels").value()},
|
|
||||||
{id: "011", name: "Bibliogram", group: store.groups.get("channels").value()},
|
|
||||||
{id: "012", name: "Monsters Inc Debate Hall", group: store.groups.get("channels").value()},
|
|
||||||
{id: "013", name: "DRB clan", group: store.groups.get("channels").value()},
|
|
||||||
{id: "014", name: "mettaton simp zone", group: store.groups.get("channels").value()},
|
|
||||||
{id: "015", name: "witches", group: store.groups.get("123").value()},
|
|
||||||
{id: "016", name: "snouts", group: store.groups.get("123").value()},
|
|
||||||
{id: "017", name: "monads", group: store.groups.get("123").value()},
|
|
||||||
{id: "018", name: "radical", group: store.groups.get("123").value()},
|
|
||||||
{id: "019", name: "blobcat", group: store.groups.get("123").value()},
|
|
||||||
{id: "020", name: "main", group: store.groups.get("456").value()},
|
|
||||||
{id: "021", name: "gaming", group: store.groups.get("456").value()},
|
|
||||||
{id: "022", name: "inhalers", group: store.groups.get("456").value()},
|
|
||||||
{id: "023", name: "minecraft", group: store.groups.get("456").value()},
|
|
||||||
{id: "024", name: "osu", group: store.groups.get("456").value()},
|
|
||||||
{id: "025", name: "covid", group: store.groups.get("456").value()}
|
|
||||||
].forEach(data => store.rooms.askAdd(data.id, data))
|
|
||||||
*/
|
|
||||||
|
|
||||||
store.activeGroup.set(store.groups.get("directs").value())
|
|
||||||
|
|
|
@ -53,6 +53,16 @@ class SubscribeMapList extends Subscribable {
|
||||||
this._add(key, value, false)
|
this._add(key, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
console.log("sorting")
|
||||||
|
this.list.sort((a, b) => {
|
||||||
|
const orderA = this.map.get(a).value().order
|
||||||
|
const orderB = this.map.get(b).value().order
|
||||||
|
return orderA - orderB
|
||||||
|
})
|
||||||
|
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)) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
import {SubscribeMapList} from "./SubscribeMapList.js"
|
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||||
import {SubscribeSet} from "./SubscribeSet.js"
|
import {SubscribeSet} from "./SubscribeSet.js"
|
||||||
import {SubscribeValue} from "./SubscribeValue.js"
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
@ -7,7 +8,8 @@ const store = {
|
||||||
rooms: new SubscribeMapList(SubscribeValue),
|
rooms: new SubscribeMapList(SubscribeValue),
|
||||||
directs: new SubscribeSet(),
|
directs: new SubscribeSet(),
|
||||||
activeGroup: new SubscribeValue(),
|
activeGroup: new SubscribeValue(),
|
||||||
activeRoom: new SubscribeValue()
|
activeRoom: new SubscribeValue(),
|
||||||
|
newEvents: new Subscribable()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.store = store
|
window.store = store
|
||||||
|
|
|
@ -3,6 +3,16 @@ import * as lsm from "../lsm.js"
|
||||||
|
|
||||||
let lastBatch = null
|
let lastBatch = null
|
||||||
|
|
||||||
|
function resolveMxc(url, size, method) {
|
||||||
|
const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
|
||||||
|
if (size && method) {
|
||||||
|
return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
|
||||||
|
} else {
|
||||||
|
return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function sync() {
|
function sync() {
|
||||||
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
||||||
url.searchParams.append("access_token", lsm.get("access_token"))
|
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||||
|
@ -35,6 +45,8 @@ function sync() {
|
||||||
|
|
||||||
function manageSync(root) {
|
function manageSync(root) {
|
||||||
try {
|
try {
|
||||||
|
let newEvents = false
|
||||||
|
|
||||||
// set up directs
|
// set up directs
|
||||||
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
||||||
if (directs) {
|
if (directs) {
|
||||||
|
@ -49,8 +61,45 @@ function manageSync(root) {
|
||||||
store.rooms.askAdd(id, room)
|
store.rooms.askAdd(id, room)
|
||||||
}
|
}
|
||||||
const timeline = store.rooms.get(id).value().timeline
|
const timeline = store.rooms.get(id).value().timeline
|
||||||
|
if (room.timeline.events.length) newEvents = true
|
||||||
timeline.updateEvents(room.timeline.events)
|
timeline.updateEvents(room.timeline.events)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// set up groups
|
||||||
|
Promise.all(
|
||||||
|
Object.keys(root.groups.join).map(id => {
|
||||||
|
if (!store.groups.has(id)) {
|
||||||
|
return Promise.all(["profile", "rooms"].map(path => {
|
||||||
|
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/groups/${id}/${path}`)
|
||||||
|
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||||
|
return fetch(url.toString()).then(res => res.json())
|
||||||
|
})).then(([profile, rooms]) => {
|
||||||
|
rooms = rooms.chunk
|
||||||
|
let order = 999
|
||||||
|
let orderEvent = root.account_data.events.find(e => e.type === "im.vector.web.tag_ordering")
|
||||||
|
if (orderEvent) {
|
||||||
|
if (orderEvent.content.tags.includes(id)) {
|
||||||
|
order = orderEvent.content.tags.indexOf(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
name: profile.name,
|
||||||
|
icon: resolveMxc(profile.avatar_url, 96, "crop"),
|
||||||
|
order
|
||||||
|
}
|
||||||
|
store.groups.askAdd(id, data)
|
||||||
|
rooms.forEach(groupRoom => {
|
||||||
|
if (store.rooms.has(groupRoom.room_id)) {
|
||||||
|
store.rooms.get(groupRoom.room_id).value().setGroup(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).then(() => {
|
||||||
|
store.rooms.sort()
|
||||||
|
})
|
||||||
|
if (newEvents) store.newEvents.broadcast("changeSelf")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(root)
|
console.error(root)
|
||||||
throw e
|
throw e
|
||||||
|
@ -61,4 +110,21 @@ function syncLoop() {
|
||||||
return sync().then(manageSync).then(syncLoop)
|
return sync().then(manageSync).then(syncLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
;[
|
||||||
|
{
|
||||||
|
id: "directs",
|
||||||
|
name: "Directs",
|
||||||
|
icon: "/static/directs.svg",
|
||||||
|
order: -2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "channels",
|
||||||
|
name: "Channels",
|
||||||
|
icon: "/static/channels.svg",
|
||||||
|
order: -1
|
||||||
|
}
|
||||||
|
].forEach(data => store.groups.askAdd(data.id, data))
|
||||||
|
|
||||||
|
store.activeGroup.set(store.groups.get("directs").value())
|
||||||
|
|
||||||
syncLoop()
|
syncLoop()
|
||||||
|
|
5
spec.js
5
spec.js
|
@ -69,6 +69,11 @@ module.exports = [
|
||||||
source: "/js/Timeline.js",
|
source: "/js/Timeline.js",
|
||||||
target: "/static/Timeline.js"
|
target: "/static/Timeline.js"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "js",
|
||||||
|
source: "/js/Anchor.js",
|
||||||
|
target: "/static/Anchor.js"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "js",
|
type: "js",
|
||||||
source: "/js/chat.js",
|
source: "/js/chat.js",
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Anchor extends ElemJS {
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll() {
|
scroll() {
|
||||||
console.log("anchor scrolled")
|
// console.log("anchor scrolled")
|
||||||
this.element.scrollIntoView({block: "start"})
|
this.element.scrollIntoView({block: "start"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class EventGroup extends ElemJS {
|
||||||
this.messages = ejs("div").class("c-message-group__messages").child(
|
this.messages = ejs("div").class("c-message-group__messages").child(
|
||||||
ejs("div").class("c-message-group__intro").child(
|
ejs("div").class("c-message-group__intro").child(
|
||||||
ejs("div").class("c-message-group__name").text(this.data.sender),
|
ejs("div").class("c-message-group__name").text(this.data.sender),
|
||||||
ejs("div").class("c-message-group__date").text("at 4:20 pm")
|
ejs("div").class("c-message-group__date").text(this.data.origin_server_ts)
|
||||||
),
|
),
|
||||||
...this.list
|
...this.list
|
||||||
)
|
)
|
||||||
|
@ -123,11 +123,13 @@ class Timeline extends Subscribable {
|
||||||
this.list = []
|
this.list = []
|
||||||
this.map = new Map()
|
this.map = new Map()
|
||||||
this.reactiveTimeline = new ReactiveTimeline([])
|
this.reactiveTimeline = new ReactiveTimeline([])
|
||||||
|
this.latest = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEvents(events) {
|
updateEvents(events) {
|
||||||
this.broadcast("beforeChange")
|
this.broadcast("beforeChange")
|
||||||
for (const eventData of events) {
|
for (const eventData of events) {
|
||||||
|
this.latest = Math.max(this.latest, eventData.origin_server_ts)
|
||||||
if (this.map.has(eventData.event_id)) {
|
if (this.map.has(eventData.event_id)) {
|
||||||
this.map.get(eventData.event_id).update(eventData)
|
this.map.get(eventData.event_id).update(eventData)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
import {q} from "./basic.js"
|
import {q} from "./basic.js"
|
||||||
import {store} from "./store/store.js"
|
import {store} from "./store/store.js"
|
||||||
import * as lsm from "./lsm.js"
|
import * as lsm from "./lsm.js"
|
||||||
|
import {chat} from "./chat.js"
|
||||||
|
|
||||||
let sentIndex = 0
|
let sentIndex = 0
|
||||||
|
|
||||||
const chat = q("#c-chat-textarea")
|
const input = q("#c-chat-textarea")
|
||||||
|
|
||||||
chat.addEventListener("keydown", event => {
|
input.addEventListener("keydown", event => {
|
||||||
if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) {
|
if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const body = chat.value
|
const body = input.value
|
||||||
send(chat.value)
|
send(input.value)
|
||||||
chat.value = ""
|
input.value = ""
|
||||||
fixHeight()
|
fixHeight()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
chat.addEventListener("input", () => {
|
input.addEventListener("input", () => {
|
||||||
fixHeight()
|
fixHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
function fixHeight() {
|
function fixHeight() {
|
||||||
chat.style.height = "0px"
|
input.style.height = "0px"
|
||||||
console.log(chat.clientHeight, chat.scrollHeight)
|
console.log(input.clientHeight, input.scrollHeight)
|
||||||
chat.style.height = (chat.scrollHeight + 1) + "px"
|
input.style.height = (input.scrollHeight + 1) + "px"
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTxnId() {
|
function getTxnId() {
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Group extends ElemJS {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.data = data
|
this.data = data
|
||||||
|
this.order = this.data.order
|
||||||
|
|
||||||
this.class("c-group")
|
this.class("c-group")
|
||||||
this.child(
|
this.child(
|
||||||
|
@ -68,6 +69,7 @@ class Room extends ElemJS {
|
||||||
this.id = id
|
this.id = id
|
||||||
this.data = data
|
this.data = data
|
||||||
this.timeline = new Timeline()
|
this.timeline = new Timeline()
|
||||||
|
this.group = null
|
||||||
|
|
||||||
this.class("c-room")
|
this.class("c-room")
|
||||||
|
|
||||||
|
@ -77,6 +79,25 @@ class Room extends ElemJS {
|
||||||
this.render()
|
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() {
|
getName() {
|
||||||
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
||||||
if (name) {
|
if (name) {
|
||||||
|
@ -102,8 +123,16 @@ class Room extends ElemJS {
|
||||||
return store.directs.has(this.id)
|
return store.directs.has(this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setGroup(id) {
|
||||||
|
this.group = id
|
||||||
|
}
|
||||||
|
|
||||||
getGroup() {
|
getGroup() {
|
||||||
return this.isDirect() ? store.groups.get("directs").value() : store.groups.get("channels").value()
|
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() {
|
onClick() {
|
||||||
|
@ -138,10 +167,16 @@ class Rooms extends ElemJS {
|
||||||
// store.rooms.subscribe("changeItem", this.render.bind(this))
|
// store.rooms.subscribe("changeItem", this.render.bind(this))
|
||||||
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))
|
||||||
|
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
store.rooms.sort()
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
askAdd(event, {key, data}) {
|
askAdd(event, {key, data}) {
|
||||||
const room = new Room(key, data)
|
const room = new Room(key, data)
|
||||||
store.rooms.addEnd(key, room)
|
store.rooms.addEnd(key, room)
|
||||||
|
@ -181,74 +216,20 @@ class Groups extends ElemJS {
|
||||||
super(q("#c-groups-list"))
|
super(q("#c-groups-list"))
|
||||||
|
|
||||||
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
store.groups.subscribe("addItem", this.addItem.bind(this))
|
store.groups.subscribe("changeItem", this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
askAdd(event, {key, data}) {
|
askAdd(event, {key, data}) {
|
||||||
const group = new Group(key, data)
|
const group = new Group(key, data)
|
||||||
store.groups.addEnd(key, group)
|
store.groups.addEnd(key, group)
|
||||||
|
store.groups.sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
addItem(event, key) {
|
render() {
|
||||||
this.child(store.groups.get(key).value())
|
this.clearChildren()
|
||||||
|
store.groups.forEach((key, item) => {
|
||||||
|
this.child(item.value())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const groups = new Groups()
|
const groups = new Groups()
|
||||||
|
|
||||||
|
|
||||||
;[
|
|
||||||
{
|
|
||||||
id: "directs",
|
|
||||||
name: "Directs",
|
|
||||||
icon: "/static/directs.svg"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "channels",
|
|
||||||
name: "Channels",
|
|
||||||
icon: "/static/channels.svg"
|
|
||||||
}/*,
|
|
||||||
{
|
|
||||||
id: "123",
|
|
||||||
name: "Fediverse Drama Museum"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "456",
|
|
||||||
name: "Epicord"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "789",
|
|
||||||
name: "Invidious"
|
|
||||||
}*/
|
|
||||||
].forEach(data => store.groups.askAdd(data.id, data))
|
|
||||||
|
|
||||||
/*
|
|
||||||
;[
|
|
||||||
{id: "001", name: "riley", group: store.groups.get("directs").value()},
|
|
||||||
{id: "002", name: "BadAtNames", group: store.groups.get("directs").value()},
|
|
||||||
{id: "003", name: "lynxano", group: store.groups.get("directs").value()},
|
|
||||||
{id: "004", name: "quarky", group: store.groups.get("directs").value()},
|
|
||||||
{id: "005", name: "lepton", group: store.groups.get("directs").value()},
|
|
||||||
{id: "006", name: "ash", group: store.groups.get("directs").value()},
|
|
||||||
{id: "007", name: "mewmew", group: store.groups.get("directs").value()},
|
|
||||||
{id: "008", name: "Toniob", group: store.groups.get("directs").value()},
|
|
||||||
{id: "009", name: "cockandball", group: store.groups.get("directs").value()},
|
|
||||||
{id: "010", name: "Carbon brainstorming", group: store.groups.get("channels").value()},
|
|
||||||
{id: "011", name: "Bibliogram", group: store.groups.get("channels").value()},
|
|
||||||
{id: "012", name: "Monsters Inc Debate Hall", group: store.groups.get("channels").value()},
|
|
||||||
{id: "013", name: "DRB clan", group: store.groups.get("channels").value()},
|
|
||||||
{id: "014", name: "mettaton simp zone", group: store.groups.get("channels").value()},
|
|
||||||
{id: "015", name: "witches", group: store.groups.get("123").value()},
|
|
||||||
{id: "016", name: "snouts", group: store.groups.get("123").value()},
|
|
||||||
{id: "017", name: "monads", group: store.groups.get("123").value()},
|
|
||||||
{id: "018", name: "radical", group: store.groups.get("123").value()},
|
|
||||||
{id: "019", name: "blobcat", group: store.groups.get("123").value()},
|
|
||||||
{id: "020", name: "main", group: store.groups.get("456").value()},
|
|
||||||
{id: "021", name: "gaming", group: store.groups.get("456").value()},
|
|
||||||
{id: "022", name: "inhalers", group: store.groups.get("456").value()},
|
|
||||||
{id: "023", name: "minecraft", group: store.groups.get("456").value()},
|
|
||||||
{id: "024", name: "osu", group: store.groups.get("456").value()},
|
|
||||||
{id: "025", name: "covid", group: store.groups.get("456").value()}
|
|
||||||
].forEach(data => store.rooms.askAdd(data.id, data))
|
|
||||||
*/
|
|
||||||
|
|
||||||
store.activeGroup.set(store.groups.get("directs").value())
|
|
||||||
|
|
|
@ -53,6 +53,15 @@ class SubscribeMapList extends Subscribable {
|
||||||
this._add(key, value, false)
|
this._add(key, value, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
this.list.sort((a, b) => {
|
||||||
|
const orderA = this.map.get(a).value().order
|
||||||
|
const orderB = this.map.get(b).value().order
|
||||||
|
return orderA - orderB
|
||||||
|
})
|
||||||
|
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)) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
import {SubscribeMapList} from "./SubscribeMapList.js"
|
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||||
import {SubscribeSet} from "./SubscribeSet.js"
|
import {SubscribeSet} from "./SubscribeSet.js"
|
||||||
import {SubscribeValue} from "./SubscribeValue.js"
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
@ -7,7 +8,8 @@ const store = {
|
||||||
rooms: new SubscribeMapList(SubscribeValue),
|
rooms: new SubscribeMapList(SubscribeValue),
|
||||||
directs: new SubscribeSet(),
|
directs: new SubscribeSet(),
|
||||||
activeGroup: new SubscribeValue(),
|
activeGroup: new SubscribeValue(),
|
||||||
activeRoom: new SubscribeValue()
|
activeRoom: new SubscribeValue(),
|
||||||
|
newEvents: new Subscribable()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.store = store
|
window.store = store
|
||||||
|
|
|
@ -3,6 +3,16 @@ import * as lsm from "../lsm.js"
|
||||||
|
|
||||||
let lastBatch = null
|
let lastBatch = null
|
||||||
|
|
||||||
|
function resolveMxc(url, size, method) {
|
||||||
|
const [server, id] = url.match(/^mxc:\/\/([^/]+)\/(.*)/).slice(1)
|
||||||
|
if (size && method) {
|
||||||
|
return `${lsm.get("domain")}/_matrix/media/r0/thumbnail/${server}/${id}?width=${size}&height=${size}&method=${method}`
|
||||||
|
} else {
|
||||||
|
return `${lsm.get("domain")}/_matrix/media/r0/download/${server}/${id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function sync() {
|
function sync() {
|
||||||
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
||||||
url.searchParams.append("access_token", lsm.get("access_token"))
|
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||||
|
@ -35,6 +45,8 @@ function sync() {
|
||||||
|
|
||||||
function manageSync(root) {
|
function manageSync(root) {
|
||||||
try {
|
try {
|
||||||
|
let newEvents = false
|
||||||
|
|
||||||
// set up directs
|
// set up directs
|
||||||
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
||||||
if (directs) {
|
if (directs) {
|
||||||
|
@ -49,8 +61,45 @@ function manageSync(root) {
|
||||||
store.rooms.askAdd(id, room)
|
store.rooms.askAdd(id, room)
|
||||||
}
|
}
|
||||||
const timeline = store.rooms.get(id).value().timeline
|
const timeline = store.rooms.get(id).value().timeline
|
||||||
|
if (room.timeline.events.length) newEvents = true
|
||||||
timeline.updateEvents(room.timeline.events)
|
timeline.updateEvents(room.timeline.events)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// set up groups
|
||||||
|
Promise.all(
|
||||||
|
Object.keys(root.groups.join).map(id => {
|
||||||
|
if (!store.groups.has(id)) {
|
||||||
|
return Promise.all(["profile", "rooms"].map(path => {
|
||||||
|
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/groups/${id}/${path}`)
|
||||||
|
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||||
|
return fetch(url.toString()).then(res => res.json())
|
||||||
|
})).then(([profile, rooms]) => {
|
||||||
|
rooms = rooms.chunk
|
||||||
|
let order = 999
|
||||||
|
let orderEvent = root.account_data.events.find(e => e.type === "im.vector.web.tag_ordering")
|
||||||
|
if (orderEvent) {
|
||||||
|
if (orderEvent.content.tags.includes(id)) {
|
||||||
|
order = orderEvent.content.tags.indexOf(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
name: profile.name,
|
||||||
|
icon: resolveMxc(profile.avatar_url, 96, "crop"),
|
||||||
|
order
|
||||||
|
}
|
||||||
|
store.groups.askAdd(id, data)
|
||||||
|
rooms.forEach(groupRoom => {
|
||||||
|
if (store.rooms.has(groupRoom.room_id)) {
|
||||||
|
store.rooms.get(groupRoom.room_id).value().setGroup(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
).then(() => {
|
||||||
|
store.rooms.sort()
|
||||||
|
})
|
||||||
|
if (newEvents) store.newEvents.broadcast("changeSelf")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(root)
|
console.error(root)
|
||||||
throw e
|
throw e
|
||||||
|
@ -61,4 +110,21 @@ function syncLoop() {
|
||||||
return sync().then(manageSync).then(syncLoop)
|
return sync().then(manageSync).then(syncLoop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
;[
|
||||||
|
{
|
||||||
|
id: "directs",
|
||||||
|
name: "Directs",
|
||||||
|
icon: "/static/directs.svg",
|
||||||
|
order: -2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "channels",
|
||||||
|
name: "Channels",
|
||||||
|
icon: "/static/channels.svg",
|
||||||
|
order: -1
|
||||||
|
}
|
||||||
|
].forEach(data => store.groups.askAdd(data.id, data))
|
||||||
|
|
||||||
|
store.activeGroup.set(store.groups.get("directs").value())
|
||||||
|
|
||||||
syncLoop()
|
syncLoop()
|
||||||
|
|
|
@ -10,7 +10,7 @@ $out-width: $base-width + rooms.$list-width
|
||||||
position: relative
|
position: relative
|
||||||
width: $base-width
|
width: $base-width
|
||||||
flex-shrink: 0
|
flex-shrink: 0
|
||||||
font-size: 24px
|
font-size: 22px
|
||||||
font-weight: 500
|
font-weight: 500
|
||||||
|
|
||||||
&__display
|
&__display
|
||||||
|
|
Loading…
Reference in a new issue