diff --git a/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx b/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx index 4fdbfe5657..227a3d812a 100644 --- a/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx @@ -93,12 +93,12 @@ export const FavoriteFolder: React.FC = (props) => { const element = elementRef.current; if (!element) return; - const initialData = { type: "PARENT", id: favorite.id }; + const initialData = { type: "PARENT", id: favorite.id, is_folder: favorite.is_folder }; return combine( draggable({ element, - // getInitialData: () => initialData, + getInitialData: () => initialData, onDragStart: () => setIsDragging(true), onDrop: (data) => { setIsDraggedOver(false); @@ -109,7 +109,7 @@ export const FavoriteFolder: React.FC = (props) => { const edge = extractClosestEdge(destinationData) || undefined; const payload = { id: favorite.id, - sequence: getDestinationStateSequence(favoriteMap, destinationData.id as string, edge), + sequence: Math.round(getDestinationStateSequence(favoriteMap, destinationData.id as string, edge) || 0), }; handleOnDropFolder(payload); @@ -127,7 +127,7 @@ export const FavoriteFolder: React.FC = (props) => { onDragEnter: (args) => { setIsDragging(true); setIsDraggedOver(true); - setClosestEdge(extractClosestEdge(args.self.data)); + args.source.data.is_folder && setClosestEdge(extractClosestEdge(args.self.data)); }, onDragLeave: () => { setIsDragging(false); @@ -142,7 +142,7 @@ export const FavoriteFolder: React.FC = (props) => { setIsDraggedOver(false); const sourceId = source?.data?.id as string | undefined; const destinationId = self?.data?.id as string | undefined; - + if (source.data.is_folder) return; if (sourceId === destinationId) return; if (!sourceId || !destinationId) return; if (favoriteMap[sourceId].parent === destinationId) return; diff --git a/web/core/components/workspace/sidebar/favorites/favorite-item.tsx b/web/core/components/workspace/sidebar/favorites/favorite-item.tsx index cf326d699c..44317dcd7b 100644 --- a/web/core/components/workspace/sidebar/favorites/favorite-item.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorite-item.tsx @@ -139,7 +139,7 @@ export const FavoriteItem = observer( {getIcon()} {!sidebarCollapsed && ( - + {favorite.entity_data ? favorite.entity_data.name : favorite.name} )} diff --git a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx index 002f34b0b3..5581efe154 100644 --- a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from "react"; import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine"; import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; -import { orderBy } from "lodash"; +import { orderBy, uniqBy } from "lodash"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { ChevronRight, FolderPlus } from "lucide-react"; @@ -131,7 +131,7 @@ export const SidebarFavoritesMenu = observer(() => { )} > toggleFavoriteMenu(!isFavoriteMenuOpen)} className="flex-1 text-start"> - MY FAVORITES + YOUR FAVORITES { static > {createNewFolder && } - {orderBy(Object.values(favoriteMap), "sequence", "desc") + {uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id") .filter((fav) => !fav.parent) .map((fav, index) => ( await this.cycleService.deleteCycle(workspaceSlug, projectId, cycleId).then(() => { runInAction(() => { + this.rootStore.favorite.removeFavoriteFromStore(cycleId); delete this.cycleMap[cycleId]; delete this.activeCycleIdMap[cycleId]; }); diff --git a/web/core/store/favorite.store.ts b/web/core/store/favorite.store.ts index 7966260597..50eca4b89c 100644 --- a/web/core/store/favorite.store.ts +++ b/web/core/store/favorite.store.ts @@ -29,6 +29,7 @@ export interface IFavoriteStore { removeFavoriteEntity: (workspaceSlug: string, entityId: string) => Promise; moveFavoriteFolder: (workspaceSlug: string, favoriteId: string, data: Partial) => Promise; removeFromFavoriteFolder: (workspaceSlug: string, favoriteId: string, data: Partial) => Promise; + removeFavoriteFromStore: (entity_identifier: string) => void; } export class FavoriteStore implements IFavoriteStore { @@ -124,14 +125,19 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ updateFavorite = async (workspaceSlug: string, favoriteId: string, data: Partial) => { + const initialState = this.favoriteMap[favoriteId]; try { - const response = await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); runInAction(() => { - set(this.favoriteMap, [response.id], response); + set(this.favoriteMap, [favoriteId], { ...this.favoriteMap[favoriteId], ...data }); }); + const response = await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); + return response; } catch (error) { console.error("Failed to update favorite from favorite store"); + runInAction(() => { + set(this.favoriteMap, [favoriteId], initialState); + }); throw error; } }; @@ -144,15 +150,15 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ moveFavorite = async (workspaceSlug: string, favoriteId: string, data: Partial) => { + const oldParent = this.favoriteMap[favoriteId].parent; + const favorite = this.favoriteMap[favoriteId]; try { - const response = await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); runInAction(() => { // add the favorite to the new parent if (!data.parent) return; - set(this.favoriteMap, [data.parent, "children"], [response, ...this.favoriteMap[data.parent].children]); + set(this.favoriteMap, [data.parent, "children"], [favorite, ...this.favoriteMap[data.parent].children]); // remove the favorite from the old parent - const oldParent = this.favoriteMap[favoriteId].parent; if (oldParent) { set( this.favoriteMap, @@ -164,8 +170,32 @@ export class FavoriteStore implements IFavoriteStore { // add parent of the favorite set(this.favoriteMap, [favoriteId, "parent"], data.parent); }); + await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); } catch (error) { console.error("Failed to move favorite from favorite store"); + + // revert the changes + runInAction(() => { + if (!data.parent) return; + // remove the favorite from the new parent + set( + this.favoriteMap, + [data.parent, "children"], + this.favoriteMap[data.parent].children.filter((child) => child.id !== favoriteId) + ); + + // add the favorite back to the old parent + if (oldParent) { + set( + this.favoriteMap, + [oldParent, "children"], + [...this.favoriteMap[oldParent].children, this.favoriteMap[favoriteId]] + ); + } + + // revert the parent + set(this.favoriteMap, [favoriteId, "parent"], oldParent); + }); throw error; } }; @@ -176,24 +206,21 @@ export class FavoriteStore implements IFavoriteStore { runInAction(() => { set(this.favoriteMap, [favoriteId, "sequence"], data.sequence); }); - console.log(JSON.parse(JSON.stringify(this.favoriteMap)), "getDestinationStateSequence"); await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); } catch (error) { + console.error("Failed to move favorite folder"); runInAction(() => { set(this.favoriteMap, [favoriteId, "sequence"], initialSequence); - console.error("Failed to move favorite folder"); throw error; }); } }; removeFromFavoriteFolder = async (workspaceSlug: string, favoriteId: string, data: Partial) => { + const parent = this.favoriteMap[favoriteId].parent; try { - await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); runInAction(() => { - const parent = this.favoriteMap[favoriteId].parent; - //remove parent set(this.favoriteMap, [favoriteId, "parent"], null); @@ -206,8 +233,20 @@ export class FavoriteStore implements IFavoriteStore { ); } }); + await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); } catch (error) { console.error("Failed to move favorite"); + runInAction(() => { + set(this.favoriteMap, [favoriteId, "parent"], parent); + if (parent) { + set( + this.favoriteMap, + [parent, "children"], + [...this.favoriteMap[parent].children, this.favoriteMap[favoriteId]] + ); + } + throw error; + }); throw error; } }; @@ -236,14 +275,13 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ deleteFavorite = async (workspaceSlug: string, favoriteId: string) => { + const parent = this.favoriteMap[favoriteId].parent; + const children = this.favoriteMap[favoriteId].children; + const entity_identifier = this.favoriteMap[favoriteId].entity_identifier; + const initialState = this.favoriteMap[favoriteId]; + try { - await this.favoriteService.deleteFavorite(workspaceSlug, favoriteId); runInAction(() => { - const parent = this.favoriteMap[favoriteId].parent; - const children = this.favoriteMap[favoriteId].children; - const entity_identifier = this.favoriteMap[favoriteId].entity_identifier; - entity_identifier && - this.removeFavoriteEntityFromStore(entity_identifier, this.favoriteMap[favoriteId].entity_type); if (parent) { set( this.favoriteMap, @@ -251,6 +289,13 @@ export class FavoriteStore implements IFavoriteStore { this.favoriteMap[parent].children.filter((child) => child.id !== favoriteId) ); } + delete this.favoriteMap[favoriteId]; + entity_identifier && delete this.entityMap[entity_identifier]; + this.favoriteIds = this.favoriteIds.filter((id) => id !== favoriteId); + }); + await this.favoriteService.deleteFavorite(workspaceSlug, favoriteId); + runInAction(() => { + entity_identifier && this.removeFavoriteEntityFromStore(entity_identifier, initialState.entity_type); if (children) { children.forEach((child) => { console.log(child.entity_type); @@ -258,13 +303,15 @@ export class FavoriteStore implements IFavoriteStore { this.removeFavoriteEntityFromStore(child.entity_identifier, child.entity_type); }); } - delete this.favoriteMap[favoriteId]; - entity_identifier && delete this.entityMap[entity_identifier]; - - this.favoriteIds = this.favoriteIds.filter((id) => id !== favoriteId); }); } catch (error) { - console.error("Failed to delete favorite from favorite store"); + console.error("Failed to delete favorite from favorite store", error); + runInAction(() => { + if (parent) set(this.favoriteMap, [parent, "children"], [...this.favoriteMap[parent].children, initialState]); + set(this.favoriteMap, [favoriteId], initialState); + entity_identifier && set(this.entityMap, [entity_identifier], initialState); + this.favoriteIds = [favoriteId, ...this.favoriteIds]; + }); throw error; } }; @@ -276,14 +323,42 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ removeFavoriteEntity = async (workspaceSlug: string, entityId: string) => { + const initialState = this.entityMap[entityId]; try { const favoriteId = this.entityMap[entityId].id; - await this.deleteFavorite(workspaceSlug, favoriteId); runInAction(() => { delete this.entityMap[entityId]; }); + await this.deleteFavorite(workspaceSlug, favoriteId); } catch (error) { - console.error("Failed to remove favorite entity from favorite store"); + console.error("Failed to remove favorite entity from favorite store", error); + runInAction(() => { + set(this.entityMap, [entityId], initialState); + }); + throw error; + } + }; + + removeFavoriteFromStore = (entity_identifier: string) => { + try { + const favoriteId = this.entityMap[entity_identifier].id; + const favorite = this.favoriteMap[favoriteId]; + const parent = favorite.parent; + + runInAction(() => { + if (parent) { + set( + this.favoriteMap, + [parent, "children"], + this.favoriteMap[parent].children.filter((child) => child.id !== favoriteId) + ); + } + delete this.favoriteMap[favoriteId]; + delete this.entityMap[entity_identifier]; + this.favoriteIds = this.favoriteIds.filter((id) => id !== favoriteId); + }); + } catch (error) { + console.error("Failed to remove favorite from favorite store", error); throw error; } }; @@ -294,6 +369,7 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ getGroupedFavorites = async (workspaceSlug: string, favoriteId: string) => { + if (!favoriteId) return []; try { const response = await this.favoriteService.getGroupedFavorites(workspaceSlug, favoriteId); runInAction(() => { diff --git a/web/core/store/module.store.ts b/web/core/store/module.store.ts index a67b479460..e5631eedfa 100644 --- a/web/core/store/module.store.ts +++ b/web/core/store/module.store.ts @@ -405,6 +405,7 @@ export class ModulesStore implements IModuleStore { await this.moduleService.deleteModule(workspaceSlug, projectId, moduleId).then(() => { runInAction(() => { delete this.moduleMap[moduleId]; + this.rootStore.favorite.removeFavoriteFromStore(moduleId); }); }); }; diff --git a/web/core/store/project-view.store.ts b/web/core/store/project-view.store.ts index 60bef41249..61721cad79 100644 --- a/web/core/store/project-view.store.ts +++ b/web/core/store/project-view.store.ts @@ -271,6 +271,7 @@ export class ProjectViewStore implements IProjectViewStore { await this.viewService.deleteView(workspaceSlug, projectId, viewId).then(() => { runInAction(() => { delete this.viewMap[viewId]; + this.rootStore.favorite.removeFavoriteFromStore(viewId); }); }); }; diff --git a/web/core/store/project/project.store.ts b/web/core/store/project/project.store.ts index b4ced1d079..fe1f305661 100644 --- a/web/core/store/project/project.store.ts +++ b/web/core/store/project/project.store.ts @@ -401,6 +401,7 @@ export class ProjectStore implements IProjectStore { await this.projectService.deleteProject(workspaceSlug, projectId); runInAction(() => { delete this.projectMap[projectId]; + this.rootStore.favorite.removeFavoriteFromStore(projectId); }); } catch (error) { console.log("Failed to delete project from project store");