mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'refactor/mobx-store' of github.com:makeplane/plane into refactor/mobx-store
This commit is contained in:
commit
db29327eb8
.github/workflows
apiserver/plane/app
web
components/core/modals
constants
hooks/store
lib
store
store_legacy/issues/project-issues
50
.github/workflows/create-sync-pr.yml
vendored
50
.github/workflows/create-sync-pr.yml
vendored
@ -1,11 +1,13 @@
|
|||||||
name: Create PR in Plane EE Repository to sync the changes
|
name: Create Sync Action
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- develop # Change this to preview
|
||||||
types:
|
types:
|
||||||
- closed
|
- closed
|
||||||
|
env:
|
||||||
|
SOURCE_BRANCH_NAME: ${{github.event.pull_request.base.ref}}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create_pr:
|
create_pr:
|
||||||
@ -16,27 +18,13 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Check SOURCE_REPO
|
|
||||||
id: check_repo
|
|
||||||
env:
|
|
||||||
SOURCE_REPO: ${{ secrets.SOURCE_REPO_NAME }}
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=is_correct_repo::$(if [[ "$SOURCE_REPO" == "makeplane/plane" ]]; then echo 'true'; else echo 'false'; fi)"
|
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
if: steps.check_repo.outputs.is_correct_repo == 'true'
|
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Branch Name
|
|
||||||
if: steps.check_repo.outputs.is_correct_repo == 'true'
|
|
||||||
run: |
|
|
||||||
echo "SOURCE_BRANCH_NAME=${{ github.head_ref }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup GH CLI
|
- name: Setup GH CLI
|
||||||
if: steps.check_repo.outputs.is_correct_repo == 'true'
|
|
||||||
run: |
|
run: |
|
||||||
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
|
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
|
||||||
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||||
@ -46,34 +34,22 @@ jobs:
|
|||||||
sudo apt install gh -y
|
sudo apt install gh -y
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.check_repo.outputs.is_correct_repo == 'true'
|
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
TARGET_REPO="${{ secrets.TARGET_REPO_NAME }}"
|
TARGET_REPO="${{ secrets.SYNC_TARGET_REPO_NAME }}"
|
||||||
TARGET_BRANCH="${{ secrets.TARGET_REPO_BRANCH }}"
|
TARGET_BRANCH="${{ secrets.SYNC_TARGET_BRANCH_NAME }}"
|
||||||
|
TARGET_BASE_BRANCH="${{ secrets.SYNC_TARGET_BASE_BRANCH_NAME }}"
|
||||||
SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}"
|
SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}"
|
||||||
|
|
||||||
git checkout $SOURCE_BRANCH
|
git checkout $SOURCE_BRANCH
|
||||||
git remote add target "https://$GH_TOKEN@github.com/$TARGET_REPO.git"
|
git remote add target-origin "https://$GH_TOKEN@github.com/$TARGET_REPO.git"
|
||||||
git push target $SOURCE_BRANCH:$SOURCE_BRANCH
|
git push target-origin $SOURCE_BRANCH:$TARGET_BRANCH
|
||||||
|
|
||||||
PR_TITLE="${{ github.event.pull_request.title }}"
|
PR_TITLE=${{secrets.SYNC_PR_TITLE}}
|
||||||
PR_BODY="${{ github.event.pull_request.body }}"
|
|
||||||
|
|
||||||
# Remove double quotes
|
|
||||||
PR_TITLE_CLEANED="${PR_TITLE//\"/}"
|
|
||||||
PR_BODY_CLEANED="${PR_BODY//\"/}"
|
|
||||||
|
|
||||||
# Construct PR_BODY_CONTENT using a here-document
|
|
||||||
PR_BODY_CONTENT=$(cat <<EOF
|
|
||||||
$PR_BODY_CLEANED
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
|
|
||||||
gh pr create \
|
gh pr create \
|
||||||
--base $TARGET_BRANCH \
|
--base $TARGET_BASE_BRANCH \
|
||||||
--head $SOURCE_BRANCH \
|
--head $TARGET_BRANCH \
|
||||||
--title "[SYNC] $PR_TITLE_CLEANED" \
|
--title "$PR_TITLE" \
|
||||||
--body "$PR_BODY_CONTENT" \
|
|
||||||
--repo $TARGET_REPO
|
--repo $TARGET_REPO
|
||||||
|
@ -31,6 +31,7 @@ from .project import (
|
|||||||
ProjectDeployBoardSerializer,
|
ProjectDeployBoardSerializer,
|
||||||
ProjectMemberAdminSerializer,
|
ProjectMemberAdminSerializer,
|
||||||
ProjectPublicMemberSerializer,
|
ProjectPublicMemberSerializer,
|
||||||
|
ProjectMemberRoleSerializer,
|
||||||
)
|
)
|
||||||
from .state import StateSerializer, StateLiteSerializer
|
from .state import StateSerializer, StateLiteSerializer
|
||||||
from .view import GlobalViewSerializer, IssueViewSerializer, IssueViewFavoriteSerializer
|
from .view import GlobalViewSerializer, IssueViewSerializer, IssueViewFavoriteSerializer
|
||||||
|
@ -159,6 +159,11 @@ class ProjectMemberAdminSerializer(BaseSerializer):
|
|||||||
model = ProjectMember
|
model = ProjectMember
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
class ProjectMemberRoleSerializer(DynamicBaseSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ProjectMember
|
||||||
|
fields = ("id", "role", "member", "project")
|
||||||
|
|
||||||
class ProjectMemberInviteSerializer(BaseSerializer):
|
class ProjectMemberInviteSerializer(BaseSerializer):
|
||||||
project = ProjectLiteSerializer(read_only=True)
|
project = ProjectLiteSerializer(read_only=True)
|
||||||
|
@ -62,7 +62,7 @@ class WorkspaceLiteSerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WorkSpaceMemberSerializer(BaseSerializer):
|
class WorkSpaceMemberSerializer(DynamicBaseSerializer):
|
||||||
member = UserLiteSerializer(read_only=True)
|
member = UserLiteSerializer(read_only=True)
|
||||||
workspace = WorkspaceLiteSerializer(read_only=True)
|
workspace = WorkspaceLiteSerializer(read_only=True)
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ class WorkspaceMemberMeSerializer(BaseSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class WorkspaceMemberAdminSerializer(BaseSerializer):
|
class WorkspaceMemberAdminSerializer(DynamicBaseSerializer):
|
||||||
member = UserAdminLiteSerializer(read_only=True)
|
member = UserAdminLiteSerializer(read_only=True)
|
||||||
workspace = WorkspaceLiteSerializer(read_only=True)
|
workspace = WorkspaceLiteSerializer(read_only=True)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ from plane.app.views import (
|
|||||||
WorkspaceUserProfileEndpoint,
|
WorkspaceUserProfileEndpoint,
|
||||||
WorkspaceUserProfileIssuesEndpoint,
|
WorkspaceUserProfileIssuesEndpoint,
|
||||||
WorkspaceLabelsEndpoint,
|
WorkspaceLabelsEndpoint,
|
||||||
|
WorkspaceProjectMemberEndpoint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +93,11 @@ urlpatterns = [
|
|||||||
WorkSpaceMemberViewSet.as_view({"get": "list"}),
|
WorkSpaceMemberViewSet.as_view({"get": "list"}),
|
||||||
name="workspace-member",
|
name="workspace-member",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/project-members/",
|
||||||
|
WorkspaceProjectMemberEndpoint.as_view(),
|
||||||
|
name="workspace-member-roles",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"workspaces/<str:slug>/members/<uuid:pk>/",
|
"workspaces/<str:slug>/members/<uuid:pk>/",
|
||||||
WorkSpaceMemberViewSet.as_view(
|
WorkSpaceMemberViewSet.as_view(
|
||||||
|
@ -45,6 +45,7 @@ from .workspace import (
|
|||||||
WorkspaceUserProfileEndpoint,
|
WorkspaceUserProfileEndpoint,
|
||||||
WorkspaceUserProfileIssuesEndpoint,
|
WorkspaceUserProfileIssuesEndpoint,
|
||||||
WorkspaceLabelsEndpoint,
|
WorkspaceLabelsEndpoint,
|
||||||
|
WorkspaceProjectMemberEndpoint
|
||||||
)
|
)
|
||||||
from .state import StateViewSet
|
from .state import StateViewSet
|
||||||
from .view import (
|
from .view import (
|
||||||
|
@ -36,6 +36,7 @@ from plane.app.serializers import (
|
|||||||
ProjectFavoriteSerializer,
|
ProjectFavoriteSerializer,
|
||||||
ProjectDeployBoardSerializer,
|
ProjectDeployBoardSerializer,
|
||||||
ProjectMemberAdminSerializer,
|
ProjectMemberAdminSerializer,
|
||||||
|
ProjectMemberRoleSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
@ -710,13 +711,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
def list(self, request, slug, project_id):
|
def list(self, request, slug, project_id):
|
||||||
project_member = ProjectMember.objects.get(
|
# Get the list of project members for the project
|
||||||
member=request.user,
|
|
||||||
workspace__slug=slug,
|
|
||||||
project_id=project_id,
|
|
||||||
is_active=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
project_members = ProjectMember.objects.filter(
|
project_members = ProjectMember.objects.filter(
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
@ -724,10 +719,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||||||
is_active=True,
|
is_active=True,
|
||||||
).select_related("project", "member", "workspace")
|
).select_related("project", "member", "workspace")
|
||||||
|
|
||||||
if project_member.role > 10:
|
serializer = ProjectMemberRoleSerializer(project_members, fields=("id", "member", "role"), many=True)
|
||||||
serializer = ProjectMemberAdminSerializer(project_members, many=True)
|
|
||||||
else:
|
|
||||||
serializer = ProjectMemberSerializer(project_members, many=True)
|
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
|
@ -44,6 +44,7 @@ from plane.app.serializers import (
|
|||||||
IssueLiteSerializer,
|
IssueLiteSerializer,
|
||||||
WorkspaceMemberAdminSerializer,
|
WorkspaceMemberAdminSerializer,
|
||||||
WorkspaceMemberMeSerializer,
|
WorkspaceMemberMeSerializer,
|
||||||
|
ProjectMemberRoleSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.views.base import BaseAPIView
|
from plane.app.views.base import BaseAPIView
|
||||||
from . import BaseViewSet
|
from . import BaseViewSet
|
||||||
@ -543,10 +544,15 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||||||
workspace_members = self.get_queryset()
|
workspace_members = self.get_queryset()
|
||||||
|
|
||||||
if workspace_member.role > 10:
|
if workspace_member.role > 10:
|
||||||
serializer = WorkspaceMemberAdminSerializer(workspace_members, many=True)
|
serializer = WorkspaceMemberAdminSerializer(
|
||||||
|
workspace_members,
|
||||||
|
fields=("id", "member", "role"),
|
||||||
|
many=True,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
serializer = WorkSpaceMemberSerializer(
|
serializer = WorkSpaceMemberSerializer(
|
||||||
workspace_members,
|
workspace_members,
|
||||||
|
fields=("id", "member", "role"),
|
||||||
many=True,
|
many=True,
|
||||||
)
|
)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
@ -711,6 +717,43 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspaceProjectMemberEndpoint(BaseAPIView):
|
||||||
|
serializer_class = ProjectMemberRoleSerializer
|
||||||
|
model = ProjectMember
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
WorkspaceEntityPermission,
|
||||||
|
]
|
||||||
|
|
||||||
|
def get(self, request, slug):
|
||||||
|
# Fetch all project IDs where the user is involved
|
||||||
|
project_ids = ProjectMember.objects.filter(
|
||||||
|
member=request.user,
|
||||||
|
member__is_bot=False,
|
||||||
|
is_active=True,
|
||||||
|
).values_list('project_id', flat=True).distinct()
|
||||||
|
|
||||||
|
# Get all the project members in which the user is involved
|
||||||
|
project_members = ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
member__is_bot=False,
|
||||||
|
project_id__in=project_ids,
|
||||||
|
is_active=True,
|
||||||
|
).select_related("project", "member", "workspace")
|
||||||
|
project_members = ProjectMemberRoleSerializer(project_members, many=True).data
|
||||||
|
|
||||||
|
project_members_dict = dict()
|
||||||
|
|
||||||
|
# Construct a dictionary with project_id as key and project_members as value
|
||||||
|
for project_member in project_members:
|
||||||
|
project_id = project_member.pop("project")
|
||||||
|
if str(project_id) not in project_members_dict:
|
||||||
|
project_members_dict[str(project_id)] = []
|
||||||
|
project_members_dict[str(project_id)].append(project_member)
|
||||||
|
|
||||||
|
return Response(project_members_dict, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class TeamMemberViewSet(BaseViewSet):
|
class TeamMemberViewSet(BaseViewSet):
|
||||||
serializer_class = TeamSerializer
|
serializer_class = TeamSerializer
|
||||||
model = Team
|
model = Team
|
||||||
|
@ -118,6 +118,7 @@ export const LinkModal: FC<Props> = (props) => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
hasError={Boolean(errors.url)}
|
hasError={Boolean(errors.url)}
|
||||||
placeholder="https://..."
|
placeholder="https://..."
|
||||||
|
pattern="^(https?://).*"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
5
web/constants/swr-config.ts
Normal file
5
web/constants/swr-config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const SWR_CONFIG = {
|
||||||
|
refreshWhenHidden: false,
|
||||||
|
revalidateIfStale: false,
|
||||||
|
errorRetryCount: 3,
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
export * from "./use-application";
|
export * from "./use-application";
|
||||||
export * from "./use-cycle";
|
export * from "./use-cycle";
|
||||||
export * from "./use-label";
|
export * from "./use-label";
|
||||||
|
export * from "./use-member";
|
||||||
export * from "./use-module";
|
export * from "./use-module";
|
||||||
export * from "./use-page";
|
export * from "./use-page";
|
||||||
export * from "./use-project-publish";
|
export * from "./use-project-publish";
|
||||||
|
11
web/hooks/store/use-member.ts
Normal file
11
web/hooks/store/use-member.ts
Normal file
@ -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;
|
||||||
|
};
|
@ -12,6 +12,9 @@ import { THEMES } from "constants/themes";
|
|||||||
import InstanceLayout from "layouts/instance-layout";
|
import InstanceLayout from "layouts/instance-layout";
|
||||||
// contexts
|
// contexts
|
||||||
import { ToastContextProvider } from "contexts/toast.context";
|
import { ToastContextProvider } from "contexts/toast.context";
|
||||||
|
import { SWRConfig } from "swr";
|
||||||
|
// constants
|
||||||
|
import { SWR_CONFIG } from "constants/swr-config";
|
||||||
// dynamic imports
|
// dynamic imports
|
||||||
const StoreWrapper = dynamic(() => import("lib/wrappers/store-wrapper"), { ssr: false });
|
const StoreWrapper = dynamic(() => import("lib/wrappers/store-wrapper"), { ssr: false });
|
||||||
const PosthogWrapper = dynamic(() => import("lib/wrappers/posthog-wrapper"), { ssr: false });
|
const PosthogWrapper = dynamic(() => import("lib/wrappers/posthog-wrapper"), { ssr: false });
|
||||||
@ -51,7 +54,7 @@ export const AppProvider: FC<IAppProvider> = observer((props) => {
|
|||||||
posthogAPIKey={envConfig?.posthog_api_key || null}
|
posthogAPIKey={envConfig?.posthog_api_key || null}
|
||||||
posthogHost={envConfig?.posthog_host || null}
|
posthogHost={envConfig?.posthog_host || null}
|
||||||
>
|
>
|
||||||
{children}
|
<SWRConfig value={SWR_CONFIG}>{children}</SWRConfig>
|
||||||
</PosthogWrapper>
|
</PosthogWrapper>
|
||||||
</CrispWrapper>
|
</CrispWrapper>
|
||||||
</StoreWrapper>
|
</StoreWrapper>
|
||||||
|
37
web/store/member/index.ts
Normal file
37
web/store/member/index.ts
Normal file
@ -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<string, IUserLite>;
|
||||||
|
// sub-stores
|
||||||
|
workspaceMember: IWorkspaceMemberStore;
|
||||||
|
projectMember: IProjectMemberStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemberRootStore implements IMemberRootStore {
|
||||||
|
// observables
|
||||||
|
memberMap: Record<string, IUserLite> = {};
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
100
web/store/member/project-member.store.ts
Normal file
100
web/store/member/project-member.store.ts
Normal file
@ -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<string, IProjectMember>;
|
||||||
|
};
|
||||||
|
// computed
|
||||||
|
projectMembers: string[] | null;
|
||||||
|
// computed actions
|
||||||
|
getProjectMemberDetails: (projectMemberId: string) => IProjectMemberDetails | null;
|
||||||
|
// actions
|
||||||
|
fetchProjectMembers: (workspaceSlug: string, projectId: string) => Promise<IProjectMember[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProjectMemberStore implements IProjectMemberStore {
|
||||||
|
// observables
|
||||||
|
projectMemberMap: {
|
||||||
|
[projectId: string]: Record<string, IProjectMember>;
|
||||||
|
} = {};
|
||||||
|
// root store
|
||||||
|
rootStore: RootStore;
|
||||||
|
// root store memberMap
|
||||||
|
memberMap: Record<string, IUserLite> = {};
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
105
web/store/member/workspace-member.store.ts
Normal file
105
web/store/member/workspace-member.store.ts
Normal file
@ -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<string, IWorkspaceMembership>;
|
||||||
|
};
|
||||||
|
// computed
|
||||||
|
workspaceMembers: string[] | null;
|
||||||
|
// computed actions
|
||||||
|
getWorkspaceMemberDetails: (workspaceMemberId: string) => IWorkspaceMember | null;
|
||||||
|
// actions
|
||||||
|
fetchWorkspaceMembers: (workspaceSlug: string) => Promise<IWorkspaceMember[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
||||||
|
// observables
|
||||||
|
workspaceMemberMap: {
|
||||||
|
[workspaceSlug: string]: Record<string, IWorkspaceMembership>;
|
||||||
|
} = {};
|
||||||
|
// root store
|
||||||
|
rootStore: RootStore;
|
||||||
|
// root store memberMap
|
||||||
|
memberMap: Record<string, IUserLite> = {};
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -11,6 +11,8 @@ import { IssueRootStore, IIssueRootStore } from "./issue/root.store";
|
|||||||
import { IStateStore, StateStore } from "./state.store";
|
import { IStateStore, StateStore } from "./state.store";
|
||||||
import { IPageStore, PageStore } from "./page.store";
|
import { IPageStore, PageStore } from "./page.store";
|
||||||
import { ILabelRootStore, LabelRootStore } from "./label";
|
import { ILabelRootStore, LabelRootStore } from "./label";
|
||||||
|
import { IMemberRootStore, MemberRootStore } from "./member";
|
||||||
|
|
||||||
enableStaticRendering(typeof window === "undefined");
|
enableStaticRendering(typeof window === "undefined");
|
||||||
|
|
||||||
export class RootStore {
|
export class RootStore {
|
||||||
@ -19,6 +21,7 @@ export class RootStore {
|
|||||||
workspaceRoot: IWorkspaceRootStore;
|
workspaceRoot: IWorkspaceRootStore;
|
||||||
projectRoot: IProjectRootStore;
|
projectRoot: IProjectRootStore;
|
||||||
labelRoot: ILabelRootStore;
|
labelRoot: ILabelRootStore;
|
||||||
|
memberRoot: IMemberRootStore;
|
||||||
cycle: ICycleStore;
|
cycle: ICycleStore;
|
||||||
module: IModuleStore;
|
module: IModuleStore;
|
||||||
projectView: IProjectViewStore;
|
projectView: IProjectViewStore;
|
||||||
@ -32,6 +35,7 @@ export class RootStore {
|
|||||||
this.workspaceRoot = new WorkspaceRootStore(this);
|
this.workspaceRoot = new WorkspaceRootStore(this);
|
||||||
this.projectRoot = new ProjectRootStore(this);
|
this.projectRoot = new ProjectRootStore(this);
|
||||||
this.labelRoot = new LabelRootStore(this);
|
this.labelRoot = new LabelRootStore(this);
|
||||||
|
this.memberRoot = new MemberRootStore(this);
|
||||||
// independent stores
|
// independent stores
|
||||||
this.state = new StateStore(this);
|
this.state = new StateStore(this);
|
||||||
this.issue = new IssueRootStore(this);
|
this.issue = new IssueRootStore(this);
|
||||||
|
@ -170,7 +170,7 @@ export class CycleIssuesFilterStore extends IssueFilterBaseStore implements ICyc
|
|||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, cycleId);
|
console.log("error in fetchCycleFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -215,7 +215,7 @@ export class CycleIssuesFilterStore extends IssueFilterBaseStore implements ICyc
|
|||||||
await this.fetchCycleFilters(workspaceSlug, projectId, cycleId);
|
await this.fetchCycleFilters(workspaceSlug, projectId, cycleId);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, cycleId);
|
console.log("error in cycleFetchFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -170,7 +170,7 @@ export class ModuleIssuesFilterStore extends IssueFilterBaseStore implements IMo
|
|||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, moduleId);
|
console.log("error in moduleFetchFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -216,7 +216,7 @@ export class ModuleIssuesFilterStore extends IssueFilterBaseStore implements IMo
|
|||||||
await this.fetchModuleFilters(workspaceSlug, projectId, moduleId);
|
await this.fetchModuleFilters(workspaceSlug, projectId, moduleId);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, moduleId);
|
console.log("error in projectFetchFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -170,7 +170,7 @@ export class ViewIssuesFilterStore extends IssueFilterBaseStore implements IView
|
|||||||
|
|
||||||
return filters;
|
return filters;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, viewId);
|
console.log("error in viewFetchFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -216,7 +216,7 @@ export class ViewIssuesFilterStore extends IssueFilterBaseStore implements IView
|
|||||||
await this.fetchViewFilters(workspaceSlug, projectId, viewId);
|
await this.fetchViewFilters(workspaceSlug, projectId, viewId);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.fetchFilters(workspaceSlug, projectId, viewId);
|
console.log("error in viewFetchFilters", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user