diff --git a/src/app/(drift)/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx b/src/app/(drift)/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx index a3611416..a671f9dc 100644 --- a/src/app/(drift)/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx +++ b/src/app/(drift)/(posts)/post/[id]/components/post-files/password-modal-wrapper.tsx @@ -44,7 +44,12 @@ const PasswordModalWrapper = ({ setPost, postId, authorId }: Props) => { return } - const data = await res.json() + // TODO: properly check type + const data = (await res.json()) as { + post: PostWithFilesAndAuthor + error?: string + } + if (data) { if (data.error) { setToast({ diff --git a/src/app/(drift)/providers.tsx b/src/app/(drift)/providers.tsx index 95c1d99a..0046e33c 100644 --- a/src/app/(drift)/providers.tsx +++ b/src/app/(drift)/providers.tsx @@ -18,17 +18,30 @@ export function Providers({ children }: PropsWithChildren) { ) } +export type ApiResponse = { + data: T + error: never +} | { + data: never + error: string +} + +async function fetcher(url: string): Promise { + const response = await fetch(url) + const data: ApiResponse = await response.json() as ApiResponse + + if (data.error) { + throw new Error(data.error) + } + + return data.data +} + function SWRProvider({ children }: PropsWithChildren) { return ( { - const data = await fetch(url).then((res) => res.json()) - if (data.error) { - throw new Error(data.error) - } - return data - }, + fetcher, keepPreviousData: true }} > diff --git a/src/app/components/badges/visibility-control/index.tsx b/src/app/components/badges/visibility-control/index.tsx index 39410c00..c710b287 100644 --- a/src/app/components/badges/visibility-control/index.tsx +++ b/src/app/components/badges/visibility-control/index.tsx @@ -10,6 +10,7 @@ import { useRouter } from "next/navigation" import { useSessionSWR } from "@lib/use-session-swr" import { fetchWithUser } from "src/app/lib/fetch-with-user" import FadeIn from "@components/fade-in" +import { PostWithFiles } from "@lib/server/prisma" type Props = { authorId: string @@ -42,7 +43,7 @@ function VisibilityControl({ }) if (res.ok) { - const json = await res.json() + const json = await res.json() as PostWithFiles setVisibility(json.visibility) router.refresh() setToast({ diff --git a/src/app/components/button-dropdown/index.tsx b/src/app/components/button-dropdown/index.tsx index 3d4eee93..e47e89c9 100644 --- a/src/app/components/button-dropdown/index.tsx +++ b/src/app/components/button-dropdown/index.tsx @@ -1,5 +1,5 @@ import Button from "@components/button" -import React from "react" +import React, { ReactNode } from "react" import styles from "./dropdown.module.css" import * as DropdownMenu from "@radix-ui/react-dropdown-menu" import { ArrowDown } from "react-feather" @@ -14,35 +14,37 @@ type ButtonDropdownProps = Props & Attrs const ButtonDropdown: React.FC< React.PropsWithChildren > = ({ type, ...props }) => { - if (!Array.isArray(props.children)) { - return null - } - return (
- {props.children[0]} - -
) diff --git a/src/app/components/post-list/index.tsx b/src/app/components/post-list/index.tsx index d9bb96a2..f5b6730c 100644 --- a/src/app/components/post-list/index.tsx +++ b/src/app/components/post-list/index.tsx @@ -59,7 +59,7 @@ const PostList = ({ } } ) - const json = await res.json() + const json = await res.json() as PostWithFiles[] setPosts(json) setSearching(false) } diff --git a/src/app/hooks/swr/use-api-tokens.ts b/src/app/hooks/swr/use-api-tokens.ts index 7f8619ad..a58d93ff 100644 --- a/src/app/hooks/swr/use-api-tokens.ts +++ b/src/app/hooks/swr/use-api-tokens.ts @@ -1,4 +1,5 @@ import { ApiToken } from "@prisma/client" +import { ApiResponse } from "src/app/(drift)/providers" import useSWR from "swr" type ConvertDateToString = { @@ -35,15 +36,15 @@ export function useApiTokens({ userId, initialTokens }: UseApiTokens) { } ) - const response = await res.json() + const response = await res.json() as ApiResponse if (response.error) { throw new Error(response.error) return } - mutate([...(data || []), response]) + mutate([...(data || []), response.data]) - return response as SerializedApiToken + return response.data } const expireToken = async (id: string) => { diff --git a/src/lib/gist/fetch.ts b/src/lib/gist/fetch.ts index 579bf7d5..31e0f76c 100644 --- a/src/lib/gist/fetch.ts +++ b/src/lib/gist/fetch.ts @@ -1,3 +1,6 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck + import { Gist, GistFile } from "./types" async function fetchHelper(response: Response): Promise { @@ -6,7 +9,7 @@ async function fetchHelper(response: Response): Promise { .get("content-type") ?.includes("application/json") const err = await (isJson ? response.json() : response.text()) - throw new Error(err) + throw new Error(err as string) } return response } diff --git a/src/pages/api/user/tokens.ts b/src/pages/api/user/tokens.ts index dc7531a6..834a65a5 100644 --- a/src/pages/api/user/tokens.ts +++ b/src/pages/api/user/tokens.ts @@ -25,7 +25,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } }) - return res.json(tokens) + return res.json({ data: tokens }) } case "POST": { const name = parseQueryParam(req.query.name) @@ -33,7 +33,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { return res.status(400).json({ error: "Missing token name" }) } const token = await createApiToken(userId, name) - return res.json(token) + return res.json({ data: token }) } case "DELETE": { const tokenId = parseQueryParam(req.query.tokenId) diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 00000000..9bea8c1a --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,2 @@ + +import "@total-typescript/ts-reset"; diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts index cfe646d8..0b1a22ed 100644 --- a/types/next-auth.d.ts +++ b/types/next-auth.d.ts @@ -1,5 +1,3 @@ -import "@total-typescript/ts-reset"; - // eslint-disable-next-line @typescript-eslint/no-unused-vars import type { User } from "next-auth" // eslint-disable-next-line @typescript-eslint/no-unused-vars