community absorbs the hub: search, transfers and browse ride the same pills, one entry in the sidebar

This commit is contained in:
KeelrLeek committed 2026-09-02 13:46:57 -05:00
1 parent 8c25f93afe
commit 64a7561750
7 files changed
+91 -22

No files matched your search

+38 -2
View File
@@ -13,9 +13,11 @@
import { onMount, untrack } from "svelte";
import Icon from "../components/Icon.svelte";
import Identicon, { hueOf } from "../components/Identicon.svelte";
import Soulseek from "./Soulseek.svelte";
import * as api from "../lib/api.js";
import {
app,
ui,
slsk,
downloads,
network,
@@ -61,6 +63,30 @@
if (app.connection === "online") void loadBuddies();
});
// ---- the fused hub -------------------------------------------------------
// Search, Transfers and Browse are the Soulseek hub rendered embedded;
// its old Chat / Users / Rooms tabs map onto this page's own sections.
const HUB_SECTIONS = new Set(["search", "transfers", "browse"]);
const hubOpen = $derived(HUB_SECTIONS.has(community.section));
function fromHub(tab) {
const map = { chat: "inbox", users: "people", rooms: "rooms" };
const s = map[tab] ?? tab;
if (s !== community.section) setSection(s);
}
// The hub's routable ids (the download ring, "get on soulseek", Home's
// CTA) arrive as ui.view; a repeat jump bumps ui.hubJump.
$effect(() => {
const v = ui.view;
void ui.hubJump;
const s = v === "search" ? "search" : v === "downloads" ? "transfers" : null;
if (s) untrack(() => setSection(s));
});
const TRANSFER_DONE = new Set(["complete", "failed", "cancelled"]);
const transfersActive = $derived(
Object.values(downloads.items).filter((d) => !TRANSFER_DONE.has(d.state)).length
+ Object.values(network.uploads).filter((u) => u.state === "transferring").length,
);
// ---- small helpers -------------------------------------------------------
const key = $derived(personaKey());
const profile = $derived(community.profile);
@@ -133,7 +159,7 @@
// Hand-offs into the Soulseek hub: browse lives there.
function browseUser(u) {
slsk.browseRequest = u;
goToView("soulseek");
setSection("browse");
}
function messageUser(u) {
openChat(u);
@@ -442,6 +468,12 @@
<!-- ── the pills ──────────────────────────────────────────────────── -->
<div class="qtabs">
<button class="qtab" class:on={community.section === "search"} onclick={() => setSection("search")}>Search</button>
<button class="qtab" class:on={community.section === "transfers"} onclick={() => setSection("transfers")}>
Transfers{#if transfersActive}<span class="n">{transfersActive}</span>{/if}
</button>
<button class="qtab" class:on={community.section === "browse"} onclick={() => setSection("browse")}>Browse</button>
<span class="pillsep" aria-hidden="true"></span>
<button class="qtab" class:on={community.section === "inbox"} onclick={() => setSection("inbox")}>
Inbox{#if unreadTotal}<span class="n">{unreadTotal}</span>{/if}
</button>
@@ -462,8 +494,12 @@
</button>
</div>
<!-- ── SEARCH · TRANSFERS · BROWSE — the hub, embedded ─────────────── -->
{#if hubOpen}
<Soulseek embedded section={community.section} onsection={fromHub} />
<!-- ── INBOX ──────────────────────────────────────────────────────── -->
{#if community.section === "inbox"}
{:else if community.section === "inbox"}
{#if !convos.length}
<div class="empty">
<Icon name="inbox" size={30} />
+26 -3
View File
@@ -42,12 +42,27 @@
import Icon from "../components/Icon.svelte";
import PlaylistMenu from "../components/PlaylistMenu.svelte";
// Fused into the Community area (2026-09-02): the page renders this hub
// EMBEDDED for its Search / Transfers / Browse pills and owns the pill
// row itself; Rooms, Chat and Users below are the retired originals of
// Community's Rooms, Inbox and People (same stores), left in place for
// the standalone shape. `onsection` reports every tab change so a
// "message this user" from a search result lands on Community's Inbox.
let { embedded = false, section = null, onsection = null } = $props();
const aliasTab = (v) =>
v === "downloads" ? "transfers" : v === "search" ? "search" : null;
let tab = $state(aliasTab(ui.view) ?? "transfers");
let tab = $state(untrack(() => section) ?? aliasTab(ui.view) ?? "transfers");
$effect(() => {
const t = aliasTab(ui.view);
if (t) untrack(() => (tab = t));
if (t && !embedded) untrack(() => (tab = t));
});
$effect(() => {
const s = section;
if (embedded && s) untrack(() => (tab = s));
});
$effect(() => {
const t = tab;
if (embedded) untrack(() => onsection?.(t));
});
// ---- transfers ----------------------------------------------------
@@ -348,7 +363,8 @@
};
</script>
<div class="slsk">
<div class="slsk" class:embedded>
{#if !embedded}
<div class="qtabs">
<button class="qtab" class:on={tab === "transfers"} onclick={() => (tab = "transfers")}>
Transfers
@@ -372,6 +388,7 @@
{#if slsk.buddies.length}<span class="n">{slsk.buddies.length}</span>{/if}
</button>
</div>
{/if}
<!-- ═══ TRANSFERS ═══ -->
{#if tab === "transfers"}
@@ -828,6 +845,12 @@
padding-top: calc(var(--bar-h) + 18px);
padding-bottom: 40px;
}
/* Inside the Community page the shell is the page's; the hub is only
the tab body. */
.slsk.embedded {
padding-top: 0;
padding-bottom: 0;
}
/* ── the Qt tab row ── */
.qtabs {