feat: block sync (#548)

This commit is contained in:
Aaryan Khandelwal 2023-03-28 00:36:20 +05:30 committed by GitHub
parent a5a96d9f66
commit 08ee5dc6b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 16 deletions

View File

@ -1,6 +1,7 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
import dynamic from "next/dynamic";
import { mutate } from "swr";
@ -9,7 +10,6 @@ import { mutate } from "swr";
import { Controller, useForm } from "react-hook-form";
// services
import pagesService from "services/pages.service";
import aiService from "services/ai.service";
// hooks
import useToast from "hooks/use-toast";
// components
@ -18,13 +18,16 @@ import { GptAssistantModal } from "components/core";
// ui
import { CustomMenu, Loader, TextArea } from "components/ui";
// icons
import { WaterDropIcon } from "components/icons";
import { LayerDiagonalIcon, WaterDropIcon } from "components/icons";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import { IPageBlock, IProject } from "types";
import { IIssue, IPageBlock, IProject } from "types";
// fetch-keys
import { PAGE_BLOCKS_LIST } from "constants/fetch-keys";
import { ArrowTopRightOnSquareIcon, CheckIcon } from "@heroicons/react/24/outline";
import issuesService from "services/issues.service";
import { ArrowPathIcon } from "@heroicons/react/20/solid";
type Props = {
block: IPageBlock;
@ -42,6 +45,7 @@ const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor
export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
const [isSyncing, setIsSyncing] = useState(false);
const [gptAssistantModal, setGptAssistantModal] = useState(false);
@ -63,6 +67,8 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
if (!formData.name || formData.name.length === 0 || formData.name === "") return;
if (block.issue && block.sync) setIsSyncing(true);
mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string),
(prevData) =>
@ -80,8 +86,16 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
description: formData.description,
description_html: formData.description_html,
})
.then(() => {
.then((res) => {
mutate(PAGE_BLOCKS_LIST(pageId as string));
if (block.issue && block.sync)
issuesService
.patchIssue(workspaceSlug as string, projectId as string, block.issue, {
name: res.name,
description: res.description,
description_html: res.description_html,
})
.finally(() => setIsSyncing(false));
});
};
@ -95,12 +109,12 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
pageId as string,
block.id
)
.then((res) => {
.then((res: IIssue) => {
mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string),
(prevData) =>
(prevData ?? []).map((p) => {
if (p.id === block.id) return { ...p, issue: res.id };
if (p.id === block.id) return { ...p, issue: res.id, issue_detail: res };
return p;
}),
@ -181,6 +195,31 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
});
};
const handleBlockSync = () => {
if (!workspaceSlug || !projectId || !pageId) return;
mutate<IPageBlock[]>(
PAGE_BLOCKS_LIST(pageId as string),
(prevData) =>
(prevData ?? []).map((p) => {
if (p.id === block.id) return { ...p, sync: !block.sync };
return p;
}),
false
);
pagesService.patchPageBlock(
workspaceSlug as string,
projectId as string,
pageId as string,
block.id,
{
sync: !block.sync,
}
);
};
const handleCopyText = () => {
const originURL =
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
@ -217,26 +256,48 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
<TextArea
id="name"
name="name"
placeholder="Enter block title"
placeholder="Block title"
value={watch("name")}
onBlur={handleSubmit(updatePageBlock)}
onChange={(e) => setValue("name", e.target.value)}
required={true}
className="min-h-10 block w-full resize-none overflow-hidden border-none bg-transparent text-base font-medium"
role="textbox"
disabled={block.issue ? true : false}
/>
<div className="flex items-center">
<div className="flex flex-shrink-0 items-center gap-2">
{block.sync && (
<div className="flex flex-shrink-0 cursor-default items-center gap-1 rounded bg-gray-100 py-1 px-1.5 text-xs">
{isSyncing ? (
<ArrowPathIcon className="h-3 w-3 animate-spin" />
) : (
<CheckIcon className="h-3 w-3" />
)}
{isSyncing ? "Syncing..." : "Synced"}
</div>
)}
{block.issue && (
<Link href={`/${workspaceSlug}/projects/${projectId}/issues/${block.issue}`}>
<a className="flex flex-shrink-0 items-center gap-1 rounded bg-gray-100 px-1.5 py-1 text-xs">
<LayerDiagonalIcon height="16" width="16" color="black" />
{projectDetails?.identifier}-{block.issue_detail?.sequence_id}
</a>
</Link>
)}
<button
type="button"
className="rounded px-1.5 py-1 text-xs hover:bg-gray-100"
className="-mr-2 rounded px-1.5 py-1 text-xs hover:bg-gray-100"
onClick={() => setGptAssistantModal((prevData) => !prevData)}
>
AI
</button>
<CustomMenu label={<WaterDropIcon width={14} height={15} />} noBorder noChevron>
{block.issue ? (
<CustomMenu.MenuItem onClick={handleCopyText}>Copy issue link</CustomMenu.MenuItem>
<>
<CustomMenu.MenuItem onClick={handleBlockSync}>
<>Turn sync {block.sync ? "off" : "on"}</>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={handleCopyText}>Copy issue link</CustomMenu.MenuItem>
</>
) : (
<>
<CustomMenu.MenuItem onClick={pushBlockIntoIssues}>
@ -265,10 +326,8 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails }) => {
onBlur={handleSubmit(updatePageBlock)}
onJSONChange={(jsonValue) => setValue("description", jsonValue)}
onHTMLChange={(htmlValue) => setValue("description_html", htmlValue)}
placeholder="Description..."
editable={block.issue ? false : true}
placeholder="Block description..."
customClassName="text-gray-500"
// gptOption
noBorder
/>
)}

View File

@ -1,5 +1,5 @@
// types
import { IIssueLabels } from "./issues";
import { IIssue, IIssueLabels } from "./issues";
export interface IPage {
access: number;
@ -36,11 +36,12 @@ export interface IPageBlock {
description_stripped: any;
id: string;
issue: string | null;
issue_detail: string | null;
issue_detail: IIssue | null;
name: string;
page: string;
project: string;
sort_order: number;
sync: boolean;
updated_at: Date;
updated_by: string;
workspace: string;