Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/(app)/saved/_client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import ArticleLoading from "@/components/ArticlePreview/ArticleLoading";

const SavedPosts = () => {
const {
data: bookmarks,
data: bookmarksData,
refetch,
status: bookmarkStatus,
} = api.post.myBookmarks.useQuery();
} = api.post.myBookmarks.useQuery({});
const bookmarks = bookmarksData?.bookmarks || [];

const { mutate: bookmark } = api.post.bookmark.useMutation({
onSettled() {
Expand Down
14 changes: 13 additions & 1 deletion components/ArticlePreview/ArticlePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from "@headlessui/react";
import { api } from "@/server/trpc/react";
import { signIn, useSession } from "next-auth/react";
import { toast } from "sonner";

type ButtonOptions = {
label: string;
Expand Down Expand Up @@ -55,8 +56,12 @@ const ArticlePreview: NextPage<Props> = ({
bookmarkedInitialState = false,
}) => {
const [bookmarked, setIsBookmarked] = useState(bookmarkedInitialState);
const howManySavedToShow = 3;
const { data: bookmarksData, refetch } = api.post.myBookmarks.useQuery({
limit: howManySavedToShow,
});
const { data: session } = useSession();

const bookmarks = bookmarksData?.bookmarks;
const dateTime = Temporal.Instant.from(new Date(date).toISOString());
const readableDate = dateTime.toLocaleString(["en-IE"], {
year: "numeric",
Expand All @@ -69,6 +74,13 @@ const ArticlePreview: NextPage<Props> = ({
onSettled() {
setIsBookmarked((isBookmarked) => !isBookmarked);
},
onSuccess: () => {
refetch();
},
onError: (error) => {
toast.error("Failed to update bookmark");
Sentry.captureException(error);
},
});

const bookmarkPost = async (postId: string, setBookmarked = true) => {
Expand Down
12 changes: 6 additions & 6 deletions components/SideBar/SideBarSavedPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import SideBarSavedArticlePreview from "./SideBarSavedArticlePreview";
import Link from "next/link";

export default React.memo(function SideBarSavedPosts() {
let { data: bookmarks } = api.post.myBookmarks.useQuery();
const { status: bookmarkStatus } = api.post.myBookmarks.useQuery();

const howManySavedToShow = 3;
const totalNumberSaved = bookmarks?.length;
const { data: bookmarksData, status: bookmarkStatus } =
api.post.myBookmarks.useQuery({
limit: howManySavedToShow,
});

// @TODO query the backend to get the last 3 instead of slice
if (bookmarks) bookmarks = bookmarks.slice(0, howManySavedToShow);
const totalNumberSaved = bookmarksData?.totalCount || 0;
const bookmarks = bookmarksData?.bookmarks || [];

return (
<div className="w-full">
Expand Down
4 changes: 4 additions & 0 deletions schema/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ export const GetSinglePostSchema = z.object({
export const GetByIdSchema = z.object({
id: z.string(),
});

export const GetLimitSidePosts = z.object({
limit: z.number().optional(),
});
71 changes: 43 additions & 28 deletions server/api/router/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
LikePostSchema,
BookmarkPostSchema,
GetByIdSchema,
GetLimitSidePosts,
} from "../../../schema/post";
import { removeMarkdown } from "../../../utils/removeMarkdown";
import { bookmark, like, post, post_tag, tag, user } from "@/server/db/schema";
Expand Down Expand Up @@ -438,37 +439,51 @@ export const postRouter = createTRPCRouter({

return currentPost;
}),
myBookmarks: protectedProcedure.query(async ({ ctx }) => {
const response = await ctx.db.query.bookmark.findMany({
columns: {
id: true,
},
where: (bookmarks, { eq }) => eq(bookmarks.userId, ctx.session.user.id),
with: {
post: {
columns: {
id: true,
title: true,
excerpt: true,
updatedAt: true,
published: true,
readTimeMins: true,
slug: true,
},
with: {
user: {
columns: {
name: true,
username: true,
image: true,
myBookmarks: protectedProcedure
.input(GetLimitSidePosts)
.query(async ({ ctx, input }) => {
const limit = input?.limit ?? undefined;

const response = await ctx.db.query.bookmark.findMany({
columns: {
id: true,
},
where: (bookmarks, { eq }) => eq(bookmarks.userId, ctx.session.user.id),
with: {
post: {
columns: {
id: true,
title: true,
excerpt: true,
updatedAt: true,
published: true,
readTimeMins: true,
slug: true,
},
with: {
user: {
columns: {
name: true,
username: true,
image: true,
},
},
},
},
},
},
orderBy: (bookmarks, { desc }) => [desc(bookmarks.id)],
});
orderBy: (bookmarks, { desc }) => [desc(bookmarks.id)],
});

return response.map(({ id, post }) => ({ bookmarkId: id, ...post }));
}),
const totalCount = response.length;

const bookmarksResponse = response.slice(0, limit || response.length);

return {
totalCount,
bookmarks: bookmarksResponse.map(({ id, post }) => ({
bookmarkId: id,
...post,
})),
};
}),
Comment on lines +442 to +488
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

LGTM: Improved myBookmarks procedure with pagination support.

The changes to the myBookmarks procedure align well with the PR objectives. The new structure allows for pagination and provides more detailed information about bookmarks, which should improve the sidebar's bookmark display functionality.

Consider optimizing for large datasets.

While the current implementation works, using slice to limit results after fetching all records may not be efficient for large datasets. Consider using database-level pagination for better performance.

Here's a suggestion to optimize the query:

const bookmarksResponse = await ctx.db.query.bookmark.findMany({
  // ... existing query options ...
  limit: limit,
});

const totalCount = await ctx.db.query.bookmark.count({
  where: (bookmarks, { eq }) => eq(bookmarks.userId, ctx.session.user.id),
});

return {
  totalCount,
  bookmarks: bookmarksResponse.map(({ id, post }) => ({
    bookmarkId: id,
    ...post,
  })),
};

Add input validation for the limit parameter.

It's important to validate the limit parameter to ensure it's a positive number. This will prevent potential issues with negative or zero limits.

Consider adding input validation:

const limit = input?.limit && input.limit > 0 ? input.limit : undefined;

});