diff --git a/build/index.html b/build/index.html
deleted file mode 100644
index 29b927a..0000000
--- a/build/index.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- Carbon
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/static/Anchor.js b/build/static/Anchor.js
deleted file mode 100644
index d07e239..0000000
--- a/build/static/Anchor.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import {ElemJS} from "../static/basic.js?static=4212436742"
-
-class Anchor extends ElemJS {
- constructor() {
- super("div")
- this.class("c-anchor")
- }
-
- scroll() {
- // console.log("anchor scrolled")
- this.element.scrollIntoView({block: "start"})
- }
-}
-
-export {Anchor}
diff --git a/build/static/Timeline.js b/build/static/Timeline.js
deleted file mode 100644
index 9383763..0000000
--- a/build/static/Timeline.js
+++ /dev/null
@@ -1,282 +0,0 @@
-import {ElemJS, ejs} from "../static/basic.js?static=4212436742"
-import {Subscribable} from "../static/store/Subscribable.js?static=19b7e44aa6"
-import {store} from "../static/store/store.js?static=ce3066287b"
-import {Anchor} from "../static/Anchor.js?static=85efd1a836"
-import * as lsm from "../static/lsm.js?static=aed2c7ca35"
-import {resolveMxc} from "../static/functions.js?static=e3784c70ce"
-
-const dateFormatter = Intl.DateTimeFormat("default", {hour: "numeric", minute: "numeric", day: "numeric", month: "short", year: "numeric"})
-
-let sentIndex = 0
-
-function getTxnId() {
- return Date.now() + (sentIndex++)
-}
-
-function eventSearch(list, event, min = 0, max = -1) {
- if (list.length === 0) return {success: false, i: 0}
-
- if (max === -1) max = list.length - 1
- let mid = Math.floor((max + min) / 2)
- // success condition
- if (list[mid] && list[mid].data.event_id === event.data.event_id) return {success: true, i: mid}
- // failed condition
- if (min >= max) {
- while (mid !== -1 && (!list[mid] || list[mid].data.origin_server_ts > event.data.origin_server_ts)) mid--
- return {
- success: false,
- i: mid + 1
- }
- }
- // recurse (below)
- if (list[mid].data.origin_server_ts > event.data.origin_server_ts) return eventSearch(list, event, min, mid-1)
- // recurse (above)
- else return eventSearch(list, event, mid+1, max)
-}
-
-class Event extends ElemJS {
- constructor(data) {
- super("div")
- this.class("c-message")
- this.data = null
- this.group = null
- this.update(data)
- }
-
- setGroup(group) {
- this.group = group
- }
-
- update(data) {
- this.data = data
- this.render()
- }
-
- removeEvent() {
- if (this.group) this.group.removeEvent(this)
- else this.remove()
- }
-
- render() {
- this.element.classList[this.data.pending ? "add" : "remove"]("c-message--pending")
- this.text(this.data.content.body)
- }
-}
-
-class Sender {
- constructor(roomID, mxid) {
- this.sender = store.rooms.get(roomID).value().members.get(mxid)
- this.sender.subscribe("changeSelf", this.update.bind(this))
- this.name = new ElemJS("div").class("c-message-group__name")
- this.avatar = new ElemJS("div").class("c-message-group__avatar")
- this.update()
- }
-
- update() {
- if (this.sender.exists()) {
- // name
- this.name.text(this.sender.value().content.displayname)
-
- // avatar
- this.avatar.clearChildren()
- if (this.sender.value().content.avatar_url) {
- this.avatar.child(
- ejs("img").class("c-message-group__icon").attribute("src", resolveMxc(this.sender.value().content.avatar_url, 96, "crop"))
- )
- } else {
- this.avatar.child(
- ejs("div").class("c-message-group__icon", "c-message-group__icon--no-icon")
- )
- }
- }
- }
-}
-
-class EventGroup extends ElemJS {
- constructor(reactive, list) {
- super("div")
- this.class("c-message-group")
- this.reactive = reactive
- this.list = list
- this.data = {
- sender: list[0].data.sender,
- origin_server_ts: list[0].data.origin_server_ts
- }
- this.sender = new Sender(this.reactive.id, this.data.sender)
- this.child(
- this.sender.avatar,
- this.messages = ejs("div").class("c-message-group__messages").child(
- ejs("div").class("c-message-group__intro").child(
- this.sender.name,
- ejs("div").class("c-message-group__date").text(dateFormatter.format(this.data.origin_server_ts))
- ),
- ...this.list
- )
- )
- }
-
- addEvent(event) {
- const index = eventSearch(this.list, event).i
- event.setGroup(this)
- this.list.splice(index, 0, event)
- this.messages.childAt(index + 1, event)
- }
-
- removeEvent(event) {
- const search = eventSearch(this.list, event)
- if (!search.success) throw new Error(`Event ${event.data.event_id} not found in this group`)
- const index = search.i
- // actually remove the event
- this.list.splice(index, 1)
- event.remove() // should get everything else
- if (this.list.length === 0) this.reactive.removeGroup(this)
- }
-}
-
-class ReactiveTimeline extends ElemJS {
- constructor(id, list) {
- super("div")
- this.class("c-event-groups")
- this.id = id
- this.list = list
- this.render()
- }
-
- addEvent(event) {
- const search = eventSearch(this.list, event)
- // console.log(search, this.list.map(l => l.data.sender), event.data)
- if (!search.success && search.i >= 1) this.tryAddGroups(event, [search.i-1, search.i])
- else this.tryAddGroups(event, [search.i])
- }
-
- tryAddGroups(event, indices) {
- const success = indices.some(i => {
- if (!this.list[i]) {
- // if (printed++ < 100) console.log("tryadd success, created group")
- const group = new EventGroup(this, [event])
- this.list.splice(i, 0, group)
- this.childAt(i, group)
- return true
- } else if (this.list[i] && this.list[i].data.sender === event.data.sender) {
- // if (printed++ < 100) console.log("tryadd success, using existing group")
- this.list[i].addEvent(event)
- return true
- }
- })
- if (!success) console.log("tryadd failure", indices, this.list.map(l => l.data.sender), event.data)
- }
-
- removeGroup(group) {
- const index = this.list.indexOf(group)
- this.list.splice(index, 1)
- group.remove() // should get everything else
- }
-
- render() {
- this.clearChildren()
- this.list.forEach(group => this.child(group))
- this.anchor = new Anchor()
- this.child(this.anchor)
- }
-}
-
-class Timeline extends Subscribable {
- constructor(id) {
- super()
- Object.assign(this.events, {
- beforeChange: [],
- afterChange: []
- })
- Object.assign(this.eventDeps, {
- beforeChange: [],
- afterChange: []
- })
- this.id = id
- this.list = []
- this.map = new Map()
- this.reactiveTimeline = new ReactiveTimeline(id, [])
- this.latest = 0
- this.pending = new Set()
- }
-
- updateEvents(events) {
- this.broadcast("beforeChange")
- for (const eventData of events) {
- this.latest = Math.max(this.latest, eventData.origin_server_ts)
- let id = eventData.event_id
- if (eventData.sender === lsm.get("mx_user_id") && eventData.content && this.pending.has(eventData.content["chat.carbon.message.pending_id"])) {
- id = eventData.content["chat.carbon.message.pending_id"]
- }
- if (this.map.has(id)) {
- this.map.get(id).update(eventData)
- } else {
- const event = new Event(eventData)
- this.map.set(id, event)
- this.reactiveTimeline.addEvent(event)
- }
- }
- this.broadcast("afterChange")
- }
-
- removeEvent(id) {
- if (!this.map.has(id)) throw new Error(`Tried to delete event ID ${id} which does not exist`)
- this.map.get(id).removeEvent()
- this.map.delete(id)
- }
-
- getTimeline() {
- return this.reactiveTimeline
- }
-
- send(body) {
- const tx = getTxnId()
- const id = `pending$${tx}`
- this.pending.add(id)
- const content = {
- msgtype: "m.text",
- body,
- "chat.carbon.message.pending_id": id
- }
- const fakeEvent = {
- origin_server_ts: Date.now(),
- event_id: id,
- sender: lsm.get("mx_user_id"),
- content,
- pending: true
- }
- this.updateEvents([fakeEvent])
- return fetch(`${lsm.get("domain")}/_matrix/client/r0/rooms/${this.id}/send/m.room.message/${tx}?access_token=${lsm.get("access_token")}`, {
- method: "PUT",
- body: JSON.stringify(content),
- headers: {
- "Content-Type": "application/json"
- }
- })/*.then(() => {
- const subscription = () => {
- this.removeEvent(id)
- this.unsubscribe("afterChange", subscription)
- }
- this.subscribe("afterChange", subscription)
- })*/
- }
-/*
- getGroupedEvents() {
- let currentSender = Symbol("N/A")
- let groups = []
- let currentGroup = []
- for (const event of this.list) {
- if (event.sender === currentSender) {
- currentGroup.push(event)
- } else {
- if (currentGroup.length) groups.push(currentGroup)
- currentGroup = [event]
- currentSender = event.sender
- }
- }
- if (currentGroup.length) groups.push(currentGroup)
- return groups
- }
- */
-}
-
-export {Timeline}
diff --git a/build/static/basic.js b/build/static/basic.js
deleted file mode 100644
index 1f3e695..0000000
--- a/build/static/basic.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * Shortcut for querySelector.
- * @template {HTMLElement} T
- * @returns {T}
- */
-const q = s => document.querySelector(s);
-/**
- * Shortcut for querySelectorAll.
- * @template {HTMLElement} T
- * @returns {T[]}
- */
-const qa = s => document.querySelectorAll(s);
-
-/**
- * An easier, chainable, object-oriented way to create and update elements
- * and children according to related data. Subclass ElemJS to create useful,
- * advanced data managers, or just use it inline to quickly make a custom element.
- * Created by Cadence Ember in 2018.
- */
-class ElemJS {
- constructor(type) {
- if (type instanceof HTMLElement) {
- // If passed an existing element, bind to it
- this.bind(type);
- } else {
- // Otherwise, create a new detached element to bind to
- this.bind(document.createElement(type));
- }
- this.children = [];
- }
-
- /** Bind this construct to an existing element on the page. */
- bind(element) {
- this.element = element;
- this.element.js = this;
- return this;
- }
-
- /** Add a class. */
- class() {
- for (let name of arguments) if (name) this.element.classList.add(name);
- return this;
- }
-
- /** Remove a class. */
- removeClass() {
- for (let name of arguments) if (name) this.element.classList.remove(name);
- return this;
- }
-
- /** Set a JS property on the element. */
- direct(name, value) {
- if (name) this.element[name] = value;
- return this;
- }
-
- /** Set an attribute on the element. */
- attribute(name, value) {
- if (name) this.element.setAttribute(name, value != undefined ? value : "");
- return this;
- }
-
- /** Set a style on the element. */
- style(name, value) {
- if (name) this.element.style[name] = value;
- return this;
- }
-
- /** Set the element's ID. */
- id(name) {
- if (name) this.element.id = name;
- return this;
- }
-
- /** Attach a callback function to an event on the element. */
- on(name, callback) {
- this.element.addEventListener(name, callback);
- return this;
- }
-
- /** Set the element's text. */
- text(name) {
- this.element.innerText = name;
- return this;
- }
-
- /** Create a text node and add it to the element. */
- addText(name) {
- const node = document.createTextNode(name);
- this.element.appendChild(node);
- return this;
- }
-
- /** Set the element's HTML content. */
- html(name) {
- this.element.innerHTML = name;
- return this;
- }
-
- /**
- * Add children to the element.
- * Children can either be an instance of ElemJS, in
- * which case the element will be appended as a child,
- * or a string, in which case the string will be added as a text node.
- * Each child should be a parameter to this method.
- */
- child(...children) {
- for (const toAdd of children) {
- if (typeof toAdd === "object" && toAdd !== null) {
- // Should be an instance of ElemJS, so append as child
- toAdd.parent = this;
- this.element.appendChild(toAdd.element);
- this.children.push(toAdd);
- } else if (typeof toAdd === "string") {
- // Is a string, so add as text node
- this.addText(toAdd);
- }
- }
- return this;
- }
-
- childAt(index, toAdd) {
- if (typeof toAdd === "object" && toAdd !== null) {
- toAdd.parent = this;
- this.children.splice(index, 0, toAdd);
- if (index >= this.element.childNodes.length) {
- this.element.appendChild(toAdd.element)
- } else {
- this.element.childNodes[index].insertAdjacentElement("beforebegin", toAdd.element)
- }
- }
- }
-
- /**
- * Remove all children from the element.
- */
- clearChildren() {
- this.children.length = 0;
- while (this.element.lastChild) this.element.removeChild(this.element.lastChild);
- }
-
- /**
- * Remove this element.
- */
- remove() {
- let index;
- if (this.parent && (index = this.parent.children.indexOf(this)) !== -1) {
- this.parent.children.splice(index, 1);
- }
- this.parent = null;
- this.element.remove();
- }
-}
-
-/** Shortcut for `new ElemJS`. */
-function ejs(tag) {
- return new ElemJS(tag);
-}
-
-export {q, qa, ElemJS, ejs}
diff --git a/build/static/channels.svg b/build/static/channels.svg
deleted file mode 100644
index 121c251..0000000
--- a/build/static/channels.svg
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
diff --git a/build/static/chat-input.js b/build/static/chat-input.js
deleted file mode 100644
index ca01cc1..0000000
--- a/build/static/chat-input.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {q} from "../static/basic.js?static=4212436742"
-import {store} from "../static/store/store.js?static=ce3066287b"
-import * as lsm from "../static/lsm.js?static=aed2c7ca35"
-import {chat} from "../static/chat.js?static=b01b3716ff"
-
-const input = q("#c-chat-textarea")
-
-store.activeRoom.subscribe("changeSelf", () => {
- if (store.activeRoom.exists()) {
- input.focus()
- }
-})
-
-input.addEventListener("keydown", event => {
- if (event.key === "Enter" && !event.shiftKey && !event.ctrlKey) {
- event.preventDefault()
- const body = input.value
- send(input.value)
- input.value = ""
- fixHeight()
- }
-})
-
-input.addEventListener("input", () => {
- fixHeight()
-})
-
-function fixHeight() {
- input.style.height = "0px"
- // console.log(input.clientHeight, input.scrollHeight)
- input.style.height = (input.scrollHeight + 1) + "px"
-}
-
-function send(body) {
- if (!store.activeRoom.exists()) return
- return store.activeRoom.value().timeline.send(body)
-}
diff --git a/build/static/chat.js b/build/static/chat.js
deleted file mode 100644
index 8bb324d..0000000
--- a/build/static/chat.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import {ElemJS, q, ejs} from "../static/basic.js?static=4212436742"
-import {store} from "../static/store/store.js?static=ce3066287b"
-
-const chatMessages = q("#c-chat-messages")
-
-class Chat extends ElemJS {
- constructor() {
- super(q("#c-chat"))
-
- this.removableSubscriptions = []
-
- store.activeRoom.subscribe("changeSelf", this.changeRoom.bind(this))
-
- this.render()
- }
-
- unsubscribe() {
- this.removableSubscriptions.forEach(({name, target, subscription}) => {
- target.unsubscribe(name, subscription)
- })
- this.removableSubscriptions.length = 0
- }
-
- changeRoom() {
- // disconnect from the previous room
- this.unsubscribe()
- // connect to the new room's timeline updater
- if (store.activeRoom.exists()) {
- const timeline = store.activeRoom.value().timeline
- const subscription = () => {
- // scroll anchor does not work if the timeline is scrolled to the top.
- // at the start, when there are not enough messages for a full screen, this is the case.
- // once there are enough messages that scrolling is necessary, we initiate a scroll down to activate the scroll anchor.
- let oldDifference = chatMessages.scrollHeight - chatMessages.clientHeight
- setTimeout(() => {
- let newDifference = chatMessages.scrollHeight - chatMessages.clientHeight
- // console.log("height difference", oldDifference, newDifference)
- if (oldDifference < 24) { // this is jank
- this.element.parentElement.scrollBy(0, 1000)
- }
- }, 0)
- }
- const name = "beforeChange"
- this.removableSubscriptions.push({name, target: timeline, subscription})
- timeline.subscribe(name, subscription)
- }
- this.render()
- }
-
- render() {
- this.clearChildren()
- if (store.activeRoom.exists()) {
- const reactiveTimeline = store.activeRoom.value().timeline.getTimeline()
- this.child(reactiveTimeline)
- setTimeout(() => {
- this.element.parentElement.scrollBy(0, 1)
- reactiveTimeline.anchor.scroll()
- }, 0)
- }
- }
-}
-
-const chat = new Chat()
-
-export {chat}
diff --git a/build/static/directs.svg b/build/static/directs.svg
deleted file mode 100644
index b1ed08b..0000000
--- a/build/static/directs.svg
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
diff --git a/build/static/groups.js b/build/static/groups.js
deleted file mode 100644
index 3500e27..0000000
--- a/build/static/groups.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import {q} from "../static/basic.js?static=4212436742"
-
-let state = "CLOSED"
-
-const groups = q("#c-groups-display")
-const rooms = q("#c-rooms")
-
-groups.addEventListener("click", () => {
- groups.classList.add("c-groups__display--closed")
-})
-
-rooms.addEventListener("mouseout", () => {
- groups.classList.remove("c-groups__display--closed")
-})
diff --git a/build/static/join-event.svg b/build/static/join-event.svg
deleted file mode 100644
index 042e3bd..0000000
--- a/build/static/join-event.svg
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
diff --git a/build/static/lsm.js b/build/static/lsm.js
deleted file mode 100644
index 7338343..0000000
--- a/build/static/lsm.js
+++ /dev/null
@@ -1,11 +0,0 @@
-function get(name) {
- return localStorage.getItem(name)
-}
-
-function set(name, value) {
- return localStorage.setItem(name, value)
-}
-
-window.lsm = {get, set}
-
-export {get, set}
diff --git a/build/static/main.css b/build/static/main.css
deleted file mode 100644
index e78cd1a..0000000
--- a/build/static/main.css
+++ /dev/null
@@ -1,261 +0,0 @@
-@font-face {
- font-family: Whitney;
- font-weight: 400;
- src: url(/static/whitney-400.woff?static=0f823bc4b5) format("woff2");
-}
-@font-face {
- font-family: Whitney;
- font-weight: 500;
- src: url(/static/whitney-500.woff?static=ba33ed18fe) format("woff2");
-}
-body {
- font-family: sans-serif;
- background-color: #36393e;
- color: #ddd;
- font-size: 16px;
- font-family: Whitney;
- line-height: 1.45;
- margin: 0;
- height: 100vh;
- font-weight: 400;
-}
-
-.main {
- height: 100vh;
- display: flex;
-}
-
-.c-rooms {
- background-color: #2f3135;
- padding: 8px;
- width: 240px;
- font-size: 18px;
- font-weight: 500;
- overflow-y: auto;
- scrollbar-width: thin;
- scrollbar-color: #202224 #2f3135;
- flex-shrink: 0;
-}
-
-.c-room {
- display: flex;
- align-items: center;
- padding: 6px 8px;
- margin: 2px 0;
- cursor: pointer;
- border-radius: 8px;
-}
-.c-room:not(.c-room--active):hover {
- background-color: #393c42;
-}
-.c-room--active {
- background-color: #42454a;
-}
-.c-room__icon {
- width: 32px;
- height: 32px;
- 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;
- text-overflow: ellipsis;
-}
-
-.c-groups {
- position: relative;
- width: 80px;
- flex-shrink: 0;
- font-size: 22px;
- font-weight: 500;
-}
-.c-groups__display {
- background-color: #202224;
- overflow: hidden;
- width: 80px;
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- right: 0;
- transition: width ease-out 0.12s;
- scrollbar-width: thin;
- scrollbar-color: #42454a #202224;
-}
-.c-groups__display:not(.c-groups__display--closed):hover {
- width: 320px;
- overflow-y: auto;
-}
-.c-groups__container {
- width: 320px;
- padding: 8px;
-}
-
-.c-group {
- display: flex;
- align-items: center;
- padding: 4px 8px;
- cursor: pointer;
- border-radius: 8px;
-}
-.c-group:hover {
- background-color: #2f3135;
-}
-.c-group__icon {
- width: 48px;
- height: 48px;
- background-color: #393c42;
- border-radius: 50%;
- margin-right: 16px;
- flex-shrink: 0;
- transition: border-radius ease-out 0.12s;
-}
-.c-group--active .c-group__icon {
- border-radius: 28%;
-}
-.c-group__name {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.c-group-marker {
- position: absolute;
- top: 5px;
- opacity: 0;
- transform: translateY(8px);
- transition: transform ease 0.12s, opacity ease-out 0.12s;
- height: 46px;
- width: 6px;
- background-color: #ccc;
- border-radius: 0px 6px 6px 0px;
-}
-
-.c-event-groups * {
- overflow-anchor: none;
-}
-
-.c-message-group, .c-message-event {
- margin-top: 12px;
- padding-top: 12px;
- border-top: 1px solid #4b4e54;
-}
-
-.c-message-group {
- display: flex;
-}
-.c-message-group__avatar {
- flex-shrink: 0;
- margin-right: 16px;
- cursor: pointer;
-}
-.c-message-group__icon {
- width: 40px;
- height: 40px;
- border-radius: 50%;
-}
-.c-message-group__icon--no-icon {
- background-color: #48d;
-}
-.c-message-group__intro {
- display: flex;
- align-items: baseline;
-}
-.c-message-group__name {
- color: #5bf;
- margin: -2px 0px -3px;
- font-size: 19px;
- font-weight: 500;
- cursor: pointer;
-}
-.c-message-group__name:hover {
- text-decoration: underline;
-}
-.c-message-group__date {
- font-size: 14px;
- margin-left: 9px;
- color: #999;
-}
-
-.c-message {
- margin-top: 4px;
- opacity: 1;
- transition: opacity 0.2s ease-out;
-}
-.c-message--pending {
- opacity: 0.5;
-}
-
-.c-message-event {
- padding-top: 10px;
- padding-left: 6px;
-}
-.c-message-event__inner {
- display: flex;
- align-items: center;
-}
-.c-message-event__icon {
- margin-right: 8px;
- position: relative;
- top: 1px;
-}
-
-.c-message-notice {
- padding: 12px;
-}
-.c-message-notice__inner {
- text-align: center;
- padding: 12px;
- background-color: #42454a;
- border-radius: 8px;
-}
-
-.c-chat {
- display: grid;
- grid-template-rows: 1fr 82px;
- align-items: end;
- flex: 1;
-}
-.c-chat__messages {
- height: 100%;
- overflow-y: scroll;
- scrollbar-color: #202224 #2f3135;
- display: grid;
- align-items: end;
-}
-.c-chat__inner {
- padding: 20px 20px 20px;
-}
-
-.c-chat-input {
- width: 100%;
- border-top: 2px solid #4b4e54;
- background-color: #36393e;
-}
-.c-chat-input__textarea {
- width: calc(100% - 40px);
- height: 39.2px;
- box-sizing: border-box;
- margin: 20px;
- padding: 8px;
- font-family: inherit;
- font-size: inherit;
- background-color: #42454a;
- color: #fff;
- appearance: none;
- -moz-appearance: none;
- -webkit-appearance: none;
- border: none;
- border-radius: 8px;
- resize: none;
-}
-
-.c-anchor {
- overflow-anchor: auto;
- height: 1px;
-}
\ No newline at end of file
diff --git a/build/static/room-picker.js b/build/static/room-picker.js
deleted file mode 100644
index 75e6c0b..0000000
--- a/build/static/room-picker.js
+++ /dev/null
@@ -1,230 +0,0 @@
-import {q, ElemJS, ejs} from "../static/basic.js?static=4212436742"
-import {store} from "../static/store/store.js?static=ce3066287b"
-import {SubscribeMapList} from "../static/store/SubscribeMapList.js?static=b2732c5460"
-import {SubscribeValue} from "../static/store/SubscribeValue.js?static=215b6a5099"
-import {Timeline} from "../static/Timeline.js?static=1393b78916"
-import * as lsm from "../static/lsm.js?static=aed2c7ca35"
-import {resolveMxc} from "../static/functions.js?static=e3784c70ce"
-
-class ActiveGroupMarker extends ElemJS {
- constructor() {
- super(q("#c-group-marker"))
- store.activeGroup.subscribe("changeSelf", this.render.bind(this))
- }
-
- render() {
- if (store.activeGroup.exists()) {
- const group = store.activeGroup.value()
- this.style("opacity", 1)
- this.style("transform", `translateY(${group.element.offsetTop}px)`)
- } else {
- this.style("opacity", 0)
- }
- }
-}
-
-const activeGroupMarker = new ActiveGroupMarker()
-
-class Group extends ElemJS {
- constructor(key, data) {
- super("div")
-
- this.data = data
- this.order = this.data.order
-
- this.class("c-group")
- this.child(
- (this.data.icon
- ? ejs("img").class("c-group__icon").attribute("src", this.data.icon)
- : ejs("div").class("c-group__icon")
- ),
- ejs("div").class("c-group__name").text(this.data.name)
- )
-
- this.on("click", this.onClick.bind(this))
-
- store.activeGroup.subscribe("changeSelf", this.render.bind(this))
- }
-
- render() {
- const active = store.activeGroup.value() === this
- this.element.classList[active ? "add" : "remove"]("c-group--active")
- }
-
- onClick() {
- store.activeGroup.set(this)
- }
-}
-
-class Room extends ElemJS {
- constructor(id, data) {
- super("div")
-
- this.id = id
- this.data = data
- this.timeline = new Timeline(this.id)
- this.group = null
- this.members = new SubscribeMapList(SubscribeValue)
-
- this.class("c-room")
-
- this.on("click", this.onClick.bind(this))
- store.activeRoom.subscribe("changeSelf", this.render.bind(this))
-
- 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() {
- 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)
- }
-
- setGroup(id) {
- this.group = id
- }
-
- getGroup() {
- 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() {
- store.activeRoom.set(this)
- }
-
- 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")
- }
-}
-
-class Rooms extends ElemJS {
- constructor() {
- super(q("#c-rooms"))
-
- this.roomData = []
- this.rooms = []
-
- store.rooms.subscribe("askAdd", this.askAdd.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))
- store.newEvents.subscribe("changeSelf", this.sort.bind(this))
-
- this.render()
- }
-
- sort() {
- store.rooms.sort()
- this.render()
- }
-
- askAdd(event, {key, data}) {
- const room = new Room(key, data)
- 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().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().getGroup() !== store.activeGroup.value()) {
- if (first) {
- store.activeRoom.set(first)
- } else {
- store.activeRoom.delete()
- }
- }
- }
-}
-const rooms = new Rooms()
-
-class Groups extends ElemJS {
- constructor() {
- super(q("#c-groups-list"))
-
- store.groups.subscribe("askAdd", this.askAdd.bind(this))
- store.groups.subscribe("changeItem", this.render.bind(this))
- }
-
- askAdd(event, {key, data}) {
- const group = new Group(key, data)
- store.groups.addEnd(key, group)
- store.groups.sort()
- }
-
- render() {
- this.clearChildren()
- store.groups.forEach((key, item) => {
- this.child(item.value())
- })
- }
-}
-const groups = new Groups()
diff --git a/build/static/store/Subscribable.js b/build/static/store/Subscribable.js
deleted file mode 100644
index 6c7640e..0000000
--- a/build/static/store/Subscribable.js
+++ /dev/null
@@ -1,38 +0,0 @@
-class Subscribable {
- constructor() {
- this.events = {
- addSelf: [],
- editSelf: [],
- removeSelf: [],
- changeSelf: []
- }
- this.eventDeps = {
- addSelf: ["changeSelf"],
- editSelf: ["changeSelf"],
- removeSelf: ["changeSelf"],
- changeSelf: []
- }
- }
-
- subscribe(event, callback) {
- if (this.events[event]) {
- this.events[event].push(callback)
- } else {
- throw new Error(`Cannot subscribe to non-existent event ${event}, available events are: ${Object.keys(this.events).join(", ")}`)
- }
- }
-
- unsubscribe(event, callback) {
- const index = this.events[event].indexOf(callback)
- if (index === -1) throw new Error(`Tried to remove a nonexisting subscription from event ${event}`)
- this.events[event].splice(index, 1)
- }
-
- broadcast(event, data) {
- this.eventDeps[event].concat(event).forEach(eventName => {
- this.events[eventName].forEach(f => f(event, data))
- })
- }
-}
-
-export {Subscribable}
diff --git a/build/static/store/SubscribeMapList.js b/build/static/store/SubscribeMapList.js
deleted file mode 100644
index 189119c..0000000
--- a/build/static/store/SubscribeMapList.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import {Subscribable} from "../../static/store/Subscribable.js?static=19b7e44aa6"
-import {SubscribeValue} from "../../static/store/SubscribeValue.js?static=215b6a5099"
-
-class SubscribeMapList extends Subscribable {
- constructor(inner) {
- super()
- this.inner = inner
- Object.assign(this.events, {
- addItem: [],
- deleteItem: [],
- editItem: [],
- changeItem: [],
- askAdd: []
- })
- Object.assign(this.eventDeps, {
- addItem: ["changeItem"],
- deleteItem: ["changeItem"],
- editItem: ["changeItem"],
- changeItem: [],
- askAdd: []
- })
- this.map = new Map()
- this.list = []
- }
-
- has(key) {
- return this.map.has(key) && this.map.get(key).exists()
- }
-
- get(key) {
- if (this.map.has(key)) {
- return this.map.get(key)
- } else {
- const item = new this.inner()
- this.map.set(key, item)
- return item
- }
- }
-
- forEach(f) {
- this.list.forEach(key => f(key, this.get(key)))
- }
-
- askAdd(key, data) {
- this.broadcast("askAdd", {key, data})
- }
-
- addStart(key, value) {
- this._add(key, value, true)
- }
-
- addEnd(key, value) {
- 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) {
- let s
- if (this.map.has(key)) {
- const exists = this.map.get(key).exists()
- s = this.map.get(key).set(value)
- if (exists) {
- this.broadcast("editItem", key)
- } else {
- this.broadcast("addItem", key)
- }
- } else {
- s = new this.inner().set(value)
- this.map.set(key, s)
- if (start) this.list.unshift(key)
- else this.list.push(key)
- this.broadcast("addItem", key)
- }
- return s
- }
-}
-
-export {SubscribeMapList}
diff --git a/build/static/store/SubscribeSet.js b/build/static/store/SubscribeSet.js
deleted file mode 100644
index e43128f..0000000
--- a/build/static/store/SubscribeSet.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import {Subscribable} from "../../static/store/Subscribable.js?static=19b7e44aa6"
-
-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}
diff --git a/build/static/store/SubscribeValue.js b/build/static/store/SubscribeValue.js
deleted file mode 100644
index e8dceb6..0000000
--- a/build/static/store/SubscribeValue.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import {Subscribable} from "../../static/store/Subscribable.js?static=19b7e44aa6"
-
-class SubscribeValue extends Subscribable {
- constructor() {
- super()
- this.hasData = false
- this.data = null
- }
-
- exists() {
- return this.hasData
- }
-
- value() {
- if (this.hasData) return this.data
- else return null
- }
-
- set(data) {
- const exists = this.exists()
- this.data = data
- this.hasData = true
- if (exists) {
- this.broadcast("editSelf", this.data)
- } else {
- this.broadcast("addSelf", this.data)
- }
- return this
- }
-
- edit(f) {
- if (this.exists()) {
- f(this.data)
- this.set(this.data)
- } else {
- throw new Error("Tried to edit a SubscribeValue that had no value")
- }
- }
-
- delete() {
- this.hasData = false
- this.broadcast("removeSelf")
- return this
- }
-}
-
-export {SubscribeValue}
diff --git a/build/static/store/store.js b/build/static/store/store.js
deleted file mode 100644
index 1450a68..0000000
--- a/build/static/store/store.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import {Subscribable} from "../../static/store/Subscribable.js?static=19b7e44aa6"
-import {SubscribeMapList} from "../../static/store/SubscribeMapList.js?static=b2732c5460"
-import {SubscribeSet} from "../../static/store/SubscribeSet.js?static=39a1c0a2a4"
-import {SubscribeValue} from "../../static/store/SubscribeValue.js?static=215b6a5099"
-
-const store = {
- groups: new SubscribeMapList(SubscribeValue),
- rooms: new SubscribeMapList(SubscribeValue),
- directs: new SubscribeSet(),
- activeGroup: new SubscribeValue(),
- activeRoom: new SubscribeValue(),
- newEvents: new Subscribable()
-}
-
-window.store = store
-
-export {store}
diff --git a/build/static/sync/sync.js b/build/static/sync/sync.js
deleted file mode 100644
index 3b6fc34..0000000
--- a/build/static/sync/sync.js
+++ /dev/null
@@ -1,127 +0,0 @@
-import {store} from "../../static/store/store.js?static=ce3066287b"
-import * as lsm from "../../static/lsm.js?static=aed2c7ca35"
-import {resolveMxc} from "../../static/functions.js?static=e3784c70ce"
-
-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 {
- let newEvents = false
-
- // 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)
- }
- const storeRoom = store.rooms.get(id).value()
- room.state.events.forEach(event => {
- if (event.type === "m.room.member") {
- storeRoom.members.get(event.state_key).set(event)
- }
- })
- const timeline = storeRoom.timeline
- if (room.timeline.events.length) newEvents = true
- 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) {
- console.error(root)
- throw e
- }
-}
-
-function syncLoop() {
- return sync().then(manageSync).then(syncLoop)
-}
-
-;[
- {
- id: "directs",
- name: "Directs",
- icon: staticFiles.get("/assets/icons/directs.svg"),
- order: -2
- },
- {
- id: "channels",
- name: "Channels",
- icon: staticFiles.get("/assets/icons/channels.svg"),
- order: -1
- }
-].forEach(data => store.groups.askAdd(data.id, data))
-
-store.activeGroup.set(store.groups.get("directs").value())
-
-syncLoop()
diff --git a/build/static/whitney-400.woff b/build/static/whitney-400.woff
deleted file mode 100644
index 2b33081..0000000
Binary files a/build/static/whitney-400.woff and /dev/null differ
diff --git a/build/static/whitney-500.woff b/build/static/whitney-500.woff
deleted file mode 100644
index fc82138..0000000
Binary files a/build/static/whitney-500.woff and /dev/null differ