soulseek: wire sections into the view, default to dashboard, persist zoom
- render dashboard/uploads/rooms/chat/users/system from the section channel - nav + view both default to the dashboard section - register the view panel's zoom header; read the persisted zoom via useDashboardState Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@ const ICONS: Record<SoulseekSectionId, LucideIcon> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SoulseekNav = () => {
|
export const SoulseekNav = () => {
|
||||||
const [section, setSection] = usePanelChannel<SoulseekSectionId>(SOULSEEK_SECTION_CHANNEL, 'search');
|
const [section, setSection] = usePanelChannel<SoulseekSectionId>(SOULSEEK_SECTION_CHANNEL, 'dashboard');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col overflow-y-auto bg-muted/30">
|
<div className="flex h-full flex-col overflow-y-auto bg-muted/30">
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import { Construction } from 'lucide-react';
|
import { Construction } from 'lucide-react';
|
||||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||||
import { SOULSEEK_SECTION_CHANNEL, SOULSEEK_SECTIONS, type SoulseekSectionId } from './shared';
|
import { useDashboardState } from 'state/useDashboardState';
|
||||||
|
import { SOULSEEK_SECTION_CHANNEL, SOULSEEK_SECTIONS, soulseekZoomKey, type SoulseekSectionId } from './shared';
|
||||||
import { SearchView } from './SearchView';
|
import { SearchView } from './SearchView';
|
||||||
import { SoulseekTransfers } from './SoulseekTransfers';
|
import { SoulseekTransfers } from './SoulseekTransfers';
|
||||||
|
import { SoulseekUploads } from './SoulseekUploads';
|
||||||
|
import { SoulseekRooms } from './SoulseekRooms';
|
||||||
|
import { SoulseekDashboard } from './SoulseekDashboard';
|
||||||
|
import { SoulseekChat } from './SoulseekChat';
|
||||||
|
import { SoulseekUsers } from './SoulseekUsers';
|
||||||
|
import { SoulseekSystem } from './SoulseekSystem';
|
||||||
|
|
||||||
// Right panel of the /soulseek workspace — renders the UI for the section the nav selected. Only Search
|
// Right panel of the /soulseek workspace — renders the UI for the section the nav selected. The panel
|
||||||
// (history + input) and Downloads (transfers) are built so far; the rest are placeholders.
|
// header's +/- controls set a per-panel zoom factor, persisted via useDashboardState (same store as the
|
||||||
|
// layout config); we read it here and apply it as a CSS scale, with a compensating width/height so the
|
||||||
|
// scaled content still fills the panel and inner scroll keeps working.
|
||||||
|
|
||||||
const Placeholder = ({ id }: { id: SoulseekSectionId }) => {
|
const Placeholder = ({ id }: { id: SoulseekSectionId }) => {
|
||||||
const label = SOULSEEK_SECTIONS.find((s) => s.id === id)?.label ?? id;
|
const label = SOULSEEK_SECTIONS.find((s) => s.id === id)?.label ?? id;
|
||||||
@@ -22,15 +31,45 @@ const Placeholder = ({ id }: { id: SoulseekSectionId }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SoulseekView = () => {
|
const sectionView = (section: SoulseekSectionId) => {
|
||||||
const [section] = usePanelChannel<SoulseekSectionId>(SOULSEEK_SECTION_CHANNEL, 'search');
|
|
||||||
|
|
||||||
switch (section) {
|
switch (section) {
|
||||||
|
case 'dashboard':
|
||||||
|
return <SoulseekDashboard />;
|
||||||
case 'search':
|
case 'search':
|
||||||
return <SearchView />;
|
return <SearchView />;
|
||||||
case 'downloads':
|
case 'downloads':
|
||||||
return <SoulseekTransfers />;
|
return <SoulseekTransfers />;
|
||||||
|
case 'uploads':
|
||||||
|
return <SoulseekUploads />;
|
||||||
|
case 'rooms':
|
||||||
|
return <SoulseekRooms />;
|
||||||
|
case 'chat':
|
||||||
|
return <SoulseekChat />;
|
||||||
|
case 'users':
|
||||||
|
return <SoulseekUsers />;
|
||||||
|
case 'system':
|
||||||
|
return <SoulseekSystem />;
|
||||||
default:
|
default:
|
||||||
return <Placeholder id={section} />;
|
return <Placeholder id={section} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SoulseekViewProps = { panelId: string };
|
||||||
|
|
||||||
|
export const SoulseekView = ({ panelId }: SoulseekViewProps) => {
|
||||||
|
const [section] = usePanelChannel<SoulseekSectionId>(SOULSEEK_SECTION_CHANNEL, 'dashboard');
|
||||||
|
const { value: zoom } = useDashboardState<number>(soulseekZoomKey(panelId), 1);
|
||||||
|
const z = zoom ?? 1;
|
||||||
|
|
||||||
|
if (z === 1) return <div className="h-full w-full">{sectionView(section)}</div>;
|
||||||
|
|
||||||
|
// transform: scale doesn't reflow, so size the box to 1/z and let the scale bring it back to 100%.
|
||||||
|
const inv = `${100 / z}%`;
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full overflow-hidden">
|
||||||
|
<div style={{ width: inv, height: inv, transform: `scale(${z})`, transformOrigin: 'top left' }}>
|
||||||
|
{sectionView(section)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,10 +2,18 @@ import type { AppRegistryMeta } from '../../AppRegistry';
|
|||||||
import { PanelLeft, LayoutGrid } from 'lucide-react';
|
import { PanelLeft, LayoutGrid } from 'lucide-react';
|
||||||
import { SoulseekNav } from './SoulseekNav';
|
import { SoulseekNav } from './SoulseekNav';
|
||||||
import { SoulseekView } from './SoulseekView';
|
import { SoulseekView } from './SoulseekView';
|
||||||
|
import { SoulseekViewHeader } from './SoulseekViewHeader';
|
||||||
|
|
||||||
export { SoulseekNav, SoulseekView };
|
export { SoulseekNav, SoulseekView };
|
||||||
|
|
||||||
export const appRegistryMetas: AppRegistryMeta[] = [
|
export const appRegistryMetas: AppRegistryMeta[] = [
|
||||||
{ key: 'soulseek-nav', name: 'Soulseek', icon: PanelLeft, component: SoulseekNav, availableOnPanel: false },
|
{ key: 'soulseek-nav', name: 'Soulseek', icon: PanelLeft, component: SoulseekNav, availableOnPanel: false },
|
||||||
{ key: 'soulseek-view', name: 'Soulseek', icon: LayoutGrid, component: SoulseekView, availableOnPanel: false },
|
{
|
||||||
|
key: 'soulseek-view',
|
||||||
|
name: 'Soulseek',
|
||||||
|
icon: LayoutGrid,
|
||||||
|
component: SoulseekView,
|
||||||
|
header: SoulseekViewHeader,
|
||||||
|
availableOnPanel: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user