- replace hardcoded benji with ?user= query param - add GET /api/users and GET /api/me?user= endpoints - serve frontend static files via tower-http ServeDir - add multi-stage Dockerfile (Rust + Vite → single image) - add docker-compose.yml + Caddyfile for apes.unslope.com - frontend: getCurrentUsername() from URL param → localStorage - sidebar shows current user Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { useState } from "react";
|
|
import type { Channel } from "@/types/Channel";
|
|
import { createChannel, getCurrentUsername } from "@/api";
|
|
|
|
interface Props {
|
|
channels: Channel[];
|
|
activeId: string | null;
|
|
onSelect: (id: string) => void;
|
|
onChannelCreated: () => void;
|
|
}
|
|
|
|
export function ChannelSidebar({
|
|
channels,
|
|
activeId,
|
|
onSelect,
|
|
onChannelCreated,
|
|
}: Props) {
|
|
const [newName, setNewName] = useState("");
|
|
const [creating, setCreating] = useState(false);
|
|
|
|
async function handleCreate() {
|
|
if (!newName.trim()) return;
|
|
setCreating(true);
|
|
await createChannel({ name: newName.trim(), description: "" });
|
|
setNewName("");
|
|
setCreating(false);
|
|
onChannelCreated();
|
|
}
|
|
|
|
return (
|
|
<div className="flex flex-col h-full w-52 border-r border-sidebar-border bg-sidebar text-sidebar-foreground">
|
|
{/* Header */}
|
|
<div className="px-3 py-3 border-b border-sidebar-border">
|
|
<div className="text-[15px] font-bold tracking-tight text-foreground">
|
|
COLONY
|
|
</div>
|
|
<div className="text-[10px] text-muted-foreground mt-0.5">
|
|
apes.unslope.com
|
|
</div>
|
|
</div>
|
|
|
|
{/* Channel list */}
|
|
<div className="flex-1 overflow-y-auto py-2">
|
|
<div className="px-2 mb-1 text-[10px] font-bold text-muted-foreground tracking-widest">
|
|
CHANNELS
|
|
</div>
|
|
{channels.map((ch) => (
|
|
<button
|
|
type="button"
|
|
key={ch.id}
|
|
onClick={() => onSelect(ch.id)}
|
|
className={`w-full text-left px-3 py-1 text-[12px] transition-colors ${
|
|
ch.id === activeId
|
|
? "bg-sidebar-accent text-sidebar-accent-foreground font-medium"
|
|
: "text-sidebar-foreground hover:bg-sidebar-accent/50"
|
|
}`}
|
|
>
|
|
<span className="text-muted-foreground mr-1">#</span>
|
|
{ch.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Current user */}
|
|
<div className="px-3 py-2 border-t border-sidebar-border text-[11px]">
|
|
<span className="text-muted-foreground">logged in as </span>
|
|
<span className="font-bold text-foreground">{getCurrentUsername()}</span>
|
|
</div>
|
|
|
|
{/* New channel input */}
|
|
<div className="p-2 border-t border-sidebar-border">
|
|
<input
|
|
type="text"
|
|
placeholder="+ new channel"
|
|
value={newName}
|
|
onChange={(e) => setNewName(e.target.value)}
|
|
onKeyDown={(e) => e.key === "Enter" && handleCreate()}
|
|
disabled={creating}
|
|
className="w-full bg-sidebar-accent text-[11px] text-sidebar-foreground placeholder:text-muted-foreground px-2 py-1 rounded-sm border border-sidebar-border focus:outline-none focus:border-[var(--color-agent-glow)]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|