mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'refactor/space-app' of github.com:makeplane/plane into refactor/space-app
This commit is contained in:
commit
62d669ee95
@ -0,0 +1,27 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import useSWR from "swr";
|
||||||
|
// hooks
|
||||||
|
import { usePublish, usePublishList } from "@/hooks/store";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
params: {
|
||||||
|
anchor: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const PageDetailsPage = (props: Props) => {
|
||||||
|
const { params } = props;
|
||||||
|
const { anchor } = params;
|
||||||
|
// store hooks
|
||||||
|
const { fetchPublishSettings } = usePublishList();
|
||||||
|
const publishSettings = usePublish(anchor);
|
||||||
|
|
||||||
|
useSWR(anchor ? `PUBLISH_SETTINGS_${anchor}` : null, anchor ? () => fetchPublishSettings(anchor) : null);
|
||||||
|
|
||||||
|
if (!publishSettings) return null;
|
||||||
|
|
||||||
|
return <>Page details</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageDetailsPage;
|
@ -5,7 +5,7 @@ import cloneDeep from "lodash/cloneDeep";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssue, useIssueFilter, usePublish } from "@/hooks/store";
|
import { useIssue, useIssueFilter } from "@/hooks/store";
|
||||||
// store
|
// store
|
||||||
import { TIssueQueryFilters } from "@/types/issue";
|
import { TIssueQueryFilters } from "@/types/issue";
|
||||||
// components
|
// components
|
||||||
@ -20,10 +20,10 @@ export const IssueAppliedFilters: FC<TIssueAppliedFilters> = observer((props) =>
|
|||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { issueFilters, initIssueFilters, updateIssueFilters } = useIssueFilter();
|
const { getIssueFilters, initIssueFilters, updateIssueFilters } = useIssueFilter();
|
||||||
const { states, labels } = useIssue();
|
const { states, labels } = useIssue();
|
||||||
const { project: projectID } = usePublish(anchor);
|
// derived values
|
||||||
|
const issueFilters = getIssueFilters(anchor);
|
||||||
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
||||||
const userFilters = issueFilters?.filters || {};
|
const userFilters = issueFilters?.filters || {};
|
||||||
|
|
||||||
@ -53,23 +53,19 @@ export const IssueAppliedFilters: FC<TIssueAppliedFilters> = observer((props) =>
|
|||||||
|
|
||||||
const handleFilters = useCallback(
|
const handleFilters = useCallback(
|
||||||
(key: keyof TIssueQueryFilters, value: string | null) => {
|
(key: keyof TIssueQueryFilters, value: string | null) => {
|
||||||
if (!projectID) return;
|
|
||||||
|
|
||||||
let newValues = cloneDeep(issueFilters?.filters?.[key]) ?? [];
|
let newValues = cloneDeep(issueFilters?.filters?.[key]) ?? [];
|
||||||
|
|
||||||
if (value === null) newValues = [];
|
if (value === null) newValues = [];
|
||||||
else if (newValues.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
else if (newValues.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||||
|
|
||||||
updateIssueFilters(projectID, "filters", key, newValues);
|
updateIssueFilters(anchor, "filters", key, newValues);
|
||||||
updateRouteParams(key, newValues);
|
updateRouteParams(key, newValues);
|
||||||
},
|
},
|
||||||
[projectID, issueFilters, updateIssueFilters, updateRouteParams]
|
[anchor, issueFilters, updateIssueFilters, updateRouteParams]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleRemoveAllFilters = () => {
|
const handleRemoveAllFilters = () => {
|
||||||
if (!projectID) return;
|
initIssueFilters(anchor, {
|
||||||
|
|
||||||
initIssueFilters(projectID, {
|
|
||||||
display_filters: { layout: activeLayout || "list" },
|
display_filters: { layout: activeLayout || "list" },
|
||||||
filters: {
|
filters: {
|
||||||
state: [],
|
state: [],
|
||||||
|
@ -17,17 +17,18 @@ import { useIssue, useIssueFilter } from "@/hooks/store";
|
|||||||
import { TIssueQueryFilters } from "@/types/issue";
|
import { TIssueQueryFilters } from "@/types/issue";
|
||||||
|
|
||||||
type IssueFiltersDropdownProps = {
|
type IssueFiltersDropdownProps = {
|
||||||
workspaceSlug: string;
|
anchor: string;
|
||||||
projectId: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueFiltersDropdown: FC<IssueFiltersDropdownProps> = observer((props) => {
|
export const IssueFiltersDropdown: FC<IssueFiltersDropdownProps> = observer((props) => {
|
||||||
|
const { anchor } = props;
|
||||||
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = props;
|
|
||||||
// hooks
|
// hooks
|
||||||
const { issueFilters, updateIssueFilters } = useIssueFilter();
|
const { getIssueFilters, updateIssueFilters } = useIssueFilter();
|
||||||
const { states, labels } = useIssue();
|
const { states, labels } = useIssue();
|
||||||
|
// derived values
|
||||||
|
const issueFilters = getIssueFilters(anchor);
|
||||||
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
||||||
|
|
||||||
const updateRouteParams = useCallback(
|
const updateRouteParams = useCallback(
|
||||||
@ -37,24 +38,24 @@ export const IssueFiltersDropdown: FC<IssueFiltersDropdownProps> = observer((pro
|
|||||||
const labels = key === "labels" ? value : issueFilters?.filters?.labels ?? [];
|
const labels = key === "labels" ? value : issueFilters?.filters?.labels ?? [];
|
||||||
|
|
||||||
const { queryParam } = queryParamGenerator({ board: activeLayout, priority, state, labels });
|
const { queryParam } = queryParamGenerator({ board: activeLayout, priority, state, labels });
|
||||||
router.push(`/${workspaceSlug}/${projectId}?${queryParam}`);
|
router.push(`/issues/${anchor}?${queryParam}`);
|
||||||
},
|
},
|
||||||
[workspaceSlug, projectId, activeLayout, issueFilters, router]
|
[anchor, activeLayout, issueFilters, router]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFilters = useCallback(
|
const handleFilters = useCallback(
|
||||||
(key: keyof TIssueQueryFilters, value: string) => {
|
(key: keyof TIssueQueryFilters, value: string) => {
|
||||||
if (!projectId || !value) return;
|
if (!value) return;
|
||||||
|
|
||||||
const newValues = cloneDeep(issueFilters?.filters?.[key]) ?? [];
|
const newValues = cloneDeep(issueFilters?.filters?.[key]) ?? [];
|
||||||
|
|
||||||
if (newValues.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
if (newValues.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||||
else newValues.push(value);
|
else newValues.push(value);
|
||||||
|
|
||||||
updateIssueFilters(projectId, "filters", key, newValues);
|
updateIssueFilters(anchor, "filters", key, newValues);
|
||||||
updateRouteParams(key, newValues);
|
updateRouteParams(key, newValues);
|
||||||
},
|
},
|
||||||
[projectId, issueFilters, updateIssueFilters, updateRouteParams]
|
[anchor, issueFilters, updateIssueFilters, updateRouteParams]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -35,16 +35,17 @@ export const NavbarControls: FC<NavbarControlsProps> = observer((props) => {
|
|||||||
const priority = searchParams.get("priority") || undefined;
|
const priority = searchParams.get("priority") || undefined;
|
||||||
const peekId = searchParams.get("peekId") || undefined;
|
const peekId = searchParams.get("peekId") || undefined;
|
||||||
// hooks
|
// hooks
|
||||||
const { issueFilters, isIssueFiltersUpdated, initIssueFilters } = useIssueFilter();
|
const { getIssueFilters, isIssueFiltersUpdated, initIssueFilters } = useIssueFilter();
|
||||||
const { setPeekId } = useIssueDetails();
|
const { setPeekId } = useIssueDetails();
|
||||||
// derived values
|
// derived values
|
||||||
|
const { anchor, views, workspace_detail } = publishSettings;
|
||||||
|
const issueFilters = anchor ? getIssueFilters(anchor) : undefined;
|
||||||
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
||||||
const { project, views, workspace_detail } = publishSettings;
|
|
||||||
|
|
||||||
const isInIframe = useIsInIframe();
|
const isInIframe = useIsInIframe();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (project && workspace_detail) {
|
if (anchor && workspace_detail) {
|
||||||
const viewsAcceptable: string[] = [];
|
const viewsAcceptable: string[] = [];
|
||||||
let currentBoard: TIssueLayout | null = null;
|
let currentBoard: TIssueLayout | null = null;
|
||||||
|
|
||||||
@ -75,14 +76,15 @@ export const NavbarControls: FC<NavbarControlsProps> = observer((props) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isIssueFiltersUpdated(params)) {
|
if (!isIssueFiltersUpdated(anchor, params)) {
|
||||||
initIssueFilters(project, params);
|
initIssueFilters(anchor, params);
|
||||||
router.push(`/${workspace_detail.slug}/${project}?${queryParam}`);
|
router.push(`/issues/${anchor}?${queryParam}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
anchor,
|
||||||
board,
|
board,
|
||||||
labels,
|
labels,
|
||||||
state,
|
state,
|
||||||
@ -94,22 +96,21 @@ export const NavbarControls: FC<NavbarControlsProps> = observer((props) => {
|
|||||||
setPeekId,
|
setPeekId,
|
||||||
isIssueFiltersUpdated,
|
isIssueFiltersUpdated,
|
||||||
views,
|
views,
|
||||||
project,
|
|
||||||
workspace_detail,
|
workspace_detail,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!workspace_detail || !project) return;
|
if (!anchor) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* issue views */}
|
{/* issue views */}
|
||||||
<div className="relative flex flex-shrink-0 items-center gap-1 transition-all delay-150 ease-in-out">
|
<div className="relative flex flex-shrink-0 items-center gap-1 transition-all delay-150 ease-in-out">
|
||||||
<NavbarIssueBoardView workspaceSlug={workspace_detail.slug} projectId={project} />
|
<NavbarIssueBoardView anchor={anchor} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* issue filters */}
|
{/* issue filters */}
|
||||||
<div className="relative flex flex-shrink-0 items-center gap-1 transition-all delay-150 ease-in-out">
|
<div className="relative flex flex-shrink-0 items-center gap-1 transition-all delay-150 ease-in-out">
|
||||||
<IssueFiltersDropdown workspaceSlug={workspace_detail.slug} projectId={project} />
|
<IssueFiltersDropdown anchor={anchor} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* theming */}
|
{/* theming */}
|
||||||
|
@ -13,11 +13,12 @@ import { useIssueFilter } from "@/hooks/store";
|
|||||||
import { TIssueLayout } from "@/types/issue";
|
import { TIssueLayout } from "@/types/issue";
|
||||||
|
|
||||||
type NavbarIssueBoardViewProps = {
|
type NavbarIssueBoardViewProps = {
|
||||||
workspaceSlug: string;
|
anchor: string;
|
||||||
projectId: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NavbarIssueBoardView: FC<NavbarIssueBoardViewProps> = observer((props) => {
|
export const NavbarIssueBoardView: FC<NavbarIssueBoardViewProps> = observer((props) => {
|
||||||
|
const { anchor } = props;
|
||||||
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
// query params
|
// query params
|
||||||
@ -25,18 +26,16 @@ export const NavbarIssueBoardView: FC<NavbarIssueBoardViewProps> = observer((pro
|
|||||||
const state = searchParams.get("state") || undefined;
|
const state = searchParams.get("state") || undefined;
|
||||||
const priority = searchParams.get("priority") || undefined;
|
const priority = searchParams.get("priority") || undefined;
|
||||||
const peekId = searchParams.get("peekId") || undefined;
|
const peekId = searchParams.get("peekId") || undefined;
|
||||||
// props
|
|
||||||
const { workspaceSlug, projectId } = props;
|
|
||||||
// hooks
|
// hooks
|
||||||
const { layoutOptions, issueFilters, updateIssueFilters } = useIssueFilter();
|
const { layoutOptions, getIssueFilters, updateIssueFilters } = useIssueFilter();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
|
const issueFilters = getIssueFilters(anchor);
|
||||||
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
const activeLayout = issueFilters?.display_filters?.layout || undefined;
|
||||||
|
|
||||||
const handleCurrentBoardView = (boardView: TIssueLayout) => {
|
const handleCurrentBoardView = (boardView: TIssueLayout) => {
|
||||||
updateIssueFilters(projectId, "display_filters", "layout", boardView);
|
updateIssueFilters(anchor, "display_filters", "layout", boardView);
|
||||||
const { queryParam } = queryParamGenerator({ board: boardView, peekId, priority, state, labels });
|
const { queryParam } = queryParamGenerator({ board: boardView, peekId, priority, state, labels });
|
||||||
router.push(`/${workspaceSlug}/${projectId}?${queryParam}`);
|
router.push(`/issues/${anchor}?${queryParam}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,7 +29,7 @@ export const FullScreenPeekView: React.FC<Props> = observer((props) => {
|
|||||||
<div className="h-full w-full overflow-y-auto px-6">
|
<div className="h-full w-full overflow-y-auto px-6">
|
||||||
{/* issue title and description */}
|
{/* issue title and description */}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<PeekOverviewIssueDetails issueDetails={issueDetails} />
|
<PeekOverviewIssueDetails anchor={anchor} issueDetails={issueDetails} />
|
||||||
</div>
|
</div>
|
||||||
{/* divider */}
|
{/* divider */}
|
||||||
<div className="my-5 h-[1] w-full border-t border-custom-border-200" />
|
<div className="my-5 h-[1] w-full border-t border-custom-border-200" />
|
||||||
|
@ -7,7 +7,7 @@ import { Button } from "@plane/ui";
|
|||||||
import { CommentCard, AddComment } from "@/components/issues/peek-overview";
|
import { CommentCard, AddComment } from "@/components/issues/peek-overview";
|
||||||
import { Icon } from "@/components/ui";
|
import { Icon } from "@/components/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetails, useProject, useUser } from "@/hooks/store";
|
import { useIssueDetails, usePublish, useUser } from "@/hooks/store";
|
||||||
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "@/types/issue";
|
import { IIssue } from "@/types/issue";
|
||||||
@ -21,13 +21,13 @@ export const PeekOverviewIssueActivity: React.FC<Props> = observer((props) => {
|
|||||||
const { anchor } = props;
|
const { anchor } = props;
|
||||||
// router
|
// router
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
// store
|
// store hooks
|
||||||
const { canComment } = useProject();
|
|
||||||
const { details, peekId } = useIssueDetails();
|
const { details, peekId } = useIssueDetails();
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const isInIframe = useIsInIframe();
|
const { canComment } = usePublish(anchor);
|
||||||
|
// derived values
|
||||||
const comments = details[peekId || ""]?.comments || [];
|
const comments = details[peekId || ""]?.comments || [];
|
||||||
|
const isInIframe = useIsInIframe();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pb-10">
|
<div className="pb-10">
|
||||||
|
@ -5,26 +5,33 @@ import { IssueReactions } from "@/components/issues/peek-overview";
|
|||||||
import { IIssue } from "@/types/issue";
|
import { IIssue } from "@/types/issue";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
anchor: string;
|
||||||
issueDetails: IIssue;
|
issueDetails: IIssue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PeekOverviewIssueDetails: React.FC<Props> = ({ issueDetails }) => (
|
export const PeekOverviewIssueDetails: React.FC<Props> = (props) => {
|
||||||
|
const { anchor, issueDetails } = props;
|
||||||
|
|
||||||
|
const description = issueDetails.description_html;
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h6 className="font-medium text-custom-text-200">
|
<h6 className="font-medium text-custom-text-200">
|
||||||
{issueDetails.project_detail.identifier}-{issueDetails.sequence_id}
|
{issueDetails.project_detail.identifier}-{issueDetails.sequence_id}
|
||||||
</h6>
|
</h6>
|
||||||
<h4 className="break-words text-2xl font-semibold">{issueDetails.name}</h4>
|
<h4 className="break-words text-2xl font-semibold">{issueDetails.name}</h4>
|
||||||
{issueDetails.description_html !== "" && issueDetails.description_html !== "<p></p>" && (
|
{description !== "" && description !== "<p></p>" && (
|
||||||
<RichTextReadOnlyEditor
|
<RichTextReadOnlyEditor
|
||||||
initialValue={
|
initialValue={
|
||||||
!issueDetails.description_html ||
|
!description ||
|
||||||
issueDetails.description_html === "" ||
|
description === "" ||
|
||||||
(typeof issueDetails.description_html === "object" && Object.keys(issueDetails.description_html).length === 0)
|
(typeof description === "object" && Object.keys(description).length === 0)
|
||||||
? "<p></p>"
|
? "<p></p>"
|
||||||
: issueDetails.description_html
|
: description
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<IssueReactions />
|
<IssueReactions anchor={anchor} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -10,11 +10,12 @@ import { queryParamGenerator } from "@/helpers/query-param-generator";
|
|||||||
import { useIssueDetails, useUser } from "@/hooks/store";
|
import { useIssueDetails, useUser } from "@/hooks/store";
|
||||||
|
|
||||||
type IssueEmojiReactionsProps = {
|
type IssueEmojiReactionsProps = {
|
||||||
workspaceSlug: string;
|
anchor: string;
|
||||||
projectId: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer((props) => {
|
export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer((props) => {
|
||||||
|
const { anchor } = props;
|
||||||
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathName = usePathname();
|
const pathName = usePathname();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@ -24,9 +25,7 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
|
|||||||
const state = searchParams.get("state") || undefined;
|
const state = searchParams.get("state") || undefined;
|
||||||
const priority = searchParams.get("priority") || undefined;
|
const priority = searchParams.get("priority") || undefined;
|
||||||
const labels = searchParams.get("labels") || undefined;
|
const labels = searchParams.get("labels") || undefined;
|
||||||
|
// store hooks
|
||||||
const { workspaceSlug, projectId } = props;
|
|
||||||
// store
|
|
||||||
const issueDetailsStore = useIssueDetails();
|
const issueDetailsStore = useIssueDetails();
|
||||||
const { data: user } = useUser();
|
const { data: user } = useUser();
|
||||||
|
|
||||||
@ -37,13 +36,13 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
|
|||||||
const userReactions = reactions?.filter((r) => r.actor_detail.id === user?.id);
|
const userReactions = reactions?.filter((r) => r.actor_detail.id === user?.id);
|
||||||
|
|
||||||
const handleAddReaction = (reactionHex: string) => {
|
const handleAddReaction = (reactionHex: string) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!issueId) return;
|
||||||
issueDetailsStore.addIssueReaction(workspaceSlug.toString(), projectId.toString(), issueId, reactionHex);
|
issueDetailsStore.addIssueReaction(anchor, issueId, reactionHex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveReaction = (reactionHex: string) => {
|
const handleRemoveReaction = (reactionHex: string) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!issueId) return;
|
||||||
issueDetailsStore.removeIssueReaction(workspaceSlug.toString(), projectId.toString(), issueId, reactionHex);
|
issueDetailsStore.removeIssueReaction(anchor, issueId, reactionHex);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReactionClick = (reactionHex: string) => {
|
const handleReactionClick = (reactionHex: string) => {
|
||||||
|
@ -1,33 +1,31 @@
|
|||||||
import { useParams } from "next/navigation";
|
import { observer } from "mobx-react-lite";
|
||||||
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/peek-overview";
|
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/peek-overview";
|
||||||
import { useProject } from "@/hooks/store";
|
// hooks
|
||||||
|
import { usePublish } from "@/hooks/store";
|
||||||
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
||||||
|
|
||||||
// type IssueReactionsProps = {
|
type Props = {
|
||||||
// workspaceSlug: string;
|
anchor: string;
|
||||||
// projectId: string;
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
export const IssueReactions: React.FC = () => {
|
export const IssueReactions: React.FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId } = useParams<any>();
|
const { anchor } = props;
|
||||||
|
// store hooks
|
||||||
const { canVote, canReact } = useProject();
|
const { canVote, canReact } = usePublish(anchor);
|
||||||
const isInIframe = useIsInIframe();
|
const isInIframe = useIsInIframe();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-4 flex items-center gap-3">
|
<div className="mt-4 flex items-center gap-3">
|
||||||
{canVote && (
|
{canVote && (
|
||||||
<>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<IssueVotes workspaceSlug={workspaceSlug} projectId={projectId} />
|
<IssueVotes anchor={anchor} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{!isInIframe && canReact && (
|
{!isInIframe && canReact && (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<IssueEmojiReactions workspaceSlug={workspaceSlug} projectId={projectId} />
|
<IssueEmojiReactions anchor={anchor} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -12,11 +12,14 @@ import { useIssueDetails, useUser } from "@/hooks/store";
|
|||||||
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
import useIsInIframe from "@/hooks/use-is-in-iframe";
|
||||||
|
|
||||||
type TIssueVotes = {
|
type TIssueVotes = {
|
||||||
workspaceSlug: string;
|
anchor: string;
|
||||||
projectId: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
|
export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
|
||||||
|
const { anchor } = props;
|
||||||
|
// states
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathName = usePathname();
|
const pathName = usePathname();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@ -26,11 +29,7 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
|
|||||||
const state = searchParams.get("state") || undefined;
|
const state = searchParams.get("state") || undefined;
|
||||||
const priority = searchParams.get("priority") || undefined;
|
const priority = searchParams.get("priority") || undefined;
|
||||||
const labels = searchParams.get("labels") || undefined;
|
const labels = searchParams.get("labels") || undefined;
|
||||||
|
// store hooks
|
||||||
const { workspaceSlug, projectId } = props;
|
|
||||||
// states
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
const issueDetailsStore = useIssueDetails();
|
const issueDetailsStore = useIssueDetails();
|
||||||
const { data: user } = useUser();
|
const { data: user } = useUser();
|
||||||
|
|
||||||
@ -47,18 +46,18 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
|
|||||||
const isDownVotedByUser = allDownVotes?.some((vote) => vote.actor === user?.id);
|
const isDownVotedByUser = allDownVotes?.some((vote) => vote.actor === user?.id);
|
||||||
|
|
||||||
const handleVote = async (e: any, voteValue: 1 | -1) => {
|
const handleVote = async (e: any, voteValue: 1 | -1) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!issueId) return;
|
||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
|
||||||
const actionPerformed = votes?.find((vote) => vote.actor === user?.id && vote.vote === voteValue);
|
const actionPerformed = votes?.find((vote) => vote.actor === user?.id && vote.vote === voteValue);
|
||||||
|
|
||||||
if (actionPerformed)
|
if (actionPerformed) await issueDetailsStore.removeIssueVote(anchor, issueId);
|
||||||
await issueDetailsStore.removeIssueVote(workspaceSlug.toString(), projectId.toString(), issueId);
|
else {
|
||||||
else
|
await issueDetailsStore.addIssueVote(anchor, issueId, {
|
||||||
await issueDetailsStore.addIssueVote(workspaceSlug.toString(), projectId.toString(), issueId, {
|
|
||||||
vote: voteValue,
|
vote: voteValue,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,7 @@ export const SidePeekView: React.FC<Props> = observer((props) => {
|
|||||||
<div className="h-full w-full overflow-y-auto px-6">
|
<div className="h-full w-full overflow-y-auto px-6">
|
||||||
{/* issue title and description */}
|
{/* issue title and description */}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<PeekOverviewIssueDetails issueDetails={issueDetails} />
|
<PeekOverviewIssueDetails anchor={anchor} issueDetails={issueDetails} />
|
||||||
</div>
|
</div>
|
||||||
{/* issue properties */}
|
{/* issue properties */}
|
||||||
<div className="mt-6 w-full">
|
<div className="mt-6 w-full">
|
||||||
|
@ -33,11 +33,12 @@ export const ProjectDetailsView: FC<ProjectDetailsViewProps> = observer((props)
|
|||||||
const priority = searchParams.get("priority") || undefined;
|
const priority = searchParams.get("priority") || undefined;
|
||||||
const labels = searchParams.get("labels") || undefined;
|
const labels = searchParams.get("labels") || undefined;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { issueFilters } = useIssueFilter();
|
const { getIssueFilters } = useIssueFilter();
|
||||||
const { loader, issues, error, fetchPublicIssues } = useIssue();
|
const { loader, issues, error, fetchPublicIssues } = useIssue();
|
||||||
const issueDetailStore = useIssueDetails();
|
const issueDetailStore = useIssueDetails();
|
||||||
// derived values
|
// derived values
|
||||||
const { anchor } = publishSettings;
|
const { anchor } = publishSettings;
|
||||||
|
const issueFilters = anchor ? getIssueFilters(anchor) : undefined;
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
anchor ? `PUBLIC_ISSUES_${anchor}` : null,
|
anchor ? `PUBLIC_ISSUES_${anchor}` : null,
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
import { useContext } from "react";
|
|
||||||
// lib
|
|
||||||
import { StoreContext } from "@/lib/store-provider";
|
|
||||||
// store
|
|
||||||
import { IProjectStore } from "@/store/project.store";
|
|
||||||
|
|
||||||
export const useProject = (): IProjectStore => {
|
|
||||||
const context = useContext(StoreContext);
|
|
||||||
if (context === undefined) throw new Error("useUserProfile must be used within StoreProvider");
|
|
||||||
return context.project;
|
|
||||||
};
|
|
@ -4,30 +4,6 @@ import { API_BASE_URL } from "@/helpers/common.helper";
|
|||||||
// services
|
// services
|
||||||
import { APIService } from "@/services/api.service";
|
import { APIService } from "@/services/api.service";
|
||||||
|
|
||||||
interface UnSplashImage {
|
|
||||||
id: string;
|
|
||||||
created_at: Date;
|
|
||||||
updated_at: Date;
|
|
||||||
promoted_at: Date;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
color: string;
|
|
||||||
blur_hash: string;
|
|
||||||
description: null;
|
|
||||||
alt_description: string;
|
|
||||||
urls: UnSplashImageUrls;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UnSplashImageUrls {
|
|
||||||
raw: string;
|
|
||||||
full: string;
|
|
||||||
regular: string;
|
|
||||||
small: string;
|
|
||||||
thumb: string;
|
|
||||||
small_s3: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileService extends APIService {
|
class FileService extends APIService {
|
||||||
private cancelSource: any;
|
private cancelSource: any;
|
||||||
|
|
||||||
@ -123,40 +99,6 @@ class FileService extends APIService {
|
|||||||
throw error?.response?.data;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFile(workspaceId: string, assetUrl: string): Promise<any> {
|
|
||||||
const lastIndex = assetUrl.lastIndexOf("/");
|
|
||||||
const assetId = assetUrl.substring(lastIndex + 1);
|
|
||||||
|
|
||||||
return this.delete(`/api/workspaces/file-assets/${workspaceId}/${assetId}/`)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response?.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async uploadUserFile(file: FormData): Promise<any> {
|
|
||||||
return this.post(`/api/users/file-assets/`, file, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "multipart/form-data",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response?.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteUserFile(assetUrl: string): Promise<any> {
|
|
||||||
const lastIndex = assetUrl.lastIndexOf("/");
|
|
||||||
const assetId = assetUrl.substring(lastIndex + 1);
|
|
||||||
|
|
||||||
return this.delete(`/api/users/file-assets/${assetId}`)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response?.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import cloneDeep from "lodash/cloneDeep";
|
import cloneDeep from "lodash/cloneDeep";
|
||||||
import isEqual from "lodash/isEqual";
|
import isEqual from "lodash/isEqual";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import { action, makeObservable, observable, runInAction, computed } from "mobx";
|
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||||
import { computedFn } from "mobx-utils";
|
import { computedFn } from "mobx-utils";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||||
@ -19,16 +19,17 @@ import {
|
|||||||
export interface IIssueFilterStore {
|
export interface IIssueFilterStore {
|
||||||
// observables
|
// observables
|
||||||
layoutOptions: TIssueLayoutOptions;
|
layoutOptions: TIssueLayoutOptions;
|
||||||
filters: { [projectId: string]: TIssueFilters } | undefined;
|
filters: { [anchor: string]: TIssueFilters } | undefined;
|
||||||
// computed
|
// computed
|
||||||
issueFilters: TIssueFilters | undefined;
|
isIssueFiltersUpdated: (anchor: string, filters: TIssueFilters) => boolean;
|
||||||
appliedFilters: TIssueQueryFiltersParams | undefined;
|
// helpers
|
||||||
isIssueFiltersUpdated: (filters: TIssueFilters) => boolean;
|
getIssueFilters: (anchor: string) => TIssueFilters | undefined;
|
||||||
|
getAppliedFilters: (anchor: string) => TIssueQueryFiltersParams | undefined;
|
||||||
// actions
|
// actions
|
||||||
updateLayoutOptions: (layout: TIssueLayoutOptions) => void;
|
updateLayoutOptions: (layout: TIssueLayoutOptions) => void;
|
||||||
initIssueFilters: (projectId: string, filters: TIssueFilters) => void;
|
initIssueFilters: (anchor: string, filters: TIssueFilters) => void;
|
||||||
updateIssueFilters: <K extends keyof TIssueFilters>(
|
updateIssueFilters: <K extends keyof TIssueFilters>(
|
||||||
projectId: string,
|
anchor: string,
|
||||||
filterKind: K,
|
filterKind: K,
|
||||||
filterKey: keyof TIssueFilters[K],
|
filterKey: keyof TIssueFilters[K],
|
||||||
filters: TIssueFilters[K][typeof filterKey]
|
filters: TIssueFilters[K][typeof filterKey]
|
||||||
@ -44,16 +45,13 @@ export class IssueFilterStore implements IIssueFilterStore {
|
|||||||
gantt: false,
|
gantt: false,
|
||||||
spreadsheet: false,
|
spreadsheet: false,
|
||||||
};
|
};
|
||||||
filters: { [projectId: string]: TIssueFilters } | undefined = undefined;
|
filters: { [anchor: string]: TIssueFilters } | undefined = undefined;
|
||||||
|
|
||||||
constructor(private store: RootStore) {
|
constructor(private store: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observables
|
// observables
|
||||||
layoutOptions: observable,
|
layoutOptions: observable,
|
||||||
filters: observable,
|
filters: observable,
|
||||||
// computed
|
|
||||||
issueFilters: computed,
|
|
||||||
appliedFilters: computed,
|
|
||||||
// actions
|
// actions
|
||||||
updateLayoutOptions: action,
|
updateLayoutOptions: action,
|
||||||
initIssueFilters: action,
|
initIssueFilters: action,
|
||||||
@ -82,79 +80,70 @@ export class IssueFilterStore implements IIssueFilterStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// computed
|
// computed
|
||||||
get issueFilters() {
|
getIssueFilters = computedFn((anchor: string) => {
|
||||||
const projectId = this.store.project.project?.id;
|
const currentFilters = this.filters?.[anchor];
|
||||||
if (!projectId) return undefined;
|
|
||||||
|
|
||||||
const currentFilters = this.filters?.[projectId];
|
|
||||||
if (!currentFilters) return undefined;
|
|
||||||
|
|
||||||
return currentFilters;
|
return currentFilters;
|
||||||
}
|
});
|
||||||
|
|
||||||
get appliedFilters() {
|
getAppliedFilters = computedFn((anchor: string) => {
|
||||||
const currentIssueFilters = this.issueFilters;
|
const issueFilters = this.getIssueFilters(anchor);
|
||||||
if (!currentIssueFilters) return undefined;
|
if (!issueFilters) return undefined;
|
||||||
|
|
||||||
const currentLayout = currentIssueFilters?.display_filters?.layout;
|
const currentLayout = issueFilters?.display_filters?.layout;
|
||||||
if (!currentLayout) return undefined;
|
if (!currentLayout) return undefined;
|
||||||
|
|
||||||
const currentFilters: TIssueQueryFilters = {
|
const currentFilters: TIssueQueryFilters = {
|
||||||
priority: currentIssueFilters?.filters?.priority || undefined,
|
priority: issueFilters?.filters?.priority || undefined,
|
||||||
state: currentIssueFilters?.filters?.state || undefined,
|
state: issueFilters?.filters?.state || undefined,
|
||||||
labels: currentIssueFilters?.filters?.labels || undefined,
|
labels: issueFilters?.filters?.labels || undefined,
|
||||||
};
|
};
|
||||||
const filteredParams = ISSUE_DISPLAY_FILTERS_BY_LAYOUT?.[currentLayout]?.filters || [];
|
const filteredParams = ISSUE_DISPLAY_FILTERS_BY_LAYOUT?.[currentLayout]?.filters || [];
|
||||||
const currentFilterQueryParams: TIssueQueryFiltersParams = this.computedFilter(currentFilters, filteredParams);
|
const currentFilterQueryParams: TIssueQueryFiltersParams = this.computedFilter(currentFilters, filteredParams);
|
||||||
|
|
||||||
return currentFilterQueryParams;
|
return currentFilterQueryParams;
|
||||||
}
|
});
|
||||||
|
|
||||||
isIssueFiltersUpdated = computedFn((userFilters: TIssueFilters) => {
|
isIssueFiltersUpdated = computedFn((anchor: string, userFilters: TIssueFilters) => {
|
||||||
if (!this.issueFilters) return false;
|
const issueFilters = this.getIssueFilters(anchor);
|
||||||
|
if (!issueFilters) return false;
|
||||||
const currentUserFilters = cloneDeep(userFilters?.filters || {});
|
const currentUserFilters = cloneDeep(userFilters?.filters || {});
|
||||||
const currentIssueFilters = cloneDeep(this.issueFilters?.filters || {});
|
const currentIssueFilters = cloneDeep(issueFilters?.filters || {});
|
||||||
return isEqual(currentUserFilters, currentIssueFilters);
|
return isEqual(currentUserFilters, currentIssueFilters);
|
||||||
});
|
});
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
updateLayoutOptions = (options: TIssueLayoutOptions) => set(this, ["layoutOptions"], options);
|
updateLayoutOptions = (options: TIssueLayoutOptions) => set(this, ["layoutOptions"], options);
|
||||||
|
|
||||||
initIssueFilters = async (projectId: string, initFilters: TIssueFilters) => {
|
initIssueFilters = async (anchor: string, initFilters: TIssueFilters) => {
|
||||||
try {
|
try {
|
||||||
if (!projectId) return;
|
|
||||||
if (this.filters === undefined) runInAction(() => (this.filters = {}));
|
if (this.filters === undefined) runInAction(() => (this.filters = {}));
|
||||||
if (this.filters && initFilters) set(this.filters, [projectId], initFilters);
|
if (this.filters && initFilters) set(this.filters, [anchor], initFilters);
|
||||||
|
|
||||||
const workspaceSlug = this.store.project.workspace?.slug;
|
const appliedFilters = this.getAppliedFilters(anchor);
|
||||||
const currentAppliedFilters = this.appliedFilters;
|
|
||||||
|
|
||||||
if (!workspaceSlug) return;
|
await this.store.issue.fetchPublicIssues(anchor, appliedFilters);
|
||||||
await this.store.issue.fetchPublicIssues(workspaceSlug, projectId, currentAppliedFilters);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateIssueFilters = async <K extends keyof TIssueFilters>(
|
updateIssueFilters = async <K extends keyof TIssueFilters>(
|
||||||
projectId: string,
|
anchor: string,
|
||||||
filterKind: K,
|
filterKind: K,
|
||||||
filterKey: keyof TIssueFilters[K],
|
filterKey: keyof TIssueFilters[K],
|
||||||
filterValue: TIssueFilters[K][typeof filterKey]
|
filterValue: TIssueFilters[K][typeof filterKey]
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
if (!projectId || !filterKind || !filterKey || !filterValue) return;
|
if (!filterKind || !filterKey || !filterValue) return;
|
||||||
if (this.filters === undefined) runInAction(() => (this.filters = {}));
|
if (this.filters === undefined) runInAction(() => (this.filters = {}));
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
if (this.filters) set(this.filters, [projectId, filterKind, filterKey], filterValue);
|
if (this.filters) set(this.filters, [anchor, filterKind, filterKey], filterValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
const workspaceSlug = this.store.project.workspace?.slug;
|
const appliedFilters = this.getAppliedFilters(anchor);
|
||||||
const currentAppliedFilters = this.appliedFilters;
|
|
||||||
|
|
||||||
if (!workspaceSlug) return;
|
await this.store.issue.fetchPublicIssues(anchor, appliedFilters);
|
||||||
await this.store.issue.fetchPublicIssues(workspaceSlug, projectId, currentAppliedFilters);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
// mobx
|
|
||||||
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
|
||||||
// service
|
|
||||||
import ProjectService from "@/services/project.service";
|
|
||||||
// store types
|
|
||||||
import { RootStore } from "@/store/root.store";
|
|
||||||
// types
|
|
||||||
import { TWorkspaceDetails, TProjectDetails, TProjectSettings } from "@/types/project";
|
|
||||||
|
|
||||||
export interface IProjectStore {
|
|
||||||
// observables
|
|
||||||
loader: boolean;
|
|
||||||
error: any | undefined;
|
|
||||||
settings: TProjectSettings | undefined;
|
|
||||||
workspace: TWorkspaceDetails | undefined;
|
|
||||||
projectMap: Record<string, TProjectDetails>; // { [projectID]: TProjectDetails }
|
|
||||||
canReact: boolean;
|
|
||||||
canComment: boolean;
|
|
||||||
canVote: boolean;
|
|
||||||
// actions
|
|
||||||
fetchProjectSettings: (workspaceSlug: string, project_slug: string) => Promise<void>;
|
|
||||||
hydrate: (projectSettings: any) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ProjectStore implements IProjectStore {
|
|
||||||
// observables
|
|
||||||
loader: boolean = false;
|
|
||||||
error: any | undefined = undefined;
|
|
||||||
settings: TProjectSettings | undefined = undefined;
|
|
||||||
workspace: TWorkspaceDetails | undefined = undefined;
|
|
||||||
projectMap: Record<string, TProjectDetails> = {};
|
|
||||||
// service
|
|
||||||
projectService;
|
|
||||||
|
|
||||||
constructor(private store: RootStore) {
|
|
||||||
makeObservable(this, {
|
|
||||||
// loaders and error observables
|
|
||||||
loader: observable,
|
|
||||||
error: observable.ref,
|
|
||||||
// observable
|
|
||||||
workspace: observable,
|
|
||||||
projectMap: observable,
|
|
||||||
settings: observable,
|
|
||||||
// computed
|
|
||||||
canReact: computed,
|
|
||||||
canComment: computed,
|
|
||||||
canVote: computed,
|
|
||||||
// actions
|
|
||||||
fetchProjectSettings: action,
|
|
||||||
hydrate: action,
|
|
||||||
});
|
|
||||||
// services
|
|
||||||
this.projectService = new ProjectService();
|
|
||||||
}
|
|
||||||
|
|
||||||
// computed
|
|
||||||
get canReact() {
|
|
||||||
return this.settings?.reactions ?? false;
|
|
||||||
}
|
|
||||||
get canComment() {
|
|
||||||
return this.settings?.comments ?? false;
|
|
||||||
}
|
|
||||||
get canVote() {
|
|
||||||
return this.settings?.votes ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchProjectSettings = async (workspaceSlug: string, project_slug: string) => {
|
|
||||||
try {
|
|
||||||
this.loader = true;
|
|
||||||
this.error = null;
|
|
||||||
|
|
||||||
const response = await this.projectService.getProjectSettings(workspaceSlug, project_slug);
|
|
||||||
|
|
||||||
if (response) {
|
|
||||||
this.store.issueFilter.updateLayoutOptions(response?.views);
|
|
||||||
runInAction(() => {
|
|
||||||
this.project = response?.project_details;
|
|
||||||
this.workspace = response?.workspace_detail;
|
|
||||||
this.settings = response;
|
|
||||||
this.loader = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
this.loader = false;
|
|
||||||
this.error = error;
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
hydrate = (projectSettings: TProjectSettings) => {
|
|
||||||
const { workspace_detail, project_details } = projectSettings;
|
|
||||||
this.workspace = workspace_detail;
|
|
||||||
this.project = project_details;
|
|
||||||
};
|
|
||||||
}
|
|
@ -8,6 +8,9 @@ import { TPublishSettings } from "@/types/publish";
|
|||||||
export interface IPublishStore extends TPublishSettings {
|
export interface IPublishStore extends TPublishSettings {
|
||||||
// computed
|
// computed
|
||||||
workspaceSlug: string | undefined;
|
workspaceSlug: string | undefined;
|
||||||
|
canComment: boolean;
|
||||||
|
canReact: boolean;
|
||||||
|
canVote: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PublishStore implements IPublishStore {
|
export class PublishStore implements IPublishStore {
|
||||||
@ -67,10 +70,37 @@ export class PublishStore implements IPublishStore {
|
|||||||
workspace_detail: observable,
|
workspace_detail: observable,
|
||||||
// computed
|
// computed
|
||||||
workspaceSlug: computed,
|
workspaceSlug: computed,
|
||||||
|
canComment: computed,
|
||||||
|
canReact: computed,
|
||||||
|
canVote: computed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns the workspace slug from the workspace details
|
||||||
|
*/
|
||||||
get workspaceSlug() {
|
get workspaceSlug() {
|
||||||
return this?.workspace_detail?.slug ?? undefined;
|
return this?.workspace_detail?.slug ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns whether commenting is enabled or not
|
||||||
|
*/
|
||||||
|
get canComment() {
|
||||||
|
return !!this.comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns whether reacting is enabled or not
|
||||||
|
*/
|
||||||
|
get canReact() {
|
||||||
|
return !!this.reactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description returns whether voting is enabled or not
|
||||||
|
*/
|
||||||
|
get canVote() {
|
||||||
|
return !!this.votes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user