Sync with matrix and populate rooms list
This commit is contained in:
parent
dd0b14720e
commit
ac6320c12c
18 changed files with 465 additions and 47 deletions
25
build.js
25
build.js
|
@ -80,29 +80,36 @@ function runHint(filename, source) {
|
|||
hint(source, {
|
||||
esversion: 9,
|
||||
undef: true,
|
||||
unused: true,
|
||||
// unused: true,
|
||||
loopfunc: true,
|
||||
globals: ["require", "console", "URLSearchParams", "L"],
|
||||
strict: "global",
|
||||
browser: true
|
||||
globals: ["console", "URLSearchParams"],
|
||||
browser: true,
|
||||
asi: true,
|
||||
})
|
||||
const result = hint.data()
|
||||
if (result.errors && result.errors.length) {
|
||||
let problems = 0
|
||||
if (result.errors) {
|
||||
for (const error of result.errors) {
|
||||
if (error.evidence) {
|
||||
const text = error.evidence.replace(/\t/g, " ")
|
||||
if ([
|
||||
"W014"
|
||||
].includes(error.code)) continue
|
||||
let type = error.code.startsWith("W") ? chalk.yellow("warning") : chalk.red("error")
|
||||
console.log(`hint: ${type} in ${filename}`)
|
||||
console.log(` ${error.line}:${error.character}: ${error.reason} (${error.code})`)
|
||||
console.log(chalk.gray(
|
||||
" "
|
||||
+ text.slice(0, error.character)
|
||||
+ chalk.inverse(text.substr(error.character, 1))
|
||||
+ text.slice(error.character+1)
|
||||
+ text.slice(0, error.character)
|
||||
+ chalk.inverse(text.substr(error.character, 1))
|
||||
+ text.slice(error.character+1)
|
||||
))
|
||||
problems++
|
||||
}
|
||||
}
|
||||
console.log(`hint: ${chalk.cyan(result.errors.length+" problems")} in ${filename}`)
|
||||
}
|
||||
if (problems) {
|
||||
console.log(`hint: ${chalk.cyan(problems+" problems")} in ${filename}`)
|
||||
} else {
|
||||
console.log(`hint: ${chalk.green("ok")} for ${filename}`)
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="static/main.css?static=79b6afceb8">
|
||||
<link rel="stylesheet" type="text/css" href="static/main.css?static=b0aba41b0b">
|
||||
<script type="module" src="static/groups.js?static=2cc7f0daf8"></script>
|
||||
<script type="module" src="static/chat-input.js?static=a90499fdac"></script>
|
||||
<script type="module" src="static/room-picker.js?static=596e719ff8"></script>
|
||||
<script type="module" src="static/room-picker.js?static=d92adfaaca"></script>
|
||||
<script type="module" src="static/sync/sync.js?static=232a64285c"></script>
|
||||
<title>Carbon</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -32,7 +33,7 @@
|
|||
<div class="c-message-group__name">Cadence</div>
|
||||
<div class="c-message-group__date">at 4:20 pm</div>
|
||||
</div>
|
||||
<div class="c-message">the second button is for rooms (gonna call them "channels to make discord users happy) that are not in a group (which will be most rooms - few people set up groups because they're so annoying, and many communities of people only need a single chatroom)</div>
|
||||
<div class="c-message">the second button is for rooms (gonna call them "channels" to make discord users happy) that are not in a group (which will be most rooms - few people set up groups because they're so annoying, and many communities of people only need a single chatroom)</div>
|
||||
<div class="c-message">for now, please assume that current groups ("groups v1") will not be recognised by this client at all</div>
|
||||
<div class="c-message">so yeah, press the second button, you see all the ungrouped channels</div>
|
||||
</div>
|
||||
|
|
11
build/static/lsm.js
Normal file
11
build/static/lsm.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
function get(name) {
|
||||
return localStorage.getItem(name)
|
||||
}
|
||||
|
||||
function set(name, value) {
|
||||
return localStorage.setItem(name, value)
|
||||
}
|
||||
|
||||
window.lsm = {get, set}
|
||||
|
||||
export {get, set}
|
|
@ -29,7 +29,7 @@ body {
|
|||
background-color: #2f3135;
|
||||
padding: 8px;
|
||||
width: 240px;
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
|
@ -54,11 +54,13 @@ body {
|
|||
.c-room__icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: #bbb;
|
||||
margin-right: 8px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.c-room__icon--no-icon {
|
||||
background-color: #bbb;
|
||||
}
|
||||
.c-room__name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import {q, ElemJS, ejs} from "./basic.js"
|
||||
import {store} from "./store/store.js"
|
||||
import * as lsm from "./lsm.js"
|
||||
|
||||
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}`
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveGroupMarker extends ElemJS {
|
||||
constructor() {
|
||||
|
@ -51,19 +61,47 @@ class Group extends ElemJS {
|
|||
}
|
||||
|
||||
class Room extends ElemJS {
|
||||
constructor(key, data) {
|
||||
constructor(id, data) {
|
||||
super("div")
|
||||
|
||||
this.id = id
|
||||
this.data = data
|
||||
|
||||
this.class("c-room")
|
||||
this.child(
|
||||
ejs("div").class("c-room__icon"),
|
||||
ejs("div").class("c-room__name").text(this.data.name)
|
||||
)
|
||||
|
||||
this.on("click", this.onClick.bind(this))
|
||||
store.activeRoom.subscribe("changeSelf", this.render.bind(this))
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
||||
getName() {
|
||||
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
||||
if (name) {
|
||||
name = name.content.name
|
||||
} else {
|
||||
const users = this.data.summary["m.heroes"]
|
||||
const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u)
|
||||
name = usernames.join(", ")
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
getIcon() {
|
||||
const avatar = this.data.state.events.find(e => e.type === "m.room.avatar")
|
||||
if (avatar) {
|
||||
return resolveMxc(avatar.content.url || avatar.content.avatar_url, 32, "crop")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
isDirect() {
|
||||
return store.directs.has(this.id)
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
return this.isDirect() ? store.groups.get("directs").value() : store.groups.get("channels").value()
|
||||
}
|
||||
|
||||
onClick() {
|
||||
|
@ -71,6 +109,16 @@ class Room extends ElemJS {
|
|||
}
|
||||
|
||||
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()))
|
||||
// active
|
||||
const active = store.activeRoom.value() === this
|
||||
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
||||
}
|
||||
|
@ -84,8 +132,10 @@ class Rooms extends ElemJS {
|
|||
this.rooms = []
|
||||
|
||||
store.rooms.subscribe("askAdd", this.askAdd.bind(this))
|
||||
store.rooms.subscribe("changeItem", this.render.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))
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
@ -95,18 +145,25 @@ class Rooms extends ElemJS {
|
|||
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().data.group === store.activeGroup.value()) {
|
||||
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().data.group !== store.activeGroup.value()) {
|
||||
if (!store.activeRoom.exists() || store.activeRoom.value().getGroup() !== store.activeGroup.value()) {
|
||||
if (first) {
|
||||
store.activeRoom.set(first)
|
||||
} else {
|
||||
|
@ -136,6 +193,7 @@ class Groups extends ElemJS {
|
|||
}
|
||||
const groups = new Groups()
|
||||
|
||||
|
||||
;[
|
||||
{
|
||||
id: "directs",
|
||||
|
@ -146,7 +204,7 @@ const groups = new Groups()
|
|||
id: "channels",
|
||||
name: "Channels",
|
||||
icon: "/static/channels.svg"
|
||||
},
|
||||
}/*,
|
||||
{
|
||||
id: "123",
|
||||
name: "Fediverse Drama Museum"
|
||||
|
@ -158,9 +216,10 @@ const groups = new Groups()
|
|||
{
|
||||
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()},
|
||||
|
@ -188,5 +247,6 @@ const groups = new Groups()
|
|||
{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())
|
||||
|
|
|
@ -7,14 +7,14 @@ class SubscribeMapList extends Subscribable {
|
|||
this.inner = inner
|
||||
Object.assign(this.events, {
|
||||
addItem: [],
|
||||
removeItem: [],
|
||||
deleteItem: [],
|
||||
editItem: [],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
Object.assign(this.eventDeps, {
|
||||
addItem: ["changeItem"],
|
||||
removeItem: ["changeItem"],
|
||||
deleteItem: ["changeItem"],
|
||||
editItem: ["changeItem"],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
|
@ -59,7 +59,7 @@ class SubscribeMapList extends Subscribable {
|
|||
const exists = this.map.get(key).exists()
|
||||
s = this.map.get(key).set(value)
|
||||
if (exists) {
|
||||
this.broadcast("changeItem", key)
|
||||
this.broadcast("editItem", key)
|
||||
} else {
|
||||
this.broadcast("addItem", key)
|
||||
}
|
||||
|
|
50
build/static/store/SubscribeSet.js
Normal file
50
build/static/store/SubscribeSet.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import {Subscribable} from "./Subscribable.js"
|
||||
|
||||
class SubscribeSet extends Subscribable {
|
||||
constructor() {
|
||||
super()
|
||||
Object.assign(this.events, {
|
||||
addItem: [],
|
||||
deleteItem: [],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
Object.assign(this.eventDeps, {
|
||||
addItem: ["changeItem"],
|
||||
deleteItem: ["changeItem"],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
this.set = new Set()
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.set.has(key)
|
||||
}
|
||||
|
||||
forEach(f) {
|
||||
for (const key of this.set.keys()) {
|
||||
f(key)
|
||||
}
|
||||
}
|
||||
|
||||
askAdd(key) {
|
||||
this.broadcast("askAdd", key)
|
||||
}
|
||||
|
||||
add(key) {
|
||||
if (!this.set.has(key)) {
|
||||
this.set.add(key)
|
||||
this.broadcast("addItem", key)
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
if (this.set.has(key)) {
|
||||
this.set.delete(key)
|
||||
this.broadcast("deleteItem", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {SubscribeSet}
|
|
@ -1,9 +1,11 @@
|
|||
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||
import {SubscribeSet} from "./SubscribeSet.js"
|
||||
import {SubscribeValue} from "./SubscribeValue.js"
|
||||
|
||||
const store = {
|
||||
groups: new SubscribeMapList(SubscribeValue),
|
||||
rooms: new SubscribeMapList(SubscribeValue),
|
||||
directs: new SubscribeSet(),
|
||||
activeGroup: new SubscribeValue(),
|
||||
activeRoom: new SubscribeValue()
|
||||
}
|
||||
|
|
62
build/static/sync/sync.js
Normal file
62
build/static/sync/sync.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {store} from "../store/store.js"
|
||||
import * as lsm from "../lsm.js"
|
||||
|
||||
let lastBatch = null
|
||||
|
||||
function sync() {
|
||||
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
||||
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||
const filter = {
|
||||
room: {
|
||||
// pulling more from the timeline massively increases download size
|
||||
timeline: {
|
||||
limit: 5
|
||||
},
|
||||
// members are not currently needed
|
||||
state: {
|
||||
lazy_load_members: true
|
||||
}
|
||||
},
|
||||
presence: {
|
||||
// presence is not implemented, ignore it
|
||||
types: []
|
||||
}
|
||||
}
|
||||
url.searchParams.append("filter", JSON.stringify(filter))
|
||||
url.searchParams.append("timeout", 20000)
|
||||
if (lastBatch) {
|
||||
url.searchParams.append("since", lastBatch)
|
||||
}
|
||||
return fetch(url.toString()).then(res => res.json()).then(root => {
|
||||
lastBatch = root.next_batch
|
||||
return root
|
||||
})
|
||||
}
|
||||
|
||||
function manageSync(root) {
|
||||
try {
|
||||
// set up directs
|
||||
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
||||
if (directs) {
|
||||
Object.values(directs.content).forEach(ids => {
|
||||
ids.forEach(id => store.directs.add(id))
|
||||
})
|
||||
}
|
||||
|
||||
// set up rooms
|
||||
Object.entries(root.rooms.join).forEach(([id, room]) => {
|
||||
if (!store.rooms.has(id)) {
|
||||
store.rooms.askAdd(id, room)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(root)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
function syncLoop() {
|
||||
return sync().then(manageSync).then(syncLoop)
|
||||
}
|
||||
|
||||
syncLoop()
|
43
spec.js
43
spec.js
|
@ -10,25 +10,60 @@ module.exports = [
|
|||
target: "/static/whitney-400.woff"
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
type: "js",
|
||||
source: "/js/basic.js",
|
||||
target: "/static/basic.js"
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
type: "js",
|
||||
source: "/js/groups.js",
|
||||
target: "/static/groups.js"
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
type: "js",
|
||||
source: "/js/chat-input.js",
|
||||
target: "/static/chat-input.js"
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
type: "js",
|
||||
source: "/js/room-picker.js",
|
||||
target: "/static/room-picker.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/store/store.js",
|
||||
target: "/static/store/store.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/store/Subscribable.js",
|
||||
target: "/static/store/Subscribable.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/store/SubscribeValue.js",
|
||||
target: "/static/store/SubscribeValue.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/store/SubscribeMapList.js",
|
||||
target: "/static/store/SubscribeMapList.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/store/SubscribeSet.js",
|
||||
target: "/static/store/SubscribeSet.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/sync/sync.js",
|
||||
target: "/static/sync/sync.js"
|
||||
},
|
||||
{
|
||||
type: "js",
|
||||
source: "/js/lsm.js",
|
||||
target: "/static/lsm.js"
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
source: "/assets/fonts/whitney-500.woff",
|
||||
|
|
|
@ -37,6 +37,7 @@ html
|
|||
script(type="module" src=getStatic("/js/groups.js"))
|
||||
script(type="module" src=getStatic("/js/chat-input.js"))
|
||||
script(type="module" src=getStatic("/js/room-picker.js"))
|
||||
script(type="module" src=getStatic("/js/sync/sync.js"))
|
||||
title Carbon
|
||||
body
|
||||
main.main
|
||||
|
@ -50,7 +51,7 @@ html
|
|||
.c-chat__inner
|
||||
+message-notice("You've reached the start of the conversation.")
|
||||
+message("Cadence", [
|
||||
`the second button is for rooms (gonna call them "channels to make discord users happy) that are not in a group (which will be most rooms - few people set up groups because they're so annoying, and many communities of people only need a single chatroom)`,
|
||||
`the second button is for rooms (gonna call them "channels" to make discord users happy) that are not in a group (which will be most rooms - few people set up groups because they're so annoying, and many communities of people only need a single chatroom)`,
|
||||
`for now, please assume that current groups ("groups v1") will not be recognised by this client at all`,
|
||||
`so yeah, press the second button, you see all the ungrouped channels`
|
||||
])
|
||||
|
|
11
src/js/lsm.js
Normal file
11
src/js/lsm.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
function get(name) {
|
||||
return localStorage.getItem(name)
|
||||
}
|
||||
|
||||
function set(name, value) {
|
||||
return localStorage.setItem(name, value)
|
||||
}
|
||||
|
||||
window.lsm = {get, set}
|
||||
|
||||
export {get, set}
|
|
@ -1,5 +1,15 @@
|
|||
import {q, ElemJS, ejs} from "./basic.js"
|
||||
import {store} from "./store/store.js"
|
||||
import * as lsm from "./lsm.js"
|
||||
|
||||
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}`
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveGroupMarker extends ElemJS {
|
||||
constructor() {
|
||||
|
@ -51,19 +61,47 @@ class Group extends ElemJS {
|
|||
}
|
||||
|
||||
class Room extends ElemJS {
|
||||
constructor(key, data) {
|
||||
constructor(id, data) {
|
||||
super("div")
|
||||
|
||||
this.id = id
|
||||
this.data = data
|
||||
|
||||
this.class("c-room")
|
||||
this.child(
|
||||
ejs("div").class("c-room__icon"),
|
||||
ejs("div").class("c-room__name").text(this.data.name)
|
||||
)
|
||||
|
||||
this.on("click", this.onClick.bind(this))
|
||||
store.activeRoom.subscribe("changeSelf", this.render.bind(this))
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
||||
getName() {
|
||||
let name = this.data.state.events.find(e => e.type === "m.room.name")
|
||||
if (name) {
|
||||
name = name.content.name
|
||||
} else {
|
||||
const users = this.data.summary["m.heroes"]
|
||||
const usernames = users.map(u => (u.match(/^@([^:]+):/) || [])[1] || u)
|
||||
name = usernames.join(", ")
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
getIcon() {
|
||||
const avatar = this.data.state.events.find(e => e.type === "m.room.avatar")
|
||||
if (avatar) {
|
||||
return resolveMxc(avatar.content.url || avatar.content.avatar_url, 32, "crop")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
isDirect() {
|
||||
return store.directs.has(this.id)
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
return this.isDirect() ? store.groups.get("directs").value() : store.groups.get("channels").value()
|
||||
}
|
||||
|
||||
onClick() {
|
||||
|
@ -71,6 +109,16 @@ class Room extends ElemJS {
|
|||
}
|
||||
|
||||
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()))
|
||||
// active
|
||||
const active = store.activeRoom.value() === this
|
||||
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
||||
}
|
||||
|
@ -84,8 +132,10 @@ class Rooms extends ElemJS {
|
|||
this.rooms = []
|
||||
|
||||
store.rooms.subscribe("askAdd", this.askAdd.bind(this))
|
||||
store.rooms.subscribe("changeItem", this.render.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))
|
||||
|
||||
this.render()
|
||||
}
|
||||
|
@ -95,18 +145,25 @@ class Rooms extends ElemJS {
|
|||
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().data.group === store.activeGroup.value()) {
|
||||
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().data.group !== store.activeGroup.value()) {
|
||||
if (!store.activeRoom.exists() || store.activeRoom.value().getGroup() !== store.activeGroup.value()) {
|
||||
if (first) {
|
||||
store.activeRoom.set(first)
|
||||
} else {
|
||||
|
@ -136,6 +193,7 @@ class Groups extends ElemJS {
|
|||
}
|
||||
const groups = new Groups()
|
||||
|
||||
|
||||
;[
|
||||
{
|
||||
id: "directs",
|
||||
|
@ -146,7 +204,7 @@ const groups = new Groups()
|
|||
id: "channels",
|
||||
name: "Channels",
|
||||
icon: "/static/channels.svg"
|
||||
},
|
||||
}/*,
|
||||
{
|
||||
id: "123",
|
||||
name: "Fediverse Drama Museum"
|
||||
|
@ -158,9 +216,10 @@ const groups = new Groups()
|
|||
{
|
||||
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()},
|
||||
|
@ -188,5 +247,6 @@ const groups = new Groups()
|
|||
{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())
|
||||
|
|
|
@ -7,14 +7,14 @@ class SubscribeMapList extends Subscribable {
|
|||
this.inner = inner
|
||||
Object.assign(this.events, {
|
||||
addItem: [],
|
||||
removeItem: [],
|
||||
deleteItem: [],
|
||||
editItem: [],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
Object.assign(this.eventDeps, {
|
||||
addItem: ["changeItem"],
|
||||
removeItem: ["changeItem"],
|
||||
deleteItem: ["changeItem"],
|
||||
editItem: ["changeItem"],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
|
@ -59,7 +59,7 @@ class SubscribeMapList extends Subscribable {
|
|||
const exists = this.map.get(key).exists()
|
||||
s = this.map.get(key).set(value)
|
||||
if (exists) {
|
||||
this.broadcast("changeItem", key)
|
||||
this.broadcast("editItem", key)
|
||||
} else {
|
||||
this.broadcast("addItem", key)
|
||||
}
|
||||
|
|
50
src/js/store/SubscribeSet.js
Normal file
50
src/js/store/SubscribeSet.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
import {Subscribable} from "./Subscribable.js"
|
||||
|
||||
class SubscribeSet extends Subscribable {
|
||||
constructor() {
|
||||
super()
|
||||
Object.assign(this.events, {
|
||||
addItem: [],
|
||||
deleteItem: [],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
Object.assign(this.eventDeps, {
|
||||
addItem: ["changeItem"],
|
||||
deleteItem: ["changeItem"],
|
||||
changeItem: [],
|
||||
askAdd: []
|
||||
})
|
||||
this.set = new Set()
|
||||
}
|
||||
|
||||
has(key) {
|
||||
return this.set.has(key)
|
||||
}
|
||||
|
||||
forEach(f) {
|
||||
for (const key of this.set.keys()) {
|
||||
f(key)
|
||||
}
|
||||
}
|
||||
|
||||
askAdd(key) {
|
||||
this.broadcast("askAdd", key)
|
||||
}
|
||||
|
||||
add(key) {
|
||||
if (!this.set.has(key)) {
|
||||
this.set.add(key)
|
||||
this.broadcast("addItem", key)
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
if (this.set.has(key)) {
|
||||
this.set.delete(key)
|
||||
this.broadcast("deleteItem", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {SubscribeSet}
|
|
@ -1,9 +1,11 @@
|
|||
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||
import {SubscribeSet} from "./SubscribeSet.js"
|
||||
import {SubscribeValue} from "./SubscribeValue.js"
|
||||
|
||||
const store = {
|
||||
groups: new SubscribeMapList(SubscribeValue),
|
||||
rooms: new SubscribeMapList(SubscribeValue),
|
||||
directs: new SubscribeSet(),
|
||||
activeGroup: new SubscribeValue(),
|
||||
activeRoom: new SubscribeValue()
|
||||
}
|
||||
|
|
62
src/js/sync/sync.js
Normal file
62
src/js/sync/sync.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {store} from "../store/store.js"
|
||||
import * as lsm from "../lsm.js"
|
||||
|
||||
let lastBatch = null
|
||||
|
||||
function sync() {
|
||||
const url = new URL(`${lsm.get("domain")}/_matrix/client/r0/sync`)
|
||||
url.searchParams.append("access_token", lsm.get("access_token"))
|
||||
const filter = {
|
||||
room: {
|
||||
// pulling more from the timeline massively increases download size
|
||||
timeline: {
|
||||
limit: 5
|
||||
},
|
||||
// members are not currently needed
|
||||
state: {
|
||||
lazy_load_members: true
|
||||
}
|
||||
},
|
||||
presence: {
|
||||
// presence is not implemented, ignore it
|
||||
types: []
|
||||
}
|
||||
}
|
||||
url.searchParams.append("filter", JSON.stringify(filter))
|
||||
url.searchParams.append("timeout", 20000)
|
||||
if (lastBatch) {
|
||||
url.searchParams.append("since", lastBatch)
|
||||
}
|
||||
return fetch(url.toString()).then(res => res.json()).then(root => {
|
||||
lastBatch = root.next_batch
|
||||
return root
|
||||
})
|
||||
}
|
||||
|
||||
function manageSync(root) {
|
||||
try {
|
||||
// set up directs
|
||||
const directs = root.account_data.events.find(e => e.type === "m.direct")
|
||||
if (directs) {
|
||||
Object.values(directs.content).forEach(ids => {
|
||||
ids.forEach(id => store.directs.add(id))
|
||||
})
|
||||
}
|
||||
|
||||
// set up rooms
|
||||
Object.entries(root.rooms.join).forEach(([id, room]) => {
|
||||
if (!store.rooms.has(id)) {
|
||||
store.rooms.askAdd(id, room)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(root)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
function syncLoop() {
|
||||
return sync().then(manageSync).then(syncLoop)
|
||||
}
|
||||
|
||||
syncLoop()
|
|
@ -8,7 +8,7 @@ $icon-padding: 8px
|
|||
background-color: c.$darker
|
||||
padding: $icon-padding
|
||||
width: $list-width
|
||||
font-size: 20px
|
||||
font-size: 18px
|
||||
font-weight: 500
|
||||
overflow-y: auto
|
||||
scrollbar-width: thin
|
||||
|
@ -32,11 +32,13 @@ $icon-padding: 8px
|
|||
&__icon
|
||||
width: $icon-size
|
||||
height: $icon-size
|
||||
background-color: #bbb
|
||||
margin-right: $icon-padding
|
||||
border-radius: 50%
|
||||
flex-shrink: 0
|
||||
|
||||
&--no-icon
|
||||
background-color: #bbb
|
||||
|
||||
&__name
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
|
|
Loading…
Reference in a new issue