Improvement: High Performance MobX Integration for Pages ✈︎ (#3397)

* fix: removed parameters `workspace`, `project` & `id` from the patch calls

* feat: modified components to work with new pages hooks

* feat: modified stores

* feat: modified initial component

* feat: component implementation changes

* feat: store implementation

* refactor pages store

* feat: updated page store to perform async operations faster

* fix: added types for archive and restore pages

* feat: implemented archive and restore pages

* fix: page creating twice when form submit

* feat: updated create-page-modal

* feat: updated page form and delete page modal

* fix: create page modal not updating isSubmitted prop

* feat: list items and list view refactored for pages

* feat: refactored project-page-store for inserting computed pagesids

* chore: renamed project pages hook

* feat: added favourite pages implementation

* fix: implemented store for archived pages

* fix: project page store for recent pages

* fix: issue suggestions breaking pages

* fix: issue embeds and suggestions breaking

* feat: implemented page store and project page store in page editor

* chore: lock file changes

* fix: modified page details header to catch mobx updates instead of swr calls

* fix: modified usePage hook to fetch page details when reloaded directly on page

* fix: fixed deleting pages

* fix: removed render on props changed

* feat: implemented page store inside page details

* fix: role change in pages archives

* fix: rerending of pages on tab change

* fix: reimplementation of peek overview inside pages

* chore: typo fixes

* fix: issue suggestion widget selecting wrong issues on click

* feat: added labels in pages

* fix: deepsource errors fixed

* fix: build errors

* fix: review comments

* fix: removed swr hooks from the `usePage` store hook and refactored `issueEmbed` hook

* fix: resolved reviewed comments

---------

Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local>
This commit is contained in:
Henit Chobisa 2024-01-19 15:18:47 +05:30 committed by sriram veeraghanta
parent d3dedc8e51
commit 06a7bdffd7
32 changed files with 960 additions and 1100 deletions

View file

@ -1,23 +1,17 @@
import { FC } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { FileText, Plus } from "lucide-react";
// hooks
import { useApplication, useProject } from "hooks/store";
// services
import { PageService } from "services/page.service";
import { useApplication, usePage, useProject } from "hooks/store";
// ui
import { Breadcrumbs, Button } from "@plane/ui";
// helpers
import { renderEmoji } from "helpers/emoji.helper";
// fetch-keys
import { PAGE_DETAILS } from "constants/fetch-keys";
export interface IPagesHeaderProps {
showButton?: boolean;
}
const pageService = new PageService();
export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
const { showButton = false } = props;
@ -28,12 +22,7 @@ export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
const { commandPalette: commandPaletteStore } = useApplication();
const { currentProjectDetails } = useProject();
const { data: pageDetails } = useSWR(
workspaceSlug && currentProjectDetails?.id && pageId ? PAGE_DETAILS(pageId as string) : null,
workspaceSlug && currentProjectDetails?.id
? () => pageService.getPageDetails(workspaceSlug as string, currentProjectDetails.id, pageId as string)
: null
);
const pageDetails = usePage(pageId as string);
return (
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">

View file

@ -2,132 +2,56 @@ import React, { FC } from "react";
import { useRouter } from "next/router";
import { Dialog, Transition } from "@headlessui/react";
// hooks
import { useApplication, usePage, useWorkspace } from "hooks/store";
import useToast from "hooks/use-toast";
import { useApplication } from "hooks/store";
// components
import { PageForm } from "./page-form";
// types
import { IPage } from "@plane/types";
import { useProjectPages } from "hooks/store/use-project-page";
import { IPageStore } from "store/page.store";
type Props = {
data?: IPage | null;
// data?: IPage | null;
pageStore?: IPageStore;
handleClose: () => void;
isOpen: boolean;
projectId: string;
};
export const CreateUpdatePageModal: FC<Props> = (props) => {
const { isOpen, handleClose, data, projectId } = props;
const { isOpen, handleClose, projectId, pageStore } = props;
// router
const router = useRouter();
const { workspaceSlug } = router.query;
const { createPage } = useProjectPages();
// store hooks
const {
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentWorkspace } = useWorkspace();
const { createPage, updatePage } = usePage();
// toast alert
const { setToastAlert } = useToast();
const onClose = () => {
handleClose();
};
const createProjectPage = async (payload: IPage) => {
if (!workspaceSlug) return;
// await createPage(workspaceSlug.toString(), projectId, payload)
// .then((res) => {
// router.push(`/${workspaceSlug}/projects/${projectId}/pages/${res.id}`);
// onClose();
// setToastAlert({
// type: "success",
// title: "Success!",
// message: "Page created successfully.",
// });
// postHogEventTracker(
// "PAGE_CREATED",
// {
// ...res,
// state: "SUCCESS",
// },
// {
// isGrouping: true,
// groupType: "Workspace_metrics",
// groupId: currentWorkspace?.id!,
// }
// );
// })
// .catch((err) => {
// setToastAlert({
// type: "error",
// title: "Error!",
// message: err.detail ?? "Page could not be created. Please try again.",
// });
// postHogEventTracker(
// "PAGE_CREATED",
// {
// state: "FAILED",
// },
// {
// isGrouping: true,
// groupType: "Workspace_metrics",
// groupId: currentWorkspace?.id!,
// }
// );
// });
};
const updateProjectPage = async (payload: IPage) => {
if (!data || !workspaceSlug) return;
// await updatePage(workspaceSlug.toString(), projectId, data.id, payload)
// .then((res) => {
// onClose();
// setToastAlert({
// type: "success",
// title: "Success!",
// message: "Page updated successfully.",
// });
// postHogEventTracker(
// "PAGE_UPDATED",
// {
// ...res,
// state: "SUCCESS",
// },
// {
// isGrouping: true,
// groupType: "Workspace_metrics",
// groupId: currentWorkspace?.id!,
// }
// );
// })
// .catch((err) => {
// setToastAlert({
// type: "error",
// title: "Error!",
// message: err.detail ?? "Page could not be updated. Please try again.",
// });
// postHogEventTracker(
// "PAGE_UPDATED",
// {
// state: "FAILED",
// },
// {
// isGrouping: true,
// groupType: "Workspace_metrics",
// groupId: currentWorkspace?.id!,
// }
// );
// });
await createPage(workspaceSlug.toString(), projectId, payload);
};
const handleFormSubmit = async (formData: IPage) => {
if (!workspaceSlug || !projectId) return;
if (!data) await createProjectPage(formData);
else await updateProjectPage(formData);
try {
if (pageStore) {
if (pageStore.name !== formData.name) {
await pageStore.updateName(formData.name);
}
if (pageStore.access !== formData.access) {
formData.access === 1 ? await pageStore.makePrivate() : await pageStore.makePublic();
}
} else {
await createProjectPage(formData);
}
handleClose();
} catch (error) {
console.log(error);
}
};
return (
@ -157,7 +81,7 @@ export const CreateUpdatePageModal: FC<Props> = (props) => {
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform rounded-lg bg-custom-background-100 p-5 px-4 text-left shadow-custom-shadow-md transition-all sm:w-full sm:max-w-2xl">
<PageForm handleFormSubmit={handleFormSubmit} handleClose={handleClose} data={data} />
<PageForm handleFormSubmit={handleFormSubmit} handleClose={handleClose} pageStore={pageStore} />
</Dialog.Panel>
</Transition.Child>
</div>

View file

@ -9,37 +9,45 @@ import useToast from "hooks/use-toast";
// ui
import { Button } from "@plane/ui";
// types
import type { IPage } from "@plane/types";
import { useProjectPages } from "hooks/store/use-project-page";
type TConfirmPageDeletionProps = {
data?: IPage | null;
pageId: string;
isOpen: boolean;
onClose: () => void;
};
export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = observer((props) => {
const { data, isOpen, onClose } = props;
const { pageId, isOpen, onClose } = props;
// states
const [isDeleting, setIsDeleting] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const { deletePage } = usePage();
const { deletePage } = useProjectPages();
const pageStore = usePage(pageId);
// toast alert
const { setToastAlert } = useToast();
if (!pageStore) return null;
const { name } = pageStore;
const handleClose = () => {
setIsDeleting(false);
onClose();
};
const handleDelete = async () => {
if (!data || !workspaceSlug || !projectId) return;
if (!pageId || !workspaceSlug || !projectId) return;
setIsDeleting(true);
await deletePage(workspaceSlug.toString(), data.project, data.id)
// Delete Page will only delete the page from the archive page map, at this point only archived pages can be deleted
await deletePage(workspaceSlug.toString(), projectId as string, pageId)
.then(() => {
handleClose();
setToastAlert({
@ -99,8 +107,8 @@ export const DeletePageModal: React.FC<TConfirmPageDeletionProps> = observer((pr
<div className="mt-2">
<p className="text-sm text-custom-text-200">
Are you sure you want to delete page-{" "}
<span className="break-words font-medium text-custom-text-100">{data?.name}</span>? The Page
will be deleted permanently. This action cannot be undone.
<span className="break-words font-medium text-custom-text-100">{name}</span>? The Page will be
deleted permanently. This action cannot be undone.
</p>
</div>
</div>

View file

@ -5,11 +5,12 @@ import { Button, Input, Tooltip } from "@plane/ui";
import { IPage } from "@plane/types";
// constants
import { PAGE_ACCESS_SPECIFIERS } from "constants/page";
import { IPageStore } from "store/page.store";
type Props = {
handleFormSubmit: (values: IPage) => Promise<void>;
handleClose: () => void;
data?: IPage | null;
pageStore?: IPageStore;
};
const defaultValues = {
@ -19,24 +20,24 @@ const defaultValues = {
};
export const PageForm: React.FC<Props> = (props) => {
const { handleFormSubmit, handleClose, data } = props;
const { handleFormSubmit, handleClose, pageStore } = props;
const {
formState: { errors, isSubmitting },
handleSubmit,
control,
} = useForm<IPage>({
defaultValues: { ...defaultValues, ...data },
defaultValues: pageStore
? { name: pageStore.name, description: pageStore.description, access: pageStore.access }
: defaultValues,
});
const handleCreateUpdatePage = async (formData: IPage) => {
await handleFormSubmit(formData);
};
const handleCreateUpdatePage = (formData: IPage) => handleFormSubmit(formData);
return (
<form onSubmit={handleSubmit(handleCreateUpdatePage)}>
<div className="space-y-4">
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{data ? "Update" : "Create"} Page</h3>
<h3 className="text-lg font-medium leading-6 text-custom-text-100">{pageStore ? "Update" : "Create"} Page</h3>
<div className="space-y-3">
<div>
<Controller
@ -104,7 +105,7 @@ export const PageForm: React.FC<Props> = (props) => {
Cancel
</Button>
<Button variant="primary" size="sm" type="submit" loading={isSubmitting} tabIndex={5}>
{data ? (isSubmitting ? "Updating..." : "Update page") : isSubmitting ? "Creating..." : "Create Page"}
{pageStore ? (isSubmitting ? "Updating..." : "Update page") : isSubmitting ? "Creating..." : "Create Page"}
</Button>
</div>
</div>

View file

@ -1,17 +1,17 @@
import { FC } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { usePage } from "hooks/store";
// components
import { PagesListView } from "components/pages/pages-list";
// ui
import { Loader } from "@plane/ui";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const AllPagesList: FC = observer(() => {
// store
const { projectPageIds } = usePage();
const pageStores = useProjectPages();
// subscribing to the projectPageStore
const { projectPageIds } = pageStores;
if (!projectPageIds)
if (!projectPageIds) {
return (
<Loader className="space-y-4">
<Loader.Item height="40px" />
@ -19,6 +19,6 @@ export const AllPagesList: FC = observer(() => {
<Loader.Item height="40px" />
</Loader>
);
}
return <PagesListView pageIds={projectPageIds} />;
});

View file

@ -3,14 +3,15 @@ import { observer } from "mobx-react-lite";
// components
import { PagesListView } from "components/pages/pages-list";
// hooks
import { usePage } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const ArchivedPagesList: FC = observer(() => {
const { archivedProjectPageIds } = usePage();
const projectPageStore = useProjectPages();
const { archivedPageIds } = projectPageStore;
if (!archivedProjectPageIds)
if (!archivedPageIds)
return (
<Loader className="space-y-4">
<Loader.Item height="40px" />
@ -19,5 +20,5 @@ export const ArchivedPagesList: FC = observer(() => {
</Loader>
);
return <PagesListView pageIds={archivedProjectPageIds} />;
return <PagesListView pageIds={archivedPageIds} />;
});

View file

@ -3,12 +3,13 @@ import { observer } from "mobx-react-lite";
// components
import { PagesListView } from "components/pages/pages-list";
// hooks
import { usePage } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const FavoritePagesList: FC = observer(() => {
const { favoriteProjectPageIds } = usePage();
const projectPageStore = useProjectPages();
const { favoriteProjectPageIds } = projectPageStore;
if (!favoriteProjectPageIds)
return (

View file

@ -13,10 +13,6 @@ import {
Star,
Trash2,
} from "lucide-react";
// hooks
import { useMember, usePage, useUser } from "hooks/store";
import useToast from "hooks/use-toast";
// helpers
import { copyUrlToClipboard } from "helpers/string.helper";
import { renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
// ui
@ -25,142 +21,120 @@ import { CustomMenu, Tooltip } from "@plane/ui";
import { CreateUpdatePageModal, DeletePageModal } from "components/pages";
// constants
import { EUserProjectRoles } from "constants/project";
import { useRouter } from "next/router";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
import { useMember, usePage, useUser } from "hooks/store";
import { IIssueLabel } from "@plane/types";
export interface IPagesListItem {
workspaceSlug: string;
projectId: string;
pageId: string;
projectId: string;
}
export const PagesListItem: FC<IPagesListItem> = observer((props) => {
const { workspaceSlug, projectId, pageId } = props;
export const PagesListItem: FC<IPagesListItem> = observer(({ pageId, projectId }: IPagesListItem) => {
const projectPageStore = useProjectPages();
// Now, I am observing only the projectPages, out of the projectPageStore.
const { archivePage, restorePage } = projectPageStore;
const pageStore = usePage(pageId);
// states
const router = useRouter();
const { workspaceSlug } = router.query;
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [deletePageModal, setDeletePageModal] = useState(false);
// store hooks
const {
currentUser,
membership: { currentProjectRole },
} = useUser();
const {
getArchivedPageById,
getUnArchivedPageById,
archivePage,
removeFromFavorites,
addToFavorites,
makePrivate,
makePublic,
restorePage,
} = usePage();
const {
project: { getProjectMemberDetails },
} = useMember();
// toast alert
const { setToastAlert } = useToast();
// derived values
const pageDetails = getUnArchivedPageById(pageId) ?? getArchivedPageById(pageId);
const handleCopyUrl = (e: any) => {
if (!pageStore) return null;
const {
archived_at,
label_details,
access,
is_favorite,
owned_by,
name,
created_at,
updated_at,
makePublic,
makePrivate,
addToFavorites,
removeFromFavorites,
} = pageStore;
const handleCopyUrl = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/pages/${pageId}`).then(() => {
setToastAlert({
type: "success",
title: "Link Copied!",
message: "Page link copied to clipboard.",
});
});
await copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/pages/${pageId}`);
};
const handleAddToFavorites = (e: any) => {
const handleAddToFavorites = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
addToFavorites();
};
const handleRemoveFromFavorites = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
addToFavorites(workspaceSlug, projectId, pageId)
.then(() => {
setToastAlert({
type: "success",
title: "Success!",
message: "Successfully added the page to favorites.",
});
})
.catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't add the page to favorites. Please try again.",
});
});
removeFromFavorites();
};
const handleRemoveFromFavorites = (e: any) => {
const handleMakePublic = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
removeFromFavorites(workspaceSlug, projectId, pageId)
.then(() => {
setToastAlert({
type: "success",
title: "Success!",
message: "Successfully removed the page from favorites.",
});
})
.catch(() => {
setToastAlert({
type: "error",
title: "Error!",
message: "Couldn't remove the page from favorites. Please try again.",
});
});
makePublic();
};
const handleMakePublic = (e: any) => {
const handleMakePrivate = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
makePublic(workspaceSlug, projectId, pageId);
makePrivate();
};
const handleMakePrivate = (e: any) => {
const handleArchivePage = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
makePrivate(workspaceSlug, projectId, pageId);
await archivePage(workspaceSlug as string, projectId as string, pageId as string);
};
const handleArchivePage = (e: any) => {
const handleRestorePage = async (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
archivePage(workspaceSlug, projectId, pageId);
await restorePage(workspaceSlug as string, projectId as string, pageId as string);
};
const handleRestorePage = (e: any) => {
e.preventDefault();
e.stopPropagation();
restorePage(workspaceSlug, projectId, pageId);
};
const handleDeletePage = (e: any) => {
const handleDeletePage = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
setDeletePageModal(true);
};
const handleEditPage = (e: any) => {
const handleEditPage = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
setCreateUpdatePageModal(true);
};
if (!pageDetails) return null;
const ownerDetails = getProjectMemberDetails(pageDetails.owned_by);
const isCurrentUserOwner = pageDetails.owned_by === currentUser?.id;
const ownerDetails = getProjectMemberDetails(owned_by);
const isCurrentUserOwner = owned_by === currentUser?.id;
const userCanEdit =
isCurrentUserOwner ||
@ -173,22 +147,21 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
return (
<>
<CreateUpdatePageModal
pageStore={pageStore}
isOpen={createUpdatePageModal}
handleClose={() => setCreateUpdatePageModal(false)}
data={pageDetails}
projectId={projectId}
/>
<DeletePageModal isOpen={deletePageModal} onClose={() => setDeletePageModal(false)} data={pageDetails} />
<DeletePageModal isOpen={deletePageModal} onClose={() => setDeletePageModal(false)} pageId={pageId} />
<li>
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${pageDetails.id}`}>
<Link href={`/${workspaceSlug}/projects/${projectId}/pages/${pageId}`}>
<div className="relative rounded p-4 text-custom-text-200 hover:bg-custom-background-80">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 overflow-hidden">
<FileText className="h-4 w-4 shrink-0" />
<p className="mr-2 truncate text-sm text-custom-text-100">{pageDetails.name}</p>
{/* FIXME: replace any with proper type */}
{pageDetails.label_details.length > 0 &&
pageDetails.label_details.map((label: any) => (
<p className="mr-2 truncate text-sm text-custom-text-100">{name}</p>
{label_details.length > 0 &&
label_details.map((label: IIssueLabel) => (
<div
key={label.id}
className="group flex items-center gap-1 rounded-2xl border border-custom-border-200 px-2 py-0.5 text-xs"
@ -207,26 +180,26 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
))}
</div>
<div className="flex items-center gap-2.5">
{pageDetails.archived_at ? (
{archived_at ? (
<Tooltip
tooltipContent={`Archived at ${renderFormattedTime(
pageDetails.archived_at
)} on ${renderFormattedDate(pageDetails.archived_at)}`}
tooltipContent={`Archived at ${renderFormattedTime(archived_at)} on ${renderFormattedDate(
archived_at
)}`}
>
<p className="text-sm text-custom-text-200">{renderFormattedTime(pageDetails.archived_at)}</p>
<p className="text-sm text-custom-text-200">{renderFormattedTime(archived_at)}</p>
</Tooltip>
) : (
<Tooltip
tooltipContent={`Last updated at ${renderFormattedTime(
pageDetails.updated_at
)} on ${renderFormattedDate(pageDetails.updated_at)}`}
tooltipContent={`Last updated at ${renderFormattedTime(updated_at)} on ${renderFormattedDate(
updated_at
)}`}
>
<p className="text-sm text-custom-text-200">{renderFormattedTime(pageDetails.updated_at)}</p>
<p className="text-sm text-custom-text-200">{renderFormattedTime(updated_at)}</p>
</Tooltip>
)}
{isEditingAllowed && (
<Tooltip tooltipContent={`${pageDetails.is_favorite ? "Remove from favorites" : "Mark as favorite"}`}>
{pageDetails.is_favorite ? (
<Tooltip tooltipContent={`${is_favorite ? "Remove from favorites" : "Mark as favorite"}`}>
{is_favorite ? (
<button type="button" onClick={handleRemoveFromFavorites}>
<Star className="h-3.5 w-3.5 fill-orange-400 text-orange-400" />
</button>
@ -240,12 +213,10 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
{userCanChangeAccess && (
<Tooltip
tooltipContent={`${
pageDetails.access
? "This page is only visible to you"
: "This page can be viewed by anyone in the project"
access ? "This page is only visible to you" : "This page can be viewed by anyone in the project"
}`}
>
{pageDetails.access ? (
{access ? (
<button type="button" onClick={handleMakePublic}>
<Lock className="h-3.5 w-3.5" />
</button>
@ -259,13 +230,13 @@ export const PagesListItem: FC<IPagesListItem> = observer((props) => {
<Tooltip
position="top-right"
tooltipContent={`Created by ${ownerDetails?.member.display_name} on ${renderFormattedDate(
pageDetails.created_at
created_at
)}`}
>
<AlertCircle className="h-3.5 w-3.5" />
</Tooltip>
<CustomMenu width="auto" placement="bottom-end" className="!-m-1" verticalEllipsis>
{pageDetails.archived_at ? (
{archived_at ? (
<>
{userCanArchive && (
<CustomMenu.MenuItem onClick={handleRestorePage}>

View file

@ -1,11 +1,9 @@
import { FC } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react";
// hooks
import { useApplication, useUser } from "hooks/store";
// components
import { PagesListItem } from "./list-item";
import { NewEmptyState } from "components/common/new-empty-state";
// ui
import { Loader } from "@plane/ui";
@ -13,14 +11,17 @@ import { Loader } from "@plane/ui";
import emptyPage from "public/empty-state/empty_page.png";
// constants
import { EUserProjectRoles } from "constants/project";
import { PagesListItem } from "./list-item";
type IPagesListView = {
pageIds: string[];
};
export const PagesListView: FC<IPagesListView> = observer((props) => {
const { pageIds } = props;
export const PagesListView: FC<IPagesListView> = (props) => {
const { pageIds: projectPageIds } = props;
// store hooks
// trace(true);
const {
commandPalette: { toggleCreatePageModal },
} = useApplication();
@ -31,21 +32,18 @@ export const PagesListView: FC<IPagesListView> = observer((props) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// here we are only observing the projectPageStore, so that we can re-render the component when the projectPageStore changes
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
return (
<>
{pageIds && workspaceSlug && projectId ? (
{projectPageIds && workspaceSlug && projectId ? (
<div className="h-full space-y-4 overflow-y-auto">
{pageIds.length > 0 ? (
{projectPageIds.length > 0 ? (
<ul role="list" className="divide-y divide-custom-border-200">
{pageIds.map((pageId) => (
<PagesListItem
key={pageId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
pageId={pageId}
/>
{projectPageIds.map((pageId: string) => (
<PagesListItem key={pageId} pageId={pageId} projectId={projectId.toString()} />
))}
</ul>
) : (
@ -77,4 +75,4 @@ export const PagesListView: FC<IPagesListView> = observer((props) => {
)}
</>
);
});
};

View file

@ -1,14 +1,15 @@
import { FC } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { usePage } from "hooks/store";
// components
import { PagesListView } from "components/pages/pages-list";
// ui
import { Loader } from "@plane/ui";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const PrivatePagesList: FC = observer(() => {
const { privateProjectPageIds } = usePage();
const projectPageStore = useProjectPages();
const { privateProjectPageIds } = projectPageStore;
if (!privateProjectPageIds)
return (

View file

@ -2,7 +2,7 @@ import React, { FC } from "react";
import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react";
// hooks
import { useApplication, usePage, useUser } from "hooks/store";
import { useApplication, useUser } from "hooks/store";
// components
import { PagesListView } from "components/pages/pages-list";
import { NewEmptyState } from "components/common/new-empty-state";
@ -14,6 +14,7 @@ import emptyPage from "public/empty-state/empty_page.png";
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// constants
import { EUserProjectRoles } from "constants/project";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const RecentPagesList: FC = observer(() => {
// store hooks
@ -21,7 +22,7 @@ export const RecentPagesList: FC = observer(() => {
const {
membership: { currentProjectRole },
} = useUser();
const { recentProjectPages } = usePage();
const { recentProjectPages } = useProjectPages();
// FIXME: replace any with proper type
const isEmpty = recentProjectPages && Object.values(recentProjectPages).every((value: any) => value.length === 0);

View file

@ -3,12 +3,13 @@ import { observer } from "mobx-react-lite";
// components
import { PagesListView } from "components/pages/pages-list";
// hooks
import { usePage } from "hooks/store";
// ui
import { Loader } from "@plane/ui";
import { useProjectPages } from "hooks/store/use-project-specific-pages";
export const SharedPagesList: FC = observer(() => {
const { publicProjectPageIds } = usePage();
const projectPageStore = useProjectPages();
const { publicProjectPageIds } = projectPageStore;
if (!publicProjectPageIds)
return (