aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-04-09 15:49:24 +0100
committerMohamedBassem <me@mbassem.com>2024-04-09 15:49:24 +0100
commitfe13408831dce4bdae4911098d6079a097cae9e8 (patch)
tree228bbb192b3a0f3417a4526c382b0a3ddf7e04ff /apps/web
parent994691b02515dfb579a5c3618631065bd76b9e4b (diff)
downloadkarakeep-fe13408831dce4bdae4911098d6079a097cae9e8.tar.zst
feature(web): Allow uploading directly into lists/tags. Fixes #69
Diffstat (limited to 'apps/web')
-rw-r--r--apps/web/app/dashboard/archive/page.tsx1
-rw-r--r--apps/web/app/dashboard/bookmarks/page.tsx21
-rw-r--r--apps/web/app/dashboard/favourites/page.tsx1
-rw-r--r--apps/web/app/dashboard/layout.tsx2
-rw-r--r--apps/web/app/dashboard/lists/[listId]/page.tsx28
-rw-r--r--apps/web/app/dashboard/tags/[tagName]/page.tsx1
-rw-r--r--apps/web/components/dashboard/UploadDropzone.tsx25
-rw-r--r--apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx6
-rw-r--r--apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx38
-rw-r--r--apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx21
-rw-r--r--apps/web/components/dashboard/bookmarks/EditorCard.tsx7
-rw-r--r--apps/web/components/dashboard/bookmarks/TagsEditor.tsx14
-rw-r--r--apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx10
-rw-r--r--apps/web/lib/hooks/list-context.tsx21
14 files changed, 75 insertions, 121 deletions
diff --git a/apps/web/app/dashboard/archive/page.tsx b/apps/web/app/dashboard/archive/page.tsx
index b2b4df4f..26ec45e9 100644
--- a/apps/web/app/dashboard/archive/page.tsx
+++ b/apps/web/app/dashboard/archive/page.tsx
@@ -14,6 +14,7 @@ export default async function ArchivedBookmarkPage() {
}
query={{ archived: true }}
showDivider={true}
+ showEditorCard={true}
/>
);
}
diff --git a/apps/web/app/dashboard/bookmarks/page.tsx b/apps/web/app/dashboard/bookmarks/page.tsx
index 9907df4d..90f4f2cb 100644
--- a/apps/web/app/dashboard/bookmarks/page.tsx
+++ b/apps/web/app/dashboard/bookmarks/page.tsx
@@ -1,23 +1,20 @@
import React from "react";
import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks";
import TopNav from "@/components/dashboard/bookmarks/TopNav";
-import UploadDropzone from "@/components/dashboard/UploadDropzone";
import { Separator } from "@/components/ui/separator";
export default async function BookmarksPage() {
return (
<div>
- <UploadDropzone>
- <TopNav />
- <Separator />
- <div className="my-4 flex-1">
- <Bookmarks
- header={<p className="text-2xl">Bookmarks</p>}
- query={{ archived: false }}
- showEditorCard={true}
- />
- </div>
- </UploadDropzone>
+ <TopNav />
+ <Separator />
+ <div className="my-4 flex-1">
+ <Bookmarks
+ header={<p className="text-2xl">Bookmarks</p>}
+ query={{ archived: false }}
+ showEditorCard={true}
+ />
+ </div>
</div>
);
}
diff --git a/apps/web/app/dashboard/favourites/page.tsx b/apps/web/app/dashboard/favourites/page.tsx
index 13d793c6..423a8e69 100644
--- a/apps/web/app/dashboard/favourites/page.tsx
+++ b/apps/web/app/dashboard/favourites/page.tsx
@@ -6,6 +6,7 @@ export default async function FavouritesBookmarkPage() {
header={<p className="text-2xl">⭐️ Favourites</p>}
query={{ favourited: true }}
showDivider={true}
+ showEditorCard={true}
/>
);
}
diff --git a/apps/web/app/dashboard/layout.tsx b/apps/web/app/dashboard/layout.tsx
index 628c3a34..3b6908f8 100644
--- a/apps/web/app/dashboard/layout.tsx
+++ b/apps/web/app/dashboard/layout.tsx
@@ -21,7 +21,7 @@ export default async function Dashboard({
<MobileSidebar />
<Separator />
</div>
- <div className="container p-4">{children}</div>
+ <div className="container min-h-screen p-4">{children}</div>
</main>
</div>
);
diff --git a/apps/web/app/dashboard/lists/[listId]/page.tsx b/apps/web/app/dashboard/lists/[listId]/page.tsx
index 2b8025e5..49bf77f7 100644
--- a/apps/web/app/dashboard/lists/[listId]/page.tsx
+++ b/apps/web/app/dashboard/lists/[listId]/page.tsx
@@ -1,7 +1,6 @@
import { notFound } from "next/navigation";
import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks";
import DeleteListButton from "@/components/dashboard/lists/DeleteListButton";
-import { BookmarkListContextProvider } from "@/lib/hooks/list-context";
import { api } from "@/server/api/client";
import { TRPCError } from "@trpc/server";
@@ -23,19 +22,18 @@ export default async function ListPage({
}
return (
- <BookmarkListContextProvider listId={list.id}>
- <Bookmarks
- query={{ listId: list.id }}
- showDivider={true}
- header={
- <div className="flex justify-between">
- <span className="text-2xl">
- {list.icon} {list.name}
- </span>
- <DeleteListButton list={list} />
- </div>
- }
- />
- </BookmarkListContextProvider>
+ <Bookmarks
+ query={{ listId: list.id }}
+ showDivider={true}
+ showEditorCard={true}
+ header={
+ <div className="flex justify-between">
+ <span className="text-2xl">
+ {list.icon} {list.name}
+ </span>
+ <DeleteListButton list={list} />
+ </div>
+ }
+ />
);
}
diff --git a/apps/web/app/dashboard/tags/[tagName]/page.tsx b/apps/web/app/dashboard/tags/[tagName]/page.tsx
index 6bbb5234..b8bf351d 100644
--- a/apps/web/app/dashboard/tags/[tagName]/page.tsx
+++ b/apps/web/app/dashboard/tags/[tagName]/page.tsx
@@ -32,6 +32,7 @@ export default async function TagPage({
</div>
}
query={{ tagId: tag.id }}
+ showEditorCard={true}
/>
);
}
diff --git a/apps/web/components/dashboard/UploadDropzone.tsx b/apps/web/components/dashboard/UploadDropzone.tsx
index 70e6483a..bd08d2cf 100644
--- a/apps/web/components/dashboard/UploadDropzone.tsx
+++ b/apps/web/components/dashboard/UploadDropzone.tsx
@@ -1,12 +1,12 @@
"use client";
import React, { useState } from "react";
-import { api } from "@/lib/trpc";
import { cn } from "@/lib/utils";
import { useMutation } from "@tanstack/react-query";
import { TRPCClientError } from "@trpc/client";
import DropZone from "react-dropzone";
+import { useCreateBookmarkWithPostHook } from "@hoarder/shared-react/hooks/bookmarks";
import {
zUploadErrorSchema,
zUploadResponseSchema,
@@ -16,20 +16,15 @@ import LoadingSpinner from "../ui/spinner";
import { toast } from "../ui/use-toast";
function useUploadAsset({ onComplete }: { onComplete: () => void }) {
- const invalidateAllBookmarks =
- api.useUtils().bookmarks.getBookmarks.invalidate;
-
- const { mutateAsync: createBookmark } =
- api.bookmarks.createBookmark.useMutation({
- onSuccess: () => {
- toast({ description: "Bookmark uploaded" });
- invalidateAllBookmarks();
- onComplete();
- },
- onError: () => {
- toast({ description: "Something went wrong", variant: "destructive" });
- },
- });
+ const { mutateAsync: createBookmark } = useCreateBookmarkWithPostHook({
+ onSuccess: () => {
+ toast({ description: "Bookmark uploaded" });
+ onComplete();
+ },
+ onError: () => {
+ toast({ description: "Something went wrong", variant: "destructive" });
+ },
+ });
const { mutateAsync: runUpload } = useMutation({
mutationFn: async (file: File) => {
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx
index e3cfc796..a8ec1ab5 100644
--- a/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx
+++ b/apps/web/components/dashboard/bookmarks/BookmarkOptions.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useContext, useState } from "react";
+import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@@ -10,7 +10,6 @@ import {
} from "@/components/ui/dropdown-menu";
import { useToast } from "@/components/ui/use-toast";
import { useClientConfig } from "@/lib/clientConfig";
-import { BookmarkListContext } from "@/lib/hooks/list-context";
import {
Link,
List,
@@ -29,6 +28,7 @@ import {
useUpdateBookmark,
} from "@hoarder/shared-react/hooks//bookmarks";
import { useRemoveBookmarkFromList } from "@hoarder/shared-react/hooks//lists";
+import { useBookmarkGridContext } from "@hoarder/shared-react/hooks/bookmark-grid-context";
import { useAddToListModal } from "./AddToListModal";
import { BookmarkedTextEditor } from "./BookmarkedTextEditor";
@@ -48,7 +48,7 @@ export default function BookmarkOptions({ bookmark }: { bookmark: ZBookmark }) {
const [isTextEditorOpen, setTextEditorOpen] = useState(false);
- const { listId } = useContext(BookmarkListContext);
+ const { listId } = useBookmarkGridContext() ?? {};
const onError = () => {
toast({
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx b/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx
index eb618474..294f2b5a 100644
--- a/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx
+++ b/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx
@@ -21,7 +21,7 @@ export function BookmarkedTextEditor({
open,
setOpen,
}: {
- bookmark?: ZBookmark;
+ bookmark: ZBookmark;
open: boolean;
setOpen: (open: boolean) => void;
}) {
@@ -30,30 +30,14 @@ export function BookmarkedTextEditor({
bookmark && bookmark.content.type == "text" ? bookmark.content.text : "",
);
- const invalidateAllBookmarksCache =
- api.useUtils().bookmarks.getBookmarks.invalidate;
const invalidateOneBookmarksCache =
api.useUtils().bookmarks.getBookmark.invalidate;
- const { mutate: createBookmarkMutator, isPending: isCreationPending } =
- api.bookmarks.createBookmark.useMutation({
- onSuccess: () => {
- invalidateAllBookmarksCache();
- toast({
- description: "Note created!",
- });
- setOpen(false);
- setNoteText("");
- },
- onError: () => {
- toast({ description: "Something went wrong", variant: "destructive" });
- },
- });
- const { mutate: updateBookmarkMutator, isPending: isUpdatePending } =
+ const { mutate: updateBookmarkMutator, isPending } =
api.bookmarks.updateBookmarkText.useMutation({
onSuccess: () => {
invalidateOneBookmarksCache({
- bookmarkId: bookmark!.id,
+ bookmarkId: bookmark.id,
});
toast({
description: "Note updated!",
@@ -64,20 +48,12 @@ export function BookmarkedTextEditor({
toast({ description: "Something went wrong", variant: "destructive" });
},
});
- const isPending = isCreationPending || isUpdatePending;
const onSave = () => {
- if (isNewBookmark) {
- createBookmarkMutator({
- type: "text",
- text: noteText,
- });
- } else {
- updateBookmarkMutator({
- bookmarkId: bookmark.id,
- text: noteText,
- });
- }
+ updateBookmarkMutator({
+ bookmarkId: bookmark.id,
+ text: noteText,
+ });
};
return (
diff --git a/apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx b/apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx
index 048dab85..bace3435 100644
--- a/apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx
+++ b/apps/web/components/dashboard/bookmarks/BookmarksGrid.tsx
@@ -1,5 +1,3 @@
-"use client";
-
import { useMemo } from "react";
import { ActionButton } from "@/components/ui/action-button";
import tailwindConfig from "@/tailwind.config";
@@ -78,15 +76,16 @@ export default function BookmarksGrid({
{bookmarks.map((b) => renderBookmark(b))}
</Masonry>
{hasNextPage && (
- <ActionButton
- ignoreDemoMode={true}
- loading={isFetchingNextPage}
- onClick={() => fetchNextPage()}
- className="mx-auto w-min"
- variant="ghost"
- >
- Load More
- </ActionButton>
+ <div className="flex justify-center">
+ <ActionButton
+ ignoreDemoMode={true}
+ loading={isFetchingNextPage}
+ onClick={() => fetchNextPage()}
+ variant="ghost"
+ >
+ Load More
+ </ActionButton>
+ </div>
)}
</>
);
diff --git a/apps/web/components/dashboard/bookmarks/EditorCard.tsx b/apps/web/components/dashboard/bookmarks/EditorCard.tsx
index b9e46a30..10ad1f13 100644
--- a/apps/web/components/dashboard/bookmarks/EditorCard.tsx
+++ b/apps/web/components/dashboard/bookmarks/EditorCard.tsx
@@ -7,12 +7,13 @@ import { Separator } from "@/components/ui/separator";
import { Textarea } from "@/components/ui/textarea";
import { toast } from "@/components/ui/use-toast";
import { useClientConfig } from "@/lib/clientConfig";
-import { api } from "@/lib/trpc";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
+import { useCreateBookmarkWithPostHook } from "@hoarder/shared-react/hooks/bookmarks";
+
function useFocusOnKeyPress(inputRef: React.RefObject<HTMLTextAreaElement>) {
useEffect(() => {
function handleKeyPress(e: KeyboardEvent) {
@@ -47,10 +48,8 @@ export default function EditorCard({ className }: { className?: string }) {
useImperativeHandle(ref, () => inputRef.current);
useFocusOnKeyPress(inputRef);
- const invalidateBookmarksCache = api.useUtils().bookmarks.invalidate;
- const { mutate, isPending } = api.bookmarks.createBookmark.useMutation({
+ const { mutate, isPending } = useCreateBookmarkWithPostHook({
onSuccess: () => {
- invalidateBookmarksCache();
form.reset();
},
onError: () => {
diff --git a/apps/web/components/dashboard/bookmarks/TagsEditor.tsx b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx
index 91294b2e..c1a4cc70 100644
--- a/apps/web/components/dashboard/bookmarks/TagsEditor.tsx
+++ b/apps/web/components/dashboard/bookmarks/TagsEditor.tsx
@@ -8,6 +8,7 @@ import CreateableSelect from "react-select/creatable";
import type { ZBookmark } from "@hoarder/trpc/types/bookmarks";
import type { ZAttachedByEnum } from "@hoarder/trpc/types/tags";
+import { useUpdateBookmarkTags } from "@hoarder/shared-react/hooks/bookmarks";
interface EditableTag {
attachedBy: ZAttachedByEnum;
@@ -17,16 +18,12 @@ interface EditableTag {
export function TagsEditor({ bookmark }: { bookmark: ZBookmark }) {
const demoMode = !!useClientConfig().demoMode;
- const apiUtils = api.useUtils();
- const { mutate } = api.bookmarks.updateTags.useMutation({
+ const { mutate } = useUpdateBookmarkTags({
onSuccess: () => {
toast({
description: "Tags has been updated!",
});
- apiUtils.bookmarks.getBookmark.invalidate({ bookmarkId: bookmark.id });
- apiUtils.tags.list.invalidate();
- apiUtils.tags.get.invalidate();
},
onError: () => {
toast({
@@ -58,7 +55,7 @@ export function TagsEditor({ bookmark }: { bookmark: ZBookmark }) {
case "create-option": {
mutate({
bookmarkId: bookmark.id,
- attach: [{ tag: actionMeta.option.label }],
+ attach: [{ tagName: actionMeta.option.label }],
detach: [],
});
break;
@@ -68,7 +65,10 @@ export function TagsEditor({ bookmark }: { bookmark: ZBookmark }) {
mutate({
bookmarkId: bookmark.id,
attach: [
- { tag: actionMeta.option.label, tagId: actionMeta.option?.value },
+ {
+ tagName: actionMeta.option.label,
+ tagId: actionMeta.option?.value,
+ },
],
detach: [],
});
diff --git a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
index a344320e..fe69201c 100644
--- a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
+++ b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
@@ -1,11 +1,13 @@
"use client";
+import UploadDropzone from "@/components/dashboard/UploadDropzone";
import { api } from "@/lib/trpc";
import type {
ZGetBookmarksRequest,
ZGetBookmarksResponse,
} from "@hoarder/trpc/types/bookmarks";
+import { BookmarkGridContextProvider } from "@hoarder/shared-react/hooks/bookmark-grid-context";
import BookmarksGrid from "./BookmarksGrid";
@@ -29,7 +31,7 @@ export default function UpdatableBookmarksGrid({
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
- return (
+ const grid = (
<BookmarksGrid
bookmarks={data!.pages.flatMap((b) => b.bookmarks)}
hasNextPage={hasNextPage}
@@ -38,4 +40,10 @@ export default function UpdatableBookmarksGrid({
showEditorCard={showEditorCard}
/>
);
+
+ return (
+ <BookmarkGridContextProvider query={query}>
+ {showEditorCard ? <UploadDropzone>{grid}</UploadDropzone> : grid}
+ </BookmarkGridContextProvider>
+ );
}
diff --git a/apps/web/lib/hooks/list-context.tsx b/apps/web/lib/hooks/list-context.tsx
deleted file mode 100644
index cb8a20b2..00000000
--- a/apps/web/lib/hooks/list-context.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-"use client";
-
-import { createContext } from "react";
-
-export const BookmarkListContext = createContext<{
- listId: string | undefined;
-}>({ listId: undefined });
-
-export function BookmarkListContextProvider({
- listId,
- children,
-}: {
- listId: string;
- children: React.ReactNode;
-}) {
- return (
- <BookmarkListContext.Provider value={{ listId }}>
- {children}
- </BookmarkListContext.Provider>
- );
-}