Skip to content

Conversation

@vamsikrishnamathala
Copy link
Member

@vamsikrishnamathala vamsikrishnamathala commented Nov 29, 2024

Problem Statement

The re-ordering sequence for favorites is not generating as expected.

Summary

Resolved the issue with the re-ordering sequence generation.

Reference

WEB-2774

Summary by CodeRabbit

  • New Features

    • Enhanced drag-and-drop functionality for managing favorites in the sidebar.
    • Updated reordering logic to improve clarity and efficiency when moving favorites.
  • Bug Fixes

    • Streamlined conditions for removing favorites from folders, ensuring accurate operations.
  • Chores

    • Removed unused functions and updated method signatures for better parameter handling.

@vamsikrishnamathala vamsikrishnamathala added 🐛bug Something isn't working 🌐frontend labels Nov 29, 2024
@vamsikrishnamathala vamsikrishnamathala added this to the v0.24.0 milestone Nov 29, 2024
@vamsikrishnamathala vamsikrishnamathala self-assigned this Nov 29, 2024
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2024

Walkthrough

The changes in this pull request involve modifications to several components related to the favorites feature within the application. Key updates include the removal of the handleReorder function from the FavoriteFolder component's props, adjustments to the SidebarFavoritesMenu for handling drop events and reordering logic, and the elimination of the getDestinationStateSequence function from the helpers. Additionally, the FavoriteStore class has been updated to enhance the reordering functionality and manage entity removals more effectively.

Changes

File Path Change Summary
web/core/components/workspace/sidebar/favorites/favorite-folder.tsx Removed handleReorder from Props type in FavoriteFolder.
web/core/components/workspace/sidebar/favorites/favorites-menu.tsx Removed import of getDestinationStateSequence. Updated handleReorder method signature and logic for drop events. Refined conditions for removing favorites from folders.
web/core/components/workspace/sidebar/favorites/favorites.helpers.ts Removed getDestinationStateSequence function. Other helper functions remain unchanged.
web/core/store/favorite.store.ts Updated reOrderFavorite and removeFavoriteEntityFromStore method signatures to include new parameters. Enhanced internal logic for reordering favorites and handling entity removals. Updated imports for lodash functions.

Possibly related PRs

Suggested labels

🌟improvement, 🧹chore, 🛠️refactor

Suggested reviewers

  • sriramveeraghanta
  • SatishGandham
  • rahulramesha

🐰 In the world of favorites, we hop and play,
Removing the clutter, making way!
With reorders and drops, we dance with glee,
Simplifying the path for you and me.
So let’s celebrate this change, oh what fun!
A tidier sidebar for everyone! 🌟


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.

@vamsikrishnamathala vamsikrishnamathala changed the title [WEB-2774]:fix:reordering favorites and favorite folders [WEB-2774]fix:reordering favorites and favorite folders Nov 29, 2024
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: 1

🧹 Outside diff range and nitpick comments (6)
web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (3)

104-105: Remove commented-out code

The commented-out lines referencing getgetDestinationSequence should be removed as they're no longer needed with the new instruction-based approach.

-          // const destinationSequence = getgetDestinationSequence(droppedFavId, instruction);
           handleReorder(sourceData.id, droppedFavId, instruction);

Also applies to: 111-112


150-151: Add documentation for the edge parameter

The handleReorder function has been successfully updated to use the new instruction-based approach. However, the edge parameter's possible values and their implications should be documented.

Add JSDoc comments to clarify the parameter usage:

   const handleReorder = useCallback(
+    /**
+     * Reorders a favorite item relative to another item
+     * @param favoriteId - ID of the favorite to be reordered
+     * @param droppedFavId - ID of the favorite item it was dropped on
+     * @param edge - Position relative to the dropped item ('top' | 'bottom' | undefined)
+     */
     (favoriteId: string, droppedFavId: string, edge: string | undefined) => {

Line range hint 150-158: Consider adding tests for the new reordering logic

The shift to instruction-based reordering is a significant change that would benefit from comprehensive test coverage to ensure reliability across different scenarios (e.g., reordering within folders, between folders, at root level).

Would you like help creating test cases for the various reordering scenarios?

web/core/store/favorite.store.ts (3)

1-1: Remove unused import 'result' from 'lodash'.

The result function imported from lodash is not used in this file. Removing unused imports can help improve code clarity and reduce bundle size.

Apply this diff to remove the unused import:

-import { orderBy, result, uniqBy } from "lodash";
+import { orderBy, uniqBy } from "lodash";

215-215: Ensure resultSequence is an integer after averaging.

When calculating resultSequence by averaging destinationSequence and prevSequence, the result may be a floating-point number. If the sequence is expected to be an integer, consider rounding or flooring the result to maintain consistency.

Apply this diff to ensure resultSequence is an integer:

-resultSequence = (destinationSequence + prevSequence) / 2;
+resultSequence = Math.floor((destinationSequence + prevSequence) / 2);

Line range hint 230-233: Avoid throwing errors inside runInAction blocks.

Throwing an error inside a runInAction block may lead to unintended side effects or suppress the error. It's better to perform state updates within the action and throw the error outside.

Apply this diff to move the throw error statement outside of runInAction:

runInAction(() => {
  set(this.favoriteMap, [favoriteId, "sequence"], initialSequence);
-  throw error;
});
+});
+throw error;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4251b11 and 6501cdd.

