From b62a1b11b1d577b2a04cb1e547b8e62dc80c8325 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Sun, 7 Jan 2024 12:05:52 +0530 Subject: [PATCH] fix: pages store structure changes --- .../issues/issue-layouts/kanban/default.tsx | 5 +- web/hooks/store/use-project-page.ts | 9 + web/store/page.store.ts | 367 ++++-------------- web/store/project-page.store.ts | 136 +++++++ web/store/root.store.ts | 6 +- 5 files changed, 237 insertions(+), 286 deletions(-) create mode 100644 web/hooks/store/use-project-page.ts create mode 100644 web/store/project-page.store.ts diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index 3820f3bac..037fb5a8f 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -89,7 +89,10 @@ const GroupByKanBan: React.FC = observer((props) => { const verticalPosition = verticalAlignPosition(_list); return ( -
+
{sub_group_by === null && (
{ + const context = useContext(StoreContext); + if (context === undefined) throw new Error("useProjectPublish must be used within StoreProvider"); + return context.projectPages; +}; diff --git a/web/store/page.store.ts b/web/store/page.store.ts index debbad0ff..327ec82fe 100644 --- a/web/store/page.store.ts +++ b/web/store/page.store.ts @@ -1,214 +1,95 @@ -import { action, computed, makeObservable, observable, runInAction } from "mobx"; +import { observable, runInAction } from "mobx"; import set from "lodash/set"; import omit from "lodash/omit"; import isToday from "date-fns/isToday"; import isThisWeek from "date-fns/isThisWeek"; import isYesterday from "date-fns/isYesterday"; -// services + +import { IPage } from "@plane/types"; import { PageService } from "services/page.service"; -// helpers -import { renderFormattedPayloadDate } from "helpers/date-time.helper"; -// types -import { IPage, IRecentPages } from "@plane/types"; -// store -import { RootStore } from "./root.store"; +import { is } from "date-fns/locale"; export interface IPageStore { - pages: Record; - archivedPages: Record; - // project computed - projectPageIds: string[] | null; - favoriteProjectPageIds: string[] | null; - privateProjectPageIds: string[] | null; - publicProjectPageIds: string[] | null; - archivedProjectPageIds: string[] | null; - recentProjectPages: IRecentPages | null; - // fetch page information actions - getUnArchivedPageById: (pageId: string) => IPage | null; - getArchivedPageById: (pageId: string) => IPage | null; - // fetch actions - fetchProjectPages: (workspaceSlug: string, projectId: string) => Promise; - fetchArchivedProjectPages: (workspaceSlug: string, projectId: string) => Promise; - // favorites actions - addToFavorites: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - removeFromFavorites: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - // crud - createPage: (workspaceSlug: string, projectId: string, data: Partial) => Promise; - updatePage: (workspaceSlug: string, projectId: string, pageId: string, data: Partial) => Promise; - deletePage: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - // access control actions - makePublic: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - makePrivate: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - // archive actions - archivePage: (workspaceSlug: string, projectId: string, pageId: string) => Promise; - restorePage: (workspaceSlug: string, projectId: string, pageId: string) => Promise; + access: number; + archived_at: string | null; + color: string; + created_at: Date; + created_by: string; + description: string; + description_html: string; + description_stripped: string | null; + id: string; + is_favorite: boolean; + is_locked: boolean; + labels: string[]; + name: string; + owned_by: string; + project: string; + updated_at: Date; + updated_by: string; + workspace: string; } -export class PageStore implements IPageStore { - pages: Record = {}; - archivedPages: Record = {}; - // services - pageService; - // stores - rootStore; +export class PageStore { + access: number; + archived_at: string | null; + color: string; + created_at: Date; + created_by: string; + description: string; + description_html: string; + description_stripped: string | null; + id: string; + is_favorite: boolean; + is_locked: boolean; + labels: string[]; + name: string; + owned_by: string; + project: string; + updated_at: Date; + updated_by: string; + workspace: string; - constructor(rootStore: RootStore) { - makeObservable(this, { - pages: observable, - archivedPages: observable, - // computed - projectPageIds: computed, - favoriteProjectPageIds: computed, - publicProjectPageIds: computed, - privateProjectPageIds: computed, - archivedProjectPageIds: computed, - recentProjectPages: computed, - // computed actions - getUnArchivedPageById: action, - getArchivedPageById: action, - // fetch actions - fetchProjectPages: action, - fetchArchivedProjectPages: action, - // favorites actions - addToFavorites: action, - removeFromFavorites: action, - // crud - createPage: action, - updatePage: action, - deletePage: action, - // access control actions - makePublic: action, - makePrivate: action, - // archive actions - archivePage: action, - restorePage: action, + pageService; + + constructor(page: IPage) { + observable(this, { + name: observable.ref, + description_html: observable.ref, + is_favorite: observable.ref, + is_locked: observable.ref, }); - // stores - this.rootStore = rootStore; - // services + this.created_by = page?.created_by || ""; + this.created_at = page?.created_at || new Date(); + this.color = page?.color || ""; + this.archived_at = page?.archived_at || null; + this.name = page?.name || ""; + this.description = page?.description || ""; + this.description_stripped = page?.description_stripped || ""; + this.description_html = page?.description_html || ""; + this.access = page?.access || 0; + this.workspace = page?.workspace || ""; + this.updated_by = page?.updated_by || ""; + this.updated_at = page?.updated_at || new Date(); + this.project = page?.project || ""; + this.owned_by = page?.owned_by || ""; + this.labels = page?.labels || []; + this.is_locked = page?.is_locked || false; + this.id = page?.id || ""; + this.is_favorite = page?.is_favorite || false; + this.pageService = new PageService(); } - /** - * retrieves all pages for a projectId that is available in the url. - */ - get projectPageIds() { - const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; - const projectPageIds = Object.keys(this.pages).filter((pageId) => this.pages?.[pageId]?.project === projectId); - return projectPageIds ?? null; - } + updateName = async (name: string) => { + this.name = name; + await this.pageService.patchPage(this.workspace, this.project, this.id, { name }); + }; - /** - * retrieves all favorite pages for a projectId that is available in the url. - */ - get favoriteProjectPageIds() { - if (!this.projectPageIds) return null; - const favoritePagesIds = Object.keys(this.projectPageIds).filter((pageId) => this.pages?.[pageId]?.is_favorite); - return favoritePagesIds ?? null; - } - - /** - * retrieves all private pages for a projectId that is available in the url. - */ - get privateProjectPageIds() { - if (!this.projectPageIds) return null; - const privatePagesIds = Object.keys(this.projectPageIds).filter((pageId) => this.pages?.[pageId]?.access === 1); - return privatePagesIds ?? null; - } - - /** - * retrieves all shared pages which are public to everyone in the project for a projectId that is available in the url. - */ - get publicProjectPageIds() { - if (!this.projectPageIds) return null; - const publicPagesIds = Object.keys(this.projectPageIds).filter((pageId) => this.pages?.[pageId]?.access === 0); - return publicPagesIds ?? null; - } - - /** - * retrieves all recent pages for a projectId that is available in the url. - * In format where today, yesterday, this_week, older are keys. - */ - get recentProjectPages() { - if (!this.projectPageIds) return null; - const data: IRecentPages = { today: [], yesterday: [], this_week: [], older: [] }; - data.today = this.projectPageIds.filter((p) => isToday(new Date(this.pages?.[p]?.created_at))) || []; - data.yesterday = this.projectPageIds.filter((p) => isYesterday(new Date(this.pages?.[p]?.created_at))) || []; - data.this_week = - this.projectPageIds.filter((p) => { - const pageCreatedAt = this.pages?.[p]?.created_at; - return ( - isThisWeek(new Date(pageCreatedAt)) && - !isToday(new Date(pageCreatedAt)) && - !isYesterday(new Date(pageCreatedAt)) - ); - }) || []; - data.older = - this.projectPageIds.filter((p) => { - const pageCreatedAt = this.pages?.[p]?.created_at; - return !isThisWeek(new Date(pageCreatedAt)) && !isYesterday(new Date(pageCreatedAt)); - }) || []; - return data; - } - - /** - * retrieves all archived pages for a projectId that is available in the url. - */ - get archivedProjectPageIds() { - const projectId = this.rootStore.app.router.projectId; - if (!projectId) return null; - const archivedProjectPageIds = Object.keys(this.archivedPages).filter( - (pageId) => this.archivedPages?.[pageId]?.project === projectId - ); - return archivedProjectPageIds ?? null; - } - - /** - * retrieves a page from pages by id. - * @param pageId - * @returns IPage | null - */ - getUnArchivedPageById = (pageId: string) => this.pages?.[pageId] ?? null; - - /** - * retrieves a page from archived pages by id. - * @param pageId - * @returns IPage | null - */ - getArchivedPageById = (pageId: string) => this.archivedPages?.[pageId] ?? null; - - /** - * fetches all pages for a project. - * @param workspaceSlug - * @param projectId - * @returns Promise - */ - fetchProjectPages = async (workspaceSlug: string, projectId: string) => - await this.pageService.getProjectPages(workspaceSlug, projectId).then((response) => { - runInAction(() => { - response.forEach((page) => { - set(this.pages, [page.id], page); - }); - }); - return response; - }); - - /** - * fetches all archived pages for a project. - * @param workspaceSlug - * @param projectId - * @returns Promise - */ - fetchArchivedProjectPages = async (workspaceSlug: string, projectId: string) => - await this.pageService.getArchivedPages(workspaceSlug, projectId).then((response) => { - runInAction(() => { - response.forEach((page) => { - set(this.archivedPages, [page.id], page); - }); - }); - return response; - }); + updateDescription = async (description: string) => { + this.description = description; + await this.pageService.patchPage(this.workspace, this.project, this.id, { description }); + }; /** * Add Page to users favorites list @@ -219,13 +100,11 @@ export class PageStore implements IPageStore { addToFavorites = async (workspaceSlug: string, projectId: string, pageId: string) => { try { runInAction(() => { - set(this.pages, [pageId, "is_favorite"], true); + this.is_favorite = true; }); await this.pageService.addPageToFavorites(workspaceSlug, projectId, pageId); } catch (error) { - runInAction(() => { - set(this.pages, [pageId, "is_favorite"], false); - }); + this.is_favorite = false; throw error; } }; @@ -238,62 +117,13 @@ export class PageStore implements IPageStore { */ removeFromFavorites = async (workspaceSlug: string, projectId: string, pageId: string) => { try { - runInAction(() => { - set(this.pages, [pageId, "is_favorite"], false); - }); + this.is_favorite = false; await this.pageService.removePageFromFavorites(workspaceSlug, projectId, pageId); } catch (error) { - runInAction(() => { - set(this.pages, [pageId, "is_favorite"], true); - }); + this.is_favorite = true; throw error; } }; - /** - * Creates a new page using the api and updated the local state in store - * @param workspaceSlug - * @param projectId - * @param data - */ - createPage = async (workspaceSlug: string, projectId: string, data: Partial) => - await this.pageService.createPage(workspaceSlug, projectId, data).then((response) => { - runInAction(() => { - set(this.pages, [response.id], response); - }); - return response; - }); - - /** - * updates the page using the api and updates the local state in store - * @param workspaceSlug - * @param projectId - * @param pageId - * @param data - * @returns - */ - updatePage = async (workspaceSlug: string, projectId: string, pageId: string, data: Partial) => - await this.pageService.patchPage(workspaceSlug, projectId, pageId, data).then((response) => { - const originalPage = this.getUnArchivedPageById(pageId); - runInAction(() => { - set(this.pages, [pageId], { ...originalPage, ...data }); - }); - return response; - }); - - /** - * delete a page using the api and updates the local state in store - * @param workspaceSlug - * @param projectId - * @param pageId - * @returns - */ - deletePage = async (workspaceSlug: string, projectId: string, pageId: string) => - await this.pageService.deletePage(workspaceSlug, projectId, pageId).then((response) => { - runInAction(() => { - omit(this.archivedPages, [pageId]); - }); - return response; - }); /** * make a page public @@ -305,12 +135,12 @@ export class PageStore implements IPageStore { makePublic = async (workspaceSlug: string, projectId: string, pageId: string) => { try { runInAction(() => { - set(this.pages, [pageId, "access"], 0); + this.access = 0; }); await this.pageService.patchPage(workspaceSlug, projectId, pageId, { access: 0 }); } catch (error) { runInAction(() => { - set(this.pages, [pageId, "access"], 1); + this.access = 1; }); throw error; } @@ -326,43 +156,14 @@ export class PageStore implements IPageStore { makePrivate = async (workspaceSlug: string, projectId: string, pageId: string) => { try { runInAction(() => { - set(this.pages, [pageId, "access"], 1); + this.access = 1; }); await this.pageService.patchPage(workspaceSlug, projectId, pageId, { access: 1 }); } catch (error) { runInAction(() => { - set(this.pages, [pageId, "access"], 0); + this.access = 0; }); throw error; } }; - - /** - * Mark a page archived - * @param workspaceSlug - * @param projectId - * @param pageId - */ - archivePage = async (workspaceSlug: string, projectId: string, pageId: string) => - await this.pageService.archivePage(workspaceSlug, projectId, pageId).then(() => { - runInAction(() => { - set(this.archivedPages, [pageId], this.pages[pageId]); - set(this.archivedPages, [pageId, "archived_at"], renderFormattedPayloadDate(new Date())); - omit(this.pages, [pageId]); - }); - }); - - /** - * Restore a page from archived pages to pages - * @param workspaceSlug - * @param projectId - * @param pageId - */ - restorePage = async (workspaceSlug: string, projectId: string, pageId: string) => - await this.pageService.restorePage(workspaceSlug, projectId, pageId).then(() => { - runInAction(() => { - set(this.pages, [pageId], this.archivedPages[pageId]); - omit(this.archivedPages, [pageId]); - }); - }); } diff --git a/web/store/project-page.store.ts b/web/store/project-page.store.ts new file mode 100644 index 000000000..2e913d9f9 --- /dev/null +++ b/web/store/project-page.store.ts @@ -0,0 +1,136 @@ +import { makeObservable, observable, runInAction, action } from "mobx"; +import { set } from "lodash"; +// services +import { PageService } from "services/page.service"; +// store +import { PageStore, IPageStore } from "store/page.store"; +// types +import { IPage } from "@plane/types"; + +export interface IProjectPageStore { + projectPages: Record; + projectArchivedPages: Record; + // fetch actions + fetchProjectPages: (workspaceSlug: string, projectId: string) => void; + fetchArchivedProjectPages: (workspaceSlug: string, projectId: string) => void; + // crud actions + createPage: (workspaceSlug: string, projectId: string, data: Partial) => void; + deletePage: (workspaceSlug: string, projectId: string, pageId: string) => void; +} + +export class ProjectPageStore implements IProjectPageStore { + projectPages: Record = {}; // { projectId: [page1, page2] } + projectArchivedPages: Record = {}; // { projectId: [page1, page2] } + + pageService; + + constructor() { + makeObservable(this, { + projectPages: observable, + projectArchivedPages: observable, + // fetch actions + fetchProjectPages: action, + fetchArchivedProjectPages: action, + // crud actions + createPage: action, + deletePage: action, + }); + this.pageService = new PageService(); + } + + /** + * Fetching all the pages for a specific project + * @param workspaceSlug + * @param projectId + */ + fetchProjectPages = async (workspaceSlug: string, projectId: string) => { + const response = await this.pageService.getProjectPages(workspaceSlug, projectId); + runInAction(() => { + this.projectPages[projectId] = response?.map((page) => new PageStore(page)); + }); + }; + + /** + * fetches all archived pages for a project. + * @param workspaceSlug + * @param projectId + * @returns Promise + */ + fetchArchivedProjectPages = async (workspaceSlug: string, projectId: string) => + await this.pageService.getArchivedPages(workspaceSlug, projectId).then((response) => { + runInAction(() => { + this.projectArchivedPages[projectId] = response?.map((page) => new PageStore(page)); + }); + return response; + }); + + /** + * Creates a new page using the api and updated the local state in store + * @param workspaceSlug + * @param projectId + * @param data + */ + createPage = async (workspaceSlug: string, projectId: string, data: Partial) => { + const response = await this.pageService.createPage(workspaceSlug, projectId, data); + runInAction(() => { + this.projectPages[projectId] = [...this.projectPages[projectId], new PageStore(response)]; + }); + return response; + }; + + /** + * delete a page using the api and updates the local state in store + * @param workspaceSlug + * @param projectId + * @param pageId + * @returns + */ + deletePage = async (workspaceSlug: string, projectId: string, pageId: string) => { + const response = await this.pageService.deletePage(workspaceSlug, projectId, pageId); + runInAction(() => { + this.projectPages = set( + this.projectPages, + [projectId], + this.projectPages[projectId].filter((page) => page.id !== pageId) + ); + }); + return response; + }; + + /** + * Mark a page archived + * @param workspaceSlug + * @param projectId + * @param pageId + */ + archivePage = async (workspaceSlug: string, projectId: string, pageId: string) => { + const response = await this.pageService.archivePage(workspaceSlug, projectId, pageId); + runInAction(() => { + set( + this.projectPages, + [projectId], + this.projectPages[projectId].filter((page) => page.id !== pageId) + ); + this.projectArchivedPages = set(this.projectArchivedPages, [projectId], this.projectArchivedPages[projectId]); + }); + return response; + }; + + /** + * Restore a page from archived pages to pages + * @param workspaceSlug + * @param projectId + * @param pageId + */ + restorePage = async (workspaceSlug: string, projectId: string, pageId: string) => + await this.pageService.restorePage(workspaceSlug, projectId, pageId).then(() => { + runInAction(() => { + set( + this.projectArchivedPages, + [projectId], + this.projectArchivedPages[projectId].filter((page) => page.id !== pageId) + ); + set(this.projectPages, [projectId], [...this.projectPages[projectId]]); + }); + }); +} diff --git a/web/store/root.store.ts b/web/store/root.store.ts index 209e30679..80745dfa9 100644 --- a/web/store/root.store.ts +++ b/web/store/root.store.ts @@ -16,6 +16,7 @@ import { IInboxRootStore, InboxRootStore } from "./inbox"; import { IEstimateStore, EstimateStore } from "./estimate.store"; import { GlobalViewStore, IGlobalViewStore } from "./global-view.store"; import { IMentionStore, MentionStore } from "./mention.store"; +import { IProjectPageStore, ProjectPageStore } from "./project-page.store"; enableStaticRendering(typeof window === "undefined"); @@ -31,11 +32,12 @@ export class RootStore { module: IModuleStore; projectView: IProjectViewStore; globalView: IGlobalViewStore; - page: IPageStore; + // page: IPageStore; issue: IIssueRootStore; state: IStateStore; estimate: IEstimateStore; mention: IMentionStore; + projectPages: IProjectPageStore; constructor() { this.app = new AppRootStore(this); @@ -50,10 +52,10 @@ export class RootStore { this.module = new ModulesStore(this); this.projectView = new ProjectViewStore(this); this.globalView = new GlobalViewStore(this); - this.page = new PageStore(this); this.issue = new IssueRootStore(this); this.state = new StateStore(this); this.estimate = new EstimateStore(this); this.mention = new MentionStore(this); + this.projectPages = new ProjectPageStore(); } }