Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes Refetch of SideBar Posts on Bookmarked #1112

Merged
merged 6 commits into from
Oct 14, 2024

Conversation

revanthnetha
Copy link
Contributor

✨ Codu Pull Request 💻

Fixes #834

Approach:

  • So whenever in article something is bookmarked or unbookmarked, refetch of the myBookmarks i.e.SidePosts would happen.
  • In order to do this, when mutate post in Article Preview is successfull, I have called the myBookmarks Query, So the recent data shown in SideBarPosts.
  • Refetch Syncs Data: When refetch() is called in Component 1 (e.g., after a bookmark mutation), Component 2 will also display the most recent data because React Query syncs the cache automatically across components.

Todo :

  • Instead of slicing the SidePosts in FE. I have modified the Trpc Query to take the limit and render the limit bookmarks SidePosts.
  • we can also do in way by making two queries one for fetching the bookmarks until the limit and other for total Count. But I have found making oneQuery to fetch all bookmarks and then slicing is optimized as this query : ctx.db.query.bookmark.count(..) is not supporting.

Happy to do changes if needed :)

Any Breaking changes

  • None

Associated Screenshots

  • None

@revanthnetha revanthnetha requested a review from a team as a code owner October 12, 2024 18:22
Copy link

vercel bot commented Oct 12, 2024

@revanthnetha is attempting to deploy a commit to the Codú Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Oct 12, 2024

Walkthrough

The changes primarily involve updates to the SavedPosts, ArticlePreview, and SideBarSavedPosts components to enhance bookmark handling. The myBookmarks query now accepts a limit parameter, allowing for pagination. Additionally, a new schema for GetLimitSidePosts has been introduced to support this functionality. The overall structure of the components remains intact, with improvements in variable naming and data access patterns to streamline bookmark management.

Changes

File Change Summary
app/(app)/saved/_client.tsx Updated SavedPosts and SideBarSavedPosts for bookmark handling; renamed variables and modified query access.
schema/post.ts Introduced new schema GetLimitSidePosts with an optional limit property for side posts retrieval.
server/api/router/post.ts Updated postRouter to include GetLimitSidePosts in myBookmarks procedure, enabling pagination and total count.

Assessment against linked issues

