aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.eslintrc.js3
-rw-r--r--package.json1
-rw-r--r--packages/web/app/dashboard/bookmarks/components/AddLink.tsx26
-rw-r--r--packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx92
-rw-r--r--packages/web/app/dashboard/settings/components/AddApiKey.tsx18
-rw-r--r--packages/web/app/dashboard/settings/components/DeleteApiKey.tsx24
-rw-r--r--packages/web/app/layout.tsx7
-rw-r--r--packages/web/lib/providers.tsx33
-rw-r--r--packages/web/lib/trpc.tsx19
-rw-r--r--packages/web/package.json3
-rw-r--r--yarn.lock67
11 files changed, 197 insertions, 96 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index a78fa42f..eccbab28 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -13,8 +13,9 @@ module.exports = {
"plugin:@typescript-eslint/recommended",
"next",
"prettier",
+ "plugin:@tanstack/eslint-plugin-query/recommended",
],
- plugins: ["tailwindcss", "@typescript-eslint"],
+ plugins: ["tailwindcss", "@typescript-eslint", "@tanstack/query"],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
diff --git a/package.json b/package.json
index ef92e2f1..c9b15e86 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"eslint-plugin-react-hooks": "^4.6.0"
},
"devDependencies": {
+ "@tanstack/eslint-plugin-query": "^5.20.1",
"@types/node": "^20",
"es-errors": "^1.3.0",
"eslint": "^8.56.0",
diff --git a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx
index e8ecec35..34f043e7 100644
--- a/packages/web/app/dashboard/bookmarks/components/AddLink.tsx
+++ b/packages/web/app/dashboard/bookmarks/components/AddLink.tsx
@@ -17,21 +17,19 @@ const formSchema = z.object({
export default function AddLink() {
const router = useRouter();
+ const bookmarkLinkMutator = api.bookmarks.bookmarkLink.useMutation({
+ onSuccess: () => {
+ router.refresh();
+ },
+ onError: () => {
+ toast({ description: "Something went wrong", variant: "destructive" });
+ },
+ });
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
- async function onSubmit(value: z.infer<typeof formSchema>) {
- try {
- await api.bookmarks.bookmarkLink.mutate({ url: value.url, type: "link" });
- } catch (e) {
- toast({ description: "Something went wrong", variant: "destructive" });
- return;
- }
- router.refresh();
- }
-
const onError: SubmitErrorHandler<z.infer<typeof formSchema>> = (errors) => {
toast({
description: Object.values(errors)
@@ -43,7 +41,13 @@ export default function AddLink() {
return (
<Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit, onError)}>
+ <form
+ onSubmit={form.handleSubmit(
+ (value) =>
+ bookmarkLinkMutator.mutate({ url: value.url, type: "link" }),
+ onError,
+ )}
+ >
<div className="container flex w-full items-center space-x-2 py-4">
<FormField
control={form.control}
diff --git a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx
index 4123dc36..1360f966 100644
--- a/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx
+++ b/packages/web/app/dashboard/bookmarks/components/BookmarkOptions.tsx
@@ -18,61 +18,45 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) {
const router = useRouter();
const linkId = bookmark.id;
- const unbookmarkLink = async () => {
- try {
- await api.bookmarks.deleteBookmark.mutate({
- bookmarkId: linkId,
- });
-
+ const onError = () => {
+ toast({
+ variant: "destructive",
+ title: "Something went wrong",
+ description: "There was a problem with your request.",
+ });
+ };
+ const onSettled = () => {
+ router.refresh();
+ };
+ const deleteBookmarkMutator = api.bookmarks.deleteBookmark.useMutation({
+ onSuccess: () => {
toast({
description: "The bookmark has been deleted!",
});
- } catch (e) {
- toast({
- variant: "destructive",
- title: "Something went wrong",
- description: "There was a problem with your request.",
- });
- }
-
- router.refresh();
- };
+ },
+ onError,
+ onSettled,
+ });
- const updateBookmark = async (req: ZUpdateBookmarksRequest) => {
- try {
- await api.bookmarks.updateBookmark.mutate(req);
+ const updateBookmarkMutator = api.bookmarks.updateBookmark.useMutation({
+ onSuccess: () => {
toast({
description: "The bookmark has been updated!",
});
- } catch (e) {
- toast({
- variant: "destructive",
- title: "Something went wrong",
- description: "There was a problem with your request.",
- });
- }
+ },
+ onError,
+ onSettled,
+ });
- router.refresh();
- };
-
- const crawlBookmark = async () => {
- try {
- await api.bookmarks.recrawlBookmark.mutate({
- bookmarkId: linkId,
- });
+ const crawlBookmarkMutator = api.bookmarks.recrawlBookmark.useMutation({
+ onSuccess: () => {
toast({
description: "Re-fetch has been enqueued!",
});
- } catch (e) {
- toast({
- variant: "destructive",
- title: "Something went wrong",
- description: "There was a problem with your request.",
- });
- }
-
- router.refresh();
- };
+ },
+ onError,
+ onSettled,
+ });
return (
<DropdownMenu>
@@ -84,7 +68,7 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) {
<DropdownMenuContent className="w-fit">
<DropdownMenuItem
onClick={() =>
- updateBookmark({
+ updateBookmarkMutator.mutate({
bookmarkId: linkId,
favourited: !bookmark.favourited,
})
@@ -95,17 +79,29 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) {
</DropdownMenuItem>
<DropdownMenuItem
onClick={() =>
- updateBookmark({ bookmarkId: linkId, archived: !bookmark.archived })
+ updateBookmarkMutator.mutate({
+ bookmarkId: linkId,
+ archived: !bookmark.archived,
+ })
}
>
<Archive className="mr-2 size-4" />
<span>{bookmark.archived ? "Un-archive" : "Archive"}</span>
</DropdownMenuItem>
- <DropdownMenuItem onClick={crawlBookmark}>
+ <DropdownMenuItem
+ onClick={() =>
+ crawlBookmarkMutator.mutate({ bookmarkId: bookmark.id })
+ }
+ >
<RotateCw className="mr-2 size-4" />
<span>Refresh</span>
</DropdownMenuItem>
- <DropdownMenuItem className="text-destructive" onClick={unbookmarkLink}>
+ <DropdownMenuItem
+ className="text-destructive"
+ onClick={() =>
+ deleteBookmarkMutator.mutate({ bookmarkId: bookmark.id })
+ }
+ >
<Trash2 className="mr-2 size-4" />
<span>Delete</span>
</DropdownMenuItem>
diff --git a/packages/web/app/dashboard/settings/components/AddApiKey.tsx b/packages/web/app/dashboard/settings/components/AddApiKey.tsx
index f4f2894c..c438f4b1 100644
--- a/packages/web/app/dashboard/settings/components/AddApiKey.tsx
+++ b/packages/web/app/dashboard/settings/components/AddApiKey.tsx
@@ -53,20 +53,22 @@ function AddApiKeyForm({ onSuccess }: { onSuccess: (key: string) => void }) {
name: z.string(),
});
const router = useRouter();
+ const mutator = api.apiKeys.create.useMutation({
+ onSuccess: (resp) => {
+ onSuccess(resp.key);
+ router.refresh();
+ },
+ onError: () => {
+ toast({ description: "Something went wrong", variant: "destructive" });
+ },
+ });
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
async function onSubmit(value: z.infer<typeof formSchema>) {
- try {
- const resp = await api.apiKeys.create.mutate({ name: value.name });
- onSuccess(resp.key);
- } catch (e) {
- toast({ description: "Something went wrong", variant: "destructive" });
- return;
- }
- router.refresh();
+ mutator.mutate({ name: value.name });
}
const onError: SubmitErrorHandler<z.infer<typeof formSchema>> = (errors) => {
diff --git a/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx b/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx
index 715b7a2c..bc3e3c92 100644
--- a/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx
+++ b/packages/web/app/dashboard/settings/components/DeleteApiKey.tsx
@@ -13,9 +13,9 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
-import { api } from "@/lib/trpc";
import { useRouter } from "next/navigation";
import { toast } from "@/components/ui/use-toast";
+import { api } from "@/lib/trpc";
export default function DeleteApiKey({
name,
@@ -25,13 +25,15 @@ export default function DeleteApiKey({
id: string;
}) {
const router = useRouter();
- const deleteKey = async () => {
- await api.apiKeys.revoke.mutate({ id });
- toast({
- description: "Key was successfully deleted",
- });
- router.refresh();
- };
+ const mutator = api.apiKeys.revoke.useMutation({
+ onSuccess: () => {
+ toast({
+ description: "Key was successfully deleted",
+ });
+ router.refresh();
+ },
+ });
+
return (
<Dialog>
<DialogTrigger asChild>
@@ -54,7 +56,11 @@ export default function DeleteApiKey({
</Button>
</DialogClose>
<DialogClose asChild>
- <Button type="button" variant="destructive" onClick={deleteKey}>
+ <Button
+ type="button"
+ variant="destructive"
+ onClick={() => mutator.mutate({ id })}
+ >
Delete
</Button>
</DialogClose>
diff --git a/packages/web/app/layout.tsx b/packages/web/app/layout.tsx
index a2d34046..f1971b20 100644
--- a/packages/web/app/layout.tsx
+++ b/packages/web/app/layout.tsx
@@ -3,6 +3,8 @@ import { Inter } from "next/font/google";
import "./globals.css";
import React from "react";
import { Toaster } from "@/components/ui/toaster";
+import Providers from "@/lib/providers";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const inter = Inter({ subsets: ["latin"] });
@@ -19,7 +21,10 @@ export default function RootLayout({
return (
<html lang="en">
<body className={inter.className}>
- {children}
+ <Providers>
+ {children}
+ <ReactQueryDevtools initialIsOpen={false} />
+ </Providers>
<Toaster />
</body>
</html>
diff --git a/packages/web/lib/providers.tsx b/packages/web/lib/providers.tsx
new file mode 100644
index 00000000..e81645dd
--- /dev/null
+++ b/packages/web/lib/providers.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import React, { useState } from "react";
+import { api } from "./trpc";
+import { loggerLink } from "@trpc/client";
+import { httpBatchLink } from "@trpc/client";
+
+export default function Providers({ children }: { children: React.ReactNode }) {
+ const [queryClient] = React.useState(() => new QueryClient());
+
+ const [trpcClient] = useState(() =>
+ api.createClient({
+ links: [
+ loggerLink({
+ enabled: (op) =>
+ process.env.NODE_ENV === "development" ||
+ (op.direction === "down" && op.result instanceof Error),
+ }),
+ httpBatchLink({
+ // TODO: Change this to be a full URL exposed as a client side setting
+ url: `/api/trpc`,
+ }),
+ ],
+ }),
+ );
+
+ return (
+ <api.Provider client={trpcClient} queryClient={queryClient}>
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+ </api.Provider>
+ );
+}
diff --git a/packages/web/lib/trpc.tsx b/packages/web/lib/trpc.tsx
index 540c6ab5..aa246047 100644
--- a/packages/web/lib/trpc.tsx
+++ b/packages/web/lib/trpc.tsx
@@ -1,20 +1,5 @@
"use client";
-import { httpBatchLink } from "@trpc/client";
import type { AppRouter } from "@/server/api/routers/_app";
+import { createTRPCReact } from "@trpc/react-query";
-import { loggerLink } from "@trpc/client";
-import { createTRPCClient } from "@trpc/client";
-
-export const api = createTRPCClient<AppRouter>({
- links: [
- loggerLink({
- enabled: (op) =>
- process.env.NODE_ENV === "development" ||
- (op.direction === "down" && op.result instanceof Error),
- }),
- httpBatchLink({
- // TODO: Change this to be a full URL exposed as a client side setting
- url: `/api/trpc`,
- }),
- ],
-});
+export const api = createTRPCReact<AppRouter>();
diff --git a/packages/web/package.json b/packages/web/package.json
index d66a3fe7..997e4042 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -20,8 +20,11 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@remember/db": "0.1.0",
+ "@tanstack/react-query": "^5.20.5",
+ "@tanstack/react-query-devtools": "^5.21.0",
"@trpc/client": "11.0.0-next-beta.274",
"@trpc/next": "11.0.0-next-beta.274",
+ "@trpc/react-query": "^11.0.0-next-beta.289",
"@trpc/server": "11.0.0-next-beta.274",
"bcrypt": "^5.1.1",
"class-variance-authority": "^0.7.0",
diff --git a/yarn.lock b/yarn.lock
index 592ed96e..34c9c8f8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1649,8 +1649,11 @@ __metadata:
"@radix-ui/react-slot": "npm:^1.0.2"
"@radix-ui/react-toast": "npm:^1.1.5"
"@remember/db": "npm:0.1.0"
+ "@tanstack/react-query": "npm:^5.20.5"
+ "@tanstack/react-query-devtools": "npm:^5.21.0"
"@trpc/client": "npm:11.0.0-next-beta.274"
"@trpc/next": "npm:11.0.0-next-beta.274"
+ "@trpc/react-query": "npm:^11.0.0-next-beta.289"
"@trpc/server": "npm:11.0.0-next-beta.274"
"@types/bcrypt": "npm:^5.0.2"
"@types/react": "npm:^18"
@@ -1973,6 +1976,54 @@ __metadata:
languageName: node
linkType: hard
+"@tanstack/eslint-plugin-query@npm:^5.20.1":
+ version: 5.20.1
+ resolution: "@tanstack/eslint-plugin-query@npm:5.20.1"
+ dependencies:
+ "@typescript-eslint/utils": "npm:^6.20.0"
+ peerDependencies:
+ eslint: ^8.0.0
+ checksum: 10c0/318bdc734497a126873ac92f6f61b0e2f771b4d339edbc6d9c28c00852a100aba7b424213fb639bbbc6f9768cf1b75392d5c802afa9c1e82567d65fe8d217aeb
+ languageName: node
+ linkType: hard
+
+"@tanstack/query-core@npm:5.20.5":
+ version: 5.20.5
+ resolution: "@tanstack/query-core@npm:5.20.5"
+ checksum: 10c0/e41b4961a3a2518536c72c1522d8512561645af2c2fe8ae49583820b3da6c4fe87112480d617202873ade9f6aa43aa78f986b5b7bbb3f0c5714fe536183de56e
+ languageName: node
+ linkType: hard
+
+"@tanstack/query-devtools@npm:5.21.0":
+ version: 5.21.0
+ resolution: "@tanstack/query-devtools@npm:5.21.0"
+ checksum: 10c0/27f035660d2e537a28880b6a54c6602ec417c861d0f94a9dd090f7aa161dd890eb9e4edd4d41d9ea7a5821d5e02e4f9796258eced511dc5a767340536a7be366
+ languageName: node
+ linkType: hard
+
+"@tanstack/react-query-devtools@npm:^5.21.0":
+ version: 5.21.0
+ resolution: "@tanstack/react-query-devtools@npm:5.21.0"
+ dependencies:
+ "@tanstack/query-devtools": "npm:5.21.0"
+ peerDependencies:
+ "@tanstack/react-query": ^5.20.5
+ react: ^18.0.0
+ checksum: 10c0/8247ab372d9d115b3058b36e8124f8ca9c81872334a13995b946f0511d2d16a0e7174d6c8fda1a3a2cb535cf55acc9d75b40f15c6f8ecdea135bb5aac5988953
+ languageName: node
+ linkType: hard
+
+"@tanstack/react-query@npm:^5.20.5":
+ version: 5.20.5
+ resolution: "@tanstack/react-query@npm:5.20.5"
+ dependencies:
+ "@tanstack/query-core": "npm:5.20.5"
+ peerDependencies:
+ react: ^18.0.0
+ checksum: 10c0/ca6cf3ee9702c15ed69246b5be04390910bfe8f04c1ebbab19a84102f610cdc7be6b8cc5ad8bc76ac59bc321c8d6d65cf96329c4e600e5e5938d6859162371d0
+ languageName: node
+ linkType: hard
+
"@tootallnate/quickjs-emscripten@npm:^0.23.0":
version: 0.23.0
resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0"
@@ -2009,6 +2060,19 @@ __metadata:
languageName: node
linkType: hard
+"@trpc/react-query@npm:^11.0.0-next-beta.289":
+ version: 11.0.0-next-beta.289
+ resolution: "@trpc/react-query@npm:11.0.0-next-beta.289"
+ peerDependencies:
+ "@tanstack/react-query": ^5.0.0
+ "@trpc/client": 11.0.0-next-beta.289+27a9183d8
+ "@trpc/server": 11.0.0-next-beta.289+27a9183d8
+ react: ">=18.2.0"
+ react-dom: ">=18.2.0"
+ checksum: 10c0/9fd35444e22a379ec7f03f896ac25ff7a5f82e303948195ed491eade616cbfdb007e578bf84c4f73f8df787cfdbb937cf8a6dbbd35306591b6e3ac99e5066b7a
+ languageName: node
+ linkType: hard
+
"@trpc/server@npm:11.0.0-next-beta.274":
version: 11.0.0-next-beta.274
resolution: "@trpc/server@npm:11.0.0-next-beta.274"
@@ -2396,7 +2460,7 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/utils@npm:6.21.0":
+"@typescript-eslint/utils@npm:6.21.0, @typescript-eslint/utils@npm:^6.20.0":
version: 6.21.0
resolution: "@typescript-eslint/utils@npm:6.21.0"
dependencies:
@@ -7998,6 +8062,7 @@ __metadata:
resolution: "remember@workspace:."
dependencies:
"@next/eslint-plugin-next": "npm:^14.1.0"
+ "@tanstack/eslint-plugin-query": "npm:^5.20.1"
"@types/node": "npm:^20"
"@typescript-eslint/eslint-plugin": "npm:^6.21.0"
"@typescript-eslint/parser": "npm:^6.21.0"