- Introduced AccessMode enum to define channel access levels: Public, PasswordProtected, AccountRequired, and OwnerOnly. - Updated Channel and ProgrammingBlock entities to include access_mode and access_password_hash fields. - Enhanced create and update channel functionality to handle access mode and password. - Implemented access checks in channel routes based on the defined access modes. - Modified frontend components to support channel creation and editing with access control options. - Added ChannelPasswordModal for handling password input when accessing restricted channels. - Updated API calls to include channel and block passwords as needed. - Created database migrations to add access_mode and access_password_hash columns to channels table.
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import { WifiOff, AlertTriangle, Loader2, Lock } from "lucide-react";
|
|
|
|
type NoSignalVariant = "no-signal" | "error" | "loading" | "locked";
|
|
|
|
interface NoSignalProps {
|
|
variant?: NoSignalVariant;
|
|
message?: string;
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
const VARIANTS: Record<
|
|
NoSignalVariant,
|
|
{ icon: React.ReactNode; heading: string; defaultMessage: string }
|
|
> = {
|
|
"no-signal": {
|
|
icon: <WifiOff className="h-10 w-10 text-zinc-600" />,
|
|
heading: "No Signal",
|
|
defaultMessage: "Nothing is scheduled to play right now.",
|
|
},
|
|
error: {
|
|
icon: <AlertTriangle className="h-10 w-10 text-zinc-600" />,
|
|
heading: "Playback Error",
|
|
defaultMessage: "Something went wrong. Try switching channels.",
|
|
},
|
|
loading: {
|
|
icon: <Loader2 className="h-10 w-10 animate-spin text-zinc-600" />,
|
|
heading: "Loading",
|
|
defaultMessage: "Tuning in…",
|
|
},
|
|
locked: {
|
|
icon: <Lock className="h-10 w-10 text-zinc-600" />,
|
|
heading: "Access Restricted",
|
|
defaultMessage: "You don't have permission to watch this channel.",
|
|
},
|
|
};
|
|
|
|
export function NoSignal({ variant = "no-signal", message, children }: NoSignalProps) {
|
|
const { icon, heading, defaultMessage } = VARIANTS[variant];
|
|
|
|
return (
|
|
<div className="relative flex h-full w-full flex-col items-center justify-center gap-4 bg-zinc-950 select-none overflow-hidden">
|
|
{/* Static noise texture */}
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-0 opacity-[0.03]"
|
|
style={{
|
|
backgroundImage:
|
|
"url(\"data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E\")",
|
|
backgroundSize: "200px 200px",
|
|
}}
|
|
/>
|
|
|
|
{icon}
|
|
|
|
<div className="flex flex-col items-center gap-1 text-center">
|
|
<p className="text-sm font-semibold uppercase tracking-widest text-zinc-500">
|
|
{heading}
|
|
</p>
|
|
<p className="max-w-xs text-xs text-zinc-700">{message ?? defaultMessage}</p>
|
|
</div>
|
|
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export type { NoSignalProps, NoSignalVariant };
|