Objective Addressed Explanation
Recent bookmarks should auto add/remove without refresh. (#834)
Toast notification for unsuccessful bookmark actions. (#834) No implementation for toast notifications present.

🐇 In the meadow, bookmarks hop,
With a click, they never stop!
Auto-update, swift as light,
No more waiting, what a delight!
So come and see, the changes bloom,
In the garden of code, there's always room! 🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 630562a and cda1902.

📒 Files selected for processing (2)
  • components/ArticlePreview/ArticlePreview.tsx (3 hunks)
  • components/SideBar/SideBarSavedPosts.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/SideBar/SideBarSavedPosts.tsx
🧰 Additional context used
🔇 Additional comments (4)
components/ArticlePreview/ArticlePreview.tsx (4)

20-20: Import statement for 'toast' is correct

The import of toast from "sonner" is appropriate for handling user notifications.


59-62: Previous suggestion still applies

The previous review comment regarding the use of queryClient.invalidateQueries() instead of refetch() is still valid.


77-79: Previous suggestion still applies

The previous review comment regarding the use of queryClient.invalidateQueries() instead of refetch() is still valid.


80-83: Error handling is properly implemented

The onError callback effectively handles errors by displaying a toast notification and logging the exception to Sentry.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

Hello @{{ contributor }}, thanks for opening your first Pull Request. The maintainers will review this Pull Request and provide feedback as soon as possible. Keep up the great work!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (4)
app/(app)/saved/_client.tsx (1)

15-15: Good addition of fallback for bookmarks data.

The introduction of const bookmarks = bookmarksData?.bookmarks || [] is a solid improvement. It ensures that bookmarks is always an array, even if bookmarksData is undefined or lacks a bookmarks property. This enhances the robustness of the code and prevents potential runtime errors.

For even better type safety and clarity, consider using a type assertion:

const bookmarks = (bookmarksData?.bookmarks ?? []) as Array<BookmarkType>;

Replace BookmarkType with the actual type of your bookmark objects. This approach maintains the fallback behavior while providing clearer type information to TypeScript.

components/SideBar/SideBarSavedPosts.tsx (2)

9-11: Approved: Query optimization with limit parameter

The addition of the limit parameter to the myBookmarks query aligns well with the PR objective of optimizing bookmark fetching. This change should improve performance by reducing the amount of data transferred.

A minor suggestion for improvement:

Consider extracting the limit value to a constant at the top of the component for easier maintenance:

const BOOKMARKS_LIMIT = 3;

// Then use it in the query
const { data: bookmarksData } = api.post.myBookmarks.useQuery({
  limit: BOOKMARKS_LIMIT,
});

This makes it easier to adjust the limit in the future if needed.


15-16: Approved: Improved data structure handling

The updates to totalNumberSaved and bookmarks reflect the new data structure returned by the query. The use of optional chaining and fallback values improves the robustness of the code.

For consistency, consider using the BOOKMARKS_LIMIT constant (suggested earlier) instead of the hardcoded value:

const totalNumberSaved = bookmarksData?.totalCount || 0;
const bookmarks = bookmarksData?.bookmarks || [];
const howManySavedToShow = BOOKMARKS_LIMIT;

This ensures that the number of bookmarks shown is always consistent with the query limit.

components/ArticlePreview/ArticlePreview.tsx (1)

62-62: Remove unused variable bookmarks

The bookmarks variable is declared but not used within this component. Removing it will clean up the code and prevent potential confusion.

Apply this change to remove the unused variable:

-  const bookmarks = bookmarksData?.bookmarks;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d45d070 and 630562a.

📒 Files selected for processing (5)
  • app/(app)/saved/_client.tsx (1 hunks)
  • components/ArticlePreview/ArticlePreview.tsx (2 hunks)
  • components/SideBar/SideBarSavedPosts.tsx (1 hunks)
  • schema/post.ts (1 hunks)
  • server/api/router/post.ts (2 hunks)
🧰 Additional context used
🔇 Additional comments (7)
schema/post.ts (1)

80-82: Approve new schema with suggestions for improvement

The addition of GetLimitSidePosts schema is a good approach for handling limited sidebar posts queries. However, consider the following improvements:

  1. Add a maximum value for the limit to prevent excessive data fetching:

    limit: z.number().optional().max(50) // or any other appropriate maximum
  2. Consider renaming the schema to be more specific, e.g., GetLimitedSidebarPostsSchema.

Let's verify the usage of this new schema in the codebase:

✅ Verification successful

Verified usage of GetLimitSidePosts schema

The GetLimitSidePosts schema is correctly utilized in server/api/router/post.ts as intended for handling procedure inputs. No additional usages were found, ensuring that the schema's scope is appropriately confined.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of GetLimitSidePosts schema

# Search for import statements
echo "Checking import statements:"
rg --type typescript "import.*GetLimitSidePosts.*from.*schema/post"

# Search for direct usage of the schema
echo "\nChecking direct usage:"
rg --type typescript "GetLimitSidePosts"

# Search for potential trpc procedure definitions using this schema
echo "\nChecking potential trpc procedure definitions:"
rg --type typescript "input:\s*GetLimitSidePosts"

Length of output: 498


Script:

#!/bin/bash
# Description: Check for usage of GetLimitSidePosts schema

# Search for import statements
echo "Checking import statements:"
rg --type ts "import.*GetLimitSidePosts.*from.*schema/post"

# Search for direct usage of the schema
echo "\nChecking direct usage:"
rg --type ts "GetLimitSidePosts"

# Search for potential trpc procedure definitions using this schema
echo "\nChecking potential trpc procedure definitions:"
rg --type ts "input:\s*GetLimitSidePosts"

Length of output: 531

app/(app)/saved/_client.tsx (3)

11-11: Improved variable naming enhances code clarity.

The change from bookmarks to bookmarksData for the destructured query result is a good improvement. It clearly distinguishes between the raw query data and the processed bookmark information, making the code more readable and maintainable.


14-14: Clarify the purpose of the empty object in the query call.

An empty object {} has been added as an argument to api.post.myBookmarks.useQuery(). While this suggests that the query might now accept parameters, no actual parameters are being passed. This could be related to the mentioned change in handling bookmark limits, but the implementation seems incomplete.

Could you please clarify:

  1. The purpose of this empty object?
  2. Are there plans to add specific parameters here, such as a limit for the number of bookmarks to fetch?

If parameters are intended to be used, consider adding them explicitly:

api.post.myBookmarks.useQuery({ limit: 10 }); // Example with a limit

If no parameters are needed, it might be clearer to remove the empty object:

api.post.myBookmarks.useQuery();

Line range hint 1-93: Overall improvements align with PR objectives, but some goals remain unaddressed.

The changes in this file contribute to better code organization and lay groundwork for potential enhancements in bookmark handling. The variable renaming and introduction of a fallback for bookmark data improve code clarity and robustness.

However, the primary PR objective of automatically updating bookmarks when added or removed is not directly addressed in this file. Consider the following suggestions:

  1. Implement real-time updates: Use React Query's invalidateQueries or similar mechanism to trigger a refetch of bookmarks when a bookmark is added or removed in other components.

  2. Add error handling: Implement toast notifications for failed bookmark actions as mentioned in the PR objectives.

  3. Optimize performance: If not implemented elsewhere, consider adding pagination or limiting the number of bookmarks fetched, as mentioned in the PR summary.

To ensure these objectives are met, please provide information on how the automatic updating of bookmarks is implemented across components. Are there any other files that handle this functionality?

components/SideBar/SideBarSavedPosts.tsx (1)

Line range hint 1-85: Summary of changes and suggestions

The changes in this file successfully implement the PR objectives of optimizing bookmark fetching and updating the UI. The new data structure is handled correctly, and the code is more robust with the use of optional chaining and fallback values.

Key points:

  1. The query optimization with the limit parameter is a good performance improvement.
  2. The handling of the new data structure is implemented correctly.
  3. There's a potential issue with an additional query that needs clarification (line 12).

Suggestions for improvement:

  1. Extract the limit value to a constant for better maintainability.
  2. Clarify or optimize the additional query on line 12.
  3. Ensure consistency in using the limit value throughout the component.

Overall, the changes are well-implemented, with room for minor improvements in code maintainability and consistency.

To ensure these changes don't introduce any regressions, consider running the following tests:

  1. Verify that the sidebar displays the correct number of bookmarks (should be 3 or less).
  2. Check that the total count of bookmarks is displayed correctly.
  3. Ensure that adding or removing a bookmark updates the sidebar correctly without a page refresh.
#!/bin/bash
# Search for test files related to bookmarks or the sidebar
rg --type typescript --type tsx -g "*test*" -g "*spec*" "bookmark|sidebar" -l

This script will help locate relevant test files that should be updated or run to verify the changes.

server/api/router/post.ts (1)

14-14: LGTM: New import added for GetLimitSidePosts.

The addition of GetLimitSidePosts to the import statement is appropriate for the changes made to the myBookmarks procedure.

components/ArticlePreview/ArticlePreview.tsx (1)

75-77: Ensure cache invalidation after bookmark mutation

By invalidating the myBookmarks query cache using queryClient.invalidateQueries() after a successful bookmark mutation, you ensure that all components relying on this data will reflect the updated bookmarks list. This is the recommended approach for cache management in React Query.

const { data: bookmarksData } = api.post.myBookmarks.useQuery({
limit: 3,
});
const { status: bookmarkStatus } = api.post.myBookmarks.useQuery({});
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Redundant myBookmarks.useQuery Calls Detected

Multiple instances of myBookmarks.useQuery are found, some of which may be redundant and could impact performance negatively:

  • components/SideBar/SideBarSavedPosts.tsx

    • const { data: bookmarksData } = api.post.myBookmarks.useQuery({ limit: 3 });
    • const { status: bookmarkStatus } = api.post.myBookmarks.useQuery({});
  • app/(app)/saved/_client.tsx

    • const { data: bookmarksData } = api.post.myBookmarks.useQuery({});

Consider consolidating these queries or utilizing the enabled option to prevent unnecessary executions.

🔗 Analysis chain

Clarify the purpose of the additional query

There's an additional myBookmarks query without parameters, from which only the status is used. This might be redundant and could potentially impact performance negatively.

Could you clarify the purpose of this second query? If it's necessary, consider the following suggestions:

  1. Combine both queries into a single one if possible.
  2. If separate queries are required, use the enabled option in the second query to prevent it from running unnecessarily.

Here's a script to check for other usages of myBookmarks query in the codebase:

This will help us understand if there are other places where this query is used differently.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of myBookmarks query
rg --type typescript --type tsx "myBookmarks\.useQuery" -C 5

Length of output: 114


Script:

#!/bin/bash
# Search for other usages of myBookmarks query in .ts and .tsx files
rg "myBookmarks\.useQuery" --glob "*.ts" --glob "*.tsx" -C 5

Length of output: 2612

Comment on lines +442 to +488
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,
})),
};
}),
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;

