Working feed: NDK + relay connection + live notes from Nostr

- Tailwind CSS + Zustand + NDK installed and configured
- Sidebar with feed/relays/settings navigation
- Global feed view with live notes from relays
- Profile fetching with caching and deduplication
- Relay connection with timeout handling
- Note cards with avatar, name, timestamp, content
- Dark theme, monospace, no-slop UI
- Devtools enabled for debugging
This commit is contained in:
Jure
2026-03-08 14:54:04 +01:00
parent 43e14f9f04
commit b75ccb7f46
22 changed files with 2066 additions and 246 deletions
+64
View File
@@ -0,0 +1,64 @@
import { useUIStore } from "../../stores/ui";
import { useFeedStore } from "../../stores/feed";
const NAV_ITEMS = [
{ id: "feed" as const, label: "feed", icon: "◈" },
{ id: "relays" as const, label: "relays", icon: "⟐" },
{ id: "settings" as const, label: "settings", icon: "⚙" },
] as const;
export function Sidebar() {
const { currentView, setView, sidebarCollapsed, toggleSidebar } = useUIStore();
const { connected, notes } = useFeedStore();
return (
<aside
className={`h-full border-r border-border bg-bg flex flex-col transition-all duration-150 ${
sidebarCollapsed ? "w-12" : "w-48"
}`}
>
{/* Logo */}
<div className="border-b border-border px-3 py-2.5 flex items-center justify-between shrink-0">
<button
onClick={toggleSidebar}
className="text-text hover:text-accent transition-colors"
>
{sidebarCollapsed ? (
<span className="text-sm font-bold">W</span>
) : (
<span className="text-sm font-bold tracking-widest">WRYSTR</span>
)}
</button>
</div>
{/* Nav */}
<nav className="flex-1 py-2">
{NAV_ITEMS.map((item) => (
<button
key={item.id}
onClick={() => setView(item.id)}
className={`w-full text-left px-3 py-1.5 flex items-center gap-2 text-[12px] transition-colors ${
currentView === item.id
? "text-accent bg-accent/8"
: "text-text-muted hover:text-text hover:bg-bg-hover"
}`}
>
<span className="w-4 text-center text-[14px]">{item.icon}</span>
{!sidebarCollapsed && <span>{item.label}</span>}
</button>
))}
</nav>
{/* Status footer */}
{!sidebarCollapsed && (
<div className="border-t border-border px-3 py-2 text-[10px] text-text-dim">
<div className="flex items-center gap-1.5">
<span className={`w-1.5 h-1.5 rounded-full ${connected ? "bg-success" : "bg-danger"}`} />
<span>{connected ? "online" : "offline"}</span>
</div>
<div className="mt-0.5">{notes.length} notes</div>
</div>
)}
</aside>
);
}