show stream state in stream-view
This commit is contained in:
@@ -23,6 +23,8 @@ interface ComposeOverlayProps {
|
||||
targetPath?: ParticlePath;
|
||||
onActiveChange?: (active: boolean) => void;
|
||||
onParticleCreated?: (particleId: string) => void;
|
||||
/** When true, composing is blocked (e.g. stream is closed). */
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +39,7 @@ export function ComposeOverlay({
|
||||
targetPath,
|
||||
onActiveChange,
|
||||
onParticleCreated,
|
||||
disabled,
|
||||
}: ComposeOverlayProps) {
|
||||
const [step, setStep] = useState<ComposeStep>("idle");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -56,6 +59,8 @@ export function ComposeOverlay({
|
||||
// Refs for synchronous reads in keyboard handlers
|
||||
const stepRef = useRef(step);
|
||||
const recordStartRef = useRef(0);
|
||||
const disabledRef = useRef(disabled);
|
||||
disabledRef.current = disabled;
|
||||
|
||||
const setStepSync = useCallback((next: ComposeStep) => {
|
||||
stepRef.current = next;
|
||||
@@ -341,6 +346,13 @@ export function ComposeOverlay({
|
||||
|
||||
switch (currentStep) {
|
||||
case "idle": {
|
||||
if (disabledRef.current) {
|
||||
if ((e.key === "`" && !e.repeat) || e.key === "t" || e.key === "T") {
|
||||
e.preventDefault();
|
||||
toast.info("This stream is closed");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (e.key === "`" && !e.repeat) {
|
||||
e.preventDefault();
|
||||
recordStartRef.current = Date.now();
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from "@/components/ui/context-menu";
|
||||
import { CircleCheckBig, CircleDot } from "lucide-react";
|
||||
import { updateStreamStatus } from "@/lib/firestore-particles";
|
||||
import { toFirestoreDocPath, particlePath } from "@/lib/particle-path";
|
||||
import type { StreamParticle } from "@/hooks/use-stream-particles";
|
||||
@@ -27,7 +28,17 @@ export function StreamContextMenu({ particle, networkId, children }: StreamConte
|
||||
<ContextMenuTrigger asChild>{children}</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem onSelect={toggleStatus}>
|
||||
{isOpen ? "Close stream" : "Open stream"}
|
||||
{isOpen ? (
|
||||
<>
|
||||
<CircleCheckBig className="size-4" />
|
||||
Close stream
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CircleDot className="size-4 text-green-500" />
|
||||
Open stream
|
||||
</>
|
||||
)}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { apiClient } from "@/api/client";
|
||||
import type { Particle } from "@/api/types";
|
||||
import { parseParticlePath, type ParticlePath } from "@/lib/particle-path";
|
||||
import { parseParticlePath, particlePath, toFirestoreDocPath, type ParticlePath } from "@/lib/particle-path";
|
||||
import { ComposeOverlay } from "@/features/compose/compose-overlay";
|
||||
import { PlaybackPageIndicator } from "@/features/particles/playback-page-indicator";
|
||||
import { MediaParticleView } from "@/features/particles/media-particle-view";
|
||||
@@ -14,7 +14,14 @@ import ControlsIndicator from "@/features/compose/controls-indicator";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useNetwork, useNetworks } from "@/hooks/use-networks";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Settings } from "lucide-react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Settings, CircleCheckBig, CircleDot, EllipsisVertical } from "lucide-react";
|
||||
import { updateStreamStatus } from "@/lib/firestore-particles";
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
|
||||
import { WindowControls } from "@/components/window-controls";
|
||||
import { RelativeTimestamp } from "@/components/relative-timestamp";
|
||||
@@ -248,6 +255,7 @@ export function StreamView({ path, streamParticle }: StreamViewProps) {
|
||||
networkId={networkId}
|
||||
targetPath={path}
|
||||
onActiveChange={setComposeActive}
|
||||
disabled={streamParticle.status === "closed"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -314,6 +322,7 @@ export function StreamView({ path, streamParticle }: StreamViewProps) {
|
||||
targetPath={path}
|
||||
onActiveChange={setComposeActive}
|
||||
onParticleCreated={onLocalParticleCreated}
|
||||
disabled={streamParticle.status === "closed"}
|
||||
/>
|
||||
|
||||
{/* BottomBar */}
|
||||
@@ -461,14 +470,48 @@ function TopBar({ networkId, particle, streamParticle }: { networkId: string; pa
|
||||
</button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="no-drag text-muted-foreground"
|
||||
onClick={() => navigate("/settings")}
|
||||
>
|
||||
<Settings className="size-3.5" />
|
||||
</Button>
|
||||
{streamParticle.status === "closed" && (
|
||||
<span className="no-drag flex items-center gap-1 rounded-full bg-white/10 px-2 py-0.5 text-xs text-muted-foreground backdrop-blur-sm">
|
||||
<CircleCheckBig className="size-3" />
|
||||
Closed
|
||||
</span>
|
||||
)}
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="no-drag text-muted-foreground"
|
||||
>
|
||||
<EllipsisVertical className="size-3.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onSelect={async () => {
|
||||
const docPath = toFirestoreDocPath(particlePath(networkId, [streamParticle.id]));
|
||||
await updateStreamStatus(docPath, streamParticle.status === "open" ? "closed" : "open");
|
||||
}}
|
||||
>
|
||||
{streamParticle.status === "open" ? (
|
||||
<>
|
||||
<CircleCheckBig className="size-4" />
|
||||
Close stream
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CircleDot className="size-4 text-green-500" />
|
||||
Open stream
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => navigate("/settings")}>
|
||||
<Settings className="size-4" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user