diff --git a/web/core/components/dropdowns/member/member-options.tsx b/web/core/components/dropdowns/member/member-options.tsx index 8e7003f24a2..a2d262130e0 100644 --- a/web/core/components/dropdowns/member/member-options.tsx +++ b/web/core/components/dropdowns/member/member-options.tsx @@ -88,7 +88,7 @@ export const MemberOptions = observer((props: Props) => { return createPortal(
= (props) => { const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme(); const { isMobile } = usePlatformOS(); - const { moveFavorite, getGroupedFavorites, favoriteMap, moveFavoriteFolder } = useFavorite(); + const { moveFavorite, getGroupedFavorites, groupedFavorites, moveFavoriteFolder } = useFavorite(); const { workspaceSlug } = useParams(); // states const [isMenuActive, setIsMenuActive] = useState(false); @@ -110,7 +111,9 @@ export const FavoriteFolder: React.FC = (props) => { const edge = extractClosestEdge(destinationData) || undefined; const payload = { id: favorite.id, - sequence: Math.round(getDestinationStateSequence(favoriteMap, destinationData.id as string, edge) || 0), + sequence: Math.round( + getDestinationStateSequence(groupedFavorites, destinationData.id as string, edge) || 0 + ), }; handleOnDropFolder(payload); @@ -146,7 +149,7 @@ export const FavoriteFolder: React.FC = (props) => { if (source.data.is_folder) return; if (sourceId === destinationId) return; if (!sourceId || !destinationId) return; - if (favoriteMap[sourceId].parent === destinationId) return; + if (groupedFavorites[sourceId].parent === destinationId) return; handleOnDrop(sourceId, destinationId); }, }) @@ -313,14 +316,14 @@ export const FavoriteFolder: React.FC = (props) => { "px-2": !isSidebarCollapsed, })} > - {favorite.children.map((child) => ( + {uniqBy(favorite.children, "id").map((child) => ( ))} diff --git a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx index 17380b079cc..f7b620f7a49 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, uniqBy } from "lodash"; +import orderBy from "lodash/orderBy"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { ChevronRight, FolderPlus } from "lucide-react"; @@ -33,7 +33,7 @@ export const SidebarFavoritesMenu = observer(() => { // store hooks const { sidebarCollapsed } = useAppTheme(); - const { favoriteIds, favoriteMap, deleteFavorite, removeFromFavoriteFolder } = useFavorite(); + const { favoriteIds, groupedFavorites, deleteFavorite, removeFromFavoriteFolder } = useFavorite(); const { workspaceSlug } = useParams(); const { isMobile } = usePlatformOS(); @@ -108,7 +108,7 @@ export const SidebarFavoritesMenu = observer(() => { setIsDragging(false); const sourceId = source?.data?.id as string | undefined; console.log({ sourceId }); - if (!sourceId || !favoriteMap[sourceId].parent) return; + if (!sourceId || !groupedFavorites[sourceId].parent) return; handleRemoveFromFavoritesFolder(sourceId); }, }) @@ -170,14 +170,14 @@ export const SidebarFavoritesMenu = observer(() => { static > {createNewFolder && } - {Object.keys(favoriteMap).length === 0 ? ( + {Object.keys(groupedFavorites).length === 0 ? ( <> {!sidebarCollapsed && ( No favorites yet )} ) : ( - uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id") + orderBy(Object.values(groupedFavorites), "sequence", "desc") .filter((fav) => !fav.parent) .map((fav, index) => ( { favorite={fav} handleRemoveFromFavorites={handleRemoveFromFavorites} handleRemoveFromFavoritesFolder={handleRemoveFromFavoritesFolder} - favoriteMap={favoriteMap} + favoriteMap={groupedFavorites} /> )} diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index 2845527c23b..b9a1f147648 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -624,6 +624,7 @@ export class CycleStore implements ICycleStore { .then((response) => { runInAction(() => { set(this.cycleMap, [cycleId, "archived_at"], response.archived_at); + if (this.rootStore.favorite.entityMap[cycleId]) this.rootStore.favorite.removeFavoriteFromStore(cycleId); }); }) .catch((error) => { diff --git a/web/core/store/favorite.store.ts b/web/core/store/favorite.store.ts index 30917afcb24..9d04ed1303e 100644 --- a/web/core/store/favorite.store.ts +++ b/web/core/store/favorite.store.ts @@ -18,6 +18,7 @@ export interface IFavoriteStore { }; // computed actions existingFolders: string[]; + groupedFavorites: { [favoriteId: string]: IFavorite }; // actions fetchFavorite: (workspaceSlug: string) => Promise; // CRUD actions @@ -57,6 +58,7 @@ export class FavoriteStore implements IFavoriteStore { favoriteIds: observable, //computed existingFolders: computed, + groupedFavorites: computed, // action fetchFavorite: action, // CRUD actions @@ -80,6 +82,23 @@ export class FavoriteStore implements IFavoriteStore { return Object.values(this.favoriteMap).map((fav) => fav.name); } + get groupedFavorites() { + const data: { [favoriteId: string]: IFavorite } = JSON.parse(JSON.stringify(this.favoriteMap)); + + Object.values(data).forEach((fav) => { + if (fav.parent && data[fav.parent]) { + if (data[fav.parent].children) { + if (!data[fav.parent].children.some((f) => f.id === fav.id)) { + data[fav.parent].children.push(fav); + } + } else { + data[fav.parent].children = [fav]; + } + } + }); + return data; + } + /** * Creates a favorite in the workspace and adds it to the store * @param workspaceSlug @@ -151,22 +170,8 @@ export class FavoriteStore implements IFavoriteStore { */ moveFavorite = async (workspaceSlug: string, favoriteId: string, data: Partial) => { const oldParent = this.favoriteMap[favoriteId].parent; - const favorite = this.favoriteMap[favoriteId]; try { runInAction(() => { - // add the favorite to the new parent - if (!data.parent) return; - set(this.favoriteMap, [data.parent, "children"], [favorite, ...this.favoriteMap[data.parent].children]); - - // remove the favorite from the old parent - if (oldParent) { - set( - this.favoriteMap, - [oldParent, "children"], - this.favoriteMap[oldParent].children.filter((child) => child.id !== favoriteId) - ); - } - // add parent of the favorite set(this.favoriteMap, [favoriteId, "parent"], data.parent); }); @@ -177,21 +182,6 @@ export class FavoriteStore implements IFavoriteStore { // 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); @@ -223,28 +213,13 @@ export class FavoriteStore implements IFavoriteStore { runInAction(() => { //remove parent set(this.favoriteMap, [favoriteId, "parent"], null); - - //remove children from parent - if (parent) { - set( - this.favoriteMap, - [parent, "children"], - this.favoriteMap[parent].children.filter((child) => child.id !== favoriteId) - ); - } }); 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; @@ -254,15 +229,26 @@ export class FavoriteStore implements IFavoriteStore { removeFavoriteEntityFromStore = (entity_identifier: string, entity_type: string) => { switch (entity_type) { case "view": - return (this.viewStore.viewMap[entity_identifier].is_favorite = false); + return ( + this.viewStore.viewMap[entity_identifier] && (this.viewStore.viewMap[entity_identifier].is_favorite = false) + ); case "module": - return (this.moduleStore.moduleMap[entity_identifier].is_favorite = false); + return ( + this.moduleStore.moduleMap[entity_identifier] && + (this.moduleStore.moduleMap[entity_identifier].is_favorite = false) + ); case "page": - return (this.pageStore.data[entity_identifier].is_favorite = false); + return this.pageStore.data[entity_identifier] && (this.pageStore.data[entity_identifier].is_favorite = false); case "cycle": - return (this.cycleStore.cycleMap[entity_identifier].is_favorite = false); + return ( + this.cycleStore.cycleMap[entity_identifier] && + (this.cycleStore.cycleMap[entity_identifier].is_favorite = false) + ); case "project": - return (this.projectStore.projectMap[entity_identifier].is_favorite = false); + return ( + this.projectStore.projectMap[entity_identifier] && + (this.projectStore.projectMap[entity_identifier].is_favorite = false) + ); default: return; } @@ -276,29 +262,21 @@ export class FavoriteStore implements IFavoriteStore { */ deleteFavorite = async (workspaceSlug: string, favoriteId: string) => { const parent = this.favoriteMap[favoriteId].parent; - const children = this.favoriteMap[favoriteId].children; + const children = this.groupedFavorites[favoriteId].children; const entity_identifier = this.favoriteMap[favoriteId].entity_identifier; const initialState = this.favoriteMap[favoriteId]; try { + await this.favoriteService.deleteFavorite(workspaceSlug, favoriteId); runInAction(() => { - if (parent) { - set( - this.favoriteMap, - [parent, "children"], - 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); if (!child.entity_identifier) return; this.removeFavoriteEntityFromStore(child.entity_identifier, child.entity_type); }); @@ -326,9 +304,6 @@ export class FavoriteStore implements IFavoriteStore { const initialState = this.entityMap[entityId]; try { const favoriteId = this.entityMap[entityId].id; - runInAction(() => { - delete this.entityMap[entityId]; - }); await this.deleteFavorite(workspaceSlug, favoriteId); } catch (error) { console.error("Failed to remove favorite entity from favorite store", error); @@ -341,19 +316,22 @@ export class FavoriteStore implements IFavoriteStore { removeFavoriteFromStore = (entity_identifier: string) => { try { - const favoriteId = this.entityMap[entity_identifier].id; - const favorite = this.favoriteMap[favoriteId]; - const parent = favorite.parent; - + const favoriteId = this.entityMap[entity_identifier]?.id; + const oldData = this.favoriteMap[favoriteId]; + const projectData = Object.values(this.favoriteMap).filter( + (fav) => fav.project_id === entity_identifier && fav.entity_type !== "project" + ); runInAction(() => { - if (parent) { - set( - this.favoriteMap, - [parent, "children"], - this.favoriteMap[parent].children.filter((child) => child.id !== favoriteId) - ); - } + projectData && + projectData.forEach(async (fav) => { + this.removeFavoriteFromStore(fav.entity_identifier!); + this.removeFavoriteEntityFromStore(fav.entity_identifier!, fav.entity_type); + }); + + if (!favoriteId) return; delete this.favoriteMap[favoriteId]; + this.removeFavoriteEntityFromStore(entity_identifier!, oldData.entity_type); + delete this.entityMap[entity_identifier]; this.favoriteIds = this.favoriteIds.filter((id) => id !== favoriteId); }); @@ -373,8 +351,6 @@ export class FavoriteStore implements IFavoriteStore { try { const response = await this.favoriteService.getGroupedFavorites(workspaceSlug, favoriteId); runInAction(() => { - // add children to the favorite - set(this.favoriteMap, [favoriteId, "children"], response); // add the favorites to the map response.forEach((favorite) => { set(this.favoriteMap, [favorite.id], favorite); diff --git a/web/core/store/module.store.ts b/web/core/store/module.store.ts index 23e5cb0127a..c3401dfbe92 100644 --- a/web/core/store/module.store.ts +++ b/web/core/store/module.store.ts @@ -557,6 +557,7 @@ export class ModulesStore implements IModuleStore { .then((response) => { runInAction(() => { set(this.moduleMap, [moduleId, "archived_at"], response.archived_at); + if (this.rootStore.favorite.entityMap[moduleId]) this.rootStore.favorite.removeFavoriteFromStore(moduleId); }); }) .catch((error) => { diff --git a/web/core/store/pages/page.ts b/web/core/store/pages/page.ts index 507259de75a..47096912656 100644 --- a/web/core/store/pages/page.ts +++ b/web/core/store/pages/page.ts @@ -443,6 +443,7 @@ export class Page implements IPage { runInAction(() => { this.archived_at = response.archived_at; }); + if (this.rootStore.favorite.entityMap[this.id]) this.rootStore.favorite.removeFavoriteFromStore(this.id); }; /** diff --git a/web/core/store/project/project.store.ts b/web/core/store/project/project.store.ts index 6e1751f71ce..ef817dab126 100644 --- a/web/core/store/project/project.store.ts +++ b/web/core/store/project/project.store.ts @@ -418,6 +418,7 @@ export class ProjectStore implements IProjectStore { .then((response) => { runInAction(() => { set(this.projectMap, [projectId, "archived_at"], response.archived_at); + this.rootStore.favorite.removeFavoriteFromStore(projectId); }); }) .catch((error) => {