[WEB-6201] refactor: merge user permission, membership and subscription related stores to core (#5794)
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
import { computedFn } from "mobx-utils";
|
||||
import type { EUserProjectRoles } from "@plane/types";
|
||||
// plane imports
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
// store
|
||||
import type { IMemberRootStore } from "@/store/member";
|
||||
import type { IBaseProjectMemberStore } from "@/store/member/project/base-project-member.store";
|
||||
import { BaseProjectMemberStore } from "@/store/member/project/base-project-member.store";
|
||||
|
||||
export type IProjectMemberStore = IBaseProjectMemberStore;
|
||||
|
||||
export class ProjectMemberStore extends BaseProjectMemberStore implements IProjectMemberStore {
|
||||
constructor(_memberRoot: IMemberRootStore, rootStore: RootStore) {
|
||||
super(_memberRoot, rootStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the highest role from the project membership
|
||||
* @param { string } userId
|
||||
* @param { string } projectId
|
||||
* @returns { EUserProjectRoles | undefined }
|
||||
*/
|
||||
getUserProjectRole = computedFn((userId: string, projectId: string): EUserProjectRoles | undefined =>
|
||||
this.getRoleFromProjectMembership(userId, projectId)
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Returns the role from the project membership
|
||||
* @param projectId
|
||||
* @param userId
|
||||
* @param role
|
||||
*/
|
||||
getProjectMemberRoleForUpdate = (_projectId: string, _userId: string, role: EUserProjectRoles): EUserProjectRoles =>
|
||||
role;
|
||||
|
||||
/**
|
||||
* @description Processes the removal of a member from a project
|
||||
* This method handles the cleanup of member data from the project member map
|
||||
* @param projectId - The ID of the project to remove the member from
|
||||
* @param userId - The ID of the user to remove from the project
|
||||
*/
|
||||
processMemberRemoval = (projectId: string, userId: string) => this.handleMemberRemoval(projectId, userId);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
// store
|
||||
import type { IBaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store";
|
||||
import { BaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
import type { IMemberRootStore } from "@/store/member";
|
||||
|
||||
export type IWorkspaceMemberStore = IBaseWorkspaceMemberStore;
|
||||
|
||||
export class WorkspaceMemberStore extends BaseWorkspaceMemberStore implements IWorkspaceMemberStore {
|
||||
constructor(_memberRoot: IMemberRootStore, _rootStore: RootStore) {
|
||||
super(_memberRoot, _rootStore);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
import { computedFn } from "mobx-utils";
|
||||
import type { EUserPermissions } from "@plane/constants";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
import { BaseUserPermissionStore } from "@/store/user/base-permissions.store";
|
||||
import type { IBaseUserPermissionStore } from "@/store/user/base-permissions.store";
|
||||
|
||||
export type IUserPermissionStore = IBaseUserPermissionStore;
|
||||
|
||||
export class UserPermissionStore extends BaseUserPermissionStore implements IUserPermissionStore {
|
||||
constructor(store: RootStore) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the project role from the workspace
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { EUserPermissions | undefined }
|
||||
*/
|
||||
getProjectRoleByWorkspaceSlugAndProjectId = computedFn(
|
||||
(workspaceSlug: string, projectId?: string): EUserPermissions | undefined =>
|
||||
this.getProjectRole(workspaceSlug, projectId)
|
||||
);
|
||||
|
||||
fetchWorkspaceLevelProjectEntities = (workspaceSlug: string, projectId: string): void => {
|
||||
void this.store.projectRoot.project.fetchProjectDetails(workspaceSlug, projectId);
|
||||
};
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import { useMember } from "@/hooks/store/use-member";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// store
|
||||
import type { IProjectMemberDetails } from "@/store/member/project/base-project-member.store";
|
||||
import type { IProjectMemberDetails } from "@/store/member/project/membership.store";
|
||||
// local imports
|
||||
import { useProjectMemberColumns } from "./useProjectMemberColumns";
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useContext } from "react";
|
||||
// mobx store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
// plane web imports
|
||||
import type { IUserPermissionStore } from "@/plane-web/store/user/permission.store";
|
||||
import type { IUserPermissionStore } from "@/store/user/permissions.store";
|
||||
|
||||
export const useUserPermissions = (): IUserPermissionStore => {
|
||||
const context = useContext(StoreContext);
|
||||
|
||||
@@ -15,11 +15,11 @@ import { makeObservable, observable } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// plane imports
|
||||
import type { IUserLite } from "@plane/types";
|
||||
// plane web imports
|
||||
import type { IProjectMemberStore } from "@/plane-web/store/member/project-member.store";
|
||||
import { ProjectMemberStore } from "@/plane-web/store/member/project-member.store";
|
||||
import type { IWorkspaceMemberStore } from "@/plane-web/store/member/workspace-member.store";
|
||||
import { WorkspaceMemberStore } from "@/plane-web/store/member/workspace-member.store";
|
||||
// store
|
||||
import type { IProjectMemberStore } from "./project/membership.store";
|
||||
import { ProjectMemberStore } from "./project/membership.store";
|
||||
import type { IWorkspaceMemberStore } from "./workspace/membership.store";
|
||||
import { WorkspaceMemberStore } from "./workspace/membership.store";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
|
||||
export interface IMemberRootStore {
|
||||
|
||||
+94
-15
@@ -15,14 +15,15 @@ import { uniq, unset, set, update, sortBy } from "lodash-es";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// plane imports
|
||||
import { EUserPermissions } from "@plane/constants";
|
||||
import { E_FEATURE_FLAGS, EUserPermissions } from "@plane/constants";
|
||||
import { EUserProjectRoles } from "@plane/types";
|
||||
import type {
|
||||
EUserProjectRoles,
|
||||
IProjectBulkAddFormData,
|
||||
IProjectUserPropertiesResponse,
|
||||
IUserLite,
|
||||
TProjectMembership,
|
||||
} from "@plane/types";
|
||||
import { getHighestRole } from "@plane/utils";
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
// services
|
||||
@@ -34,14 +35,14 @@ import type { IUserStore } from "@/store/user";
|
||||
// local imports
|
||||
import type { IMemberRootStore } from "../index";
|
||||
import { sortProjectMembers } from "../utils";
|
||||
import type { IProjectMemberFiltersStore } from "./project-member-filters.store";
|
||||
import { ProjectMemberFiltersStore } from "./project-member-filters.store";
|
||||
import type { IProjectMemberFiltersStore } from "./filters.store";
|
||||
import { ProjectMemberFiltersStore } from "./filters.store";
|
||||
|
||||
export interface IProjectMemberDetails extends Omit<TProjectMembership, "member"> {
|
||||
member: IUserLite;
|
||||
}
|
||||
|
||||
export interface IBaseProjectMemberStore {
|
||||
export interface IProjectMemberStore {
|
||||
// observables
|
||||
projectMemberFetchStatusMap: {
|
||||
[projectId: string]: boolean;
|
||||
@@ -91,7 +92,7 @@ export interface IBaseProjectMemberStore {
|
||||
removeMemberFromProject: (workspaceSlug: string, projectId: string, userId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore {
|
||||
export class ProjectMemberStore implements IProjectMemberStore {
|
||||
// observables
|
||||
projectMemberFetchStatusMap: {
|
||||
[projectId: string]: boolean;
|
||||
@@ -211,7 +212,19 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
* @param { string } projectId - The ID of the project
|
||||
* @returns { EUserProjectRoles | undefined } The user's role in the project, or undefined if not found
|
||||
*/
|
||||
abstract getUserProjectRole: (userId: string, projectId: string) => EUserProjectRoles | undefined;
|
||||
getUserProjectRole = computedFn((userId: string, projectId: string): EUserProjectRoles | undefined => {
|
||||
// Get the roles from the project and teamspace membership
|
||||
const projectRoleFromProjectMembership = this.getRoleFromProjectMembership(userId, projectId);
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
|
||||
// Filter out undefined roles and get the highest role
|
||||
const roles = [projectRoleFromProjectMembership, projectRoleFromTeamspaceMembership].filter(
|
||||
(role): role is EUserProjectRoles => role !== undefined
|
||||
);
|
||||
|
||||
// Return the highest role
|
||||
return getHighestRole(roles);
|
||||
});
|
||||
|
||||
/**
|
||||
* @description get the details of a project member
|
||||
@@ -331,22 +344,26 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
this.projectRoot.projectMap[projectId].members = this.projectRoot.projectMap?.[projectId]?.members?.concat(
|
||||
data.members.map((m) => m.member_id)
|
||||
);
|
||||
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description update the role of a member in a project
|
||||
* @description Returns the highest role from the project and teamspace membership
|
||||
* @param projectId
|
||||
* @param userId
|
||||
* @param role
|
||||
*/
|
||||
abstract getProjectMemberRoleForUpdate: (
|
||||
projectId: string,
|
||||
userId: string,
|
||||
role: EUserProjectRoles
|
||||
) => EUserProjectRoles;
|
||||
getProjectMemberRoleForUpdate = (projectId: string, userId: string, role: EUserProjectRoles): EUserProjectRoles => {
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
|
||||
const availableRoles = [projectRoleFromTeamspaceMembership, role].filter(
|
||||
(role): role is EUserProjectRoles => role !== undefined
|
||||
);
|
||||
|
||||
return getHighestRole(availableRoles) ?? role;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description update the role of a member in a project
|
||||
@@ -386,6 +403,7 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
role,
|
||||
}
|
||||
);
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
return response;
|
||||
} catch (error) {
|
||||
// revert back to original members in case of error
|
||||
@@ -429,7 +447,28 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
* @param projectId - The ID of the project to remove the member from
|
||||
* @param userId - The ID of the user to remove from the project
|
||||
*/
|
||||
abstract processMemberRemoval: (projectId: string, userId: string) => void;
|
||||
|
||||
/**
|
||||
* @description Processes the removal of a member from a project
|
||||
* This method handles the cleanup of member data from the project member map
|
||||
* @param projectId - The ID of the project to remove the member from
|
||||
* @param userId - The ID of the user to remove from the project
|
||||
*/
|
||||
processMemberRemoval = (projectId: string, userId: string) => {
|
||||
// Get the role from the teamspace membership
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
if (projectRoleFromTeamspaceMembership) {
|
||||
// If the user is a member of the teamspace, update user membership detail
|
||||
update(this.projectMemberMap, [projectId, userId], (prev: TProjectMembership) => ({
|
||||
...prev,
|
||||
id: null,
|
||||
original_role: null,
|
||||
role: projectRoleFromTeamspaceMembership,
|
||||
}));
|
||||
} else {
|
||||
this.handleMemberRemoval(projectId, userId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description remove a member from a project
|
||||
@@ -444,6 +483,7 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
runInAction(() => {
|
||||
this.processMemberRemoval(projectId, userId);
|
||||
});
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,4 +542,43 @@ export abstract class BaseProjectMemberStore implements IBaseProjectMemberStore
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// private helpers
|
||||
/**
|
||||
* @description Returns the project role from the teamspace membership
|
||||
* @param { string } userId
|
||||
* @param { string } projectId
|
||||
* @returns { EUserProjectRoles | undefined }
|
||||
*/
|
||||
private getProjectRoleFromTeamspaceMembership = computedFn(
|
||||
(userId: string, projectId: string): EUserProjectRoles | undefined => {
|
||||
// Find all the teamspaces linked to the project
|
||||
const projectTeamspaceIds = this.rootStore.teamspaceRoot.teamspaces.getProjectTeamspaceIds(projectId);
|
||||
if (!projectTeamspaceIds || projectTeamspaceIds.length === 0) return undefined;
|
||||
// Check if the user is a member of any of the teamspaces
|
||||
const isUserMemberOfTeamspace = projectTeamspaceIds.some((teamspaceId) =>
|
||||
this.rootStore.teamspaceRoot.teamspaces.isUserMemberOfTeamspace(userId, teamspaceId)
|
||||
);
|
||||
if (!isUserMemberOfTeamspace) return undefined;
|
||||
// Return MEMBER if the user is a member of any of the teamspaces
|
||||
return EUserProjectRoles.MEMBER;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Mutate project members activity
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
*/
|
||||
private mutateProjectMembersActivity = async (workspaceSlug: string, projectId: string) => {
|
||||
const isProjectMembersActivityEnabled = this.rootStore.featureFlags.getFeatureFlag(
|
||||
workspaceSlug,
|
||||
E_FEATURE_FLAGS.PROJECT_MEMBER_ACTIVITY,
|
||||
false
|
||||
);
|
||||
|
||||
if (isProjectMembersActivityEnabled) {
|
||||
await this.rootStore.projectMembersActivityStore.fetchProjectMembersActivity(workspaceSlug, projectId);
|
||||
}
|
||||
};
|
||||
}
|
||||
+29
-15
@@ -15,6 +15,7 @@ import { set, sortBy } from "lodash-es";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
import type { EUserPermissions } from "@plane/constants";
|
||||
import type {
|
||||
IWorkspaceBulkInviteFormData,
|
||||
@@ -32,9 +33,9 @@ import { WorkspaceService } from "@/services/workspace.service";
|
||||
import type { IRouterStore } from "@/store/router.store";
|
||||
import type { IUserStore } from "@/store/user";
|
||||
// store
|
||||
import type { IMemberRootStore } from "../index.ts";
|
||||
import type { IWorkspaceMemberFiltersStore } from "./workspace-member-filters.store";
|
||||
import { WorkspaceMemberFiltersStore } from "./workspace-member-filters.store";
|
||||
import type { IMemberRootStore } from "../index";
|
||||
import type { IWorkspaceMemberFiltersStore } from "./filters.store";
|
||||
import { WorkspaceMemberFiltersStore } from "./filters.store";
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
export interface IWorkspaceMembership {
|
||||
@@ -44,7 +45,7 @@ export interface IWorkspaceMembership {
|
||||
is_active?: boolean;
|
||||
}
|
||||
|
||||
export interface IBaseWorkspaceMemberStore {
|
||||
export interface IWorkspaceMemberStore {
|
||||
// observables
|
||||
workspaceMemberMap: Record<string, Record<string, IWorkspaceMembership>>;
|
||||
workspaceMemberInvitations: Record<string, IWorkspaceMemberInvitation[]>;
|
||||
@@ -91,9 +92,11 @@ export interface IBaseWorkspaceMemberStore {
|
||||
checklist: Partial<Record<TGettingStartedChecklistKeys, boolean>>
|
||||
) => Promise<IWorkspaceMemberMe>;
|
||||
updateChecklistIfNotDoneAlready: (workspaceSlug: string, key: TGettingStartedChecklistKeys) => Promise<void>;
|
||||
// mutation helpers
|
||||
mutateWorkspaceMembersActivity: (workspaceSlug: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberStore {
|
||||
export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
||||
// observables
|
||||
workspaceMemberMap: {
|
||||
[workspaceSlug: string]: Record<string, IWorkspaceMembership>;
|
||||
@@ -300,6 +303,7 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
set(this.workspaceMemberMap, [workspaceSlug, userId, "role"], data.role);
|
||||
});
|
||||
await this.workspaceService.updateWorkspaceMember(workspaceSlug, memberDetails.id, data);
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
} catch (error) {
|
||||
// revert back to original members in case of error
|
||||
runInAction(() => {
|
||||
@@ -321,6 +325,7 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
runInAction(() => {
|
||||
set(this.workspaceMemberMap, [workspaceSlug, userId, "is_active"], false);
|
||||
});
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
}
|
||||
/**
|
||||
* @description fetch all the member invitations of a workspace
|
||||
@@ -344,6 +349,7 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
await this.fetchWorkspaceMemberInvitations(workspaceSlug);
|
||||
// Auto-complete getting started checklist
|
||||
void this.updateChecklistIfNotDoneAlready(workspaceSlug, "team_members_invited");
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,6 +395,7 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
(inv) => inv.id !== invitationId
|
||||
);
|
||||
});
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
}
|
||||
|
||||
isUserSuspended = computedFn((userId: string, workspaceSlug: string) => {
|
||||
@@ -404,18 +411,15 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
*/
|
||||
getGettingStartedChecklistByWorkspaceSlug = computedFn(
|
||||
(workspaceSlug: string): Partial<Record<TGettingStartedChecklistKeys, boolean>> | undefined => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const checklist = this.rootStore.user.permission.workspaceUserInfo[workspaceSlug]?.getting_started_checklist;
|
||||
if (!checklist) return undefined;
|
||||
|
||||
// Filter out null values to match return type
|
||||
const filtered: Partial<Record<TGettingStartedChecklistKeys, boolean>> = {};
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
|
||||
(Object.keys(checklist) as TGettingStartedChecklistKeys[]).forEach((key) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||
const value = checklist[key];
|
||||
if (value !== null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||
filtered[key] = value;
|
||||
}
|
||||
});
|
||||
@@ -439,12 +443,10 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
errorMessage: string
|
||||
): Promise<IWorkspaceMemberMe> => {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const existingData = this.rootStore.user.permission.workspaceUserInfo[workspaceSlug]?.[fieldKey];
|
||||
const filteredExisting: Record<string, boolean> = {};
|
||||
|
||||
if (existingData && typeof existingData === "object") {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
Object.entries(existingData).forEach(([key, value]) => {
|
||||
if (value !== null && typeof value === "boolean") {
|
||||
filteredExisting[key] = value;
|
||||
@@ -452,7 +454,6 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
||||
const response = await this.workspaceService.updateMemberOnboarding(workspaceSlug, {
|
||||
[fieldKey]: {
|
||||
...filteredExisting,
|
||||
@@ -462,12 +463,10 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const currentFieldData = this.rootStore.user.permission.workspaceUserInfo[workspaceSlug]?.[fieldKey];
|
||||
const mergedData: Record<string, boolean> = {};
|
||||
|
||||
if (currentFieldData && typeof currentFieldData === "object") {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
Object.entries(currentFieldData).forEach(([key, value]) => {
|
||||
if (value !== null && typeof value === "boolean") {
|
||||
mergedData[key] = value;
|
||||
@@ -485,7 +484,6 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error(errorMessage, error);
|
||||
@@ -543,4 +541,20 @@ export abstract class BaseWorkspaceMemberStore implements IBaseWorkspaceMemberSt
|
||||
await this.updateChecklist(workspaceSlug, { [key]: true });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mutate workspace members activity
|
||||
* @param workspaceSlug
|
||||
*/
|
||||
mutateWorkspaceMembersActivity = async (workspaceSlug: string) => {
|
||||
const isMembersActivityEnabled = this.rootStore.featureFlags.getFeatureFlag(
|
||||
workspaceSlug,
|
||||
E_FEATURE_FLAGS.WORKSPACE_MEMBER_ACTIVITY,
|
||||
false
|
||||
);
|
||||
|
||||
if (isMembersActivityEnabled) {
|
||||
await this.rootStore.workspaceMembersActivityStore.fetchWorkspaceMembersActivity(workspaceSlug);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -177,6 +177,11 @@ import type { IEpicAnalyticStore } from "./work-items/epic/analytic.store";
|
||||
import { EpicAnalytics } from "./work-items/epic/analytic.store";
|
||||
import type { IEpicBaseStore } from "./work-items/epic/base.store";
|
||||
import { EpicBaseStore } from "./work-items/epic/base.store";
|
||||
// subscriptions
|
||||
import type { ISelfHostedSubscriptionStore } from "./subscription/self-hosted-subscription.store";
|
||||
import { SelfHostedSubscriptionStore } from "./subscription/self-hosted-subscription.store";
|
||||
import type { IWorkspaceSubscriptionStore } from "./subscription/subscription.store";
|
||||
import { WorkspaceSubscriptionStore } from "./subscription/subscription.store";
|
||||
|
||||
enableStaticRendering(typeof window === "undefined");
|
||||
|
||||
@@ -252,6 +257,9 @@ export class CoreRootStore {
|
||||
// epics
|
||||
epicAnalytics: IEpicAnalyticStore;
|
||||
epicBaseStore: IEpicBaseStore;
|
||||
// subscriptions
|
||||
workspaceSubscription: IWorkspaceSubscriptionStore;
|
||||
selfHostedSubscription: ISelfHostedSubscriptionStore;
|
||||
|
||||
constructor() {
|
||||
this.router = new RouterStore();
|
||||
@@ -325,6 +333,9 @@ export class CoreRootStore {
|
||||
// epics
|
||||
this.epicAnalytics = new EpicAnalytics(this as unknown as RootStore);
|
||||
this.epicBaseStore = new EpicBaseStore(this as unknown as RootStore);
|
||||
// subscriptions
|
||||
this.workspaceSubscription = new WorkspaceSubscriptionStore(this as unknown as RootStore);
|
||||
this.selfHostedSubscription = new SelfHostedSubscriptionStore(this as unknown as RootStore);
|
||||
}
|
||||
|
||||
resetOnSignOut() {
|
||||
@@ -401,5 +412,8 @@ export class CoreRootStore {
|
||||
// epics
|
||||
this.epicAnalytics = new EpicAnalytics(this as unknown as RootStore);
|
||||
this.epicBaseStore = new EpicBaseStore(this as unknown as RootStore);
|
||||
// subscriptions
|
||||
this.workspaceSubscription = new WorkspaceSubscriptionStore(this as unknown as RootStore);
|
||||
this.selfHostedSubscription = new SelfHostedSubscriptionStore(this as unknown as RootStore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ import { EUserPermissions, API_BASE_URL } from "@plane/constants";
|
||||
import type { IUser, TUserPermissions } from "@plane/types";
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
import type { IUserPermissionStore } from "@/plane-web/store/user/permission.store";
|
||||
import { UserPermissionStore } from "@/plane-web/store/user/permission.store";
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
import { UserService } from "@/services/user.service";
|
||||
// stores
|
||||
import type { IUserPermissionStore } from "@/store/user/permissions.store";
|
||||
import { UserPermissionStore } from "@/store/user/permissions.store";
|
||||
import type { IAccountStore } from "@/store/user/account.store";
|
||||
import type { IUserProfileStore } from "@/store/user/profile.store";
|
||||
import { ProfileStore } from "@/store/user/profile.store";
|
||||
|
||||
+38
-8
@@ -23,6 +23,7 @@ import {
|
||||
} from "@plane/constants";
|
||||
import type { EUserProjectRoles, IUserProjectsRole, IWorkspaceMemberMe, TProjectMembership } from "@plane/types";
|
||||
import { EUserWorkspaceRoles } from "@plane/types";
|
||||
import { getHighestRole } from "@plane/utils";
|
||||
// plane web imports
|
||||
import { WorkspaceService } from "@/services/workspace.service";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
@@ -35,7 +36,7 @@ const workspaceService = new WorkspaceService();
|
||||
|
||||
type ETempUserRole = TUserPermissions | EUserWorkspaceRoles | EUserProjectRoles; // TODO: Remove this once we have migrated user permissions to enums to plane constants package
|
||||
|
||||
export interface IBaseUserPermissionStore {
|
||||
export interface IUserPermissionStore {
|
||||
loader: boolean;
|
||||
// observables
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe>; // workspaceSlug -> IWorkspaceMemberMe
|
||||
@@ -71,7 +72,7 @@ export interface IBaseUserPermissionStore {
|
||||
* @description This store is used to handle permission layer for the currently logged user.
|
||||
* It manages workspace and project level permissions, roles and access control.
|
||||
*/
|
||||
export abstract class BaseUserPermissionStore implements IBaseUserPermissionStore {
|
||||
export class UserPermissionStore implements IUserPermissionStore {
|
||||
loader: boolean = false;
|
||||
// constants
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe> = {};
|
||||
@@ -152,16 +153,27 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Returns the current project permissions
|
||||
* @description Returns the highest role from the project and teamspace membership
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { EUserPermissions | undefined }
|
||||
*/
|
||||
abstract getProjectRoleByWorkspaceSlugAndProjectId: (
|
||||
workspaceSlug: string,
|
||||
projectId?: string
|
||||
) => EUserPermissions | undefined;
|
||||
getProjectRoleByWorkspaceSlugAndProjectId = computedFn(
|
||||
(workspaceSlug: string, projectId?: string): EUserPermissions | undefined => {
|
||||
// Early return for invalid inputs
|
||||
if (!workspaceSlug || !projectId) return undefined;
|
||||
|
||||
// Get permissions from the project and teamspace membership
|
||||
const projectRole = this.getProjectRole(workspaceSlug, projectId);
|
||||
const projectRoleFromTeamspace = this.getProjectRoleFromTeamspace(projectId);
|
||||
|
||||
// Filter out undefined roles and get the highest role
|
||||
const roles = [projectRole, projectRoleFromTeamspace].filter(
|
||||
(role): role is EUserPermissions => role !== undefined
|
||||
);
|
||||
return getHighestRole(roles);
|
||||
}
|
||||
);
|
||||
/**
|
||||
* @description Fetches project-level entities that are not automatically loaded by the project wrapper.
|
||||
* This is used when joining a project to ensure all necessary workspace-level project data is available.
|
||||
@@ -169,7 +181,12 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
abstract fetchWorkspaceLevelProjectEntities: (workspaceSlug: string, projectId: string) => void;
|
||||
fetchWorkspaceLevelProjectEntities = (workspaceSlug: string, projectId: string): void => {
|
||||
void Promise.all([
|
||||
this.store.projectRoot.project.fetchProjectDetails(workspaceSlug, projectId),
|
||||
this.store.issueTypes.fetchAll(workspaceSlug, projectId),
|
||||
]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Returns whether the user has the permission to access a page
|
||||
@@ -362,4 +379,17 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// private helpers
|
||||
/**
|
||||
* @description Returns the project membership permission from the teamspace
|
||||
* @description If project is linked to any teamspace, then the permission is MEMBER
|
||||
* @param { string } projectId
|
||||
* @returns { EUserPermissions | undefined }
|
||||
*/
|
||||
private getProjectRoleFromTeamspace = computedFn((projectId: string): EUserPermissions | undefined => {
|
||||
const projectTeamspaceIds = this.store.teamspaceRoot.teamspaces.getProjectTeamspaceIds(projectId);
|
||||
if (!projectTeamspaceIds || projectTeamspaceIds.length === 0) return undefined;
|
||||
return EUserPermissions.MEMBER;
|
||||
});
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import { TeamViewIssues, TeamViewIssuesFilter } from "@/store/work-items/team-vi
|
||||
import type { IWorkspaceIssues } from "@/store/work-items/workspace/issue.store";
|
||||
import { WorkspaceIssues } from "@/store/work-items/workspace/issue.store";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
import type { IWorkspaceMembership } from "@/store/member/workspace/workspace-member.store";
|
||||
import type { IWorkspaceMembership } from "@/store/member/workspace/membership.store";
|
||||
// issues data store
|
||||
import type { IArchivedIssuesFilter, IArchivedIssues } from "./archived";
|
||||
import { ArchivedIssuesFilter, ArchivedIssues } from "./archived";
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useContext } from "react";
|
||||
// context
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
// plane web stores
|
||||
import type { ISelfHostedSubscriptionStore } from "@/plane-web/store/subscription/self-hosted-subscription.store";
|
||||
import type { ISelfHostedSubscriptionStore } from "@/store/subscription/self-hosted-subscription.store";
|
||||
|
||||
export const useSelfHostedSubscription = (): ISelfHostedSubscriptionStore => {
|
||||
const context = useContext(StoreContext);
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useContext } from "react";
|
||||
// context
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
// plane web stores
|
||||
import type { IWorkspaceSubscriptionStore } from "@/plane-web/store/subscription/subscription.store";
|
||||
import type { IWorkspaceSubscriptionStore } from "@/store/subscription/subscription.store";
|
||||
|
||||
export const useWorkspaceSubscription = (): IWorkspaceSubscriptionStore => {
|
||||
const context = useContext(StoreContext);
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
import { update } from "lodash-es";
|
||||
import { computedFn } from "mobx-utils";
|
||||
import { EUserProjectRoles } from "@plane/types";
|
||||
import type { IProjectBulkAddFormData, TProjectMembership } from "@plane/types";
|
||||
// plane imports
|
||||
import { getHighestRole } from "@plane/utils";
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
// store
|
||||
import type { IMemberRootStore } from "@/store/member";
|
||||
import type { IBaseProjectMemberStore } from "@/store/member/project/base-project-member.store";
|
||||
import { BaseProjectMemberStore } from "@/store/member/project/base-project-member.store";
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
|
||||
export type IProjectMemberStore = IBaseProjectMemberStore;
|
||||
|
||||
export class ProjectMemberStore extends BaseProjectMemberStore implements IProjectMemberStore {
|
||||
constructor(_memberRoot: IMemberRootStore, rootStore: RootStore) {
|
||||
super(_memberRoot, rootStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the project role from the teamspace membership
|
||||
* @param { string } userId
|
||||
* @param { string } projectId
|
||||
* @returns { EUserProjectRoles | undefined }
|
||||
*/
|
||||
private getProjectRoleFromTeamspaceMembership = computedFn(
|
||||
(userId: string, projectId: string): EUserProjectRoles | undefined => {
|
||||
// Find all the teamspaces linked to the project
|
||||
const projectTeamspaceIds = this.rootStore.teamspaceRoot.teamspaces.getProjectTeamspaceIds(projectId);
|
||||
if (!projectTeamspaceIds || projectTeamspaceIds.length === 0) return undefined;
|
||||
// Check if the user is a member of any of the teamspaces
|
||||
const isUserMemberOfTeamspace = projectTeamspaceIds.some((teamspaceId) =>
|
||||
this.rootStore.teamspaceRoot.teamspaces.isUserMemberOfTeamspace(userId, teamspaceId)
|
||||
);
|
||||
if (!isUserMemberOfTeamspace) return undefined;
|
||||
// Return MEMBER if the user is a member of any of the teamspaces
|
||||
return EUserProjectRoles.MEMBER;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* @description Returns the highest role from the project and teamspace membership
|
||||
* @param { string } userId
|
||||
* @param { string } projectId
|
||||
* @returns { EUserProjectRoles | undefined }
|
||||
*/
|
||||
getUserProjectRole = computedFn((userId: string, projectId: string): EUserProjectRoles | undefined => {
|
||||
// Get the roles from the project and teamspace membership
|
||||
const projectRoleFromProjectMembership = this.getRoleFromProjectMembership(userId, projectId);
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
|
||||
// Filter out undefined roles and get the highest role
|
||||
const roles = [projectRoleFromProjectMembership, projectRoleFromTeamspaceMembership].filter(
|
||||
(role): role is EUserProjectRoles => role !== undefined
|
||||
);
|
||||
|
||||
// Return the highest role
|
||||
return getHighestRole(roles);
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Returns the highest role from the project and teamspace membership
|
||||
* @param projectId
|
||||
* @param userId
|
||||
* @param role
|
||||
*/
|
||||
getProjectMemberRoleForUpdate = (projectId: string, userId: string, role: EUserProjectRoles): EUserProjectRoles => {
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
|
||||
const availableRoles = [projectRoleFromTeamspaceMembership, role].filter(
|
||||
(role): role is EUserProjectRoles => role !== undefined
|
||||
);
|
||||
|
||||
return getHighestRole(availableRoles) ?? role;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Processes the removal of a member from a project
|
||||
* This method handles the cleanup of member data from the project member map
|
||||
* @param projectId - The ID of the project to remove the member from
|
||||
* @param userId - The ID of the user to remove from the project
|
||||
*/
|
||||
processMemberRemoval = (projectId: string, userId: string) => {
|
||||
// Get the role from the teamspace membership
|
||||
const projectRoleFromTeamspaceMembership = this.getProjectRoleFromTeamspaceMembership(userId, projectId);
|
||||
if (projectRoleFromTeamspaceMembership) {
|
||||
// If the user is a member of the teamspace, update user membership detail
|
||||
update(this.projectMemberMap, [projectId, userId], (prev: TProjectMembership) => ({
|
||||
...prev,
|
||||
id: null,
|
||||
original_role: null,
|
||||
role: projectRoleFromTeamspaceMembership,
|
||||
}));
|
||||
} else {
|
||||
this.handleMemberRemoval(projectId, userId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Mutate project members activity
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
*/
|
||||
mutateProjectMembersActivity = async (workspaceSlug: string, projectId: string) => {
|
||||
const isProjectMembersActivityEnabled = this.rootStore.featureFlags.getFeatureFlag(
|
||||
workspaceSlug,
|
||||
E_FEATURE_FLAGS.PROJECT_MEMBER_ACTIVITY,
|
||||
false
|
||||
);
|
||||
|
||||
if (isProjectMembersActivityEnabled) {
|
||||
await this.rootStore.projectMembersActivityStore.fetchProjectMembersActivity(workspaceSlug, projectId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description bulk add members to a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param data
|
||||
* @returns Promise<TProjectMembership[]>
|
||||
*/
|
||||
override bulkAddMembersToProject = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IProjectBulkAddFormData
|
||||
) => {
|
||||
const response = await super.bulkAddMembersToProject(workspaceSlug, projectId, data);
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description update the role of a member in a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param userId
|
||||
* @param data
|
||||
*/
|
||||
override updateMemberRole = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
userId: string,
|
||||
role: EUserProjectRoles
|
||||
) => {
|
||||
const response = await super.updateMemberRole(workspaceSlug, projectId, userId, role);
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description remove a member from a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param userId
|
||||
*/
|
||||
override removeMemberFromProject = async (workspaceSlug: string, projectId: string, userId: string) => {
|
||||
await super.removeMemberFromProject(workspaceSlug, projectId, userId);
|
||||
void this.mutateProjectMembersActivity(workspaceSlug, projectId);
|
||||
};
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
// store
|
||||
import type { IBaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store";
|
||||
import { BaseWorkspaceMemberStore } from "@/store/member/workspace/workspace-member.store";
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
import type { IMemberRootStore } from "@/store/member";
|
||||
// types
|
||||
import type { EUserPermissions } from "@plane/constants";
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
import type { IWorkspaceBulkInviteFormData } from "@plane/types";
|
||||
|
||||
export type IWorkspaceMemberStore = IBaseWorkspaceMemberStore & {
|
||||
mutateWorkspaceMembersActivity: (workspaceSlug: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export class WorkspaceMemberStore extends BaseWorkspaceMemberStore implements IWorkspaceMemberStore {
|
||||
constructor(_memberRoot: IMemberRootStore, _rootStore: RootStore) {
|
||||
super(_memberRoot, _rootStore);
|
||||
}
|
||||
|
||||
// actions
|
||||
/**
|
||||
* Mutate workspace members activity
|
||||
* @param workspaceSlug
|
||||
*/
|
||||
mutateWorkspaceMembersActivity = async (workspaceSlug: string) => {
|
||||
const isMembersActivityEnabled = this.rootStore.featureFlags.getFeatureFlag(
|
||||
workspaceSlug,
|
||||
E_FEATURE_FLAGS.WORKSPACE_MEMBER_ACTIVITY,
|
||||
false
|
||||
);
|
||||
|
||||
if (isMembersActivityEnabled) {
|
||||
await this.rootStore.workspaceMembersActivityStore.fetchWorkspaceMembersActivity(workspaceSlug);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description update the role of a workspace member
|
||||
* @param workspaceSlug
|
||||
* @param userId
|
||||
* @param data
|
||||
*/
|
||||
override updateMember = async (workspaceSlug: string, userId: string, data: { role: EUserPermissions }) => {
|
||||
await super.updateMember(workspaceSlug, userId, data);
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description remove a member from workspace
|
||||
* @param workspaceSlug
|
||||
* @param userId
|
||||
*/
|
||||
override removeMemberFromWorkspace = async (workspaceSlug: string, userId: string) => {
|
||||
await super.removeMemberFromWorkspace(workspaceSlug, userId);
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description bulk invite members to a workspace
|
||||
* @param workspaceSlug
|
||||
* @param data
|
||||
*/
|
||||
override inviteMembersToWorkspace = async (workspaceSlug: string, data: IWorkspaceBulkInviteFormData) => {
|
||||
await super.inviteMembersToWorkspace(workspaceSlug, data);
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description delete a member invitation
|
||||
* @param workspaceSlug
|
||||
* @param memberId
|
||||
*/
|
||||
override deleteMemberInvitation = async (workspaceSlug: string, invitationId: string) => {
|
||||
await super.deleteMemberInvitation(workspaceSlug, invitationId);
|
||||
void this.mutateWorkspaceMembersActivity(workspaceSlug);
|
||||
};
|
||||
}
|
||||
@@ -20,10 +20,6 @@ import type { IPublishPageStore } from "@/plane-web/store/pages/publish-page.sto
|
||||
import { PublishPageStore } from "@/plane-web/store/pages/publish-page.store";
|
||||
import type { IWorkspacePageStore } from "@/plane-web/store/pages/workspace-page.store";
|
||||
import { WorkspacePageStore } from "@/plane-web/store/pages/workspace-page.store";
|
||||
import type { ISelfHostedSubscriptionStore } from "@/plane-web/store/subscription/self-hosted-subscription.store";
|
||||
import { SelfHostedSubscriptionStore } from "@/plane-web/store/subscription/self-hosted-subscription.store";
|
||||
import type { IWorkspaceSubscriptionStore } from "@/plane-web/store/subscription/subscription.store";
|
||||
import { WorkspaceSubscriptionStore } from "@/plane-web/store/subscription/subscription.store";
|
||||
import { TimeLineStore } from "@/plane-web/store/timeline";
|
||||
import type { IProjectFilterStore, IWorkspaceProjectStatesStore } from "@/plane-web/store/workspace-project-states";
|
||||
import { ProjectFilterStore, WorkspaceProjectStatesStore } from "@/plane-web/store/workspace-project-states";
|
||||
@@ -51,8 +47,6 @@ export class RootStore extends CoreRootStore {
|
||||
theme: IThemeStore;
|
||||
workspacePages: IWorkspacePageStore;
|
||||
publishPage: IPublishPageStore;
|
||||
workspaceSubscription: IWorkspaceSubscriptionStore;
|
||||
selfHostedSubscription: ISelfHostedSubscriptionStore;
|
||||
workspaceProjectStates: IWorkspaceProjectStatesStore;
|
||||
projectFilter: IProjectFilterStore;
|
||||
cycle: ICycleStore;
|
||||
@@ -69,8 +63,6 @@ export class RootStore extends CoreRootStore {
|
||||
this.theme = new ThemeStore();
|
||||
this.workspacePages = new WorkspacePageStore(this);
|
||||
this.publishPage = new PublishPageStore(this);
|
||||
this.workspaceSubscription = new WorkspaceSubscriptionStore(this);
|
||||
this.selfHostedSubscription = new SelfHostedSubscriptionStore(this);
|
||||
this.workspaceProjectStates = new WorkspaceProjectStatesStore(this);
|
||||
this.projectFilter = new ProjectFilterStore(this);
|
||||
this.cycle = new CycleStore(this);
|
||||
@@ -90,8 +82,6 @@ export class RootStore extends CoreRootStore {
|
||||
this.theme = new ThemeStore();
|
||||
this.workspacePages = new WorkspacePageStore(this);
|
||||
this.publishPage = new PublishPageStore(this);
|
||||
this.workspaceSubscription = new WorkspaceSubscriptionStore(this);
|
||||
this.selfHostedSubscription = new SelfHostedSubscriptionStore(this);
|
||||
this.workspaceProjectStates = new WorkspaceProjectStatesStore(this);
|
||||
this.projectFilter = new ProjectFilterStore(this);
|
||||
this.cycle = new CycleStore(this);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-present Plane Software, Inc.
|
||||
* SPDX-License-Identifier: LicenseRef-Plane-Commercial
|
||||
*
|
||||
* Licensed under the Plane Commercial License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* https://plane.so/legals/eula
|
||||
*
|
||||
* DO NOT remove or modify this notice.
|
||||
* NOTICE: Proprietary and confidential. Unauthorized use or distribution is prohibited.
|
||||
*/
|
||||
|
||||
import { computedFn } from "mobx-utils";
|
||||
// plane imports
|
||||
import { EUserPermissions } from "@plane/constants";
|
||||
import { getHighestRole } from "@plane/utils";
|
||||
// plane web imports
|
||||
import type { RootStore } from "@/plane-web/store/root.store";
|
||||
// store
|
||||
import type { IBaseUserPermissionStore } from "@/store/user/base-permissions.store";
|
||||
import { BaseUserPermissionStore } from "@/store/user/base-permissions.store";
|
||||
|
||||
export type IUserPermissionStore = IBaseUserPermissionStore;
|
||||
|
||||
export class UserPermissionStore extends BaseUserPermissionStore implements IUserPermissionStore {
|
||||
constructor(store: RootStore) {
|
||||
super(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the project membership permission from the teamspace
|
||||
* @description If project is linked to any teamspace, then the permission is MEMBER
|
||||
* @param { string } projectId
|
||||
* @returns { EUserPermissions | undefined }
|
||||
*/
|
||||
private getProjectRoleFromTeamspace = computedFn((projectId: string): EUserPermissions | undefined => {
|
||||
const projectTeamspaceIds = this.store.teamspaceRoot.teamspaces.getProjectTeamspaceIds(projectId);
|
||||
if (!projectTeamspaceIds || projectTeamspaceIds.length === 0) return undefined;
|
||||
return EUserPermissions.MEMBER;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Returns the highest role from the project and teamspace membership
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { EUserPermissions | undefined }
|
||||
*/
|
||||
getProjectRoleByWorkspaceSlugAndProjectId = computedFn(
|
||||
(workspaceSlug: string, projectId?: string): EUserPermissions | undefined => {
|
||||
// Early return for invalid inputs
|
||||
if (!workspaceSlug || !projectId) return undefined;
|
||||
|
||||
// Get permissions from the project and teamspace membership
|
||||
const projectRole = this.getProjectRole(workspaceSlug, projectId);
|
||||
const projectRoleFromTeamspace = this.getProjectRoleFromTeamspace(projectId);
|
||||
|
||||
// Filter out undefined roles and get the highest role
|
||||
const roles = [projectRole, projectRoleFromTeamspace].filter(
|
||||
(role): role is EUserPermissions => role !== undefined
|
||||
);
|
||||
return getHighestRole(roles);
|
||||
}
|
||||
);
|
||||
|
||||
fetchWorkspaceLevelProjectEntities = (workspaceSlug: string, projectId: string): void => {
|
||||
void Promise.all([
|
||||
this.store.projectRoot.project.fetchProjectDetails(workspaceSlug, projectId),
|
||||
this.store.issueTypes.fetchAll(workspaceSlug, projectId),
|
||||
]);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user