fix: mobx changes

This commit is contained in:
sriram veeraghanta 2023-08-31 16:35:26 +05:30
parent 6246a740d5
commit 240acc6cd0
18 changed files with 463 additions and 132 deletions

View File

@ -0,0 +1,6 @@
export * from "./email-code-form";
export * from "./email-password-form";
export * from "./email-reset-password-form";
export * from "./github-login-button";
export * from "./google-login";
export * from "./onboarding-form";

View File

@ -1,6 +1,5 @@
"use client";
// mobx react lite
import { FC } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// components
import { IssueBlockPriority } from "components/issues/board-views/block-priority";
@ -11,10 +10,32 @@ import { IssueBlockDueDate } from "components/issues/board-views/block-due-date"
import { useMobxStore } from "lib/mobx/store-provider";
// interfaces
import { IIssue } from "types/issue";
// store
import { RootStore } from "store/root";
export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
export const IssueListBlock: FC<{ issue: IIssue }> = observer((props) => {
const { issue } = props;
// store
const { project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
// router
const router = useRouter();
const { workspace_slug, project_slug, board } = router.query;
const handleBlockClick = () => {
issueDetailStore.setPeekId(issue.id);
router.replace(
{
pathname: `/${workspace_slug?.toString()}/${project_slug}`,
query: {
board: board?.toString(),
peekId: issue.id,
},
},
undefined,
{ shallow: true }
);
// router.push(`/${workspace_slug?.toString()}/${project_slug}?board=${board?.toString()}&peekId=${issue.id}`);
};
return (
<div className="flex items-center px-9 py-3.5 relative gap-10 border-b border-custom-border-200 bg-custom-background-100 last:border-b-0">
@ -25,12 +46,7 @@ export const IssueListBlock = observer(({ issue }: { issue: IIssue }) => {
</div>
{/* name */}
<div className="h-full line-clamp-1 w-full overflow-ellipsis cursor-pointer">
<p
onClick={() => {
issueDetailStore.setPeekId(issue.id);
}}
className="text-[0.825rem] font-medium text-sm truncate text-custom-text-100"
>
<p onClick={handleBlockClick} className="text-[0.825rem] font-medium text-sm truncate text-custom-text-100">
{issue.name}
</p>
</div>

View File

@ -1,6 +1,4 @@
"use client";
// mobx react lite
import { useEffect } from "react";
import { observer } from "mobx-react-lite";
// components
import { IssueListHeader } from "components/issues/board-views/list/header";
@ -9,11 +7,13 @@ import { IssueListBlock } from "components/issues/board-views/list/block";
import { IIssueState, IIssue } from "types/issue";
// mobx hook
import { useMobxStore } from "lib/mobx/store-provider";
// store
import { RootStore } from "store/root";
import { useRouter } from "next/router";
export const IssueListView = observer(() => {
const { issue: issueStore }: RootStore = useMobxStore();
console.log("issueStore", issueStore.states);
return (
<>
{issueStore?.states &&

View File

@ -11,13 +11,7 @@ import { SecondaryButton } from "components/ui";
// types
import { Comment } from "types";
// components
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>((props, ref) => (
<Tiptap {...props} forwardedRef={ref} />
));
TiptapEditor.displayName = "TiptapEditor";
import { TipTapEditor } from "components/tiptap";
const defaultValues: Partial<Comment> = {
comment_json: "",
@ -25,12 +19,11 @@ const defaultValues: Partial<Comment> = {
};
type Props = {
issueId: string | null;
disabled?: boolean;
};
export const AddComment: React.FC<Props> = observer((props) => {
const { issueId, disabled = false } = props;
const { disabled = false } = props;
const {
handleSubmit,
@ -44,7 +37,9 @@ export const AddComment: React.FC<Props> = observer((props) => {
const router = useRouter();
const { workspace_slug, project_slug } = router.query as { workspace_slug: string; project_slug: string };
const { issue: issueStore, user: userStore } = useMobxStore();
const { issue: issueStore, user: userStore, issueDetails: issueDetailStore } = useMobxStore();
const issueId = issueDetailStore.peekId;
const editorRef = useRef<any>(null);
@ -61,8 +56,8 @@ export const AddComment: React.FC<Props> = observer((props) => {
)
return;
await issueStore
.createIssueCommentAsync(workspace_slug, project_slug, issueId, formData)
await issueDetailStore
.addIssueComment(workspace_slug, project_slug, issueId, formData)
.then(() => {
reset(defaultValues);
editorRef.current?.clearEditor();
@ -83,7 +78,7 @@ export const AddComment: React.FC<Props> = observer((props) => {
name="comment_html"
control={control}
render={({ field: { value, onChange } }) => (
<TiptapEditor
<TipTapEditor
workspaceSlug={workspace_slug as string}
ref={editorRef}
value={

View File

@ -17,13 +17,7 @@ import { ChatBubbleLeftEllipsisIcon, CheckIcon, XMarkIcon, EllipsisVerticalIcon
import { timeAgo } from "helpers/date-time.helper";
// types
import { Comment } from "types";
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>((props, ref) => (
<Tiptap {...props} forwardedRef={ref} />
));
TiptapEditor.displayName = "TiptapEditor";
import { TipTapEditor } from "components/tiptap";
type Props = {
workspaceSlug: string;
@ -124,7 +118,7 @@ export const CommentCard: React.FC<Props> = observer((props) => {
className={`flex-col gap-2 ${isEditing ? "flex" : "hidden"}`}
>
<div>
<TiptapEditor
<TipTapEditor
workspaceSlug={workspaceSlug as string}
ref={editorRef}
value={watch("comment_html")}
@ -154,7 +148,7 @@ export const CommentCard: React.FC<Props> = observer((props) => {
</div>
</form>
<div className={`${isEditing ? "hidden" : ""}`}>
<TiptapEditor
<TipTapEditor
workspaceSlug={workspaceSlug as string}
ref={showEditorRef}
value={comment.comment_html}

View File

@ -14,25 +14,24 @@ type Props = {
export const PeekOverviewIssueActivity: React.FC<Props> = observer((props) => {
const router = useRouter();
const { workspaceSlug } = router.query;
const { workspace_slug } = router.query;
const { issueDetails: issueDetailStore, user: userStore } = useMobxStore();
const issueId = issueDetailStore?.peekId;
const comments = issueDetailStore?.details[issueId ?? ""]?.comments ?? [];
const comments = issueDetailStore.details[issueDetailStore.peekId || ""]?.comments || [];
return (
<div>
<h4 className="font-medium">Activity</h4>
{workspaceSlug && (
{workspace_slug && (
<div className="mt-4">
<div className="space-y-4">
{comments.map((comment: any) => (
<CommentCard comment={comment} workspaceSlug={workspaceSlug?.toString()} />
<CommentCard comment={comment} workspaceSlug={workspace_slug?.toString()} />
))}
</div>
<div className="mt-4">
<AddComment disabled={!userStore.currentUser} issueId={issueId} />
<AddComment disabled={!userStore.currentUser} />
</div>
</div>
)}

View File

@ -1,4 +1,3 @@
// components
import { IssueReactions } from "components/issues/peek-overview";
// types
import { IIssue } from "types";

View File

@ -9,22 +9,21 @@ import { groupReactions, renderEmoji } from "helpers/emoji.helper";
import { ReactionSelector } from "components/ui";
export const IssueEmojiReactions: React.FC = observer(() => {
// router
const router = useRouter();
const { workspace_slug, project_slug } = router.query as { workspace_slug: string; project_slug: string };
const { user: userStore, issue: issueStore } = useMobxStore();
// store
const { user: userStore, issue: issueStore, issueDetails: issueDetailsStore } = useMobxStore();
const user = userStore?.currentUser;
const issueId = issueStore.activePeekOverviewIssueId;
const reactions = issueId ? issueStore.issue_detail[issueId]?.reactions || [] : [];
const issueId = issueDetailsStore.peekId;
const reactions = issueId ? issueDetailsStore.details[issueId]?.reactions || [] : [];
const groupedReactions = groupReactions(reactions, "reaction");
const handleReactionClick = (reactionHexa: string) => {
if (!workspace_slug || !project_slug || !issueId) return;
const userReaction = reactions?.find((r) => r.created_by === user?.id && r.reaction === reactionHexa);
const userReaction = reactions?.find((r: any) => r.created_by === user?.id && r.reaction === reactionHexa);
if (userReaction)
issueStore.deleteIssueReactionAsync(workspace_slug, userReaction.project, userReaction.issue, reactionHexa);
@ -34,12 +33,6 @@ export const IssueEmojiReactions: React.FC = observer(() => {
});
};
useEffect(() => {
if (user) return;
userStore.getUserAsync();
}, [user, userStore]);
return (
<>
<ReactionSelector
@ -63,7 +56,7 @@ export const IssueEmojiReactions: React.FC = observer(() => {
}}
key={reaction}
className={`flex items-center gap-1 text-custom-text-100 text-sm h-full px-2 py-1 rounded-md border ${
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
reactions?.some((r: any) => r.actor === user?.id && r.reaction === reaction)
? "bg-custom-primary-100/10 border-custom-primary-100"
: "bg-custom-background-80 border-transparent"
}`}
@ -71,7 +64,7 @@ export const IssueEmojiReactions: React.FC = observer(() => {
<span>{renderEmoji(reaction)}</span>
<span
className={
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
reactions?.some((r: any) => r.actor === user?.id && r.reaction === reaction)
? "text-custom-primary-100"
: ""
}

View File

@ -1,18 +1,25 @@
"use client";
// ui
import { IssueEmojiReactions, IssueVotes } from "components/issues/peek-overview";
import { useMobxStore } from "lib/mobx/store-provider";
export const IssueReactions: React.FC = () => (
<div className="flex gap-3 items-center mt-4">
<div className="flex gap-2 items-center">
<IssueVotes />
export const IssueReactions: React.FC = () => {
const { project: projectStore } = useMobxStore();
return (
<div className="flex gap-3 items-center mt-4">
{projectStore?.deploySettings?.votes && (
<>
<div className="flex gap-2 items-center">
<IssueVotes />
</div>
<div className="w-0.5 h-8 bg-custom-background-200" />
</>
)}
{projectStore?.deploySettings?.reactions && (
<div className="flex gap-2 items-center">
<IssueEmojiReactions />
</div>
)}
</div>
<div className="w-0.5 h-8 bg-custom-background-200" />
<div className="flex gap-2 items-center">
<IssueEmojiReactions />
</div>
</div>
);
);
};

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo } from "react";
import { useRouter } from "next/router";
import { Dialog, Transition } from "@headlessui/react";
import { observer } from "mobx-react-lite";
@ -18,20 +18,19 @@ export const IssuePeekOverview: React.FC<Props> = observer((props) => {
const { isOpen, onClose } = props;
// router
const router = useRouter();
const { workspace_slug, project_slug } = router.query;
const { workspace_slug, project_slug, peekId } = router.query;
// store
const { issueDetails: issueDetailStore } = useMobxStore();
const { issueDetails: issueDetailStore, issue: issueStore } = useMobxStore();
const issueDetails = issueDetailStore.peekId ? issueDetailStore.details[issueDetailStore.peekId] : null;
console.log("issueDetails", issueDetails);
const issueDetails = issueDetailStore.peekId && peekId ? issueDetailStore.details[peekId.toString()] : null;
useEffect(() => {
if (workspace_slug && project_slug && issueDetailStore.peekId) {
if (workspace_slug && project_slug && peekId && issueStore.issues && issueStore.issues.length > 0) {
if (!issueDetails) {
issueDetailStore.fetchIssueDetails(workspace_slug.toString(), project_slug.toString(), issueDetailStore.peekId);
issueDetailStore.fetchIssueDetails(workspace_slug.toString(), project_slug.toString(), peekId.toString());
}
}
}, [workspace_slug, project_slug, issueDetailStore, issueDetails]);
}, [workspace_slug, project_slug, issueDetailStore, issueDetails, peekId, issueStore.issues]);
const handleClose = () => {
onClose();

View File

@ -11,6 +11,7 @@ import {
} from "components/issues/peek-overview";
// types
import { IIssue } from "types/issue";
import { RootStore } from "store/root";
type Props = {
handleClose: () => void;
@ -20,6 +21,8 @@ type Props = {
export const SidePeekView: React.FC<Props> = observer((props) => {
const { handleClose, issueDetails } = props;
const { project: projectStore } = useMobxStore();
return (
<div className="h-full w-full flex flex-col overflow-hidden">
<div className="w-full p-5">
@ -38,9 +41,11 @@ export const SidePeekView: React.FC<Props> = observer((props) => {
{/* divider */}
<div className="h-[1] w-full border-t border-custom-border-200 my-5" />
{/* issue activity/comments */}
<div className="w-full pb-5">
<PeekOverviewIssueActivity issueDetails={issueDetails} />
</div>
{projectStore?.deploySettings?.comments && (
<div className="w-full pb-5">
<PeekOverviewIssueActivity issueDetails={issueDetails} />
</div>
)}
</div>
)}
</div>

View File

@ -3,10 +3,10 @@ import { useDebouncedCallback } from "use-debounce";
import { EditorBubbleMenu } from "./bubble-menu";
import { TiptapExtensions } from "./extensions";
import { TiptapEditorProps } from "./props";
import { useImperativeHandle, useRef } from "react";
import { useImperativeHandle, useRef, forwardRef } from "react";
import { ImageResizer } from "./extensions/image-resize";
export interface ITiptapRichTextEditor {
export interface ITipTapRichTextEditor {
value: string;
noBorder?: boolean;
borderOnFocus?: boolean;
@ -21,7 +21,7 @@ export interface ITiptapRichTextEditor {
debouncedUpdatesEnabled?: boolean;
}
const Tiptap = (props: ITiptapRichTextEditor) => {
const Tiptap = (props: ITipTapRichTextEditor) => {
const {
onChange,
debouncedUpdatesEnabled,
@ -73,9 +73,10 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
}, 500);
}, 1000);
const editorClassNames = `relative w-full max-w-screen-lg sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md
${noBorder ? "" : "border border-custom-border-200"} ${borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
} ${customClassName}`;
const editorClassNames = `relative w-full max-w-full sm:rounded-lg mt-2 p-3 relative focus:outline-none rounded-md
${noBorder ? "" : "border border-custom-border-200"} ${
borderOnFocus ? "focus:border border-custom-border-300" : "focus:border-0"
} ${customClassName}`;
if (!editor) return null;
editorRef.current = editor;
@ -97,4 +98,10 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
);
};
export default Tiptap;
const TipTapEditor = forwardRef<ITipTapRichTextEditor, ITipTapRichTextEditor>((props, ref) => (
<Tiptap {...props} forwardedRef={ref} />
));
TipTapEditor.displayName = "TipTapEditor";
export { TipTapEditor };

View File

@ -14,12 +14,23 @@ import { useMobxStore } from "lib/mobx/store-provider";
export const ProjectDetailsView = observer(() => {
const router = useRouter();
const { workspace_slug, project_slug, states, labels, priorities } = router.query;
const { workspace_slug, project_slug, states, labels, priorities, board, peekId } = router.query;
const { issue: issueStore, project: projectStore, issueDetails: issueDetailStore }: RootStore = useMobxStore();
const {
issue: issueStore,
project: projectStore,
issueDetails: issueDetailStore,
user: userStore,
}: RootStore = useMobxStore();
const activeIssueId = issueDetailStore.peekId;
useEffect(() => {
if (!userStore.currentUser) {
userStore.fetchCurrentUser();
}
}, [userStore]);
useEffect(() => {
if (workspace_slug && project_slug) {
const params = {
@ -31,11 +42,29 @@ export const ProjectDetailsView = observer(() => {
}
}, [workspace_slug, project_slug, issueStore, states, labels, priorities]);
useEffect(() => {
if (peekId && workspace_slug && project_slug) {
issueDetailStore.setPeekId(peekId.toString());
}
}, [peekId, issueDetailStore, project_slug, workspace_slug]);
const handlePeekClose = () => {
issueDetailStore.setPeekId(null);
router.replace(
{
pathname: `/${workspace_slug?.toString()}/${project_slug}`,
query: {
...(board && { board: board.toString() }),
},
},
undefined,
{ shallow: true }
);
};
return (
<div className="relative w-full h-full overflow-hidden">
{workspace_slug && (
<IssuePeekOverview isOpen={Boolean(activeIssueId)} onClose={() => issueDetailStore.setPeekId(null)} />
)}
{workspace_slug && <IssuePeekOverview isOpen={Boolean(activeIssueId)} onClose={handlePeekClose} />}
{issueStore?.loader && !issueStore.issues ? (
<div className="text-sm text-center py-10 text-custom-text-100">Loading...</div>

View File

@ -1,7 +1,157 @@
import React from "react";
import React, { useEffect } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
// assets
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
// mobx
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// services
import authenticationService from "services/authentication.service";
// hooks
import useToast from "hooks/use-toast";
// components
import { EmailPasswordForm, GithubLoginButton, GoogleLoginButton, EmailCodeForm } from "components/accounts";
const HomePage = () => (
<div className="relative w-screen h-screen flex justify-center items-center text-5xl">Plane Deploy</div>
);
const HomePage = () => {
const { user: userStore } = useMobxStore();
const router = useRouter();
const { next_path = "/" } = router.query;
const { setToastAlert } = useToast();
const onSignInError = (error: any) => {
setToastAlert({
title: "Error signing in!",
type: "error",
message: error?.error || "Something went wrong. Please try again later or contact the support team.",
});
};
const onSignInSuccess = (response: any) => {
const isOnboarded = response?.user?.onboarding_step?.profile_complete || false;
userStore.setCurrentUser(response?.user);
if (!isOnboarded) {
router.push(`/onboarding?next_path=${next_path}`);
return;
}
console.log("hello");
router.push(next_path.toString());
};
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
try {
if (clientId && credential) {
const socialAuthPayload = {
medium: "google",
credential,
clientId,
};
const response = await authenticationService.socialAuth(socialAuthPayload);
onSignInSuccess(response);
} else {
throw Error("Cant find credentials");
}
} catch (err: any) {
onSignInError(err);
}
};
const handleGitHubSignIn = async (credential: string) => {
try {
if (process.env.NEXT_PUBLIC_GITHUB_ID && credential) {
const socialAuthPayload = {
medium: "github",
credential,
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
};
const response = await authenticationService.socialAuth(socialAuthPayload);
onSignInSuccess(response);
} else {
throw Error("Cant find credentials");
}
} catch (err: any) {
onSignInError(err);
}
};
const handlePasswordSignIn = async (formData: any) => {
await authenticationService
.emailLogin(formData)
.then((response) => {
try {
if (response) {
onSignInSuccess(response);
}
} catch (err: any) {
onSignInError(err);
}
})
.catch((err) => onSignInError(err));
};
const handleEmailCodeSignIn = async (response: any) => {
try {
if (response) {
onSignInSuccess(response);
}
} catch (err: any) {
onSignInError(err);
}
};
return (
<div className="h-screen w-full overflow-hidden">
<div className="hidden sm:block sm:fixed border-r-[0.5px] border-custom-border-200 h-screen w-[0.5px] top-0 left-20 lg:left-32" />
<div className="fixed grid place-items-center bg-custom-background-100 sm:py-5 top-11 sm:top-12 left-7 sm:left-16 lg:left-28">
<div className="grid place-items-center bg-custom-background-100">
<div className="h-[30px] w-[30px]">
<Image src={BluePlaneLogoWithoutText} alt="Plane Logo" />
</div>
</div>
</div>
<div className="grid place-items-center h-full overflow-y-auto py-5 px-7">
<div>
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
<>
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
Sign in to Plane
</h1>
<div className="flex flex-col divide-y divide-custom-border-200">
<div className="pb-7">
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
</div>
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
</div>
</div>
</>
) : (
<EmailPasswordForm onSubmit={handlePasswordSignIn} />
)}
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? (
<p className="pt-16 text-custom-text-200 text-sm text-center">
By signing up, you agree to the{" "}
<a
href="https://plane.so/terms-and-conditions"
target="_blank"
rel="noopener noreferrer"
className="font-medium underline"
>
Terms & Conditions
</a>
</p>
) : null}
</div>
</div>
</div>
);
};
export default HomePage;

View File

@ -13,10 +13,20 @@ export interface IIssueDetailStore {
peekId: string | null;
peekMode: IPeekMode;
details: any;
// actions
// peek actions
setPeekId: (issueId: string | null) => void;
setPeekMode: (mode: IPeekMode) => void;
// issue details
fetchIssueDetails: (workspaceId: string, projectId: string, issueId: string) => void;
// issue comments
addIssueComment: (workspaceId: string, projectId: string, issueId: string, data: any) => void;
deleteIssueComment: (workspaceId: string, projectId: string, issueId: string) => void;
// issue reactions
addIssueReaction: (workspaceId: string, projectId: string, issueId: string) => void;
removeIssueReaction: (workspaceId: string, projectId: string, issueId: string) => void;
// issue votes
addIssueVote: (workspaceId: string, projectId: string, issueId: string) => void;
removeIssueVote: (workspaceId: string, projectId: string, issueId: string) => void;
}
class IssueDetailStore implements IssueDetailStore {
@ -59,9 +69,7 @@ class IssueDetailStore implements IssueDetailStore {
this.error = null;
const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
const reactionsResponse = await this.issueService.getIssueReactions(workspaceSlug, projectId, issueId);
const commentsResponse = await this.issueService.getIssueComments(workspaceSlug, projectId, issueId);
const votesResponse = await this.issueService.getIssueVotes(workspaceSlug, projectId, issueId);
if (issueDetails) {
runInAction(() => {
@ -70,8 +78,6 @@ class IssueDetailStore implements IssueDetailStore {
[issueId]: {
...issueDetails,
comments: commentsResponse,
reactions: reactionsResponse,
votes: votesResponse,
},
};
});
@ -79,20 +85,148 @@ class IssueDetailStore implements IssueDetailStore {
} catch (error) {
this.loader = false;
this.error = error;
}
};
addIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
try {
const issueDetails = this.rootStore.issue.issues?.find((i) => i.id === issueId);
const issueCommentResponse = await this.issueService.createIssueComment(workspaceSlug, projectId, issueId, data);
console.log("issueCommentResponse", issueCommentResponse);
if (issueDetails) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
comments: [...this.details[issueId].comments, issueCommentResponse],
},
};
});
}
return issueCommentResponse;
} catch (error) {
console.log("Failed to add issue comment");
throw error;
}
};
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
comments: [],
reactions: [],
votes: [],
},
};
});
updateIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
try {
const issueVoteResponse = await this.issueService.updateIssueComment(workspaceSlug, projectId, issueId, data);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
comments: commentsResponse,
},
};
});
}
} catch (error) {
console.log("Failed to add issue comment");
}
};
deleteIssueComment = async () => {
try {
const issueVoteResponse = await this.issueService.deleteIssueComment(workspaceSlug, projectId, issueId, data);
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
},
};
});
}
} catch (error) {
console.log("Failed to add issue vote");
}
};
addIssueReaction = async () => {
try {
const issueVoteResponse = await this.issueService.createIssueReaction(workspaceSlug, projectId, issueId, data);
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
},
};
});
}
} catch (error) {
console.log("Failed to add issue vote");
}
};
removeIssueReaction = async () => {
try {
const issueVoteResponse = await this.issueService.deleteIssueReaction(workspaceSlug, projectId, issueId, data);
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
},
};
});
}
} catch (error) {
console.log("Failed to add issue vote");
}
};
addIssueVote = async (workspaceSlug: string, projectId: string, issueId: string, data: { vote: 1 | -1 }) => {
try {
const issueVoteResponse = await this.issueService.createIssueVote(workspaceSlug, projectId, issueId, data);
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
},
};
});
}
} catch (error) {
console.log("Failed to add issue vote");
}
};
removeIssueVote = async (workspaceSlug: string, projectId: string, issueId: string) => {
try {
const issueVoteResponse = await this.issueService.deleteIssueVote(workspaceSlug, projectId, issueId, data);
// const issueDetails = await this.issueService.fetchIssueDetails(workspaceSlug, projectId, issueId);
if (issueVoteResponse) {
runInAction(() => {
this.details = {
...this.details,
[issueId]: {
...issueDetails,
},
};
});
}
} catch (error) {
console.log("Failed to remove issue vote");
}
};
}

View File

@ -10,7 +10,7 @@ export interface IProjectStore {
error: any | null;
workspace: IWorkspace | null;
project: IProject | null;
projectDeploySettings: IProjectSettings | null;
deploySettings: IProjectSettings | null;
viewOptions: any;
activeBoard: string | null;
fetchProjectSettings: (workspace_slug: string, project_slug: string) => Promise<void>;
@ -23,7 +23,7 @@ class ProjectStore implements IProjectStore {
// data
workspace: IWorkspace | null = null;
project: IProject | null = null;
projectDeploySettings: IProjectSettings | null = null;
deploySettings: IProjectSettings | null = null;
viewOptions: any = null;
activeBoard: string | null = null;
// root store
@ -39,7 +39,7 @@ class ProjectStore implements IProjectStore {
// observable
workspace: observable.ref,
project: observable.ref,
projectDeploySettings: observable.ref,
deploySettings: observable.ref,
viewOptions: observable.ref,
activeBoard: observable.ref,
// actions
@ -63,10 +63,12 @@ class ProjectStore implements IProjectStore {
const _project: IProject = { ...response?.project_details };
const _workspace: IWorkspace = { ...response?.workspace_detail };
const _viewOptions = { ...response?.views };
const _deploySettings = { ...response };
runInAction(() => {
this.project = _project;
this.workspace = _workspace;
this.viewOptions = _viewOptions;
this.deploySettings = _deploySettings;
this.loader = false;
});
}

View File

@ -2,8 +2,11 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// service
import UserService from "services/user.service";
// types
import { IUserStore } from "../types";
export interface IUserStore {
currentUser: any | null;
fetchCurrentUser: () => void;
}
class UserStore implements IUserStore {
currentUser: any | null = null;
@ -42,7 +45,7 @@ class UserStore implements IUserStore {
return;
}
this.getUserAsync()
this.fetchCurrentUser()
.then(() => {
if (!this.currentUser) {
const currentPath = window.location.pathname;
@ -55,7 +58,7 @@ class UserStore implements IUserStore {
});
};
getUserAsync = async () => {
fetchCurrentUser = async () => {
try {
const response = await this.userService.currentUser();
if (response) {
@ -64,10 +67,7 @@ class UserStore implements IUserStore {
});
}
} catch (error) {
console.error("error", error);
runInAction(() => {
// render error actions
});
console.error("Failed to fetch current user", error);
}
};
}

View File

@ -1,4 +0,0 @@
export interface IUserStore {
currentUser: any | null;
getUserAsync: () => void;
}