Comment on lines 58 to 60
const { data: bookmarksData, refetch } = api.post.myBookmarks.useQuery({
limit: 3,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider invalidating queries instead of refetching bookmarksData

The bookmarksData and bookmarks variables are not used within this component. Fetching them here solely to access the refetch function may be unnecessary. Instead, you can use the queryClient.invalidateQueries() method provided by React Query to invalidate the myBookmarks query after a successful mutation. This approach avoids fetching unused data and ensures that any component depending on myBookmarks will refetch the updated data.

Apply this change to implement the suggestion:

+import { useQueryClient } from "@tanstack/react-query";
+// Add the import statement for useQueryClient

 const ArticlePreview: NextPage<Props> = ({
   // props
 }) => {
+  const queryClient = useQueryClient();
   const [bookmarked, setIsBookmarked] = useState(bookmarkedInitialState);
-  const { data: bookmarksData, refetch } = api.post.myBookmarks.useQuery({
-    limit: 3,
-  });
   const { data: session } = useSession();
-  const bookmarks = bookmarksData?.bookmarks;
   const dateTime = Temporal.Instant.from(new Date(date).toISOString());

   const { mutate: bookmark, status: bookmarkStatus } =
     api.post.bookmark.useMutation({
       onSettled() {
         setIsBookmarked((isBookmarked) => !isBookmarked);
       },
       onSuccess: () => {
-        refetch();
+        queryClient.invalidateQueries(
+          api.post.myBookmarks.getQueryKey({ limit: 3 })
+        );
+        // Invalidate the 'myBookmarks' query to ensure up-to-date data
       },
     });
   // rest of the component

Also applies to: 62-62

let { data: bookmarks } = api.post.myBookmarks.useQuery();
const { status: bookmarkStatus } = api.post.myBookmarks.useQuery();
const { data: bookmarksData } = api.post.myBookmarks.useQuery({
limit: 3,
Copy link
Member

Choose a reason for hiding this comment

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

Instead of the magic number 3 for the limit, you should use the howManySavedToShow const

@John-Paul-Larkin
Copy link
Member

Hey @revanthnetha
This works great, thanks.
I just left a small comment, but otherwise this is good to go 👍

@revanthnetha
Copy link
Contributor Author

Got it @John-Paul-Larkin . I'll make the changes as necessary.

@revanthnetha
Copy link
Contributor Author

Review the changes @John-Paul-Larkin . Happy to do if any changes needed :)

Could you please add the labels hacktober-fest and hactober-fest-accepted tags and add my name in assigness. It really means a lot.

@John-Paul-Larkin
Copy link
Member

Hey @revanthnetha
This is great work. Thanks.
I was playing with this earlier and I received the error Toast, but I dont know why that happened and try as I may I cant seem to recreate it.

@NiallJoeMaher Do you think we remove the toast on error?
Apart from that, I think this is ready to go.

Copy link

vercel bot commented Oct 14, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
codu ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 14, 2024 6:00pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Recent bookmarks - auto update UI and update feedback
3 participants