📒 Files selected for processing (4)
  • web/core/components/workspace/sidebar/favorites/favorite-folder.tsx (0 hunks)
  • web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (3 hunks)
  • web/core/components/workspace/sidebar/favorites/favorites.helpers.ts (0 hunks)
  • web/core/store/favorite.store.ts (4 hunks)
💤 Files with no reviewable changes (2)
  • web/core/components/workspace/sidebar/favorites/favorite-folder.tsx
  • web/core/components/workspace/sidebar/favorites/favorites.helpers.ts
🔇 Additional comments (3)
web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (2)

97-98: LGTM: Improved folder removal logic

The condition for removing favorites from folders is now more precise, properly checking both the parent ID difference and the child status.


31-31: Verify the architectural change in reordering logic

The shift from sequence-based to instruction-based reordering represents a significant architectural change. This approach might be more maintainable but needs verification across different scenarios.

✅ Verification successful

Architectural change in reordering logic is part of a broader refactoring

The shift from sequence-based to instruction-based reordering is a deliberate architectural change that's been consistently implemented across the codebase. The old destinationSequence references only remain in favorite.store.ts for the actual sequence calculations, while the UI components have been fully migrated to use the new instruction-based approach with getInstructionFromPayload and TargetData. This pattern is also consistently used in the labels component, showing it's a standardized approach across similar drag-and-drop functionalities.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old sequence-based approach
rg "getDestinationStateSequence|destinationSequence" --type typescript

Length of output: 107


Script:

#!/bin/bash
# Search for any remaining references to the old sequence-based approach, using ripgrep without file type filter
rg "getDestinationStateSequence|destinationSequence"

# Also search for the new instruction-based approach to understand the scope of changes
rg "getInstructionFromPayload|TargetData"

Length of output: 6284

web/core/store/favorite.store.ts (1)

206-223: Verify the logic for calculating resultSequence.

The computation of resultSequence may have potential issues:

  • When edge is undefined, resultSequence remains at the initial value of 10000. Is this the intended default behavior?
  • In the reorder-above case, if destinationIndex - 1 is less than 0, accessing sortedIds[destinationIndex - 1] will result in undefined, which may cause unexpected behavior.
  • In the else block, subtracting resultSequence (which is 10000) from destinationSequence might result in a negative sequence number.

Consider reviewing and testing the sequence calculations to ensure they handle all edge cases correctly.

