feat: add dropdown and default personal channel to settings

This commit is contained in:
2025-06-25 23:20:46 +02:00
parent cd90281cf9
commit a3d6d587e5
6 changed files with 93 additions and 4 deletions

View File

@@ -1,8 +1,13 @@
'use client'
import { UniversalForm } from "@/components/app/UniversalForm/UniversalForm";
import { createChannel } from "@/lib/form/actions";
import { Hash } from "lucide-react";
import { useRouter } from "next/navigation";
function CreateChannelPage() {
const router = useRouter();
return (
<div className="min-h-screen bg-background">
<div className="flex h-full w-full flex-col items-center justify-center px-4 py-12">
@@ -26,6 +31,13 @@ function CreateChannelPage() {
]}
schemaName="createChannel"
action={createChannel}
onActionComplete={(r) => {
// @ts-expect-error
const channelName = r?.channel;
if (channelName) {
router.push(`/${channelName}`);
}
}}
/>
</div>

View File

@@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
@@ -44,6 +44,9 @@ import { parseAsString, useQueryState } from 'nuqs';
import { Write } from '@/components/ui/channel-desc-fancy-area/write';
import { Preview } from '@/components/ui/channel-desc-fancy-area/preview';
import { UploadButton } from '@/lib/uploadthing';
import { useOwnedChannels } from '@/lib/hooks/useUserList';
import { ChannelSelect } from '@/components/app/ChannelSelect/ChannelSelect';
import { useRouter } from 'next/navigation';
interface ChannelSettingsClientProps {
channel: Channel & {
@@ -73,6 +76,8 @@ export default function ChannelSettingsClient({
const [selTab, setSelTab] = useQueryState('tabs', parseAsString.withDefault('general'));
const [isUploading, setIsUploading] = useState(false);
const [uploadError, setUploadError] = useState<string | null>(null);
const channelList = useOwnedChannels();
const router = useRouter();
const copyStreamKey = async () => {
if (streamKey) {
@@ -105,7 +110,7 @@ export default function ChannelSettingsClient({
return (
<div className="container max-w-4xl mx-auto py-6 px-4">
<div className="mb-6">
<div className="mb-6 flex">
<div className="flex items-center gap-4 mb-4">
<Avatar className="h-16 w-16">
<AvatarImage src={channel.pfpUrl} alt={channel.name} />
@@ -122,6 +127,20 @@ export default function ChannelSettingsClient({
</div>
</div>
</div>
<div className='flex-1' />
<div>
<ChannelSelect
channelList={channelList.channels.map(c => c.channel)}
value={channel.name}
onSelect={(value) => {
if (value === 'create') {
router.push('/create');
} else {
router.push(`/settings/channel/${value}`);
}
}}
/>
</div>
</div>
<Tabs className="w-full" value={selTab} onValueChange={setSelTab}>

View File

@@ -0,0 +1,12 @@
import { resolvePersonalChannel } from "@/lib/auth/resolve";
import { redirect } from "next/navigation";
export default async function ChannelSettingsRedirector() {
const personalChannel = await resolvePersonalChannel();
if (personalChannel) {
return redirect(`/settings/channel/${personalChannel.name}`);
}
// lil easter egg which i doubt anyone will see
return <p>erm</p>;
}

View File

@@ -0,0 +1,46 @@
'use client'
import type { Channel } from "@hctv/db";
import * as React from 'react';
import { Plus } from 'lucide-react';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
export function ChannelSelect(props: Props) {
const { channelList } = props;
return (
<Select onValueChange={props.onSelect} value={props.value}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Channel" />
</SelectTrigger>
<SelectContent>
{channelList.map((channel) => (
<SelectItem key={channel.id} value={channel.name}>
<div className="flex items-center gap-3">
<Avatar className="h-8 w-8">
<AvatarImage src={channel.pfpUrl} alt={channel.name} />
<AvatarFallback>{channel.name[0]}</AvatarFallback>
</Avatar>
<div className="font-medium">{channel.name}</div>
</div>
</SelectItem>
))}
<SelectItem key="create" value="create" icon={<Plus className="h-4 w-4" />} className='h-11'>
Create Channel
</SelectItem>
</SelectContent>
</Select>
);
}
interface Props {
channelList: Channel[];
value?: string;
onSelect: (value: string) => void;
}

View File

@@ -63,7 +63,7 @@ export default function EditLivestreamDialog(props: Props) {
setSelectedChannel('');
// using window location href as a really janky way to just close the dialog
// TODO: use a proper dialog close method
window.location.href = '/settings/channel/create';
window.location.href = '/create';
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedChannel]);

View File

@@ -155,7 +155,7 @@ export async function createChannel(prev: any, formData: FormData) {
await initializeStreamInfo(createdChannel.id);
return { success: true };
return { success: true, channel: createdChannel.name };
}
export async function updateChannelSettings(prev: any, formData: FormData) {