Reactive state for groups and rooms
This commit is contained in:
parent
f42ea1493b
commit
dd0b14720e
17 changed files with 671 additions and 162 deletions
|
@ -2,10 +2,10 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" type="text/css" href="static/main.css?static=d352e5de1f">
|
<link rel="stylesheet" type="text/css" href="static/main.css?static=79b6afceb8">
|
||||||
<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=a90499fdac"></script>
|
<script type="module" src="static/chat-input.js?static=a90499fdac"></script>
|
||||||
<script type="module" src="static/room-picker.js?static=c7bdd4a2f3"></script>
|
<script type="module" src="static/room-picker.js?static=596e719ff8"></script>
|
||||||
<title>Carbon</title>
|
<title>Carbon</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -25,19 +25,22 @@
|
||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="5.6568542"
|
inkscape:zoom="22.627417"
|
||||||
inkscape:cx="30.795644"
|
inkscape:cx="27.665561"
|
||||||
inkscape:cy="43.802047"
|
inkscape:cy="33.324951"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="true"
|
||||||
units="px"
|
units="px"
|
||||||
inkscape:window-width="1440"
|
inkscape:window-width="1440"
|
||||||
inkscape:window-height="879"
|
inkscape:window-height="879"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
showborder="false">
|
showborder="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-grids="true">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid824" />
|
id="grid824" />
|
||||||
|
@ -61,7 +64,7 @@
|
||||||
transform="translate(36.739286,-225.97828)">
|
transform="translate(36.739286,-225.97828)">
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill:#e2e2e2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
style="opacity:1;fill:#e2e2e2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
d="m -33.035119,229.41786 h 5.067509 c 0.868277,0 1.567287,0.69901 1.567287,1.56728 v 1.88109 c 0,0.86828 -0.69901,1.56729 -1.567287,1.56729 h -2.157093 l -1.322916,1.86351 -1.322917,-1.86351 h -0.264583 c -0.868277,0 -1.567287,-0.69901 -1.567287,-1.56729 v -1.88109 c 0,-0.86827 0.69901,-1.56728 1.567287,-1.56728 z"
|
d="m -33.035119,228.8887 h 5.067509 c 0.868277,0 1.567287,0.69901 1.567287,1.56728 v 2.66473 c 0,0.86828 -0.69901,1.56729 -1.567287,1.56729 h -2.157093 l -1.322916,1.86351 -1.322917,-1.86351 h -0.264583 c -0.868277,0 -1.567287,-0.69901 -1.567287,-1.56729 v -2.66473 c 0,-0.86827 0.69901,-1.56728 1.567287,-1.56728 z"
|
||||||
id="rect820"
|
id="rect820"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="ssssscccssss" />
|
sodipodi:nodetypes="ssssscccssss" />
|
||||||
|
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -31,7 +31,7 @@ body {
|
||||||
width: 240px;
|
width: 240px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #202224 #2f3135;
|
scrollbar-color: #202224 #2f3135;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -40,16 +40,20 @@ body {
|
||||||
.c-room {
|
.c-room {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px;
|
padding: 6px 8px;
|
||||||
|
margin: 2px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
|
||||||
.c-room:hover {
|
|
||||||
background-color: #393c42;
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
.c-room:not(.c-room--active):hover {
|
||||||
|
background-color: #393c42;
|
||||||
|
}
|
||||||
|
.c-room--active {
|
||||||
|
background-color: #42454a;
|
||||||
|
}
|
||||||
.c-room__icon {
|
.c-room__icon {
|
||||||
width: 36px;
|
width: 32px;
|
||||||
height: 36px;
|
height: 32px;
|
||||||
background-color: #bbb;
|
background-color: #bbb;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
@ -121,8 +125,9 @@ body {
|
||||||
.c-group-marker {
|
.c-group-marker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
|
opacity: 0;
|
||||||
transform: translateY(8px);
|
transform: translateY(8px);
|
||||||
transition: transform ease 0.12s;
|
transition: transform ease 0.12s, opacity ease-out 0.12s;
|
||||||
height: 46px;
|
height: 46px;
|
||||||
width: 6px;
|
width: 6px;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
import {q, ElemJS, ejs} from "./basic.js"
|
import {q, ElemJS, ejs} from "./basic.js"
|
||||||
|
import {store} from "./store/store.js"
|
||||||
|
|
||||||
class ActiveGroupMarker extends ElemJS {
|
class ActiveGroupMarker extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-group-marker"))
|
super(q("#c-group-marker"))
|
||||||
|
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
follow(group) {
|
render() {
|
||||||
this.style("transform", `translateY(${group.element.offsetTop}px)`)
|
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()
|
const activeGroupMarker = new ActiveGroupMarker()
|
||||||
|
|
||||||
class Group extends ElemJS {
|
class Group extends ElemJS {
|
||||||
constructor(groups, data) {
|
constructor(key, data) {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.groups = groups
|
|
||||||
this.data = data
|
this.data = data
|
||||||
this.active = false
|
|
||||||
|
|
||||||
this.on("click", this.onClick.bind(this))
|
|
||||||
|
|
||||||
this.class("c-group")
|
this.class("c-group")
|
||||||
this.child(
|
this.child(
|
||||||
|
@ -29,29 +34,45 @@ class Group extends ElemJS {
|
||||||
),
|
),
|
||||||
ejs("div").class("c-group__name").text(this.data.name)
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
setActive(active) {
|
render() {
|
||||||
this.active = active
|
const active = store.activeGroup.value() === this
|
||||||
this.element.classList[active ? "add" : "remove"]("c-group--active")
|
this.element.classList[active ? "add" : "remove"]("c-group--active")
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
this.groups.setGroup(this)
|
store.activeGroup.set(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Room extends ElemJS {
|
class Room extends ElemJS {
|
||||||
constructor(name) {
|
constructor(key, data) {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.name = name
|
this.data = data
|
||||||
|
|
||||||
this.class("c-room")
|
this.class("c-room")
|
||||||
this.child(
|
this.child(
|
||||||
ejs("div").class("c-room__icon"),
|
ejs("div").class("c-room__icon"),
|
||||||
ejs("div").class("c-room__name").text(this.name)
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
store.activeRoom.set(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const active = store.activeRoom.value() === this
|
||||||
|
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,20 +80,38 @@ class Rooms extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-rooms"))
|
super(q("#c-rooms"))
|
||||||
|
|
||||||
|
this.roomData = []
|
||||||
this.rooms = []
|
this.rooms = []
|
||||||
|
|
||||||
|
store.rooms.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
|
store.rooms.subscribe("changeItem", this.render.bind(this))
|
||||||
|
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
||||||
|
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
setRooms(rooms) {
|
askAdd(event, {key, data}) {
|
||||||
this.rooms = rooms
|
const room = new Room(key, data)
|
||||||
this.render()
|
store.rooms.addEnd(key, room)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.clearChildren()
|
this.clearChildren()
|
||||||
for (const room of this.rooms) {
|
let first = null
|
||||||
this.child(new Room(room.name))
|
// set room list
|
||||||
|
store.rooms.forEach((id, room) => {
|
||||||
|
if (room.value().data.group === 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 (first) {
|
||||||
|
store.activeRoom.set(first)
|
||||||
|
} else {
|
||||||
|
store.activeRoom.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,59 +120,73 @@ const rooms = new Rooms()
|
||||||
class Groups extends ElemJS {
|
class Groups extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-groups-list"))
|
super(q("#c-groups-list"))
|
||||||
this.groupData = [
|
|
||||||
{name: "Directs", icon: "/static/directs.svg", rooms: [
|
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
{name: "riley"},
|
store.groups.subscribe("addItem", this.addItem.bind(this))
|
||||||
{name: "BadAtNames"},
|
|
||||||
{name: "lynxano"},
|
|
||||||
{name: "quarky"},
|
|
||||||
{name: "lepton"},
|
|
||||||
{name: "ash"},
|
|
||||||
{name: "mewmew"},
|
|
||||||
{name: "Toniob"},
|
|
||||||
{name: "cockandball"}
|
|
||||||
]},
|
|
||||||
{name: "Channels", icon: "/static/channels.svg", rooms: [
|
|
||||||
{name: "Carbon brainstorming"},
|
|
||||||
{name: "Bibliogram"},
|
|
||||||
{name: "Monsters Inc Debate Hall"},
|
|
||||||
{name: "DRB clan"},
|
|
||||||
{name: "mettaton simp zone"}
|
|
||||||
]},
|
|
||||||
{name: "Fediverse Drama Museum", rooms: [
|
|
||||||
{name: "witches"},
|
|
||||||
{name: "snouts"},
|
|
||||||
{name: "monads"},
|
|
||||||
{name: "radical"},
|
|
||||||
{name: "blobcat"}
|
|
||||||
]},
|
|
||||||
{name: "Epicord", rooms: [
|
|
||||||
{name: "main"},
|
|
||||||
{name: "gaming"},
|
|
||||||
{name: "inhalers"},
|
|
||||||
{name: "minecraft"},
|
|
||||||
{name: "osu"},
|
|
||||||
{name: "covid"}
|
|
||||||
]},
|
|
||||||
{name: "Invidious", rooms: [
|
|
||||||
]}
|
|
||||||
]
|
|
||||||
this.groups = []
|
|
||||||
this.render()
|
|
||||||
this.setGroup(this.children[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setGroup(group) {
|
askAdd(event, {key, data}) {
|
||||||
rooms.setRooms(group.data.rooms)
|
const group = new Group(key, data)
|
||||||
this.groups.forEach(g => g.setActive(g === group))
|
store.groups.addEnd(key, group)
|
||||||
activeGroupMarker.follow(group)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
addItem(event, key) {
|
||||||
this.groups = this.groupData.map(data => new Group(this, data))
|
this.child(store.groups.get(key).value())
|
||||||
for (const group of this.groups) {
|
|
||||||
this.child(group)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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())
|
||||||
|
|
36
build/static/store/Subscribable.js
Normal file
36
build/static/store/Subscribable.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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) {
|
||||||
|
this.events[event].push(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast(event, data) {
|
||||||
|
this.eventDeps[event].concat(event).forEach(eventName => {
|
||||||
|
this.events[eventName].forEach(f => f(event, data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {Subscribable}
|
77
build/static/store/SubscribeMapList.js
Normal file
77
build/static/store/SubscribeMapList.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
|
||||||
|
class SubscribeMapList extends Subscribable {
|
||||||
|
constructor(inner) {
|
||||||
|
super()
|
||||||
|
this.inner = inner
|
||||||
|
Object.assign(this.events, {
|
||||||
|
addItem: [],
|
||||||
|
removeItem: [],
|
||||||
|
editItem: [],
|
||||||
|
changeItem: [],
|
||||||
|
askAdd: []
|
||||||
|
})
|
||||||
|
Object.assign(this.eventDeps, {
|
||||||
|
addItem: ["changeItem"],
|
||||||
|
removeItem: ["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)
|
||||||
|
}
|
||||||
|
|
||||||
|
_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("changeItem", 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}
|
47
build/static/store/SubscribeValue.js
Normal file
47
build/static/store/SubscribeValue.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
|
|
||||||
|
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}
|
13
build/static/store/store.js
Normal file
13
build/static/store/store.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||||
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
|
||||||
|
const store = {
|
||||||
|
groups: new SubscribeMapList(SubscribeValue),
|
||||||
|
rooms: new SubscribeMapList(SubscribeValue),
|
||||||
|
activeGroup: new SubscribeValue(),
|
||||||
|
activeRoom: new SubscribeValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.store = store
|
||||||
|
|
||||||
|
export {store}
|
|
@ -25,19 +25,22 @@
|
||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0.0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="5.6568542"
|
inkscape:zoom="22.627417"
|
||||||
inkscape:cx="30.795644"
|
inkscape:cx="27.665561"
|
||||||
inkscape:cy="43.802047"
|
inkscape:cy="33.324951"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="false"
|
showgrid="true"
|
||||||
units="px"
|
units="px"
|
||||||
inkscape:window-width="1440"
|
inkscape:window-width="1440"
|
||||||
inkscape:window-height="879"
|
inkscape:window-height="879"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
showborder="false">
|
showborder="true"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-grids="true">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid824" />
|
id="grid824" />
|
||||||
|
@ -61,7 +64,7 @@
|
||||||
transform="translate(36.739286,-225.97828)">
|
transform="translate(36.739286,-225.97828)">
|
||||||
<path
|
<path
|
||||||
style="opacity:1;fill:#e2e2e2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
style="opacity:1;fill:#e2e2e2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
d="m -33.035119,229.41786 h 5.067509 c 0.868277,0 1.567287,0.69901 1.567287,1.56728 v 1.88109 c 0,0.86828 -0.69901,1.56729 -1.567287,1.56729 h -2.157093 l -1.322916,1.86351 -1.322917,-1.86351 h -0.264583 c -0.868277,0 -1.567287,-0.69901 -1.567287,-1.56729 v -1.88109 c 0,-0.86827 0.69901,-1.56728 1.567287,-1.56728 z"
|
d="m -33.035119,228.8887 h 5.067509 c 0.868277,0 1.567287,0.69901 1.567287,1.56728 v 2.66473 c 0,0.86828 -0.69901,1.56729 -1.567287,1.56729 h -2.157093 l -1.322916,1.86351 -1.322917,-1.86351 h -0.264583 c -0.868277,0 -1.567287,-0.69901 -1.567287,-1.56729 v -2.66473 c 0,-0.86827 0.69901,-1.56728 1.567287,-1.56728 z"
|
||||||
id="rect820"
|
id="rect820"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
sodipodi:nodetypes="ssssscccssss" />
|
sodipodi:nodetypes="ssssscccssss" />
|
||||||
|
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -1,25 +1,30 @@
|
||||||
import {q, ElemJS, ejs} from "./basic.js"
|
import {q, ElemJS, ejs} from "./basic.js"
|
||||||
|
import {store} from "./store/store.js"
|
||||||
|
|
||||||
class ActiveGroupMarker extends ElemJS {
|
class ActiveGroupMarker extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-group-marker"))
|
super(q("#c-group-marker"))
|
||||||
|
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
follow(group) {
|
render() {
|
||||||
this.style("transform", `translateY(${group.element.offsetTop}px)`)
|
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()
|
const activeGroupMarker = new ActiveGroupMarker()
|
||||||
|
|
||||||
class Group extends ElemJS {
|
class Group extends ElemJS {
|
||||||
constructor(groups, data) {
|
constructor(key, data) {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.groups = groups
|
|
||||||
this.data = data
|
this.data = data
|
||||||
this.active = false
|
|
||||||
|
|
||||||
this.on("click", this.onClick.bind(this))
|
|
||||||
|
|
||||||
this.class("c-group")
|
this.class("c-group")
|
||||||
this.child(
|
this.child(
|
||||||
|
@ -29,29 +34,45 @@ class Group extends ElemJS {
|
||||||
),
|
),
|
||||||
ejs("div").class("c-group__name").text(this.data.name)
|
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))
|
||||||
}
|
}
|
||||||
|
|
||||||
setActive(active) {
|
render() {
|
||||||
this.active = active
|
const active = store.activeGroup.value() === this
|
||||||
this.element.classList[active ? "add" : "remove"]("c-group--active")
|
this.element.classList[active ? "add" : "remove"]("c-group--active")
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
this.groups.setGroup(this)
|
store.activeGroup.set(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Room extends ElemJS {
|
class Room extends ElemJS {
|
||||||
constructor(name) {
|
constructor(key, data) {
|
||||||
super("div")
|
super("div")
|
||||||
|
|
||||||
this.name = name
|
this.data = data
|
||||||
|
|
||||||
this.class("c-room")
|
this.class("c-room")
|
||||||
this.child(
|
this.child(
|
||||||
ejs("div").class("c-room__icon"),
|
ejs("div").class("c-room__icon"),
|
||||||
ejs("div").class("c-room__name").text(this.name)
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
store.activeRoom.set(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const active = store.activeRoom.value() === this
|
||||||
|
this.element.classList[active ? "add" : "remove"]("c-room--active")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,20 +80,38 @@ class Rooms extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-rooms"))
|
super(q("#c-rooms"))
|
||||||
|
|
||||||
|
this.roomData = []
|
||||||
this.rooms = []
|
this.rooms = []
|
||||||
|
|
||||||
|
store.rooms.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
|
store.rooms.subscribe("changeItem", this.render.bind(this))
|
||||||
|
store.activeGroup.subscribe("changeSelf", this.render.bind(this))
|
||||||
|
|
||||||
this.render()
|
this.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
setRooms(rooms) {
|
askAdd(event, {key, data}) {
|
||||||
this.rooms = rooms
|
const room = new Room(key, data)
|
||||||
this.render()
|
store.rooms.addEnd(key, room)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.clearChildren()
|
this.clearChildren()
|
||||||
for (const room of this.rooms) {
|
let first = null
|
||||||
this.child(new Room(room.name))
|
// set room list
|
||||||
|
store.rooms.forEach((id, room) => {
|
||||||
|
if (room.value().data.group === 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 (first) {
|
||||||
|
store.activeRoom.set(first)
|
||||||
|
} else {
|
||||||
|
store.activeRoom.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,59 +120,73 @@ const rooms = new Rooms()
|
||||||
class Groups extends ElemJS {
|
class Groups extends ElemJS {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(q("#c-groups-list"))
|
super(q("#c-groups-list"))
|
||||||
this.groupData = [
|
|
||||||
{name: "Directs", icon: "/static/directs.svg", rooms: [
|
store.groups.subscribe("askAdd", this.askAdd.bind(this))
|
||||||
{name: "riley"},
|
store.groups.subscribe("addItem", this.addItem.bind(this))
|
||||||
{name: "BadAtNames"},
|
|
||||||
{name: "lynxano"},
|
|
||||||
{name: "quarky"},
|
|
||||||
{name: "lepton"},
|
|
||||||
{name: "ash"},
|
|
||||||
{name: "mewmew"},
|
|
||||||
{name: "Toniob"},
|
|
||||||
{name: "cockandball"}
|
|
||||||
]},
|
|
||||||
{name: "Channels", icon: "/static/channels.svg", rooms: [
|
|
||||||
{name: "Carbon brainstorming"},
|
|
||||||
{name: "Bibliogram"},
|
|
||||||
{name: "Monsters Inc Debate Hall"},
|
|
||||||
{name: "DRB clan"},
|
|
||||||
{name: "mettaton simp zone"}
|
|
||||||
]},
|
|
||||||
{name: "Fediverse Drama Museum", rooms: [
|
|
||||||
{name: "witches"},
|
|
||||||
{name: "snouts"},
|
|
||||||
{name: "monads"},
|
|
||||||
{name: "radical"},
|
|
||||||
{name: "blobcat"}
|
|
||||||
]},
|
|
||||||
{name: "Epicord", rooms: [
|
|
||||||
{name: "main"},
|
|
||||||
{name: "gaming"},
|
|
||||||
{name: "inhalers"},
|
|
||||||
{name: "minecraft"},
|
|
||||||
{name: "osu"},
|
|
||||||
{name: "covid"}
|
|
||||||
]},
|
|
||||||
{name: "Invidious", rooms: [
|
|
||||||
]}
|
|
||||||
]
|
|
||||||
this.groups = []
|
|
||||||
this.render()
|
|
||||||
this.setGroup(this.children[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setGroup(group) {
|
askAdd(event, {key, data}) {
|
||||||
rooms.setRooms(group.data.rooms)
|
const group = new Group(key, data)
|
||||||
this.groups.forEach(g => g.setActive(g === group))
|
store.groups.addEnd(key, group)
|
||||||
activeGroupMarker.follow(group)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
addItem(event, key) {
|
||||||
this.groups = this.groupData.map(data => new Group(this, data))
|
this.child(store.groups.get(key).value())
|
||||||
for (const group of this.groups) {
|
|
||||||
this.child(group)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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())
|
||||||
|
|
36
src/js/store/Subscribable.js
Normal file
36
src/js/store/Subscribable.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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) {
|
||||||
|
this.events[event].push(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast(event, data) {
|
||||||
|
this.eventDeps[event].concat(event).forEach(eventName => {
|
||||||
|
this.events[eventName].forEach(f => f(event, data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {Subscribable}
|
41
src/js/store/SubscribeMap.js
Normal file
41
src/js/store/SubscribeMap.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
|
||||||
|
class SubscribeMap extends Subscribable {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
Object.assign(this.events, {
|
||||||
|
addItem: [],
|
||||||
|
changeItem: [],
|
||||||
|
removeItem: []
|
||||||
|
})
|
||||||
|
this.map = new Map()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
this.map.set(key, new SubscribeValue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
let s
|
||||||
|
if (this.map.has(key)) {
|
||||||
|
s = this.map.get(key).set(value)
|
||||||
|
this.broadcast("changeItem", key)
|
||||||
|
} else {
|
||||||
|
s = new SubscribeValue().set(value)
|
||||||
|
this.map.set(key, s)
|
||||||
|
this.broadcast("addItem", key)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {SubscribeMap}
|
77
src/js/store/SubscribeMapList.js
Normal file
77
src/js/store/SubscribeMapList.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
|
||||||
|
class SubscribeMapList extends Subscribable {
|
||||||
|
constructor(inner) {
|
||||||
|
super()
|
||||||
|
this.inner = inner
|
||||||
|
Object.assign(this.events, {
|
||||||
|
addItem: [],
|
||||||
|
removeItem: [],
|
||||||
|
editItem: [],
|
||||||
|
changeItem: [],
|
||||||
|
askAdd: []
|
||||||
|
})
|
||||||
|
Object.assign(this.eventDeps, {
|
||||||
|
addItem: ["changeItem"],
|
||||||
|
removeItem: ["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)
|
||||||
|
}
|
||||||
|
|
||||||
|
_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("changeItem", 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}
|
47
src/js/store/SubscribeValue.js
Normal file
47
src/js/store/SubscribeValue.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import {Subscribable} from "./Subscribable.js"
|
||||||
|
|
||||||
|
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}
|
13
src/js/store/store.js
Normal file
13
src/js/store/store.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import {SubscribeMapList} from "./SubscribeMapList.js"
|
||||||
|
import {SubscribeValue} from "./SubscribeValue.js"
|
||||||
|
|
||||||
|
const store = {
|
||||||
|
groups: new SubscribeMapList(SubscribeValue),
|
||||||
|
rooms: new SubscribeMapList(SubscribeValue),
|
||||||
|
activeGroup: new SubscribeValue(),
|
||||||
|
activeRoom: new SubscribeValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.store = store
|
||||||
|
|
||||||
|
export {store}
|
|
@ -64,8 +64,9 @@ $out-width: $base-width + rooms.$list-width
|
||||||
.c-group-marker
|
.c-group-marker
|
||||||
position: absolute
|
position: absolute
|
||||||
top: 5px
|
top: 5px
|
||||||
|
opacity: 0
|
||||||
transform: translateY(8px)
|
transform: translateY(8px)
|
||||||
transition: transform ease 0.12s
|
transition: transform ease 0.12s, opacity ease-out 0.12s
|
||||||
height: $icon-size - 2px
|
height: $icon-size - 2px
|
||||||
width: 6px
|
width: 6px
|
||||||
background-color: #ccc
|
background-color: #ccc
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@use "../colors" as c
|
@use "../colors" as c
|
||||||
|
|
||||||
$list-width: 240px
|
$list-width: 240px
|
||||||
$icon-size: 36px
|
$icon-size: 32px
|
||||||
$icon-padding: 8px
|
$icon-padding: 8px
|
||||||
|
|
||||||
.c-rooms
|
.c-rooms
|
||||||
|
@ -10,7 +10,7 @@ $icon-padding: 8px
|
||||||
width: $list-width
|
width: $list-width
|
||||||
font-size: 20px
|
font-size: 20px
|
||||||
font-weight: 500
|
font-weight: 500
|
||||||
overflow-y: scroll
|
overflow-y: auto
|
||||||
scrollbar-width: thin
|
scrollbar-width: thin
|
||||||
scrollbar-color: c.$darkest c.$darker
|
scrollbar-color: c.$darkest c.$darker
|
||||||
flex-shrink: 0
|
flex-shrink: 0
|
||||||
|
@ -18,12 +18,16 @@ $icon-padding: 8px
|
||||||
.c-room
|
.c-room
|
||||||
display: flex
|
display: flex
|
||||||
align-items: center
|
align-items: center
|
||||||
padding: $icon-padding
|
padding: $icon-padding * 0.75 $icon-padding
|
||||||
|
margin: $icon-padding * 0.25 0
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
|
border-radius: 8px
|
||||||
|
|
||||||
&:hover
|
&:not(&--active):hover
|
||||||
background-color: c.$mild
|
background-color: c.$mild
|
||||||
border-radius: 8px
|
|
||||||
|
&--active
|
||||||
|
background-color: c.$milder
|
||||||
|
|
||||||
&__icon
|
&__icon
|
||||||
width: $icon-size
|
width: $icon-size
|
||||||
|
|
Loading…
Reference in a new issue