fix(ui): show restricted streams and fix bot combobox search

This commit is contained in:
2026-03-20 20:31:09 +01:00
parent 3e86ad70c2
commit e31c7a1620
3 changed files with 53 additions and 20 deletions

View File

@@ -1,8 +1,10 @@
import LiveStream from "@/components/app/Livestream/Livestream";
import LiveStream from '@/components/app/Livestream/Livestream';
import { validateRequest } from '@/lib/auth/validate';
import { prisma } from '@hctv/db';
export default async function Page({ params }: { params: Promise<{ username: string }> }) {
const { username } = await params;
const { user } = await validateRequest();
const streamInfo = await prisma.streamInfo.findUnique({
where: { username },
include: {
@@ -17,23 +19,31 @@ export default async function Page({ params }: { params: Promise<{ username: str
return <div>Stream not found</div>;
}
if (streamInfo.channel.restriction) {
const isExpired = streamInfo.channel.restriction.expiresAt &&
new Date(streamInfo.channel.restriction.expiresAt) < new Date();
if (!isExpired) {
return (
<div className="flex flex-col items-center justify-center h-[calc(100vh-64px)] p-4">
<h1 className="text-2xl font-bold text-destructive mb-2">Channel Restricted</h1>
<p className="text-muted-foreground text-center max-w-md">
This channel has been restricted by a moderator and is not currently available for viewing.
</p>
</div>
);
}
const hasActiveRestriction = streamInfo.channel.restriction
? !streamInfo.channel.restriction.expiresAt ||
new Date(streamInfo.channel.restriction.expiresAt) >= new Date()
: false;
const canBypassRestriction = Boolean(user?.isAdmin);
if (hasActiveRestriction && !canBypassRestriction) {
return (
<div className="flex flex-col items-center justify-center h-[calc(100vh-64px)] p-4">
<h1 className="text-2xl font-bold text-destructive mb-2">Channel Restricted</h1>
<p className="text-muted-foreground text-center max-w-md">
This channel has been restricted by a moderator and is not currently available for
viewing.
</p>
</div>
);
}
return (
<LiveStream username={username} streamInfo={streamInfo} />
<LiveStream
username={username}
streamInfo={streamInfo}
canViewRestrictedStream={canBypassRestriction}
initialRestrictionActive={hasActiveRestriction}
initialRestrictionExpiresAt={streamInfo.channel.restriction?.expiresAt?.toISOString() ?? null}
/>
);
}

View File

@@ -61,6 +61,7 @@ export function BotCombobox({ bots, filter, value, modal, onValueChange }: Props
<CommandItem
key={bot.id}
value={bot.id}
keywords={[bot.displayName, bot.slug]}
onSelect={(currentValue) => {
onValueChange(currentValue === value ? '' : currentValue);
setOpen(false);

View File

@@ -2,6 +2,7 @@
import { useEffect, useState } from 'react';
import { format } from 'date-fns';
import { AlertTriangle, RefreshCw } from 'lucide-react';
import StreamPlayer from '../StreamPlayer/StreamPlayer';
import UserInfoCard from '../UserInfoCard/UserInfoCard';
import ChatPanel from '../ChatPanel/ChatPanel';
@@ -9,13 +10,14 @@ import { Button } from '@/components/ui/button';
import type { StreamInfo, Channel } from '@hctv/db';
import { useIsMobile } from '@/lib/hooks/useMobile';
import { useAllChannels } from '@/lib/hooks/useUserList';
import { RefreshCw } from 'lucide-react';
export default function LiveStream(props: Props) {
const isMobile = useIsMobile();
const { channels, refresh } = useAllChannels(5000);
const [isRestricted, setIsRestricted] = useState(false);
const [restrictionExpiresAt, setRestrictionExpiresAt] = useState<string | null>(null);
const [isRestricted, setIsRestricted] = useState(props.initialRestrictionActive);
const [restrictionExpiresAt, setRestrictionExpiresAt] = useState<string | null>(
props.initialRestrictionExpiresAt
);
const [isRefreshing, setIsRefreshing] = useState(false);
useEffect(() => {
@@ -38,7 +40,7 @@ export default function LiveStream(props: Props) {
}
};
if (isRestricted) {
if (isRestricted && !props.canViewRestrictedStream) {
return (
<div className="flex flex-col items-center justify-center h-[calc(100vh-64px)] p-4">
<h1 className="text-2xl font-bold text-destructive mb-2">Channel Restricted</h1>
@@ -61,6 +63,23 @@ export default function LiveStream(props: Props) {
return (
<div className={`${isMobile ? 'flex flex-col' : 'flex'} h-[calc(100vh-64px)] w-full`}>
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
{isRestricted && props.canViewRestrictedStream && (
<div className="flex items-start gap-3 border-b border-amber-500/30 bg-amber-500/10 px-4 py-3 text-foreground">
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
<div className="text-sm">
<p className="font-medium">Restricted stream override</p>
<p>
This channel is restricted and unavailable to regular viewers. You are watching it
because you are a platform admin.
</p>
{restrictionExpiresAt && (
<p className="mt-1 text-muted-foreground">
Restriction lifts: {format(new Date(restrictionExpiresAt), 'PPP p')}
</p>
)}
</div>
</div>
)}
<StreamPlayer />
{isMobile && (
<div className="flex-1 min-h-[250px] max-h-[400px] border-t border-border">
@@ -82,4 +101,7 @@ export default function LiveStream(props: Props) {
interface Props {
username: string;
streamInfo: StreamInfo & { channel: Channel };
canViewRestrictedStream: boolean;
initialRestrictionActive: boolean;
initialRestrictionExpiresAt: string | null;
}