try {
let resultSequence = 10000;
if (edge) {
const sortedIds = orderBy(this.favoriteMap, "sequence", "desc").map((fav: IFavorite) => fav.id);
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 29, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect usage of orderBy on an object.

At line 208, orderBy is being called on this.favoriteMap, which is an object. The orderBy function expects an array as its first argument. To sort the favorites correctly, convert the object to an array using Object.values.

Apply this diff to fix the issue:

-const sortedIds = orderBy(this.favoriteMap, "sequence", "desc").map((fav: IFavorite) => fav.id);
+const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sortedIds = orderBy(this.favoriteMap, "sequence", "desc").map((fav: IFavorite) => fav.id);
const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

@mathalav55, thank you for fixing the issue!


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

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: 1

🧹 Outside diff range and nitpick comments (1)
web/core/store/favorite.store.ts (1)

Line range hint 256-282: Enhance type safety and reduce code duplication

The switch statement has repetitive patterns and could benefit from type safety improvements.

Consider these improvements:

+ type EntityType = 'view' | 'module' | 'page' | 'cycle' | 'project';
+ 
- removeFavoriteEntityFromStore = (entity_identifier: string, entity_type: string) => {
+ removeFavoriteEntityFromStore = (entity_identifier: string, entity_type: EntityType) => {
+   const entityMaps = {
+     view: this.viewStore.viewMap,
+     module: this.moduleStore.moduleMap,
+     page: this.pageStore.data,
+     cycle: this.cycleStore.cycleMap,
+     project: this.projectStore.projectMap
+   };
+ 
+   const entityMap = entityMaps[entity_type];
+   if (entityMap?.[entity_identifier]) {
+     entityMap[entity_identifier].is_favorite = false;
+   }
- switch (entity_type) {
-   case "view":
-     return (
-       this.viewStore.viewMap[entity_identifier] && (this.viewStore.viewMap[entity_identifier].is_favorite = false)
-     );
-   case "module":
-     return (
-       this.moduleStore.moduleMap[entity_identifier] &&
-       (this.moduleStore.moduleMap[entity_identifier].is_favorite = false)
-     );
-   // ... other cases
-   default:
-     return;
- }
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6501cdd and e1e7865.

📒 Files selected for processing (1)
  • web/core/store/favorite.store.ts (4 hunks)
🔇 Additional comments (2)
web/core/store/favorite.store.ts (2)

31-36: LGTM: Interface changes for reordering functionality

The updated interface for reOrderFavorite with the new parameters destinationId and edge is well-defined and properly typed.


208-208: Fix incorrect usage of orderBy on favoriteMap

This issue was previously identified and still needs to be addressed.

Comment on lines +206 to +223
let resultSequence = 10000;
if (edge) {
const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);
const destinationSequence = this.favoriteMap[destinationId]?.sequence || undefined;
if (destinationSequence) {
const destinationIndex = sortedIds.findIndex((id) => id === destinationId);
if (edge === "reorder-above") {
const prevSequence = this.favoriteMap[sortedIds[destinationIndex - 1]]?.sequence || undefined;
if (prevSequence) {
resultSequence = (destinationSequence + prevSequence) / 2;
} else {
resultSequence = destinationSequence + resultSequence;
}
} else {
resultSequence = destinationSequence - resultSequence;
}
}
}
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 sequence normalization and bounds checking

The current sequence calculation has several potential issues:

  1. When reordering above (line 217-218), adding a fixed value (10000) could lead to very large numbers over time
  2. When reordering below (line 220), subtracting could lead to negative sequences
  3. No handling for when sequences get too close to each other

Consider implementing sequence normalization:

  reOrderFavorite = async (workspaceSlug: string, favoriteId: string, destinationId: string, edge: string | undefined) => {
    const initialSequence = this.favoriteMap[favoriteId].sequence;
    try {
-     let resultSequence = 10000;
+     const SEQUENCE_STEP = 1000;
+     const MAX_SEQUENCE = 1000000;
+     
+     // Normalize sequences if they get too large
+     const normalizeSequences = () => {
+       const favorites = Object.values(this.favoriteMap);
+       if (favorites.some(f => f.sequence > MAX_SEQUENCE)) {
+         const sorted = orderBy(favorites, "sequence", "desc");
+         sorted.forEach((fav, idx) => {
+           fav.sequence = (sorted.length - idx) * SEQUENCE_STEP;
+         });
+       }
+     };
      
      let resultSequence;
      if (edge) {
        const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);
        const destinationSequence = this.favoriteMap[destinationId]?.sequence || undefined;
        if (destinationSequence) {
          const destinationIndex = sortedIds.findIndex((id) => id === destinationId);
          if (edge === "reorder-above") {
            const prevSequence = this.favoriteMap[sortedIds[destinationIndex - 1]]?.sequence || destinationSequence + SEQUENCE_STEP;
            resultSequence = (destinationSequence + prevSequence) / 2;
          } else {
            const nextSequence = this.favoriteMap[sortedIds[destinationIndex + 1]]?.sequence || Math.max(0, destinationSequence - SEQUENCE_STEP);
            resultSequence = (destinationSequence + nextSequence) / 2;
          }
        }
      }
+     normalizeSequences();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let resultSequence = 10000;
if (edge) {
const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);
const destinationSequence = this.favoriteMap[destinationId]?.sequence || undefined;
if (destinationSequence) {
const destinationIndex = sortedIds.findIndex((id) => id === destinationId);
if (edge === "reorder-above") {
const prevSequence = this.favoriteMap[sortedIds[destinationIndex - 1]]?.sequence || undefined;
if (prevSequence) {
resultSequence = (destinationSequence + prevSequence) / 2;
} else {
resultSequence = destinationSequence + resultSequence;
}
} else {
resultSequence = destinationSequence - resultSequence;
}
}
}
const SEQUENCE_STEP = 1000;
const MAX_SEQUENCE = 1000000;
// Normalize sequences if they get too large
const normalizeSequences = () => {
const favorites = Object.values(this.favoriteMap);
if (favorites.some(f => f.sequence > MAX_SEQUENCE)) {
const sorted = orderBy(favorites, "sequence", "desc");
sorted.forEach((fav, idx) => {
fav.sequence = (sorted.length - idx) * SEQUENCE_STEP;
});
}
};
let resultSequence;
if (edge) {
const sortedIds = orderBy(Object.values(this.favoriteMap), "sequence", "desc").map((fav: IFavorite) => fav.id);
const destinationSequence = this.favoriteMap[destinationId]?.sequence || undefined;
if (destinationSequence) {
const destinationIndex = sortedIds.findIndex((id) => id === destinationId);
if (edge === "reorder-above") {
const prevSequence = this.favoriteMap[sortedIds[destinationIndex - 1]]?.sequence || destinationSequence + SEQUENCE_STEP;
resultSequence = (destinationSequence + prevSequence) / 2;
} else {
const nextSequence = this.favoriteMap[sortedIds[destinationIndex + 1]]?.sequence || Math.max(0, destinationSequence - SEQUENCE_STEP);
resultSequence = (destinationSequence + nextSequence) / 2;
}
}
}
normalizeSequences();

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: 0

🧹 Outside diff range and nitpick comments (1)
web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (1)

Line range hint 148-156: Enhance error handling specificity

While the reordering logic is solid, the error message could be more specific to help users understand what went wrong.

Consider this improvement:

     reOrderFavorite(workspaceSlug.toString(), favoriteId, droppedFavId, edge).catch(() => {
       setToast({
         type: TOAST_TYPE.ERROR,
         title: "Error!",
-        message: "Failed reorder favorite",
+        message: `Failed to reorder favorite${edge ? ` to ${edge} of target` : ''}. Please try again.`,
       });
     });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e1e7865 and 9541f5b.

📒 Files selected for processing (1)
  • web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (3 hunks)
🔇 Additional comments (3)
web/core/components/workspace/sidebar/favorites/favorites-menu.tsx (3)

31-31: LGTM: Import changes align with the new reordering strategy

The removal of getDestinationStateSequence and addition of getInstructionFromPayload and TargetData imports reflect the shift from sequence-based to instruction-based reordering.


110-110: LGTM: Reordering logic updated correctly

The reordering call has been updated to use the new instruction-based approach, which better handles the edge cases in the drag-and-drop reordering.


97-105: Verify folder removal edge cases

The condition for removing favorites from folders has been improved, but we should verify that this handles all edge cases correctly, particularly when:

  1. Moving between nested folders
  2. Moving from a nested folder to root
  3. Moving from root to a nested folder
✅ Verification successful

Based on the search results, I can now generate a final response as I have enough context about the folder handling logic:

The folder removal logic correctly handles all edge cases

The implementation properly handles all folder movement scenarios:

  • Moving between folders: Checks parentId !== sourceData.parentId && sourceData.isChild
  • Moving to root: Additional check if (!parentId && sourceData.isChild)
  • Moving to another folder: Uses handleMoveToFolder when parentId && parentId !== sourceData.parentId

The code maintains proper state by:

  1. Tracking child status via isChild flag set in initialData
  2. Preserving parent-child relationships through explicit parentId checks
  3. Using separate handlers for folder operations (handleMoveToFolder, handleRemoveFromFavoritesFolder)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for nested folder handling in the codebase
ast-grep --pattern 'parent: $parent' 

Length of output: 38


Script:

#!/bin/bash
# Search for folder-related operations and parent handling
rg -A 5 "parentId|isChild" web/core/components/workspace/sidebar/favorites/

# Search for handleRemoveFromFavoritesFolder implementation
ast-grep --pattern 'handleRemoveFromFavoritesFolder($$$)'

# Search for handleReorder implementation
ast-grep --pattern 'handleReorder($$$)'

# Search for folder-related types or interfaces
rg -A 5 "interface.*Favorite|type.*Favorite" web/core/components/workspace/sidebar/favorites/

Length of output: 9793

@pushya22 pushya22 merged commit 63bc01f into preview Dec 2, 2024
14 of 15 checks passed
@pushya22 pushya22 deleted the chore-favorites_reorder branch December 2, 2024 08:05
@coderabbitai coderabbitai bot mentioned this pull request Mar 19, 2025
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛bug Something isn't working 🌐frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants