forked from github/plane
added workspace slug to all tiptap instances
This commit is contained in:
parent
1633765ebb
commit
9bca3ac48d
@ -140,14 +140,14 @@ export const GptAssistantModal: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${
|
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${isOpen ? "block" : "hidden"
|
||||||
isOpen ? "block" : "hidden"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && (
|
{((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && (
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
Content:
|
Content:
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
value={htmlContent ?? `<p>${content}</p>`}
|
value={htmlContent ?? `<p>${content}</p>`}
|
||||||
customClassName="-m-3"
|
customClassName="-m-3"
|
||||||
noBorder
|
noBorder
|
||||||
@ -161,6 +161,7 @@ export const GptAssistantModal: React.FC<Props> = ({
|
|||||||
<div className="page-block-section text-sm">
|
<div className="page-block-section text-sm">
|
||||||
Response:
|
Response:
|
||||||
<Tiptap
|
<Tiptap
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
value={`<p>${response}</p>`}
|
value={`<p>${response}</p>`}
|
||||||
customClassName="-mx-3 -my-3"
|
customClassName="-mx-3 -my-3"
|
||||||
noBorder
|
noBorder
|
||||||
@ -179,8 +180,7 @@ export const GptAssistantModal: React.FC<Props> = ({
|
|||||||
type="text"
|
type="text"
|
||||||
name="task"
|
name="task"
|
||||||
register={register}
|
register={register}
|
||||||
placeholder={`${
|
placeholder={`${content && content !== ""
|
||||||
content && content !== ""
|
|
||||||
? "Tell AI what action to perform on this content..."
|
? "Tell AI what action to perform on this content..."
|
||||||
: "Ask AI anything..."
|
: "Ask AI anything..."
|
||||||
}`}
|
}`}
|
||||||
|
@ -293,6 +293,7 @@ export const InboxMainContent: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<IssueDescriptionForm
|
<IssueDescriptionForm
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
issue={{
|
issue={{
|
||||||
name: issueDetails.name,
|
name: issueDetails.name,
|
||||||
description: issueDetails.description,
|
description: issueDetails.description,
|
||||||
|
@ -171,6 +171,7 @@ export const IssueActivitySection: React.FC<Props> = ({ issueId, user }) => {
|
|||||||
return (
|
return (
|
||||||
<div key={activityItem.id} className="mt-4">
|
<div key={activityItem.id} className="mt-4">
|
||||||
<CommentCard
|
<CommentCard
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
comment={activityItem as IIssueComment}
|
comment={activityItem as IIssueComment}
|
||||||
onSubmit={handleCommentUpdate}
|
onSubmit={handleCommentUpdate}
|
||||||
handleCommentDeletion={handleCommentDelete}
|
handleCommentDeletion={handleCommentDelete}
|
||||||
|
@ -93,6 +93,7 @@ export const AddComment: React.FC<Props> = ({ issueId, user, disabled = false })
|
|||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={
|
value={
|
||||||
!value ||
|
!value ||
|
||||||
|
@ -22,12 +22,13 @@ const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEdit
|
|||||||
TiptapEditor.displayName = "TiptapEditor";
|
TiptapEditor.displayName = "TiptapEditor";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
workspaceSlug: string;
|
||||||
comment: IIssueComment;
|
comment: IIssueComment;
|
||||||
onSubmit: (comment: IIssueComment) => void;
|
onSubmit: (comment: IIssueComment) => void;
|
||||||
handleCommentDeletion: (comment: string) => void;
|
handleCommentDeletion: (comment: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentDeletion }) => {
|
export const CommentCard: React.FC<Props> = ({ comment, workspaceSlug, onSubmit, handleCommentDeletion }) => {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
const editorRef = React.useRef<any>(null);
|
const editorRef = React.useRef<any>(null);
|
||||||
@ -103,6 +104,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={watch("comment_html")}
|
value={watch("comment_html")}
|
||||||
debouncedUpdatesEnabled={false}
|
debouncedUpdatesEnabled={false}
|
||||||
@ -132,6 +134,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
|
|||||||
</form>
|
</form>
|
||||||
<div className={`${isEditing ? "hidden" : ""}`}>
|
<div className={`${isEditing ? "hidden" : ""}`}>
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={showEditorRef}
|
ref={showEditorRef}
|
||||||
value={comment.comment_html}
|
value={comment.comment_html}
|
||||||
editable={false}
|
editable={false}
|
||||||
|
@ -24,6 +24,7 @@ export interface IssueDetailsProps {
|
|||||||
description: string;
|
description: string;
|
||||||
description_html: string;
|
description_html: string;
|
||||||
};
|
};
|
||||||
|
workspaceSlug: string;
|
||||||
handleFormSubmit: (value: IssueDescriptionFormValues) => Promise<void>;
|
handleFormSubmit: (value: IssueDescriptionFormValues) => Promise<void>;
|
||||||
isAllowed: boolean;
|
isAllowed: boolean;
|
||||||
}
|
}
|
||||||
@ -31,6 +32,7 @@ export interface IssueDetailsProps {
|
|||||||
export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
||||||
issue,
|
issue,
|
||||||
handleFormSubmit,
|
handleFormSubmit,
|
||||||
|
workspaceSlug,
|
||||||
isAllowed,
|
isAllowed,
|
||||||
}) => {
|
}) => {
|
||||||
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
||||||
@ -140,6 +142,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
|||||||
? watch("description_html")
|
? watch("description_html")
|
||||||
: value
|
: value
|
||||||
}
|
}
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
debouncedUpdatesEnabled={true}
|
debouncedUpdatesEnabled={true}
|
||||||
setShouldShowAlert={setShowAlert}
|
setShouldShowAlert={setShowAlert}
|
||||||
setIsSubmitting={setIsSubmitting}
|
setIsSubmitting={setIsSubmitting}
|
||||||
|
@ -370,6 +370,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
debouncedUpdatesEnabled={false}
|
debouncedUpdatesEnabled={false}
|
||||||
value={
|
value={
|
||||||
|
@ -124,6 +124,7 @@ export const IssueMainContent: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<IssueDescriptionForm
|
<IssueDescriptionForm
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
issue={issueDetails}
|
issue={issueDetails}
|
||||||
handleFormSubmit={submitChanges}
|
handleFormSubmit={submitChanges}
|
||||||
isAllowed={memberRole.isMember || memberRole.isOwner || !uneditable}
|
isAllowed={memberRole.isMember || memberRole.isOwner || !uneditable}
|
||||||
|
@ -292,6 +292,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
|||||||
if (!data)
|
if (!data)
|
||||||
return (
|
return (
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={"<p></p>"}
|
value={"<p></p>"}
|
||||||
debouncedUpdatesEnabled={false}
|
debouncedUpdatesEnabled={false}
|
||||||
@ -311,6 +312,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={
|
value={
|
||||||
value && value !== "" && Object.keys(value).length > 0
|
value && value !== "" && Object.keys(value).length > 0
|
||||||
@ -334,8 +336,7 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
|||||||
<div className="m-2 mt-6 flex">
|
<div className="m-2 mt-6 flex">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${
|
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : ""
|
||||||
iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : ""
|
|
||||||
}`}
|
}`}
|
||||||
onClick={handleAutoGenerateDescription}
|
onClick={handleAutoGenerateDescription}
|
||||||
disabled={iAmFeelingLucky}
|
disabled={iAmFeelingLucky}
|
||||||
|
@ -456,6 +456,7 @@ export const SinglePageBlock: React.FC<Props> = ({
|
|||||||
{showBlockDetails
|
{showBlockDetails
|
||||||
? block.description_html.length > 7 && (
|
? block.description_html.length > 7 && (
|
||||||
<TiptapEditor
|
<TiptapEditor
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
value={block.description_html}
|
value={block.description_html}
|
||||||
customClassName="text-sm min-h-[150px]"
|
customClassName="text-sm min-h-[150px]"
|
||||||
noBorder
|
noBorder
|
||||||
|
@ -23,7 +23,7 @@ import isValidHttpUrl from "../bubble-menu/utils/link-validator";
|
|||||||
|
|
||||||
lowlight.registerLanguage("ts", ts);
|
lowlight.registerLanguage("ts", ts);
|
||||||
|
|
||||||
export const TiptapExtensions = [
|
export const TiptapExtensions = (workspaceSlug: string) => [
|
||||||
StarterKit.configure({
|
StarterKit.configure({
|
||||||
bulletList: {
|
bulletList: {
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
@ -112,7 +112,7 @@ export const TiptapExtensions = [
|
|||||||
UniqueID.configure({
|
UniqueID.configure({
|
||||||
types: ["image"],
|
types: ["image"],
|
||||||
}),
|
}),
|
||||||
SlashCommand,
|
SlashCommand(workspaceSlug),
|
||||||
TiptapUnderline,
|
TiptapUnderline,
|
||||||
TextStyle,
|
TextStyle,
|
||||||
Color,
|
Color,
|
||||||
|
@ -20,6 +20,7 @@ export interface ITiptapRichTextEditor {
|
|||||||
onChange?: (json: any, html: string) => void;
|
onChange?: (json: any, html: string) => void;
|
||||||
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void;
|
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void;
|
||||||
setShouldShowAlert?: (showAlert: boolean) => void;
|
setShouldShowAlert?: (showAlert: boolean) => void;
|
||||||
|
workspaceSlug: string;
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
forwardedRef?: any;
|
forwardedRef?: any;
|
||||||
debouncedUpdatesEnabled?: boolean;
|
debouncedUpdatesEnabled?: boolean;
|
||||||
@ -36,14 +37,15 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
|||||||
editorContentCustomClassNames,
|
editorContentCustomClassNames,
|
||||||
value,
|
value,
|
||||||
noBorder,
|
noBorder,
|
||||||
|
workspaceSlug,
|
||||||
borderOnFocus,
|
borderOnFocus,
|
||||||
customClassName,
|
customClassName,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
editable: editable ?? true,
|
editable: editable ?? true,
|
||||||
editorProps: TiptapEditorProps,
|
editorProps: TiptapEditorProps(workspaceSlug),
|
||||||
extensions: TiptapExtensions,
|
extensions: TiptapExtensions(workspaceSlug),
|
||||||
content: value,
|
content: value,
|
||||||
onUpdate: async ({ editor }) => {
|
onUpdate: async ({ editor }) => {
|
||||||
// for instant feedback loop
|
// for instant feedback loop
|
||||||
|
@ -57,7 +57,7 @@ function findPlaceholder(state: EditorState, id: {}) {
|
|||||||
return found.length ? found[0].from : null;
|
return found.length ? found[0].from : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startImageUpload(file: File, view: EditorView, pos: number) {
|
export async function startImageUpload(file: File, view: EditorView, pos: number, workspaceSlug: string) {
|
||||||
if (!file.type.includes("image/")) {
|
if (!file.type.includes("image/")) {
|
||||||
return;
|
return;
|
||||||
} else if (file.size / 1024 / 1024 > 20) {
|
} else if (file.size / 1024 / 1024 > 20) {
|
||||||
@ -82,7 +82,10 @@ export async function startImageUpload(file: File, view: EditorView, pos: number
|
|||||||
view.dispatch(tr);
|
view.dispatch(tr);
|
||||||
};
|
};
|
||||||
|
|
||||||
const src = await UploadImageHandler(file);
|
if (!workspaceSlug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const src = await UploadImageHandler(file, workspaceSlug);
|
||||||
const { schema } = view.state;
|
const { schema } = view.state;
|
||||||
pos = findPlaceholder(view.state, id);
|
pos = findPlaceholder(view.state, id);
|
||||||
|
|
||||||
@ -96,19 +99,24 @@ export async function startImageUpload(file: File, view: EditorView, pos: number
|
|||||||
view.dispatch(transaction);
|
view.dispatch(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
const UploadImageHandler = (file: File): Promise<string> => {
|
const UploadImageHandler = (file: File, workspaceSlug: string): Promise<string> => {
|
||||||
|
if (!workspaceSlug) {
|
||||||
|
return Promise.reject("Workspace slug is missing");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
console.log("in here",workspaceSlug)
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("asset", file);
|
formData.append("asset", file);
|
||||||
formData.append("attributes", JSON.stringify({}));
|
formData.append("attributes", JSON.stringify({}));
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const imageUrl = await fileService
|
const imageUrl = await fileService
|
||||||
.uploadFile("plane", formData)
|
.uploadFile(workspaceSlug, formData)
|
||||||
.then((response) => response.asset);
|
.then((response) => response.asset);
|
||||||
|
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.src = imageUrl;
|
image.src = imageUrl;
|
||||||
|
console.log("image uploaded", imageUrl)
|
||||||
image.onload = () => {
|
image.onload = () => {
|
||||||
resolve(imageUrl);
|
resolve(imageUrl);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { EditorProps } from "@tiptap/pm/view";
|
import { EditorProps } from "@tiptap/pm/view";
|
||||||
import { startImageUpload } from "./plugins/upload-image";
|
import { startImageUpload } from "./plugins/upload-image";
|
||||||
|
|
||||||
export const TiptapEditorProps: EditorProps = {
|
export function TiptapEditorProps(workspaceSlug: string): EditorProps {
|
||||||
|
return {
|
||||||
attributes: {
|
attributes: {
|
||||||
class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`,
|
class: `prose prose-brand max-w-full prose-headings:font-display font-default focus:outline-none`,
|
||||||
},
|
},
|
||||||
@ -26,7 +27,7 @@ export const TiptapEditorProps: EditorProps = {
|
|||||||
const file = event.clipboardData.files[0];
|
const file = event.clipboardData.files[0];
|
||||||
const pos = view.state.selection.from;
|
const pos = view.state.selection.from;
|
||||||
|
|
||||||
startImageUpload(file, view, pos);
|
startImageUpload(file, view, pos, workspaceSlug);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -46,11 +47,11 @@ export const TiptapEditorProps: EditorProps = {
|
|||||||
});
|
});
|
||||||
// here we deduct 1 from the pos or else the image will create an extra node
|
// here we deduct 1 from the pos or else the image will create an extra node
|
||||||
if (coordinates) {
|
if (coordinates) {
|
||||||
startImageUpload(file, view, coordinates.pos - 1);
|
startImageUpload(file, view, coordinates.pos - 1, workspaceSlug);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
@ -52,7 +52,7 @@ const Command = Extension.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getSuggestionItems = ({ query }: { query: string }) =>
|
const getSuggestionItems = (workspaceSlug: string) => ({ query }: { query: string }) =>
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
title: "Text",
|
title: "Text",
|
||||||
@ -163,7 +163,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
|||||||
if (input.files?.length) {
|
if (input.files?.length) {
|
||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
const pos = editor.view.state.selection.from;
|
const pos = editor.view.state.selection.from;
|
||||||
startImageUpload(file, editor.view, pos);
|
startImageUpload(file, editor.view, pos, workspaceSlug);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
input.click();
|
input.click();
|
||||||
@ -328,9 +328,10 @@ const renderItems = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const SlashCommand = Command.configure({
|
export const SlashCommand = (workspaceSlug: string) =>
|
||||||
|
Command.configure({
|
||||||
suggestion: {
|
suggestion: {
|
||||||
items: getSuggestionItems,
|
items: getSuggestionItems(workspaceSlug),
|
||||||
render: renderItems,
|
render: renderItems,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -106,6 +106,7 @@ const ProfileActivity = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="issue-comments-section p-0">
|
<div className="issue-comments-section p-0">
|
||||||
<Tiptap
|
<Tiptap
|
||||||
|
workspaceSlug={workspaceSlug as string}
|
||||||
value={
|
value={
|
||||||
activityItem?.new_value !== ""
|
activityItem?.new_value !== ""
|
||||||
? activityItem.new_value
|
? activityItem.new_value
|
||||||
|
Loading…
Reference in New Issue
Block a user