Feat: first steps to space exploration

This commit is contained in:
tezlm 2022-12-20 03:04:54 -08:00
parent 00001749ba
commit 000017524a
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
4 changed files with 158 additions and 24 deletions

View file

@ -1,3 +1,20 @@
export function orderSpaceRooms(a, b) {
const cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0;
if (a.room.type === "m.space" && b.room.type !== "m.space") return 1;
if (a.room.type !== "m.space" && b.room.type === "m.space") return -1;
return cmpOrder(a.event.content.order, b.event.content.order)
|| cmp(a.room.name, b.room.name)
|| cmp(a.event.date, b.event.date)
|| cmp(a.room.id, b.room.id);
function cmpOrder(a, b) {
if (a === undefined && b === undefined) return 0;
if (a === undefined) return 1;
if (b === undefined) return -1;
return cmp(a, b);
}
}
export function update() {
const dms = state.accountDataRef.get("m.direct");
state.dms.clear();
@ -17,7 +34,7 @@ export function update() {
const children = room.getAllState("m.space.child")
.filter(i => i.content.via && state.rooms.has(i.stateKey))
.map(i => ({ event: i, room: state.rooms.get(i.stateKey) }))
.sort(orderSpaces)
.sort(orderSpaceRooms)
.map(i => i.room);
children.forEach(i => inSpaces.add(i.id));
state.spaces.set(id, children);
@ -28,23 +45,6 @@ export function update() {
state.spaces.set("orphanSpaces", orphans.filter(i => i.type === "m.space"));
refresh();
function orderSpaces(a, b) {
const cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0;
if (a.room.type === "m.space" && b.room.type !== "m.space") return 1;
if (a.room.type !== "m.space" && b.room.type === "m.space") return -1;
return cmpOrder(a.event.content.order, b.event.content.order)
|| cmp(a.room.name, b.room.name)
|| cmp(a.event.date, b.event.date)
|| cmp(a.room.id, b.room.id);
function cmpOrder(a, b) {
if (a === undefined && b === undefined) return 0;
if (a === undefined) return 1;
if (b === undefined) return -1;
return cmp(a, b);
}
}
}
export function refresh() {

View file

@ -1,10 +1,14 @@
<script>
import Search from "../../atoms/Search.svelte";
import Avatar from "../../atoms/Avatar.svelte";
import Loading from "../../atoms/Loading.svelte";
import Hierarchy from "../space/Hierarchy.svelte";
import { parseHtml } from "../../../util/html";
export let room;
let members = false;
$: hierarchy = room.hierarchy().then(res => res.all());
$: {
room.members.fetchAll("join").then(() => {
members = room.members.with("join", false);
@ -18,6 +22,25 @@ function formatJoinRule(rule) {
case "restricted": return "Subspace";
}
}
function buildTree(rooms, top = rooms[0], topEvent = null) {
const item = { room: top, event: topEvent, children: [] };
for (let event of top.childrenState) {
if (!event.content.via?.length) continue;
const room = rooms.find(i => i.id === event.state_key);
if (!room) continue;
if (room.type === "m.space") {
item.children.push(buildTree(rooms, room, event));
} else {
item.children.push({ room, event });
}
}
item.children.sort(actions.spaces.orderSpaceRooms);
return item;
}
</script>
<style>
.content {
@ -32,13 +55,13 @@ function formatJoinRule(rule) {
.main {
max-width: 600px;
width: 100%;
margin: 0 16px;
margin: 0 8px;
}
.side {
width: 100%;
max-width: 320px;
margin: 36px 16px;
margin: 36px 8px;
}
.side > div {
@ -76,10 +99,17 @@ function formatJoinRule(rule) {
line-height: 1.3;
}
.placeholder {
.rooms {
background: var(--bg-rooms-members);
min-height: 400px;
padding: 8px;
margin-bottom: 16px;
}
.loading {
height: 400px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
@ -90,8 +120,15 @@ function formatJoinRule(rule) {
<div style="height: 16px"></div>
<Search size="tall" placeholder="Search..." />
<div style="height: 16px"></div>
<div class="placeholder">
<div>// TODO</div>
<div class="rooms">
{#await hierarchy}
<div class="loading">
<Loading />
<i style="margin-top: 8px; color: var(--fg-light)">Loading rooms...</i>
</div>
{:then rooms}
<Hierarchy item={buildTree(rooms)} />
{/await}
</div>
</div>
<div class="side">

View file

@ -0,0 +1,97 @@
<script>
import Avatar from "../../atoms/Avatar.svelte";
import { parseHtml } from "../../../util/html";
export let item;
const { dms } = state;
const collapsed = {};
// move dm status into the room object?
function getName(room) {
if (!dms.has(room.id)) return room.name;
const other = dms.get(room.id);
return other.name ?? other.id;
}
</script>
<style>
.expander {
display: flex;
margin-left: -6px;
margin-top: 8px;
gap: 4px;
align-items: end;
font-weight: bold;
color: var(--fg-muted);
cursor: pointer;
}
.expander:hover {
color: var(--fg-notice);
}
.expander .icon {
transform: rotate(90deg);
transition: transform .2s;
}
.expander.collapsed .icon {
transform: rotate(0);
}
.children {
position: relative;
border-left: solid var(--color-gray) 2px;
padding: 0 8px;
padding-bottom: 1px;
margin-bottom: 16px;
}
.children::after {
content: "";
position: absolute;
left: -2px;
bottom: -8px;
display: block;
height: 24px;
width: 24px;
border-left: solid var(--color-gray) 2px;
border-bottom: solid var(--color-gray) 2px;
border-bottom-left-radius: 8px;
}
</style>
{#each item.children as item}
{@const { room } = item}
{#if room.type === "m.space"}
<div
class="expander"
class:collapsed={collapsed[room.id]}
on:click={() => collapsed[room.id] = !collapsed[room.id]}
>
<div class="icon">chevron_right</div>
<div>{getName(room)}</div>
{#if room.topic}
<div style="color: var(--fg-muted)">{@html parseHtml(room.topic, { linkify: true, twemojify: true, sanitize: true, inline: true })}</div>
{/if}
</div>
{#if !collapsed[room.id]}
<div class="children">
{#if item.children.length}
<svelte:self {item} />
{:else}
<div style="color: var(--fg-muted); font-style: italic; padding: 8px 2px">no children</div>
{/if}
</div>
{/if}
{:else}
<div style="display: flex; margin: 8px 0">
<Avatar user={room} size={36} link />
<div style="margin-left: 12px">
<div style="font-weight: bold">{getName(room)}</div>
{#if room.topic}
<div style="color: var(--fg-muted)">{@html parseHtml(room.topic, { linkify: true, twemojify: true, sanitize: true, inline: true })}</div>
{:else}
<div style="color: var(--fg-muted); font-style: italic">no topic</div>
{/if}
</div>
</div>
{/if}
{/each}

View file

@ -41,7 +41,7 @@ i {
}
</style>
<div class="wrapper">
<Loading color="var(--fg-dim)" />
<Loading />
<div class="title">syncing...</div>
<i>{rnd(someFactsIThinkSomePeopleMightFindInteresting)}</i>
</div>