Linting, component clean up

This commit is contained in:
Max Leiter 2023-01-12 23:56:04 -08:00
parent ba732dcd71
commit e49ca2e749
25 changed files with 97 additions and 87 deletions

View file

@ -27,6 +27,7 @@ function Auth({
const signText = signingIn ? "In" : "Up" const signText = signingIn ? "In" : "Up"
const [username, setUsername] = useState("") const [username, setUsername] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [submitting, setSubmitting] = useState(false)
const queryParams = useSearchParams() const queryParams = useSearchParams()
useEffect(() => { useEffect(() => {
@ -40,6 +41,7 @@ function Auth({
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) { async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault() event.preventDefault()
setSubmitting(true)
const res = await signIn("credentials", { const res = await signIn("credentials", {
username, username,
@ -54,6 +56,7 @@ function Auth({
type: "error", type: "error",
message: res.error message: res.error
}) })
setSubmitting(false)
} else { } else {
startTransition(() => { startTransition(() => {
router.push("/new") router.push("/new")
@ -126,7 +129,7 @@ function Auth({
width="100%" width="100%"
aria-label="Password" aria-label="Password"
/> />
<Button width={"100%"} type="submit"> <Button width={"100%"} type="submit" loading={submitting}>
Sign {signText} Sign {signText}
</Button> </Button>
{isGithubEnabled ? <hr style={{ width: "100%" }} /> : null} {isGithubEnabled ? <hr style={{ width: "100%" }} /> : null}

View file

@ -9,7 +9,8 @@ import { Spinner } from "@components/spinner"
import Link from "next/link" import Link from "next/link"
function FileDropdown({ function FileDropdown({
files, loading files,
loading
}: { }: {
files: Pick<PostWithFiles, "files">["files"] files: Pick<PostWithFiles, "files">["files"]
loading?: boolean loading?: boolean

View file

@ -12,9 +12,7 @@ type Props = {
title?: string title?: string
} }
function MarkdownPreview({ function MarkdownPreview({ height = 500, fileId, content = "", title }: Props) {
height = 500, fileId, content = "", title
}: Props) {
const [preview, setPreview] = useState<string>(content) const [preview, setPreview] = useState<string>(content)
const [isLoading, setIsLoading] = useState<boolean>(true) const [isLoading, setIsLoading] = useState<boolean>(true)
useEffect(() => { useEffect(() => {
@ -61,7 +59,8 @@ function MarkdownPreview({
export default memo(MarkdownPreview) export default memo(MarkdownPreview)
export function StaticPreview({ export function StaticPreview({
preview, height = 500 preview,
height = 500
}: { }: {
preview: string preview: string
height: string | number height: string | number
@ -72,6 +71,7 @@ export function StaticPreview({
dangerouslySetInnerHTML={{ __html: preview }} dangerouslySetInnerHTML={{ __html: preview }}
style={{ style={{
height height
}} /> }}
/>
) )
} }

View file

@ -17,7 +17,8 @@ function Description({ onChange, description }: props) {
label="Description" label="Description"
maxLength={256} maxLength={256}
width="100%" width="100%"
placeholder="An optional description of your post" /> placeholder="An optional description of your post"
/>
</div> </div>
) )
} }

View file

@ -15,7 +15,8 @@ import clsx from "clsx"
// TODO: clean up // TODO: clean up
function FormattingIcons({ function FormattingIcons({
textareaRef, className textareaRef,
className
}: { }: {
textareaRef?: RefObject<TextareaMarkdownRef> textareaRef?: RefObject<TextareaMarkdownRef>
className?: string className?: string
@ -26,7 +27,8 @@ function FormattingIcons({
const handleLinkClick = () => textareaRef?.current?.trigger("link") const handleLinkClick = () => textareaRef?.current?.trigger("link")
const handleImageClick = () => textareaRef?.current?.trigger("image") const handleImageClick = () => textareaRef?.current?.trigger("image")
const handleCodeClick = () => textareaRef?.current?.trigger("code") const handleCodeClick = () => textareaRef?.current?.trigger("code")
const handleListClick = () => textareaRef?.current?.trigger("unordered-list") const handleListClick = () =>
textareaRef?.current?.trigger("unordered-list")
return [ return [
{ {
icon: <Bold />, icon: <Bold />,
@ -81,7 +83,8 @@ function FormattingIcons({
iconRight={icon} iconRight={icon}
onMouseDown={(e) => e.preventDefault()} onMouseDown={(e) => e.preventDefault()}
onClick={action} onClick={action}
buttonType="secondary" /> buttonType="secondary"
/>
</Tooltip> </Tooltip>
))} ))}
</div> </div>

View file

@ -31,7 +31,8 @@ function Title({ onChange, title }: props) {
label="Title" label="Title"
className={styles.labelAndInput} className={styles.labelAndInput}
style={{ width: "100%" }} style={{ width: "100%" }}
labelClassName={styles.labelAndInput} /> labelClassName={styles.labelAndInput}
/>
</div> </div>
) )
} }

View file

@ -1,3 +1,5 @@
export default function NewLayout({ children }: { children: React.ReactNode }) { import { ChildrenProps } from "src/app/providers"
export default function NewLayout({ children }: ChildrenProps) {
return <>{children}</> return <>{children}</>
} }

View file

@ -1,7 +1,6 @@
import CreatedAgoBadge from "@components/badges/created-ago-badge" import CreatedAgoBadge from "@components/badges/created-ago-badge"
import ExpirationBadge from "@components/badges/expiration-badge" import ExpirationBadge from "@components/badges/expiration-badge"
import VisibilityBadge from "@components/badges/visibility-badge" import VisibilityBadge from "@components/badges/visibility-badge"
import Link from "@components/link"
import Skeleton from "@components/skeleton" import Skeleton from "@components/skeleton"
import styles from "./title.module.css" import styles from "./title.module.css"

View file

@ -5,16 +5,16 @@ import { PostButtons } from "./components/header/post-buttons"
import styles from "./layout.module.css" import styles from "./layout.module.css"
import { PostTitle } from "./components/header/title" import { PostTitle } from "./components/header/title"
import { getPost } from "./get-post" import { getPost } from "./get-post"
import { PropsWithChildren } from "react"
export default async function PostLayout({ export default async function PostLayout({
children, children,
params params
}: { }: PropsWithChildren<{
params: { params: {
id: string id: string
} }
children: React.ReactNode }>) {
}) {
const { post } = (await getPost(params.id)) as { const { post } = (await getPost(params.id)) as {
post: PostWithFilesAndAuthor post: PostWithFilesAndAuthor
} }

View file

@ -1,11 +1,10 @@
import { getCurrentUser } from "@lib/server/session" import { getCurrentUser } from "@lib/server/session"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { PropsWithChildren } from "react"
export default async function AdminLayout({ export default async function AdminLayout({
children children
}: { }: PropsWithChildren<unknown>) {
children: React.ReactNode
}) {
const user = await getCurrentUser() const user = await getCurrentUser()
const isAdmin = user?.role === "admin" const isAdmin = user?.role === "admin"

View file

@ -2,7 +2,6 @@ import React from "react"
import styles from "./badge.module.css" import styles from "./badge.module.css"
type BadgeProps = { type BadgeProps = {
type: "primary" | "secondary" | "error" | "warning" type: "primary" | "secondary" | "error" | "warning"
children: React.ReactNode
} & React.HTMLAttributes<HTMLDivElement> } & React.HTMLAttributes<HTMLDivElement>
const Badge = React.forwardRef<HTMLDivElement, BadgeProps>( const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(

View file

@ -16,11 +16,9 @@ type Props = {
visibility: string visibility: string
} }
const VisibilityControl = ({ function VisibilityControl({
authorId, authorId, postId, visibility: postVisibility
postId, }: Props) {
visibility: postVisibility
}: Props) => {
const { session } = useSessionSWR() const { session } = useSessionSWR()
const isAuthor = session?.user && session?.user?.id === authorId const isAuthor = session?.user && session?.user?.id === authorId
const [visibility, setVisibility] = useState<string>(postVisibility) const [visibility, setVisibility] = useState<string>(postVisibility)
@ -32,16 +30,13 @@ const VisibilityControl = ({
const sendRequest = useCallback( const sendRequest = useCallback(
async (visibility: string, password?: string) => { async (visibility: string, password?: string) => {
const res = await fetchWithUser( const res = await fetchWithUser(`/api/post/${postId}`, {
`/api/post/${postId}`,
{
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
}, },
body: JSON.stringify({ visibility, password }) body: JSON.stringify({ visibility, password })
} })
)
if (res.ok) { if (res.ok) {
const json = await res.json() const json = await res.json()
@ -129,8 +124,7 @@ const VisibilityControl = ({
creating={true} creating={true}
isOpen={passwordModalVisible} isOpen={passwordModalVisible}
onClose={onClosePasswordModal} onClose={onClosePasswordModal}
onSubmit={submitPassword} onSubmit={submitPassword} />
/>
</> </>
) )
} }

View file

@ -8,7 +8,7 @@ type Props = {
height?: number | string height?: number | string
} }
type Attrs = Omit<React.HTMLAttributes<any>, keyof Props> type Attrs = Omit<React.HTMLAttributes<HTMLDivElement>, keyof Props>
type ButtonDropdownProps = Props & Attrs type ButtonDropdownProps = Props & Attrs
const ButtonDropdown: React.FC< const ButtonDropdown: React.FC<

View file

@ -5,7 +5,6 @@ export default function ButtonGroup({
verticalIfMobile, verticalIfMobile,
...props ...props
}: { }: {
children: React.ReactNode | React.ReactNode[]
verticalIfMobile?: boolean verticalIfMobile?: boolean
} & React.HTMLAttributes<HTMLDivElement>) { } & React.HTMLAttributes<HTMLDivElement>) {
return ( return (

View file

@ -1,19 +1,14 @@
// https://www.joshwcomeau.com/snippets/react-components/fade-in/ // https://www.joshwcomeau.com/snippets/react-components/fade-in/
import styles from "./fade.module.css" import styles from "./fade.module.css"
const FadeIn = ({ function FadeIn({
duration = 300, duration = 300, delay = 0, children, as, ...delegated
delay = 0,
children,
as,
...delegated
}: { }: {
duration?: number duration?: number
delay?: number delay?: number
children: React.ReactNode children: React.ReactNode
as?: React.ElementType as?: React.ElementType
[key: string]: any } & React.HTMLAttributes<HTMLElement>) {
}) => {
const Element = as || "div" const Element = as || "div"
return ( return (
<Element <Element

View file

@ -0,0 +1,8 @@
"use client"
import { PropsWithChildren } from "react"
import styles from "./page.module.css"
export default function Layout({ children }: PropsWithChildren<unknown>) {
return <div className={styles.page}>{children}</div>
}

View file

@ -1,5 +0,0 @@
import styles from "./page.module.css"
export default function Page({ children }: { children: React.ReactNode }) {
return <div className={styles.page}>{children}</div>
}

View file

@ -5,7 +5,7 @@ import Skeleton from "@components/skeleton"
export const ListItemSkeleton = () => ( export const ListItemSkeleton = () => (
<li> <li>
<Card style={{ overflowY: "scroll" }}> <Card style={{ overflowY: "scroll" }}>
<div style={{display: 'flex', gap: 16}}> <div style={{ display: "flex", gap: 16 }}>
<div className={styles.title}> <div className={styles.title}>
{/* title */} {/* title */}
<Skeleton width={80} height={32} /> <Skeleton width={80} height={32} />

View file

@ -1,29 +1,28 @@
import "@styles/globals.css" import "@styles/globals.css"
import { Providers } from "./providers" import { Providers } from "./providers"
import Page from "@components/page" import Layout from "@components/layout"
import { Toasts } from "@components/toasts" import { Toasts } from "@components/toasts"
import Header from "@components/header" import Header from "@components/header"
import { Inter } from "@next/font/google" import { Inter } from "@next/font/google"
import { PropsWithChildren } from "react"
const inter = Inter({ subsets: ["latin"], variable: "--inter-font" }) const inter = Inter({ subsets: ["latin"], variable: "--inter-font" })
interface RootLayoutProps { export default async function RootLayout({
children: React.ReactNode children
} }: PropsWithChildren<unknown>) {
export default async function RootLayout({ children }: RootLayoutProps) {
return ( return (
// suppressHydrationWarning is required because of next-themes // suppressHydrationWarning is required because of next-themes
<html lang="en" className={inter.variable} suppressHydrationWarning> <html lang="en" className={inter.variable} suppressHydrationWarning>
<head /> <head />
<body> <body>
<Toasts /> <Toasts />
<Page> <Layout>
<Providers> <Providers>
<Header /> <Header />
{children} {children}
</Providers> </Providers>
</Page> </Layout>
</body> </body>
</html> </html>
) )

View file

@ -3,11 +3,27 @@
import * as RadixTooltip from "@radix-ui/react-tooltip" import * as RadixTooltip from "@radix-ui/react-tooltip"
import { SessionProvider } from "next-auth/react" import { SessionProvider } from "next-auth/react"
import { ThemeProvider } from "next-themes" import { ThemeProvider } from "next-themes"
import { PropsWithChildren } from "react"
import { SWRConfig } from "swr" import { SWRConfig } from "swr"
export function Providers({ children }: { children: React.ReactNode }) { export type ChildrenProps = {
children?: React.ReactNode
}
export function Providers({ children }: ChildrenProps) {
return ( return (
<SessionProvider> <SessionProvider>
<RadixTooltip.Provider delayDuration={200}>
<ThemeProvider enableSystem defaultTheme="dark">
<SWRProvider>{children}</SWRProvider>
</ThemeProvider>
</RadixTooltip.Provider>
</SessionProvider>
)
}
function SWRProvider({ children }: PropsWithChildren<unknown>) {
return (
<SWRConfig <SWRConfig
value={{ value={{
fetcher: async (url: string) => { fetcher: async (url: string) => {
@ -20,12 +36,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
keepPreviousData: true keepPreviousData: true
}} }}
> >
<RadixTooltip.Provider delayDuration={200}>
<ThemeProvider enableSystem defaultTheme="dark">
{children} {children}
</ThemeProvider>
</RadixTooltip.Provider>
</SWRConfig> </SWRConfig>
</SessionProvider>
) )
} }

View file

@ -7,7 +7,7 @@
--small-gap: 4rem; --small-gap: 4rem;
--big-gap: 4rem; --big-gap: 4rem;
--main-content: 55rem; --main-content: 50rem;
--radius: 8px; --radius: 8px;
--inline-radius: 5px; --inline-radius: 5px;

View file

@ -49,6 +49,7 @@ const providers = () => {
// @ts-expect-error TODO: fix types // @ts-expect-error TODO: fix types
credentials: credentialsOptions() as unknown, credentials: credentialsOptions() as unknown,
async authorize(credentials) { async authorize(credentials) {
console.log("credentials")
if (!credentials || !credentials.username || !credentials.password) { if (!credentials || !credentials.username || !credentials.password) {
throw new Error("Missing credentials") throw new Error("Missing credentials")
} }