[PE-238] refactor: page store hooks (#6409)

* refactor: page store hooks

* fix: page details instances

* fix: build errors

* refactor: page store hooks

* fix: minor bug
This commit is contained in:
Aaryan Khandelwal 2025-02-19 18:02:14 +05:30 committed by GitHub
parent dd11ebf335
commit 827f47809b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 216 additions and 103 deletions

View file

@ -19,7 +19,9 @@ import { IssuePeekOverview } from "@/components/issues";
import { PageRoot, TPageRootConfig, TPageRootHandlers } from "@/components/pages";
// hooks
import { useEditorConfig } from "@/hooks/editor";
import { useEditorAsset, useProjectPage, useProjectPages, useWorkspace } from "@/hooks/store";
import { useEditorAsset, useWorkspace } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePage, usePageStore } from "@/plane-web/hooks/store";
// plane web services
import { WorkspaceService } from "@/plane-web/services";
// services
@ -31,13 +33,16 @@ const projectPageVersionService = new ProjectPageVersionService();
const PageDetailsPage = observer(() => {
const { workspaceSlug, projectId, pageId } = useParams();
// store hooks
const { createPage, getPageById } = useProjectPages();
const page = useProjectPage(pageId?.toString() ?? "");
const { createPage, fetchPageDetails } = usePageStore(EPageStoreType.PROJECT);
const page = usePage({
pageId: pageId?.toString() ?? "",
storeType: EPageStoreType.PROJECT,
});
const { getWorkspaceBySlug } = useWorkspace();
const { uploadEditorAsset } = useEditorAsset();
// derived values
const workspaceId = workspaceSlug ? (getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "") : "";
const { canCurrentUserAccessPage, id, name, updateDescription } = page;
const { canCurrentUserAccessPage, id, name, updateDescription } = page ?? {};
// entity search handler
const fetchEntityCallback = useCallback(
async (payload: TSearchEntityRequestPayload) =>
@ -53,7 +58,7 @@ const PageDetailsPage = observer(() => {
const { error: pageDetailsError } = useSWR(
workspaceSlug && projectId && pageId ? `PAGE_DETAILS_${pageId}` : null,
workspaceSlug && projectId && pageId
? () => getPageById(workspaceSlug?.toString(), projectId?.toString(), pageId.toString())
? () => fetchPageDetails(workspaceSlug?.toString(), projectId?.toString(), pageId.toString())
: null,
{
revalidateIfStale: true,
@ -70,8 +75,8 @@ const PageDetailsPage = observer(() => {
return await projectPageVersionService.fetchAllVersions(workspaceSlug.toString(), projectId.toString(), pageId);
},
fetchDescriptionBinary: async () => {
if (!workspaceSlug || !projectId || !page.id) return;
return await projectPageService.fetchDescriptionBinary(workspaceSlug.toString(), projectId.toString(), page.id);
if (!workspaceSlug || !projectId || !id) return;
return await projectPageService.fetchDescriptionBinary(workspaceSlug.toString(), projectId.toString(), id);
},
fetchEntity: fetchEntityCallback,
fetchVersionDetails: async (pageId, versionId) => {
@ -84,9 +89,9 @@ const PageDetailsPage = observer(() => {
);
},
getRedirectionLink: (pageId) => `/${workspaceSlug}/projects/${projectId}/pages/${pageId}`,
updateDescription,
updateDescription: updateDescription ?? (async () => {}),
}),
[createPage, fetchEntityCallback, page.id, projectId, updateDescription, workspaceSlug]
[createPage, fetchEntityCallback, id, projectId, updateDescription, workspaceSlug]
);
// page root config
const pageRootConfig: TPageRootConfig = useMemo(
@ -145,6 +150,8 @@ const PageDetailsPage = observer(() => {
</div>
);
if (!page) return null;
return (
<>
<PageHead title={name} />
@ -154,6 +161,7 @@ const PageDetailsPage = observer(() => {
config={pageRootConfig}
handlers={pageRootHandlers}
page={page}
storeType={EPageStoreType.PROJECT}
webhookConnectionParams={webhookConnectionParams}
workspaceSlug={workspaceSlug?.toString() ?? ""}
/>

View file

@ -15,11 +15,13 @@ import { PageEditInformationPopover } from "@/components/pages";
import { convertHexEmojiToDecimal } from "@/helpers/emoji.helper";
import { getPageName } from "@/helpers/page.helper";
// hooks
import { useProjectPage, useProject } from "@/hooks/store";
import { useProject } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs";
import { PageDetailsHeaderExtraActions } from "@/plane-web/components/pages";
// plane web hooks
import { EPageStoreType, usePage } from "@/plane-web/hooks/store";
export interface IPagesHeaderProps {
showButton?: boolean;
@ -32,7 +34,12 @@ export const PageDetailsHeader = observer(() => {
const [isOpen, setIsOpen] = useState(false);
// store hooks
const { currentProjectDetails, loader } = useProject();
const page = useProjectPage(pageId?.toString() ?? "");
const page = usePage({
pageId: pageId?.toString() ?? "",
storeType: EPageStoreType.PROJECT,
});
if (!page) return null;
// derived values
const { name, logo_props, updatePageLogo, isContentEditable } = page;
// use platform
const { isMobile } = usePlatformOS();

View file

@ -13,9 +13,11 @@ import { Breadcrumbs, Button, Header, setToast, TOAST_TYPE } from "@plane/ui";
// helpers
import { BreadcrumbLink } from "@/components/common";
// hooks
import { useEventTracker, useProject, useProjectPages } from "@/hooks/store";
import { useEventTracker, useProject } from "@/hooks/store";
// plane web
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
export const PagesListHeader = observer(() => {
// states
@ -27,7 +29,7 @@ export const PagesListHeader = observer(() => {
const pageType = searchParams.get("type");
// store hooks
const { currentProjectDetails, loader } = useProject();
const { canCurrentUserCreatePage, createPage } = useProjectPages();
const { canCurrentUserCreatePage, createPage } = usePageStore(EPageStoreType.PROJECT);
const { setTrackElement } = useEventTracker();
// handle page create
const handleCreatePage = async () => {

View file

@ -14,6 +14,8 @@ import { PagesListRoot, PagesListView } from "@/components/pages";
import { useProject, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
const ProjectPagesPage = observer(() => {
// router
@ -63,11 +65,12 @@ const ProjectPagesPage = observer(() => {
<>
<PageHead title={pageTitle} />
<PagesListView
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
pageType={currentPageType()}
projectId={projectId.toString()}
storeType={EPageStoreType.PROJECT}
workspaceSlug={workspaceSlug.toString()}
>
<PagesListRoot pageType={currentPageType()} />
<PagesListRoot pageType={currentPageType()} storeType={EPageStoreType.PROJECT} />
</PagesListView>
</>
);

View file

@ -6,6 +6,8 @@ import { CreatePageModal } from "@/components/pages";
import { CreateUpdateProjectViewModal } from "@/components/views";
// hooks
import { useCommandPalette } from "@/hooks/store";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
export type TProjectLevelModalsProps = {
workspaceSlug: string;
@ -53,6 +55,7 @@ export const ProjectLevelModals = observer((props: TProjectLevelModalsProps) =>
pageAccess={createPageModal.pageAccess}
handleModalClose={() => toggleCreatePageModal({ isOpen: false })}
redirectionEnabled
storeType={EPageStoreType.PROJECT}
/>
</>
);

View file

@ -0,0 +1,2 @@
export * from "./use-page-store";
export * from "./use-page";

View file

@ -0,0 +1,24 @@
import { useContext } from "react";
// context
import { StoreContext } from "@/lib/store-context";
// mobx store
import { IProjectPageStore } from "@/store/pages/project-page.store";
export enum EPageStoreType {
PROJECT = "PROJECT_PAGE",
}
export type TReturnType = {
[EPageStoreType.PROJECT]: IProjectPageStore;
};
export const usePageStore = <T extends EPageStoreType>(storeType: T): TReturnType[T] => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("usePageStore must be used within StoreProvider");
if (storeType === EPageStoreType.PROJECT) {
return context.projectPages;
}
throw new Error(`Invalid store type: ${storeType}`);
};

View file

@ -0,0 +1,23 @@
import { useContext } from "react";
// mobx store
import { StoreContext } from "@/lib/store-context";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
export type TArgs = {
pageId: string;
storeType: EPageStoreType;
};
export const usePage = (args: TArgs) => {
const { pageId, storeType } = args;
// context
const context = useContext(StoreContext);
// store hooks
const pageStore = usePageStore(storeType);
if (context === undefined) throw new Error("usePage must be used within StoreProvider");
if (!pageId) throw new Error("pageId is required");
return pageStore.getPageById(pageId);
};

View file

@ -30,6 +30,7 @@ import { usePageOperations } from "@/hooks/use-page-operations";
// plane web components
import { MovePageModal } from "@/plane-web/components/pages";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
import { usePageFlag } from "@/plane-web/hooks/use-page-flag";
// store types
import { TPageInstance } from "@/store/pages/base-page";
@ -55,10 +56,11 @@ type Props = {
optionsOrder: TPageActions[];
page: TPageInstance;
parentRef?: React.RefObject<HTMLElement>;
storeType: EPageStoreType;
};
export const PageActions: React.FC<Props> = observer((props) => {
const { editorRef, extraOptions, optionsOrder, page, parentRef } = props;
const { editorRef, extraOptions, optionsOrder, page, parentRef, storeType } = props;
// states
const [deletePageModal, setDeletePageModal] = useState(false);
const [movePageModal, setMovePageModal] = useState(false);
@ -175,7 +177,12 @@ export const PageActions: React.FC<Props> = observer((props) => {
return (
<>
<MovePageModal isOpen={movePageModal} onClose={() => setMovePageModal(false)} page={page} />
<DeletePageModal isOpen={deletePageModal} onClose={() => setDeletePageModal(false)} page={page} />
<DeletePageModal
isOpen={deletePageModal}
onClose={() => setDeletePageModal(false)}
page={page}
storeType={storeType}
/>
{parentRef && <ContextMenu parentRef={parentRef} items={arrangedOptions} />}
<CustomMenu placement="bottom-end" optionsClassName="max-h-[90vh]" ellipsis closeOnSelect>
{arrangedOptions.map((item) => {

View file

@ -12,16 +12,19 @@ import { PageInfoPopover, PageOptionsDropdown } from "@/components/pages";
import { renderFormattedDate } from "@/helpers/date-time.helper";
// hooks
import useOnlineStatus from "@/hooks/use-online-status";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
type Props = {
editorRef: EditorRefApi;
page: TPageInstance;
storeType: EPageStoreType;
};
export const PageExtraOptions: React.FC<Props> = observer((props) => {
const { editorRef, page } = props;
const { editorRef, page, storeType } = props;
// derived values
const {
archived_at,
@ -84,7 +87,7 @@ export const PageExtraOptions: React.FC<Props> = observer((props) => {
/>
)}
<PageInfoPopover editorRef={editorRef} page={page} />
<PageOptionsDropdown editorRef={editorRef} page={page} />
<PageOptionsDropdown editorRef={editorRef} page={page} storeType={storeType} />
</div>
);
});

View file

@ -5,6 +5,8 @@ import { Header, EHeaderVariant } from "@plane/ui";
import { PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
// hooks
import { usePageFilters } from "@/hooks/use-page-filters";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
@ -13,10 +15,11 @@ type Props = {
page: TPageInstance;
setSidePeekVisible: (sidePeekState: boolean) => void;
sidePeekVisible: boolean;
storeType: EPageStoreType;
};
export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
const { editorRef, page, setSidePeekVisible, sidePeekVisible } = props;
const { editorRef, page, setSidePeekVisible, sidePeekVisible, storeType } = props;
// derived values
const { isContentEditable } = page;
// page filters
@ -33,7 +36,7 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
setSidePeekVisible={setSidePeekVisible}
/>
</div>
<PageExtraOptions editorRef={editorRef} page={page} />
<PageExtraOptions editorRef={editorRef} page={page} storeType={storeType} />
</Header>
<Header variant={EHeaderVariant.TERNARY}>
{isContentEditable && editorRef && <PageToolbar editorRef={editorRef} />}

View file

@ -15,16 +15,19 @@ import { copyTextToClipboard } from "@/helpers/string.helper";
// hooks
import { usePageFilters } from "@/hooks/use-page-filters";
import { useQueryParams } from "@/hooks/use-query-params";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
type Props = {
editorRef: EditorRefApi | null;
page: TPageInstance;
storeType: EPageStoreType;
};
export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
const { editorRef, page } = props;
const { editorRef, page, storeType } = props;
// states
const [isExportModalOpen, setIsExportModalOpen] = useState(false);
// router
@ -136,6 +139,7 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
"export",
]}
page={page}
storeType={storeType}
/>
</>
);

View file

@ -7,6 +7,8 @@ import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageT
import { cn } from "@/helpers/common.helper";
// hooks
import { usePageFilters } from "@/hooks/use-page-filters";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
@ -16,10 +18,11 @@ type Props = {
page: TPageInstance;
setSidePeekVisible: (sidePeekState: boolean) => void;
sidePeekVisible: boolean;
storeType: EPageStoreType;
};
export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
const { editorReady, editorRef, page, setSidePeekVisible, sidePeekVisible } = props;
const { editorReady, editorRef, page, setSidePeekVisible, sidePeekVisible, storeType } = props;
// derived values
const { isContentEditable } = page;
// page filters
@ -52,7 +55,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
<PageToolbar editorRef={editorRef?.current} />
)}
</Header.LeftItem>
<PageExtraOptions editorRef={resolvedEditorRef} page={page} />
<PageExtraOptions editorRef={resolvedEditorRef} page={page} storeType={storeType} />
</Header>
<div className="md:hidden">
<PageEditorMobileHeaderRoot
@ -60,6 +63,7 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
page={page}
sidePeekVisible={sidePeekVisible}
setSidePeekVisible={setSidePeekVisible}
storeType={storeType}
/>
</div>
</>

View file

@ -18,6 +18,8 @@ import {
import { useAppRouter } from "@/hooks/use-app-router";
import { usePageFallback } from "@/hooks/use-page-fallback";
import { useQueryParams } from "@/hooks/use-query-params";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
@ -36,12 +38,13 @@ type TPageRootProps = {
config: TPageRootConfig;
handlers: TPageRootHandlers;
page: TPageInstance;
storeType: EPageStoreType;
webhookConnectionParams: TWebhookConnectionQueryParams;
workspaceSlug: string;
};
export const PageRoot = observer((props: TPageRootProps) => {
const { config, handlers, page, webhookConnectionParams, workspaceSlug } = props;
const { config, handlers, page, storeType, webhookConnectionParams, workspaceSlug } = props;
// states
const [editorReady, setEditorReady] = useState(false);
const [hasConnectionFailed, setHasConnectionFailed] = useState(false);
@ -107,6 +110,7 @@ export const PageRoot = observer((props: TPageRootProps) => {
page={page}
setSidePeekVisible={(state) => setSidePeekVisible(state)}
sidePeekVisible={sidePeekVisible}
storeType={storeType}
/>
<PageEditorBody
config={config}

View file

@ -16,19 +16,22 @@ import {
// helpers
import { calculateTotalFilters } from "@/helpers/filter.helper";
// hooks
import { useMember, useProjectPages } from "@/hooks/store";
import { useMember } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
type Props = {
pageType: TPageNavigationTabs;
projectId: string;
storeType: EPageStoreType;
workspaceSlug: string;
};
export const PagesListHeaderRoot: React.FC<Props> = observer((props) => {
const { pageType, projectId, workspaceSlug } = props;
const { pageType, projectId, storeType, workspaceSlug } = props;
const { t } = useTranslation();
// store hooks
const { filters, updateFilters, clearAllFilters } = useProjectPages();
const { filters, updateFilters, clearAllFilters } = usePageStore(storeType);
const {
workspace: { workspaceMemberIds },
} = useMember();

View file

@ -13,15 +13,19 @@ import { getFileURL } from "@/helpers/file.helper";
// hooks
import { useMember } from "@/hooks/store";
import { usePageOperations } from "@/hooks/use-page-operations";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
type Props = {
page: TPageInstance;
parentRef: React.RefObject<HTMLElement>;
storeType: EPageStoreType;
};
export const BlockItemAction: FC<Props> = observer((props) => {
const { page, parentRef } = props;
const { page, parentRef, storeType } = props;
// store hooks
const { getUserDetails } = useMember();
// page operations
@ -80,6 +84,7 @@ export const BlockItemAction: FC<Props> = observer((props) => {
]}
page={page}
parentRef={parentRef}
storeType={storeType}
/>
</>
);

View file

@ -11,20 +11,26 @@ import { BlockItemAction } from "@/components/pages/list";
import { getPageName } from "@/helpers/page.helper";
// hooks
import { usePlatformOS } from "@/hooks/use-platform-os";
import { TUsePage } from "@/store/pages/base-page";
// plane web hooks
import { EPageStoreType, usePage } from "@/plane-web/hooks/store";
type TPageListBlock = {
pageId: string;
usePage: TUsePage;
storeType: EPageStoreType;
};
export const PageListBlock: FC<TPageListBlock> = observer((props) => {
const { pageId, usePage } = props;
const { pageId, storeType } = props;
// refs
const parentRef = useRef(null);
// hooks
const page = usePage(pageId);
const page = usePage({
pageId,
storeType,
});
const { isMobile } = usePlatformOS();
// handle page check
if (!page) return null;
// derived values
const { name, logo_props, getRedirectionLink } = page;
@ -41,7 +47,7 @@ export const PageListBlock: FC<TPageListBlock> = observer((props) => {
}
title={getPageName(name)}
itemLink={getRedirectionLink()}
actionableItems={<BlockItemAction page={page} parentRef={parentRef} />}
actionableItems={<BlockItemAction page={page} parentRef={parentRef} storeType={storeType} />}
isMobile={isMobile}
parentRef={parentRef}
/>

View file

@ -4,19 +4,20 @@ import { observer } from "mobx-react";
import { TPageNavigationTabs } from "@plane/types";
// components
import { ListLayout } from "@/components/core/list";
// hooks
import { useProjectPage, useProjectPages } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
// components
import { PageListBlock } from "./";
type TPagesListRoot = {
pageType: TPageNavigationTabs;
storeType: EPageStoreType;
};
export const PagesListRoot: FC<TPagesListRoot> = observer((props) => {
const { pageType } = props;
const { pageType, storeType } = props;
// store hooks
const { getCurrentProjectFilteredPageIds } = useProjectPages();
const { getCurrentProjectFilteredPageIds } = usePageStore(storeType);
// derived values
const filteredPageIds = getCurrentProjectFilteredPageIds(pageType);
@ -24,7 +25,7 @@ export const PagesListRoot: FC<TPagesListRoot> = observer((props) => {
return (
<ListLayout>
{filteredPageIds.map((pageId) => (
<PageListBlock key={pageId} pageId={pageId} usePage={useProjectPage} />
<PageListBlock key={pageId} pageId={pageId} storeType={storeType} />
))}
</ListLayout>
);

View file

@ -7,8 +7,10 @@ import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// components
import { PageForm } from "@/components/pages";
// hooks
import { useProjectPages, useEventTracker } from "@/hooks/store";
import { useEventTracker } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
type Props = {
workspaceSlug: string;
@ -17,10 +19,19 @@ type Props = {
pageAccess?: EPageAccess;
handleModalClose: () => void;
redirectionEnabled?: boolean;
storeType: EPageStoreType;
};
export const CreatePageModal: FC<Props> = (props) => {
const { workspaceSlug, projectId, isModalOpen, pageAccess, handleModalClose, redirectionEnabled = false } = props;
const {
workspaceSlug,
projectId,
isModalOpen,
pageAccess,
handleModalClose,
redirectionEnabled = false,
storeType,
} = props;
// states
const [pageFormData, setPageFormData] = useState<Partial<TPage>>({
id: undefined,
@ -30,7 +41,7 @@ export const CreatePageModal: FC<Props> = (props) => {
// router
const router = useAppRouter();
// store hooks
const { createPage } = useProjectPages();
const { createPage } = usePageStore(storeType);
const { capturePageEvent } = useEventTracker();
const handlePageFormData = <T extends keyof TPage>(key: T, value: TPage[T]) =>
setPageFormData((prev) => ({ ...prev, [key]: value }));

View file

@ -7,21 +7,25 @@ import { PAGE_DELETED } from "@plane/constants";
import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
// constants
// hooks
import { useEventTracker, useProjectPages } from "@/hooks/store";
import { useEventTracker } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
// store
import { TPageInstance } from "@/store/pages/base-page";
type TConfirmPageDeletionProps = {
page: TPageInstance;
isOpen: boolean;
onClose: () => void;
page: TPageInstance;
storeType: EPageStoreType;
};
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = observer((props) => {
const { page, isOpen, onClose } = props;
const { isOpen, onClose, page, storeType } = props;
// states
const [isDeleting, setIsDeleting] = useState(false);
// store hooks
const { removePage } = useProjectPages();
const { removePage } = usePageStore(storeType);
const { capturePageEvent } = useEventTracker();
if (!page || !page.id) return null;
// derived values

View file

@ -7,8 +7,10 @@ import { TPageNavigationTabs } from "@plane/types";
// components
import { DetailedEmptyState } from "@/components/empty-state";
import { PageLoader } from "@/components/pages";
import { useCommandPalette, useProjectPages, useUserPermissions } from "@/hooks/store";
import { useCommandPalette, useUserPermissions } from "@/hooks/store";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
// assets
import AllFiltersImage from "@/public/empty-state/pages/all-filters.svg";
import NameFilterImage from "@/public/empty-state/pages/name-filter.svg";
@ -16,15 +18,16 @@ import NameFilterImage from "@/public/empty-state/pages/name-filter.svg";
type Props = {
children: React.ReactNode;
pageType: TPageNavigationTabs;
storeType: EPageStoreType;
};
export const PagesListMainContent: React.FC<Props> = observer((props) => {
const { children, pageType } = props;
const { children, pageType, storeType } = props;
// plane hooks
const { t } = useTranslation();
// store hooks
const { loader, isAnyPageAvailable, getCurrentProjectFilteredPageIds, getCurrentProjectPageIds, filters } =
useProjectPages();
usePageStore(storeType);
const { toggleCreatePageModal } = useCommandPalette();
const { allowPermissions } = useUserPermissions();
// derived values

View file

@ -3,24 +3,25 @@ import useSWR from "swr";
import { TPageNavigationTabs } from "@plane/types";
// components
import { PagesListHeaderRoot, PagesListMainContent } from "@/components/pages";
// hooks
import { useProjectPages } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
type TPageView = {
workspaceSlug: string;
projectId: string;
pageType: TPageNavigationTabs;
children: React.ReactNode;
pageType: TPageNavigationTabs;
projectId: string;
storeType: EPageStoreType;
workspaceSlug: string;
};
export const PagesListView: React.FC<TPageView> = observer((props) => {
const { workspaceSlug, projectId, pageType, children } = props;
const { children, pageType, projectId, storeType, workspaceSlug } = props;
// store hooks
const { isAnyPageAvailable, getAllPages } = useProjectPages();
const { isAnyPageAvailable, fetchPagesList } = usePageStore(storeType);
// fetching pages list
useSWR(
workspaceSlug && projectId && pageType ? `PROJECT_PAGES_${projectId}` : null,
workspaceSlug && projectId && pageType ? () => getAllPages(workspaceSlug, projectId, pageType) : null
workspaceSlug && projectId && pageType ? () => fetchPagesList(workspaceSlug, projectId, pageType) : null
);
// pages loader
@ -28,9 +29,16 @@ export const PagesListView: React.FC<TPageView> = observer((props) => {
<div className="relative w-full h-full overflow-hidden flex flex-col">
{/* tab header */}
{isAnyPageAvailable && (
<PagesListHeaderRoot pageType={pageType} projectId={projectId} workspaceSlug={workspaceSlug} />
<PagesListHeaderRoot
pageType={pageType}
projectId={projectId}
storeType={storeType}
workspaceSlug={workspaceSlug}
/>
)}
<PagesListMainContent pageType={pageType}>{children}</PagesListMainContent>
<PagesListMainContent pageType={pageType} storeType={storeType}>
{children}
</PagesListMainContent>
</div>
);
});

View file

@ -1,6 +1,5 @@
export * from "./estimates";
export * from "./notifications";
export * from "./pages";
export * from "./use-app-theme";
export * from "./use-calendar-view";
export * from "./use-command-palette";

View file

@ -1,2 +0,0 @@
export * from "./use-page";
export * from "./use-project-page";

View file

@ -1,15 +0,0 @@
import { useContext } from "react";
// mobx store
import { StoreContext } from "@/lib/store-context";
import { TUsePage } from "@/store/pages/base-page";
// store
import { TProjectPage } from "@/store/pages/project-page";
export const useProjectPage: TUsePage = (pageId: string | undefined): TProjectPage => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("usePage must be used within StoreProvider");
if (!pageId) return {} as TProjectPage;
return context.projectPages.data?.[pageId] ?? {};
};

View file

@ -1,11 +0,0 @@
import { useContext } from "react";
// context
import { StoreContext } from "@/lib/store-context";
// mobx store
import { IProjectPageStore } from "@/store/pages/project-page.store";
export const useProjectPages = (): IProjectPageStore => {
const context = useContext(StoreContext);
if (context === undefined) throw new Error("useProjectPage must be used within StoreProvider");
return context.projectPages;
};

View file

@ -8,7 +8,9 @@ import {
// helpers
import { getPageName } from "@/helpers/page.helper";
// hooks
import { useProject, useProjectPage, useProjectView, useCycle, useModule } from "@/hooks/store";
import { useProject, useProjectView, useCycle, useModule } from "@/hooks/store";
// plane web hooks
import { EPageStoreType, usePage } from "@/plane-web/hooks/store";
export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorite) => {
const favoriteItemId = favorite?.entity_data?.id;
@ -23,7 +25,10 @@ export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorit
const { getModuleById } = useModule();
// derived values
const pageDetail = useProjectPage(favoriteItemId ?? "");
const pageDetail = usePage({
pageId: favoriteItemId ?? "",
storeType: EPageStoreType.PROJECT,
});
const viewDetails = getViewById(favoriteItemId ?? "");
const cycleDetail = getCycleById(favoriteItemId ?? "");
const moduleDetail = getModuleById(favoriteItemId ?? "");
@ -40,7 +45,7 @@ export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorit
itemIcon = getFavoriteItemIcon("project", currentProjectDetails?.logo_props || favoriteItemLogoProps);
break;
case "page":
itemTitle = getPageName(pageDetail.name || favoriteItemName);
itemTitle = getPageName(pageDetail?.name || favoriteItemName);
itemIcon = getFavoriteItemIcon("page", pageDetail?.logo_props || favoriteItemLogoProps);
break;
case "view":

View file

@ -64,8 +64,6 @@ export type TPageInstance = TBasePage &
getRedirectionLink: () => string;
};
export type TUsePage = (pageId: string | undefined) => TPageInstance;
export class BasePage implements TBasePage {
// loaders
isSubmitting: TNameDescriptionLoader = "saved";

View file

@ -39,16 +39,16 @@ export interface IProjectPageStore {
// helper actions
getCurrentProjectPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
getCurrentProjectFilteredPageIds: (pageType: TPageNavigationTabs) => string[] | undefined;
pageById: (pageId: string) => TProjectPage | undefined;
getPageById: (pageId: string) => TProjectPage | undefined;
updateFilters: <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => void;
clearAllFilters: () => void;
// actions
getAllPages: (
fetchPagesList: (
workspaceSlug: string,
projectId: string,
pageType: TPageNavigationTabs
) => Promise<TPage[] | undefined>;
getPageById: (workspaceSlug: string, projectId: string, pageId: string) => Promise<TPage | undefined>;
fetchPageDetails: (workspaceSlug: string, projectId: string, pageId: string) => Promise<TPage | undefined>;
createPage: (pageData: Partial<TPage>) => Promise<TPage | undefined>;
removePage: (pageId: string) => Promise<void>;
movePage: (workspaceSlug: string, projectId: string, pageId: string, newProjectId: string) => Promise<void>;
@ -82,8 +82,8 @@ export class ProjectPageStore implements IProjectPageStore {
updateFilters: action,
clearAllFilters: action,
// actions
getAllPages: action,
getPageById: action,
fetchPagesList: action,
fetchPageDetails: action,
createPage: action,
removePage: action,
movePage: action,
@ -164,7 +164,7 @@ export class ProjectPageStore implements IProjectPageStore {
* @description get the page store by id
* @param {string} pageId
*/
pageById = computedFn((pageId: string) => this.data?.[pageId] || undefined);
getPageById = computedFn((pageId: string) => this.data?.[pageId] || undefined);
updateFilters = <T extends keyof TPageFilters>(filterKey: T, filterValue: TPageFilters[T]) => {
runInAction(() => {
@ -183,7 +183,7 @@ export class ProjectPageStore implements IProjectPageStore {
/**
* @description fetch all the pages
*/
getAllPages = async (workspaceSlug: string, projectId: string, pageType: TPageNavigationTabs) => {
fetchPagesList = async (workspaceSlug: string, projectId: string, pageType: TPageNavigationTabs) => {
try {
if (!workspaceSlug || !projectId) return undefined;
@ -216,11 +216,11 @@ export class ProjectPageStore implements IProjectPageStore {
* @description fetch the details of a page
* @param {string} pageId
*/
getPageById = async (workspaceSlug: string, projectId: string, pageId: string) => {
fetchPageDetails = async (workspaceSlug: string, projectId: string, pageId: string) => {
try {
if (!workspaceSlug || !projectId || !pageId) return undefined;
const currentPageId = this.pageById(pageId);
const currentPageId = this.getPageById(pageId);
runInAction(() => {
this.loader = currentPageId ? `mutation-loader` : `init-loader`;
this.error = undefined;

View file

@ -0,0 +1 @@
export * from "ce/hooks/store";