Skip to content

Commit

Permalink
feat: add user auth
Browse files Browse the repository at this point in the history
  • Loading branch information
CaliCastle committed Jun 3, 2023
1 parent 4daa674 commit 40ce8f6
Show file tree
Hide file tree
Showing 10 changed files with 705 additions and 47 deletions.
11 changes: 11 additions & 0 deletions app/(main)/(auth)/sign-in/[[...sign-in]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { SignIn } from '@clerk/nextjs'

import { Container } from '~/components/ui/Container'

export default function Page() {
return (
<Container className="mt-24 flex items-center justify-center">
<SignIn />
</Container>
)
}
74 changes: 72 additions & 2 deletions app/(main)/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { SignedIn, SignedOut, SignInButton, UserButton } from '@clerk/nextjs'
import { clsxm } from '@zolplay/utils'
import {
AnimatePresence,
Expand All @@ -13,10 +14,12 @@ import React from 'react'
import { Activity } from '~/app/(main)/Activity'
import { NavigationBar } from '~/app/(main)/NavigationBar'
import { ThemeSwitcher } from '~/app/(main)/ThemeSwitcher'
import { UserArrowLeftIcon } from '~/assets'
import { Avatar } from '~/components/Avatar'
import { Container } from '~/components/ui/Container'
import { Tooltip } from '~/components/ui/Tooltip'
import { url } from '~/lib'
import { clamp } from '~/lib/math'

export function Header() {
const isHomePage = usePathname() === '/'

Expand Down Expand Up @@ -258,10 +261,11 @@ export function Header() {
<NavigationBar.Desktop className="pointer-events-auto relative z-50 hidden md:block" />
</div>
<motion.div
className="flex justify-end md:flex-1"
className="flex justify-end gap-3 md:flex-1"
initial={{ opacity: 0, y: -20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
>
<UserInfo />
<div className="pointer-events-auto">
<ThemeSwitcher />
</div>
Expand Down Expand Up @@ -290,3 +294,69 @@ export function Header() {
</>
)
}

function UserInfo() {
const [tooltipOpen, setTooltipOpen] = React.useState(false)
const pathname = usePathname()

return (
<AnimatePresence>
<SignedIn>
<motion.div
className="pointer-events-auto flex h-10 items-center"
initial={{ opacity: 0, x: 25 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 25 }}
>
<UserButton
afterSignOutUrl={url('/').href}
appearance={{
elements: {
avatarBox: 'w-10 h-10',
},
}}
/>
</motion.div>
</SignedIn>
<SignedOut>
<motion.div
className="pointer-events-auto"
initial={{ opacity: 0, x: 25 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: 25 }}
>
<Tooltip.Provider disableHoverableContent>
<Tooltip.Root open={tooltipOpen} onOpenChange={setTooltipOpen}>
<SignInButton mode="modal" redirectUrl={url(pathname).href}>
<Tooltip.Trigger asChild>
<button
type="button"
className="group h-10 rounded-full bg-gradient-to-b from-zinc-50/50 to-white/90 px-3 text-sm shadow-lg shadow-zinc-800/5 ring-1 ring-zinc-900/5 backdrop-blur transition dark:from-zinc-900/50 dark:to-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
>
<UserArrowLeftIcon className="h-5 w-5" />
</button>
</Tooltip.Trigger>
</SignInButton>

<AnimatePresence>
{tooltipOpen && (
<Tooltip.Portal forceMount>
<Tooltip.Content asChild>
<motion.div
initial={{ opacity: 0, scale: 0.96 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
>
登录
</motion.div>
</Tooltip.Content>
</Tooltip.Portal>
)}
</AnimatePresence>
</Tooltip.Root>
</Tooltip.Provider>
</motion.div>
</SignedOut>
</AnimatePresence>
)
}
35 changes: 19 additions & 16 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '~/app/globals.css'

import { ClerkProvider } from '@clerk/nextjs'
import { type Metadata } from 'next'

import { ThemeProvider } from '~/app/(main)/ThemeProvider'
Expand Down Expand Up @@ -63,21 +64,23 @@ export default function RootLayout({
children: React.ReactNode
}) {
return (
<html
lang="zh-CN"
className={`${sansFont.variable} m-0 h-full p-0 font-sans antialiased`}
suppressHydrationWarning
>
<body className="flex h-full flex-col">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
<ClerkProvider>
<html
lang="zh-CN"
className={`${sansFont.variable} m-0 h-full p-0 font-sans antialiased`}
suppressHydrationWarning
>
<body className="flex h-full flex-col">
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
</ClerkProvider>
)
}
22 changes: 22 additions & 0 deletions assets/icons/UserArrowLeftIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { type IconProps } from '~/assets'

export function UserArrowLeftIcon(props: IconProps = {}) {
return (
<svg
width="1em"
height="1em"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M11 15H7C4.79086 15 3 16.7909 3 19C3 20.1046 3.89543 21 5 21H15M16.8744 13C16.2164 13.4935 15.6221 14.066 15.1049 14.7043C15.035 14.7906 15 14.8953 15 15M16.8744 17C16.2164 16.5065 15.6221 15.934 15.1049 15.2957C15.035 15.2094 15 15.1047 15 15M15 15H21M15 7C15 9.20914 13.2091 11 11 11C8.79086 11 7 9.20914 7 7C7 4.79086 8.79086 3 11 3C13.2091 3 15 4.79086 15 7Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
22 changes: 22 additions & 0 deletions assets/icons/XSquareIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { type IconProps } from '~/assets'

export function XSquareIcon(props: IconProps = {}) {
return (
<svg
width="1em"
height="1em"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="M9.00004 15L12 12M12 12L15 8.99998M12 12L9.00004 8.99998M12 12L15 15M12 20.9999C9.20435 20.9999 7.80653 20.9999 6.7039 20.5432C5.23373 19.9342 4.06569 18.7661 3.45672 17.296C3 16.1934 3 14.7955 3 11.9999C3 9.20423 3 7.8064 3.45672 6.70378C4.06569 5.23361 5.23373 4.06556 6.7039 3.4566C7.80653 2.99988 9.20435 2.99988 12 2.99988C14.7956 2.99988 16.1935 2.99988 17.2961 3.4566C18.7663 4.06556 19.9343 5.23361 20.5433 6.70378C21 7.8064 21 9.20423 21 11.9999C21 14.7955 21 16.1934 20.5433 17.296C19.9343 18.7661 18.7663 19.9342 17.2961 20.5432C16.1935 20.9999 14.7956 20.9999 12 20.9999Z"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
}
2 changes: 2 additions & 0 deletions assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export { TelegramIcon } from './icons/TelegramIcon'
export { TiltedSendIcon } from './icons/TiltedSendIcon'
export { TwitterIcon } from './icons/TwitterIcon'
export { UFOIcon } from './icons/UFOIcon'
export { UserArrowLeftIcon } from './icons/UserArrowLeftIcon'
export { UserSecurityIcon } from './icons/UserSecurityIcon'
export { UsersIcon } from './icons/UsersIcon'
export { UTurnLeftIcon } from './icons/UTurnLeftIcon'
export { XSquareIcon } from './icons/XSquareIcon'
export { YouTubeIcon } from './icons/YouTubeIcon'
128 changes: 128 additions & 0 deletions components/ui/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use client'

import * as DialogPrimitive from '@radix-ui/react-dialog'
import { clsxm } from '@zolplay/utils'
import * as React from 'react'

import { XSquareIcon } from '~/assets'

const Root = DialogPrimitive.Root

const Trigger = DialogPrimitive.Trigger

const DialogPortal = ({
className,
children,
...props
}: DialogPrimitive.DialogPortalProps) => (
<DialogPrimitive.Portal className={clsxm(className)} {...props}>
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
{children}
</div>
</DialogPrimitive.Portal>
)
DialogPortal.displayName = DialogPrimitive.Portal.displayName

const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={clsxm(
'bg-background/80 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in fixed inset-0 z-50 backdrop-blur-sm transition-all duration-100',
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName

const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={clsxm(
'bg-background animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0 fixed z-50 grid w-full gap-4 rounded-b-lg border p-6 shadow-lg sm:max-w-lg sm:rounded-lg',
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
<XSquareIcon className="h-6 w-6" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName

const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={clsxm(
'flex flex-col space-y-1.5 text-center sm:text-left',
className
)}
{...props}
/>
)
DialogHeader.displayName = 'DialogHeader'

const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={clsxm(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className
)}
{...props}
/>
)
DialogFooter.displayName = 'DialogFooter'

const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={clsxm(
'text-lg font-semibold leading-none tracking-tight',
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName

const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={clsxm('text-muted-foreground text-sm', className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName

export const Dialog = {
Root,
Trigger,
Content: DialogContent,
Header: DialogHeader,
Footer: DialogFooter,
Title: DialogTitle,
Description: DialogDescription,
} as const
Loading

0 comments on commit 40ce8f6

Please sign in to comment.