Feat: first steps to space exploration
This commit is contained in:
parent
00001749ba
commit
000017524a
4 changed files with 158 additions and 24 deletions
|
@ -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() {
|
||||
|
|
|
@ -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">
|
||||
|
|
97
src/ui/room/space/Hierarchy.svelte
Normal file
97
src/ui/room/space/Hierarchy.svelte
Normal 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}
|
|
@ -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>
|
||||
|
|
Reference in a new issue