From e629977ad69776e0a1d8af741f2d532f3aecf1a2 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 15 Dec 2023 18:02:15 +0530 Subject: [PATCH] fix: merge conflicts resolved --- web/hooks/store/index.ts | 1 + web/hooks/store/use-member.ts | 11 +++ web/store/member/index.ts | 37 ++++++++ web/store/member/project-member.store.ts | 100 ++++++++++++++++++++ web/store/member/workspace-member.store.ts | 105 +++++++++++++++++++++ web/store/root.store.ts | 4 + 6 files changed, 258 insertions(+) create mode 100644 web/hooks/store/use-member.ts create mode 100644 web/store/member/index.ts create mode 100644 web/store/member/project-member.store.ts create mode 100644 web/store/member/workspace-member.store.ts diff --git a/web/hooks/store/index.ts b/web/hooks/store/index.ts index b87cd2568..4b497e388 100644 --- a/web/hooks/store/index.ts +++ b/web/hooks/store/index.ts @@ -1,6 +1,7 @@ export * from "./use-application"; export * from "./use-cycle"; export * from "./use-label"; +export * from "./use-member"; export * from "./use-module"; export * from "./use-page"; export * from "./use-project-publish"; diff --git a/web/hooks/store/use-member.ts b/web/hooks/store/use-member.ts new file mode 100644 index 000000000..fe084d32d --- /dev/null +++ b/web/hooks/store/use-member.ts @@ -0,0 +1,11 @@ +import { useContext } from "react"; +// mobx store +import { MobxStoreContext } from "lib/mobx/store-provider"; +// types; +import { IMemberRootStore } from "store/member"; + +export const useMember = (): IMemberRootStore => { + const context = useContext(MobxStoreContext); + if (context === undefined) throw new Error("useMobxStore must be used within MobxStoreProvider"); + return context.memberRoot; +}; diff --git a/web/store/member/index.ts b/web/store/member/index.ts new file mode 100644 index 000000000..a8f615607 --- /dev/null +++ b/web/store/member/index.ts @@ -0,0 +1,37 @@ +import { makeObservable, observable } from "mobx"; +// types +import { RootStore } from "store/root.store"; +import { IUserLite } from "types"; +import { IWorkspaceMemberStore, WorkspaceMemberStore } from "./workspace-member.store"; +import { IProjectMemberStore, ProjectMemberStore } from "./project-member.store"; + +export interface IMemberRootStore { + // observables + memberMap: Record; + // sub-stores + workspaceMember: IWorkspaceMemberStore; + projectMember: IProjectMemberStore; +} + +export class MemberRootStore implements IMemberRootStore { + // observables + memberMap: Record = {}; + // root store + rootStore: RootStore; + // sub-stores + workspaceMember: IWorkspaceMemberStore; + projectMember: IProjectMemberStore; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observables + memberMap: observable, + }); + + // root store + this.rootStore = _rootStore; + // sub-stores + this.workspaceMember = new WorkspaceMemberStore(_rootStore); + this.projectMember = new ProjectMemberStore(_rootStore); + } +} diff --git a/web/store/member/project-member.store.ts b/web/store/member/project-member.store.ts new file mode 100644 index 000000000..49f052987 --- /dev/null +++ b/web/store/member/project-member.store.ts @@ -0,0 +1,100 @@ +import { action, computed, makeObservable, observable, runInAction } from "mobx"; +import { set } from "lodash"; +// services +import { ProjectMemberService } from "services/project"; +// types +import { RootStore } from "store/root.store"; +import { IProjectMember, IUserLite } from "types"; +// constants +import { EUserProjectRoles } from "constants/project"; + +interface IProjectMemberDetails { + id: string; + member: IUserLite; + role: EUserProjectRoles; +} + +export interface IProjectMemberStore { + // observables + projectMemberMap: { + [projectId: string]: Record; + }; + // computed + projectMembers: string[] | null; + // computed actions + getProjectMemberDetails: (projectMemberId: string) => IProjectMemberDetails | null; + // actions + fetchProjectMembers: (workspaceSlug: string, projectId: string) => Promise; +} + +export class ProjectMemberStore implements IProjectMemberStore { + // observables + projectMemberMap: { + [projectId: string]: Record; + } = {}; + // root store + rootStore: RootStore; + // root store memberMap + memberMap: Record = {}; + // services + projectMemberService; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observables + projectMemberMap: observable, + // computed + projectMembers: computed, + // computed actions + getProjectMemberDetails: action, + // actions + fetchProjectMembers: action, + }); + + // root store + this.rootStore = _rootStore; + this.memberMap = this.rootStore.memberRoot.memberMap; + // services + this.projectMemberService = new ProjectMemberService(); + } + + get projectMembers() { + const projectId = this.rootStore.app.router.projectId; + + if (!projectId) return null; + + return Object.keys(this.projectMemberMap?.[projectId] ?? {}); + } + + getProjectMemberDetails = (projectMemberId: string) => { + const projectId = this.rootStore.app.router.projectId; + + if (!projectId) return null; + + const projectMember = this.projectMemberMap?.[projectId]?.[projectMemberId]; + + const memberDetails: IProjectMemberDetails = { + id: projectMember.id, + role: projectMember.role, + member: this.memberMap?.[projectMember.member], + }; + + return memberDetails; + }; + + fetchProjectMembers = async (workspaceSlug: string, projectId: string) => { + try { + const response = await this.projectMemberService.fetchProjectMembers(workspaceSlug, projectId); + + runInAction(() => { + response.forEach((member) => { + set(this.projectMemberMap, [projectId, member.member], member); + }); + }); + + return response; + } catch (error) { + throw error; + } + }; +} diff --git a/web/store/member/workspace-member.store.ts b/web/store/member/workspace-member.store.ts new file mode 100644 index 000000000..e534e4daf --- /dev/null +++ b/web/store/member/workspace-member.store.ts @@ -0,0 +1,105 @@ +import { action, computed, makeObservable, observable, runInAction } from "mobx"; +import { set } from "lodash"; +// services +import { WorkspaceService } from "services/workspace.service"; +// types +import { RootStore } from "store/root.store"; +import { IUserLite, IWorkspaceMember } from "types"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; + +interface IWorkspaceMembership { + id: string; + member: string; + role: EUserWorkspaceRoles; +} + +export interface IWorkspaceMemberStore { + // observables + workspaceMemberMap: { + [workspaceSlug: string]: Record; + }; + // computed + workspaceMembers: string[] | null; + // computed actions + getWorkspaceMemberDetails: (workspaceMemberId: string) => IWorkspaceMember | null; + // actions + fetchWorkspaceMembers: (workspaceSlug: string) => Promise; +} + +export class WorkspaceMemberStore implements IWorkspaceMemberStore { + // observables + workspaceMemberMap: { + [workspaceSlug: string]: Record; + } = {}; + // root store + rootStore: RootStore; + // root store memberMap + memberMap: Record = {}; + // services + workspaceService; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observables + workspaceMemberMap: observable, + // computed + workspaceMembers: computed, + // computed actions + getWorkspaceMemberDetails: action, + // actions + fetchWorkspaceMembers: action, + }); + + // root store + this.rootStore = _rootStore; + this.memberMap = this.rootStore.memberRoot.memberMap; + // services + this.workspaceService = new WorkspaceService(); + } + + get workspaceMembers() { + const workspaceSlug = this.rootStore.app.router.workspaceSlug; + + if (!workspaceSlug) return null; + + return Object.keys(this.workspaceMemberMap?.[workspaceSlug] ?? {}); + } + + getWorkspaceMemberDetails = (workspaceMemberId: string) => { + const workspaceSlug = this.rootStore.app.router.workspaceSlug; + + if (!workspaceSlug) return null; + + const workspaceMember = this.workspaceMemberMap?.[workspaceSlug]?.[workspaceMemberId]; + + const memberDetails: IWorkspaceMember = { + id: workspaceMember.id, + role: workspaceMember.role, + member: this.memberMap?.[workspaceMember.member], + }; + + return memberDetails; + }; + + fetchWorkspaceMembers = async (workspaceSlug: string) => { + try { + const response = await this.workspaceService.fetchWorkspaceMembers(workspaceSlug); + + runInAction(() => { + response.forEach((member) => { + set(this.memberMap, member.member.id, member.member); + set(this.workspaceMemberMap, [workspaceSlug, member.member.id], { + id: member.id, + member: member.member.id, + role: member.role, + }); + }); + }); + + return response; + } catch (error) { + throw error; + } + }; +} diff --git a/web/store/root.store.ts b/web/store/root.store.ts index f60e000d1..1899ce44e 100644 --- a/web/store/root.store.ts +++ b/web/store/root.store.ts @@ -11,6 +11,8 @@ import { IssueRootStore, IIssueRootStore } from "./issue/root.store"; import { IStateStore, StateStore } from "./state.store"; import { IPageStore, PageStore } from "./page.store"; import { ILabelRootStore, LabelRootStore } from "./label"; +import { IMemberRootStore, MemberRootStore } from "./member"; + enableStaticRendering(typeof window === "undefined"); export class RootStore { @@ -19,6 +21,7 @@ export class RootStore { workspaceRoot: IWorkspaceRootStore; projectRoot: IProjectRootStore; labelRoot: ILabelRootStore; + memberRoot: IMemberRootStore; cycle: ICycleStore; module: IModuleStore; projectView: IProjectViewStore; @@ -32,6 +35,7 @@ export class RootStore { this.workspaceRoot = new WorkspaceRootStore(this); this.projectRoot = new ProjectRootStore(this); this.labelRoot = new LabelRootStore(this); + this.memberRoot = new MemberRootStore(this); // independent stores this.state = new StateStore(this); this.issue = new IssueRootStore(this);