[WEB-994] fix: pages list mutation between projects (#4179)

* fix: pages list mutation between projects

* fix: page tab logic

* chore: remove pageType from the project pages store

* chore: rename computed helper functions
This commit is contained in:
Aaryan Khandelwal 2024-04-15 12:50:22 +05:30 committed by GitHub
parent d58bc03db6
commit 7507cb0a0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 52 deletions

View File

@ -1,19 +1,24 @@
import { FC } from "react"; import { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
// types
import { TPageNavigationTabs } from "@plane/types";
// hooks // hooks
import { useProjectPages } from "@/hooks/store"; import { useProjectPages } from "@/hooks/store";
// components // components
import { PageListBlock } from "./"; import { PageListBlock } from "./";
type TPagesListRoot = { type TPagesListRoot = {
workspaceSlug: string; pageType: TPageNavigationTabs;
projectId: string; projectId: string;
workspaceSlug: string;
}; };
export const PagesListRoot: FC<TPagesListRoot> = observer((props) => { export const PagesListRoot: FC<TPagesListRoot> = observer((props) => {
const { workspaceSlug, projectId } = props; const { pageType, projectId, workspaceSlug } = props;
// hooks // store hooks
const { filteredPageIds } = useProjectPages(projectId); const { getCurrentProjectFilteredPageIds } = useProjectPages(projectId);
// derived values
const filteredPageIds = getCurrentProjectFilteredPageIds(pageType);
if (!filteredPageIds) return <></>; if (!filteredPageIds) return <></>;
return ( return (

View File

@ -42,21 +42,18 @@ export const PageTabNavigation: FC<TPageTabNavigation> = (props) => {
href={`/${workspaceSlug}/projects/${projectId}/pages?type=${tab.key}`} href={`/${workspaceSlug}/projects/${projectId}/pages?type=${tab.key}`}
onClick={(e) => handleTabClick(e, tab.key)} onClick={(e) => handleTabClick(e, tab.key)}
> >
<div> <span
<div className={cn(`block p-3 py-4 text-sm font-medium transition-all`, {
className={cn(`p-3 py-4 text-sm font-medium transition-all`, {
"text-custom-primary-100": tab.key === pageType, "text-custom-primary-100": tab.key === pageType,
})} })}
> >
{tab.label} {tab.label}
</div> </span>
<div <div
className={cn(`rounded-t border-t-2 transition-all`, { className={cn(`rounded-t border-t-2 transition-all border-transparent`, {
"border-custom-primary-100": tab.key === pageType, "border-custom-primary-100": tab.key === pageType,
"border-transparent": tab.key !== pageType,
})} })}
/> />
</div>
</Link> </Link>
))} ))}
</div> </div>

View File

@ -22,7 +22,10 @@ type Props = {
export const PagesListMainContent: React.FC<Props> = observer((props) => { export const PagesListMainContent: React.FC<Props> = observer((props) => {
const { children, pageType, projectId } = props; const { children, pageType, projectId } = props;
// store hooks // store hooks
const { loader, filteredPageIds, pageIds, filters } = useProjectPages(projectId); const { loader, getCurrentProjectFilteredPageIds, getCurrentProjectPageIds, filters } = useProjectPages(projectId);
// derived values
const pageIds = getCurrentProjectPageIds(pageType);
const filteredPageIds = getCurrentProjectFilteredPageIds(pageType);
if (loader === "init-loader") return <PageLoader />; if (loader === "init-loader") return <PageLoader />;
// if no pages exist in the active page type // if no pages exist in the active page type

View File

@ -18,10 +18,7 @@ export const PagesListView: React.FC<TPageView> = observer((props) => {
// store hooks // store hooks
const { getAllPages } = useProjectPages(projectId); const { getAllPages } = useProjectPages(projectId);
// fetching pages list // fetching pages list
useSWR( useSWR(projectId ? `PROJECT_PAGES_${projectId}` : null, projectId ? () => getAllPages(pageType) : null);
projectId && pageType ? `PROJECT_PAGES_${projectId}_${pageType}` : null,
projectId && pageType ? () => getAllPages(pageType) : null
);
// pages loader // pages loader
return ( return (

View File

@ -36,7 +36,11 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
projectId={projectId.toString()} projectId={projectId.toString()}
pageType={currentPageType()} pageType={currentPageType()}
> >
<PagesListRoot workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} /> <PagesListRoot
pageType={currentPageType()}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
</PagesListView> </PagesListView>
); );
}); });

View File

@ -1,12 +1,14 @@
import set from "lodash/set"; import set from "lodash/set";
import unset from "lodash/unset"; import unset from "lodash/unset";
import { makeObservable, observable, runInAction, action, computed } from "mobx"; import { makeObservable, observable, runInAction, action } from "mobx";
import { computedFn } from "mobx-utils"; import { computedFn } from "mobx-utils";
// types
import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types"; import { TPage, TPageFilters, TPageNavigationTabs } from "@plane/types";
// helpers // helpers
import { filterPagesByPageType, orderPages, shouldFilterPage } from "@/helpers/page.helper"; import { filterPagesByPageType, orderPages, shouldFilterPage } from "@/helpers/page.helper";
// services // services
import { PageService } from "@/services/page.service"; import { PageService } from "@/services/page.service";
// store
import { IPageStore, PageStore } from "@/store/pages/page.store"; import { IPageStore, PageStore } from "@/store/pages/page.store";
import { RootStore } from "../root.store"; import { RootStore } from "../root.store";
@ -17,14 +19,12 @@ type TError = { title: string; description: string };
export interface IProjectPageStore { export interface IProjectPageStore {
// observables // observables
loader: TLoader; loader: TLoader;
pageType: TPageNavigationTabs;
data: Record<string, IPageStore>; // pageId => PageStore data: Record<string, IPageStore>; // pageId => PageStore
error: TError | undefined; error: TError | undefined;
filters: TPageFilters; filters: TPageFilters;
// computed
pageIds: string[] | undefined;
filteredPageIds: string[] | undefined;
// helper actions // helper actions
getCurrentProjectPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
getCurrentProjectFilteredPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
pageById: (pageId: string) => IPageStore | undefined; pageById: (pageId: string) => IPageStore | undefined;
updateFilters: <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => void; updateFilters: <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => void;
clearAllFilters: () => void; clearAllFilters: () => void;
@ -38,7 +38,6 @@ export interface IProjectPageStore {
export class ProjectPageStore implements IProjectPageStore { export class ProjectPageStore implements IProjectPageStore {
// observables // observables
loader: TLoader = "init-loader"; loader: TLoader = "init-loader";
pageType: TPageNavigationTabs = "public";
data: Record<string, IPageStore> = {}; // pageId => PageStore data: Record<string, IPageStore> = {}; // pageId => PageStore
error: TError | undefined = undefined; error: TError | undefined = undefined;
filters: TPageFilters = { filters: TPageFilters = {
@ -53,13 +52,9 @@ export class ProjectPageStore implements IProjectPageStore {
makeObservable(this, { makeObservable(this, {
// observables // observables
loader: observable.ref, loader: observable.ref,
pageType: observable.ref,
data: observable, data: observable,
error: observable, error: observable,
filters: observable, filters: observable,
// computed
pageIds: computed,
filteredPageIds: computed,
// helper actions // helper actions
updateFilters: action, updateFilters: action,
clearAllFilters: action, clearAllFilters: action,
@ -73,26 +68,35 @@ export class ProjectPageStore implements IProjectPageStore {
this.service = new PageService(); this.service = new PageService();
} }
get pageIds() { /**
* @description get the current project page ids based on the pageType
* @param {TPageNavigationTabs} pageType
*/
getCurrentProjectPageIds = computedFn((pageType: TPageNavigationTabs) => {
const { projectId } = this.store.app.router; const { projectId } = this.store.app.router;
if (!projectId) return undefined; if (!projectId) return undefined;
// helps to filter pages based on the pageType // helps to filter pages based on the pageType
const pagesByType = filterPagesByPageType(this.pageType, Object.values(this?.data || {})); let pagesByType = filterPagesByPageType(pageType, Object.values(this?.data || {}));
pagesByType = pagesByType.filter((p) => p.project === projectId);
const pages = (pagesByType.map((page) => page.id) as string[]) || undefined; const pages = (pagesByType.map((page) => page.id) as string[]) || undefined;
return pages ?? undefined; return pages ?? undefined;
} });
get filteredPageIds() { /**
* @description get the current project filtered page ids based on the pageType
* @param {TPageNavigationTabs} pageType
*/
getCurrentProjectFilteredPageIds = computedFn((pageType: TPageNavigationTabs) => {
const { projectId } = this.store.app.router; const { projectId } = this.store.app.router;
if (!projectId) return undefined; if (!projectId) return undefined;
// helps to filter pages based on the pageType // helps to filter pages based on the pageType
const pagesByType = filterPagesByPageType(this.pageType, Object.values(this?.data || {})); const pagesByType = filterPagesByPageType(pageType, Object.values(this?.data || {}));
let filteredPages = pagesByType.filter( let filteredPages = pagesByType.filter(
(p) => (p) =>
p.project === projectId &&
p.name?.toLowerCase().includes(this.filters.searchQuery.toLowerCase()) && p.name?.toLowerCase().includes(this.filters.searchQuery.toLowerCase()) &&
shouldFilterPage(p, this.filters.filters) shouldFilterPage(p, this.filters.filters)
); );
@ -101,15 +105,14 @@ export class ProjectPageStore implements IProjectPageStore {
const pages = (filteredPages.map((page) => page.id) as string[]) || undefined; const pages = (filteredPages.map((page) => page.id) as string[]) || undefined;
return pages ?? undefined; return pages ?? undefined;
}
pageById = computedFn((pageId: string) => {
const { projectId } = this.store.app.router;
if (!projectId) return undefined;
return this.data?.[pageId] || undefined;
}); });
/**
* @description get the page store by id
* @param {string} pageId
*/
pageById = computedFn((pageId: string) => this.data?.[pageId] || undefined);
updateFilters = <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => { updateFilters = <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => {
runInAction(() => { runInAction(() => {
set(this.filters, [filterKey], filterValue); set(this.filters, [filterKey], filterValue);
@ -125,17 +128,15 @@ export class ProjectPageStore implements IProjectPageStore {
}); });
/** /**
* @description fetch all the pages based on the navigation tab * @description fetch all the pages
* @param {TPageNavigationTabs} pageType
*/ */
getAllPages = async (pageType: TPageNavigationTabs) => { getAllPages = async (pageType: TPageNavigationTabs) => {
try { try {
const { workspaceSlug, projectId } = this.store.app.router; const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return undefined; if (!workspaceSlug || !projectId) return undefined;
const currentPageIds = this.pageIds; const currentPageIds = this.getCurrentProjectPageIds(pageType);
runInAction(() => { runInAction(() => {
this.pageType = pageType;
this.loader = currentPageIds && currentPageIds.length > 0 ? `mutation-loader` : `init-loader`; this.loader = currentPageIds && currentPageIds.length > 0 ? `mutation-loader` : `init-loader`;
this.error = undefined; this.error = undefined;
}); });