mirror of
https://github.com/ershisan99/www.git
synced 2025-12-17 12:34:17 +00:00
add social links support
This commit is contained in:
@@ -1,11 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -32,6 +26,7 @@ import {
|
||||
} from '@/components/ui/select'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { auth } from '@/server/auth'
|
||||
import { RANKED_CHANNEL, VANILLA_CHANNEL } from '@/shared/constants'
|
||||
import { api } from '@/trpc/react'
|
||||
import {
|
||||
@@ -40,15 +35,16 @@ import {
|
||||
BarChart3,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
EllipsisVertical,
|
||||
Filter,
|
||||
IceCreamCone,
|
||||
ShieldHalf,
|
||||
Star,
|
||||
Trophy,
|
||||
Twitch,
|
||||
UserIcon,
|
||||
Youtube,
|
||||
} from 'lucide-react'
|
||||
import { ExternalIcon } from 'next/dist/client/components/react-dev-overlay/ui/icons/external'
|
||||
import { useSession } from 'next-auth/react'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { isNonNullish } from 'remeda'
|
||||
@@ -206,20 +202,6 @@ export function UserInfo() {
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{/*<DropdownMenu>*/}
|
||||
{/* <DropdownMenuTrigger asChild>*/}
|
||||
{/* <Button variant={'ghost'} size={'iconSm'}>*/}
|
||||
{/* <EllipsisVertical className={'size-4'} />*/}
|
||||
{/* </Button>*/}
|
||||
{/* </DropdownMenuTrigger>*/}
|
||||
{/* <DropdownMenuContent>*/}
|
||||
{/* <DropdownMenuItem asChild>*/}
|
||||
{/* <Link href={`/stream-card/${id}`} target={'_blank'}>*/}
|
||||
{/* Stream widget <ExternalIcon />*/}
|
||||
{/* </Link>*/}
|
||||
{/* </DropdownMenuItem>*/}
|
||||
{/* </DropdownMenuContent>*/}
|
||||
{/*</DropdownMenu>*/}
|
||||
</div>
|
||||
|
||||
<p className='pt-2 text-gray-500 text-sm dark:text-zinc-400'>
|
||||
@@ -258,6 +240,38 @@ export function UserInfo() {
|
||||
</span>
|
||||
</Badge>
|
||||
)}
|
||||
{discord_user.twitch_url && (
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='border-gray-200 bg-gray-50 dark:border-zinc-700 dark:bg-zinc-800'
|
||||
>
|
||||
<Twitch className='mr-1 h-3 w-3 text-purple-500' />
|
||||
<a
|
||||
href={discord_user.twitch_url}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-gray-700 hover:underline dark:text-zinc-300'
|
||||
>
|
||||
Twitch
|
||||
</a>
|
||||
</Badge>
|
||||
)}
|
||||
{discord_user.youtube_url && (
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='border-gray-200 bg-gray-50 dark:border-zinc-700 dark:bg-zinc-800'
|
||||
>
|
||||
<Youtube className='mr-1 h-3 w-3 text-red-500' />
|
||||
<a
|
||||
href={discord_user.youtube_url}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-gray-700 hover:underline dark:text-zinc-300'
|
||||
>
|
||||
YouTube
|
||||
</a>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
116
src/app/(home)/profile/settings/page-client.tsx
Normal file
116
src/app/(home)/profile/settings/page-client.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { api } from '@/trpc/react'
|
||||
import { SiTwitch, SiYoutube } from '@icons-pack/react-simple-icons'
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
export function ProfileSettingsPageClient({
|
||||
userId,
|
||||
}: { userId: string | null }) {
|
||||
const [socialLinks] = api.profile.getSocialLinks.useSuspenseQuery()
|
||||
const [twitchUrl, setTwitchUrl] = useState<string | null>(
|
||||
socialLinks.twitch_url
|
||||
)
|
||||
const [youtubeUrl, setYoutubeUrl] = useState<string | null>(
|
||||
socialLinks.youtube_url
|
||||
)
|
||||
|
||||
const { mutate: updateSocialLinks, isPending } =
|
||||
api.profile.updateSocialLinks.useMutation({
|
||||
onSuccess: () => {
|
||||
toast.success('Social links updated successfully')
|
||||
},
|
||||
})
|
||||
|
||||
const handleSave = () => {
|
||||
updateSocialLinks({
|
||||
twitch_url: twitchUrl,
|
||||
youtube_url: youtubeUrl,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mx-auto flex w-[calc(100%-1rem)] max-w-fd-container flex-1 flex-col py-8'>
|
||||
<div className='mb-6'>
|
||||
<h1 className='font-bold text-3xl text-gray-900 dark:text-white'>
|
||||
Profile Settings
|
||||
</h1>
|
||||
<p className='mt-2 text-gray-500 dark:text-zinc-400'>
|
||||
Manage your profile settings and social links
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className='space-y-6'>
|
||||
<div className='rounded-lg border bg-white p-6 dark:bg-zinc-800/20'>
|
||||
<h2 className='mb-4 font-semibold text-xl'>Stream Widget</h2>
|
||||
<p className='mb-4 text-gray-500 dark:text-zinc-400'>
|
||||
Use this widget to display your stats on your stream
|
||||
</p>
|
||||
{userId && (
|
||||
<Link
|
||||
href={`/stream-card/${userId}`}
|
||||
target='_blank'
|
||||
className='inline-flex items-center gap-2 text-violet-600 hover:text-violet-700 dark:text-violet-400 dark:hover:text-violet-300'
|
||||
>
|
||||
Open Stream Widget <ExternalLink className='h-4 w-4' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handleSave()
|
||||
}}
|
||||
className='rounded-lg border bg-white p-6 dark:bg-zinc-800/20'
|
||||
>
|
||||
<h2 className='mb-4 font-semibold text-xl'>Social Links</h2>
|
||||
<div className='space-y-4'>
|
||||
<div>
|
||||
<Label className='mb-2' htmlFor={'ttv-url'}>
|
||||
Twitch URL
|
||||
</Label>
|
||||
<div className='flex items-center gap-2'>
|
||||
<SiTwitch className='h-5 w-5 text-purple-500' />
|
||||
<Input
|
||||
id={'ttv-url'}
|
||||
type='url'
|
||||
value={twitchUrl || ''}
|
||||
onChange={(e) => setTwitchUrl(e.target.value || null)}
|
||||
placeholder='https://twitch.tv/yourusername'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label className='mb-2' htmlFor={'yt-url'}>
|
||||
YouTube URL
|
||||
</Label>
|
||||
<div className='flex items-center gap-2'>
|
||||
<SiYoutube className='h-5 w-5 text-red-500' />
|
||||
<Input
|
||||
id={'yt-url'}
|
||||
type='url'
|
||||
value={youtubeUrl || ''}
|
||||
onChange={(e) => setYoutubeUrl(e.target.value || null)}
|
||||
placeholder='https://youtube.com/c/yourchannel'
|
||||
className='w-full rounded-md border border-gray-300 p-2 dark:border-zinc-700 dark:bg-zinc-900'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-6 flex justify-end gap-2'>
|
||||
<Button disabled={isPending} type={'submit'}>
|
||||
{isPending ? 'Saving...' : 'Save Changes'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import { ThemeToggle } from 'fumadocs-ui/components/layout/theme-toggle'
|
||||
import type { HomeLayoutProps } from 'fumadocs-ui/layouts/home'
|
||||
import type { LinkItemType } from 'fumadocs-ui/layouts/links'
|
||||
import { replaceOrDefault } from 'fumadocs-ui/layouts/shared'
|
||||
import { LogIn, LogOut, Tv, User } from 'lucide-react'
|
||||
import { LogIn, LogOut, Settings, Tv, User } from 'lucide-react'
|
||||
import { signIn, signOut, useSession } from 'next-auth/react'
|
||||
import Link from 'next/link'
|
||||
import { Fragment } from 'react'
|
||||
@@ -106,6 +106,15 @@ export function Header({
|
||||
<span>Profile</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href='/profile/settings'
|
||||
className='flex w-full items-center'
|
||||
>
|
||||
<Settings className='mr-2 h-4 w-4' />
|
||||
<span>Settings</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
href={`/stream-card/${session.user.discord_id}`}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { discord_router } from '@/server/api/routers/discord'
|
||||
import { history_router } from '@/server/api/routers/history'
|
||||
import { leaderboard_router } from '@/server/api/routers/leaderboard'
|
||||
import { playerStateRouter } from '@/server/api/routers/player-state'
|
||||
import { profileRouter } from '@/server/api/routers/profile'
|
||||
import { releasesRouter } from '@/server/api/routers/releases'
|
||||
import { createCallerFactory, createTRPCRouter } from '@/server/api/trpc'
|
||||
|
||||
@@ -17,6 +18,7 @@ export const appRouter = createTRPCRouter({
|
||||
discord: discord_router,
|
||||
leaderboard: leaderboard_router,
|
||||
playerState: playerStateRouter,
|
||||
profile: profileRouter,
|
||||
releases: releasesRouter,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { createTRPCRouter, publicProcedure } from '@/server/api/trpc'
|
||||
import { db } from '@/server/db'
|
||||
import { users } from '@/server/db/schema'
|
||||
import { discord_service } from '@/server/services/discord.service'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const discord_router = createTRPCRouter({
|
||||
@@ -10,6 +13,21 @@ export const discord_router = createTRPCRouter({
|
||||
})
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await discord_service.get_user_by_id(input.user_id)
|
||||
const discordUser = await discord_service.get_user_by_id(input.user_id)
|
||||
|
||||
// Get social media links from the database
|
||||
const userData = await db.query.users.findFirst({
|
||||
where: eq(users.discord_id, input.user_id),
|
||||
columns: {
|
||||
twitch_url: true,
|
||||
youtube_url: true,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
...discordUser,
|
||||
twitch_url: userData?.twitch_url || null,
|
||||
youtube_url: userData?.youtube_url || null,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -63,6 +63,8 @@ export const users = pgTable('user', (d) => ({
|
||||
.default(sql`CURRENT_TIMESTAMP`),
|
||||
image: d.varchar({ length: 255 }),
|
||||
discord_id: d.varchar({ length: 255 }),
|
||||
twitch_url: d.varchar({ length: 255 }),
|
||||
youtube_url: d.varchar({ length: 255 }),
|
||||
role: d.varchar({ length: 255 }).notNull().default('user'),
|
||||
}))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user