mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fixed mentions not showing in modals
This commit is contained in:
parent
a3033203af
commit
43fd94827f
@ -5,7 +5,7 @@ import { CoreEditorExtensions } from "src/ui/extensions";
|
||||
import { EditorProps } from "@tiptap/pm/view";
|
||||
import { getTrimmedHTML } from "src/lib/utils";
|
||||
import { DeleteImage } from "src/types/delete-image";
|
||||
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||
import { IMentionHighlight, IMentionSuggestion } from "src/types/mention-suggestion";
|
||||
import { RestoreImage } from "src/types/restore-image";
|
||||
import { UploadImage } from "src/types/upload-image";
|
||||
|
||||
@ -27,8 +27,8 @@ interface CustomEditorProps {
|
||||
extensions?: any;
|
||||
editorProps?: EditorProps;
|
||||
forwardedRef?: any;
|
||||
mentionHighlights?: string[];
|
||||
mentionSuggestions?: IMentionSuggestion[];
|
||||
mentionHighlights?: () => Promise<IMentionHighlight[]>;
|
||||
mentionSuggestions?: () => Promise<IMentionSuggestion[]>;
|
||||
}
|
||||
|
||||
export const useEditor = ({
|
||||
@ -48,6 +48,7 @@ export const useEditor = ({
|
||||
mentionHighlights,
|
||||
mentionSuggestions,
|
||||
}: CustomEditorProps) => {
|
||||
console.log("the mentions", mentionHighlights);
|
||||
const editor = useCustomEditor(
|
||||
{
|
||||
editorProps: {
|
||||
@ -57,8 +58,8 @@ export const useEditor = ({
|
||||
extensions: [
|
||||
...CoreEditorExtensions(
|
||||
{
|
||||
mentionSuggestions: mentionSuggestions ?? [],
|
||||
mentionHighlights: mentionHighlights ?? [],
|
||||
mentionSuggestions: mentionSuggestions,
|
||||
mentionHighlights: mentionHighlights,
|
||||
},
|
||||
deleteFile,
|
||||
restoreFile,
|
||||
|
@ -68,7 +68,7 @@
|
||||
top: 0;
|
||||
bottom: -2px;
|
||||
width: 4px;
|
||||
z-index: 99;
|
||||
z-index: 5;
|
||||
background-color: rgba(var(--color-primary-400));
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -81,7 +81,7 @@
|
||||
.tableWrapper .tableControls .rowsControl {
|
||||
transition: opacity ease-in 100ms;
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@ -22,94 +22,92 @@ import { CustomKeymap } from "src/ui/extensions/keymap";
|
||||
import { CustomQuoteExtension } from "src/ui/extensions/quote";
|
||||
|
||||
import { DeleteImage } from "src/types/delete-image";
|
||||
import { IMentionSuggestion } from "src/types/mention-suggestion";
|
||||
import { IMentionHighlight, IMentionSuggestion } from "src/types/mention-suggestion";
|
||||
import { RestoreImage } from "src/types/restore-image";
|
||||
import { CustomLinkExtension } from "src/ui/extensions/custom-link";
|
||||
import { CustomCodeInlineExtension } from "./code-inline";
|
||||
|
||||
export const CoreEditorExtensions = (
|
||||
mentionConfig: {
|
||||
mentionSuggestions: IMentionSuggestion[];
|
||||
mentionHighlights: string[];
|
||||
mentionSuggestions?: () => Promise<IMentionSuggestion[]>;
|
||||
mentionHighlights?: () => Promise<IMentionHighlight[]>;
|
||||
},
|
||||
deleteFile: DeleteImage,
|
||||
restoreFile: RestoreImage,
|
||||
cancelUploadImage?: () => any
|
||||
) => {
|
||||
return [
|
||||
StarterKit.configure({
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
class: "list-disc list-outside leading-3 -mt-2",
|
||||
},
|
||||
},
|
||||
orderedList: {
|
||||
HTMLAttributes: {
|
||||
class: "list-decimal list-outside leading-3 -mt-2",
|
||||
},
|
||||
},
|
||||
listItem: {
|
||||
HTMLAttributes: {
|
||||
class: "leading-normal -mb-2",
|
||||
},
|
||||
},
|
||||
code: false,
|
||||
codeBlock: false,
|
||||
horizontalRule: {
|
||||
HTMLAttributes: { class: "mt-4 mb-4" },
|
||||
},
|
||||
blockquote: false,
|
||||
dropcursor: {
|
||||
color: "rgba(var(--color-text-100))",
|
||||
width: 2,
|
||||
},
|
||||
}),
|
||||
CustomQuoteExtension.configure({
|
||||
HTMLAttributes: { className: "border-l-4 border-custom-border-300" },
|
||||
}),
|
||||
CustomKeymap,
|
||||
ListKeymap,
|
||||
CustomLinkExtension.configure({
|
||||
openOnClick: true,
|
||||
autolink: true,
|
||||
linkOnPaste: true,
|
||||
protocols: ["http", "https"],
|
||||
validate: (url: string) => isValidHttpUrl(url),
|
||||
) => [
|
||||
StarterKit.configure({
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
class:
|
||||
"text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer",
|
||||
class: "list-disc list-outside leading-3 -mt-2",
|
||||
},
|
||||
}),
|
||||
ImageExtension(deleteFile, restoreFile, cancelUploadImage).configure({
|
||||
},
|
||||
orderedList: {
|
||||
HTMLAttributes: {
|
||||
class: "rounded-lg border border-custom-border-300",
|
||||
class: "list-decimal list-outside leading-3 -mt-2",
|
||||
},
|
||||
}),
|
||||
TiptapUnderline,
|
||||
TextStyle,
|
||||
Color,
|
||||
TaskList.configure({
|
||||
},
|
||||
listItem: {
|
||||
HTMLAttributes: {
|
||||
class: "not-prose pl-2",
|
||||
class: "leading-normal -mb-2",
|
||||
},
|
||||
}),
|
||||
TaskItem.configure({
|
||||
HTMLAttributes: {
|
||||
class: "flex items-start my-4",
|
||||
},
|
||||
nested: true,
|
||||
}),
|
||||
CustomCodeBlockExtension,
|
||||
CustomCodeInlineExtension,
|
||||
Markdown.configure({
|
||||
html: true,
|
||||
transformCopiedText: true,
|
||||
transformPastedText: true,
|
||||
}),
|
||||
Table,
|
||||
TableHeader,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Mentions(mentionConfig.mentionSuggestions, mentionConfig.mentionHighlights, false),
|
||||
];
|
||||
};
|
||||
},
|
||||
code: false,
|
||||
codeBlock: false,
|
||||
horizontalRule: {
|
||||
HTMLAttributes: { class: "mt-4 mb-4" },
|
||||
},
|
||||
blockquote: false,
|
||||
dropcursor: {
|
||||
color: "rgba(var(--color-text-100))",
|
||||
width: 2,
|
||||
},
|
||||
}),
|
||||
CustomQuoteExtension.configure({
|
||||
HTMLAttributes: { className: "border-l-4 border-custom-border-300" },
|
||||
}),
|
||||
CustomKeymap,
|
||||
ListKeymap,
|
||||
CustomLinkExtension.configure({
|
||||
openOnClick: true,
|
||||
autolink: true,
|
||||
linkOnPaste: true,
|
||||
protocols: ["http", "https"],
|
||||
validate: (url: string) => isValidHttpUrl(url),
|
||||
HTMLAttributes: {
|
||||
class:
|
||||
"text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer",
|
||||
},
|
||||
}),
|
||||
ImageExtension(deleteFile, restoreFile, cancelUploadImage).configure({
|
||||
HTMLAttributes: {
|
||||
class: "rounded-lg border border-custom-border-300",
|
||||
},
|
||||
}),
|
||||
TiptapUnderline,
|
||||
TextStyle,
|
||||
Color,
|
||||
TaskList.configure({
|
||||
HTMLAttributes: {
|
||||
class: "not-prose pl-2",
|
||||
},
|
||||
}),
|
||||
TaskItem.configure({
|
||||
HTMLAttributes: {
|
||||
class: "flex items-start my-4",
|
||||
},
|
||||
nested: true,
|
||||
}),
|
||||
CustomCodeBlockExtension,
|
||||
CustomCodeInlineExtension,
|
||||
Markdown.configure({
|
||||
html: true,
|
||||
transformCopiedText: true,
|
||||
transformPastedText: true,
|
||||
}),
|
||||
Table,
|
||||
TableHeader,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Mentions(mentionConfig.mentionSuggestions, mentionConfig.mentionHighlights, false),
|
||||
];
|
||||
|
@ -5,7 +5,7 @@ import { MentionNodeView } from "src/ui/mentions/mention-node-view";
|
||||
import { IMentionHighlight } from "src/types/mention-suggestion";
|
||||
|
||||
export interface CustomMentionOptions extends MentionOptions {
|
||||
mentionHighlights: IMentionHighlight[];
|
||||
mentionHighlights: () => Promise<IMentionHighlight[]>;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
|
@ -7,29 +7,9 @@ import tippy from "tippy.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { MentionList } from "src/ui/mentions/mention-list";
|
||||
|
||||
export const getSuggestionItems =
|
||||
(getSuggestions: () => Promise<IMentionSuggestion[]>) =>
|
||||
async ({ query }: { query: string }) => {
|
||||
console.log("yaa");
|
||||
const suggestions = await getSuggestions();
|
||||
const mappedSuggestions: IMentionSuggestion[] = suggestions.map((suggestion): IMentionSuggestion => {
|
||||
const transactionId = uuidv4();
|
||||
return {
|
||||
...suggestion,
|
||||
id: transactionId,
|
||||
};
|
||||
});
|
||||
const filteredSuggestions = mappedSuggestions
|
||||
.filter((suggestion) => suggestion.title.toLowerCase().startsWith(query.toLowerCase()))
|
||||
.slice(0, 5);
|
||||
|
||||
console.log("yoo", filteredSuggestions);
|
||||
return filteredSuggestions;
|
||||
};
|
||||
|
||||
export const Mentions = (
|
||||
mentionSuggestions: () => Promise<IMentionSuggestion[]>,
|
||||
mentionHighlights: IMentionHighlight[],
|
||||
mentionHighlights: () => Promise<IMentionHighlight[]>,
|
||||
readonly: boolean
|
||||
) =>
|
||||
CustomMention.configure({
|
||||
@ -39,8 +19,8 @@ export const Mentions = (
|
||||
readonly: readonly,
|
||||
mentionHighlights: mentionHighlights,
|
||||
suggestion: {
|
||||
items: ({ query }) => {
|
||||
const suggestions = mentionSuggestions();
|
||||
items: async ({ query }) => {
|
||||
const suggestions = await mentionSuggestions();
|
||||
const mappedSuggestions: IMentionSuggestion[] = suggestions.map((suggestion): IMentionSuggestion => {
|
||||
const transactionId = uuidv4();
|
||||
return {
|
||||
@ -48,11 +28,11 @@ export const Mentions = (
|
||||
id: transactionId,
|
||||
};
|
||||
});
|
||||
|
||||
const filteredSuggestions = mappedSuggestions
|
||||
.filter((suggestion) => suggestion.title.toLowerCase().startsWith(query.toLowerCase()))
|
||||
.slice(0, 5);
|
||||
|
||||
console.log("yoo", filteredSuggestions);
|
||||
return filteredSuggestions;
|
||||
},
|
||||
// @ts-ignore
|
||||
@ -60,33 +40,44 @@ export const Mentions = (
|
||||
let reactRenderer: ReactRenderer | null = null;
|
||||
let popup: any | null = null;
|
||||
|
||||
const hidePopup = () => {
|
||||
popup?.[0].hide();
|
||||
};
|
||||
return {
|
||||
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
|
||||
props.editor.storage.mentionsOpen = true;
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
reactRenderer = new ReactRenderer(MentionList, {
|
||||
props,
|
||||
editor: props.editor,
|
||||
});
|
||||
props.editor.storage.mentionsOpen = true;
|
||||
// @ts-ignore
|
||||
popup = tippy("body", {
|
||||
getReferenceClientRect: props.clientRect,
|
||||
appendTo: () => document.querySelector("#editor-container"),
|
||||
appendTo: () => document.body,
|
||||
content: reactRenderer.element,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
trigger: "manual",
|
||||
placement: "bottom-start",
|
||||
});
|
||||
// document.addEventListener("scroll", hidePopup, true);
|
||||
},
|
||||
|
||||
onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => {
|
||||
reactRenderer?.updateProps(props);
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup &&
|
||||
popup[0].setProps({
|
||||
getReferenceClientRect: props.clientRect,
|
||||
});
|
||||
},
|
||||
|
||||
onKeyDown: (props: { event: KeyboardEvent }) => {
|
||||
if (props.event.key === "Escape") {
|
||||
popup?.[0].hide();
|
||||
@ -108,6 +99,8 @@ export const Mentions = (
|
||||
props.editor.storage.mentionsOpen = false;
|
||||
popup?.[0].destroy();
|
||||
reactRenderer?.destroy();
|
||||
|
||||
// document.removeEventListener("scroll", hidePopup, true);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ interface MentionListProps {
|
||||
export const MentionList = forwardRef((props: MentionListProps, ref) => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
|
||||
console.log("props", props);
|
||||
const selectItem = (index: number) => {
|
||||
const item = props.items[index];
|
||||
|
||||
|
@ -4,11 +4,22 @@ import { NodeViewWrapper } from "@tiptap/react";
|
||||
import { cn } from "src/lib/utils";
|
||||
import { useRouter } from "next/router";
|
||||
import { IMentionHighlight } from "src/types/mention-suggestion";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export const MentionNodeView = (props) => {
|
||||
const router = useRouter();
|
||||
const highlights = props.extension.options.mentionHighlights as IMentionHighlight[];
|
||||
// const highlights = props.extension.options.mentionHighlights as IMentionHighlight[];
|
||||
const [highlightsState, setHighlightsState] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("hightlights type", props.extension.options.mentionHighlights);
|
||||
const hightlights = async () => {
|
||||
const userId = await props.extension.options.mentionHighlights();
|
||||
setHighlightsState(userId);
|
||||
};
|
||||
hightlights();
|
||||
}, []);
|
||||
|
||||
const handleClick = () => {
|
||||
if (!props.extension.options.readonly) {
|
||||
@ -16,12 +27,13 @@ export const MentionNodeView = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
console.log("state of highlight", highlightsState);
|
||||
return (
|
||||
<NodeViewWrapper className="mention-component inline w-fit">
|
||||
<span
|
||||
className={cn("mention rounded bg-custom-primary-100/20 px-1 py-0.5 font-medium text-custom-primary-100", {
|
||||
"bg-yellow-500/20 text-yellow-500": highlights
|
||||
? highlights.includes(props.node.attrs.entity_identifier)
|
||||
"bg-yellow-500/20 text-yellow-500": highlightsState
|
||||
? highlightsState.includes(props.node.attrs.entity_identifier)
|
||||
: false,
|
||||
"cursor-pointer": !props.extension.options.readonly,
|
||||
// "hover:bg-custom-primary-300" : !props.extension.options.readonly && !highlights.includes(props.node.attrs.id)
|
||||
|
@ -126,6 +126,7 @@ const DocumentEditor = ({
|
||||
extensions: DocumentEditorExtensions(uploadFile, setHideDragHandleFunction, setIsSubmitting),
|
||||
});
|
||||
|
||||
console.log("in document editor", mentionHighlights);
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
@ -327,10 +327,10 @@ const renderItems = () => {
|
||||
// @ts-ignore
|
||||
popup = tippy("body", {
|
||||
getReferenceClientRect: props.clientRect,
|
||||
appendTo: () => document.querySelector("#editor-container"),
|
||||
appendTo: () => document.body,
|
||||
content: component.element,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
// interactive: true,
|
||||
trigger: "manual",
|
||||
placement: "bottom-start",
|
||||
});
|
||||
|
1
packages/types/src/users.d.ts
vendored
1
packages/types/src/users.d.ts
vendored
@ -15,7 +15,6 @@ export interface IUser {
|
||||
is_email_verified: boolean;
|
||||
is_managed: boolean;
|
||||
is_onboarded: boolean;
|
||||
is_password_autoset: boolean;
|
||||
is_tour_completed: boolean;
|
||||
is_password_autoset: boolean;
|
||||
mobile_number: string | null;
|
||||
|
@ -48,7 +48,7 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
|
||||
const editorRef = useRef<any>(null);
|
||||
// toast alert
|
||||
const { setToastAlert } = useToast();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, inboxId } = router.query as {
|
||||
@ -59,6 +59,13 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
|
||||
const workspaceStore = useWorkspace();
|
||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
||||
|
||||
console.log("in create issue modal", workspaceSlug, projectId);
|
||||
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
|
||||
// store hooks
|
||||
const {
|
||||
issues: { createInboxIssue },
|
||||
|
@ -48,7 +48,11 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = observer((props) => {
|
||||
|
||||
const { setShowAlert } = useReloadConfirmations();
|
||||
// store hooks
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
|
||||
// form info
|
||||
const {
|
||||
handleSubmit,
|
||||
|
@ -106,10 +106,6 @@ export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
|
||||
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
|
||||
// store hooks
|
||||
const { areEstimatesEnabledForProject } = useEstimate();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
// hooks
|
||||
const { setValue: setLocalStorageValue } = useLocalStorage("draftedIssue", {});
|
||||
const { setToastAlert } = useToast();
|
||||
// refs
|
||||
const editorRef = useRef<any>(null);
|
||||
// router
|
||||
@ -118,6 +114,14 @@ export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
|
||||
const workspaceStore = useWorkspace();
|
||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
||||
|
||||
// hooks
|
||||
const { setValue: setLocalStorageValue } = useLocalStorage("draftedIssue", {});
|
||||
const { setToastAlert } = useToast();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
|
||||
// store
|
||||
const {
|
||||
config: { envConfig },
|
||||
|
@ -10,13 +10,14 @@ import { TActivityOperations } from "./root";
|
||||
|
||||
type TIssueActivityCommentRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
issueId: string;
|
||||
activityOperations: TActivityOperations;
|
||||
showAccessSpecifier?: boolean;
|
||||
};
|
||||
|
||||
export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer((props) => {
|
||||
const { workspaceSlug, issueId, activityOperations, showAccessSpecifier } = props;
|
||||
const { workspaceSlug, issueId, activityOperations, showAccessSpecifier, projectId } = props;
|
||||
// hooks
|
||||
const {
|
||||
activity: { getActivityCommentByIssueId },
|
||||
@ -31,6 +32,7 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
|
||||
{activityComments.map((activityComment, index) =>
|
||||
activityComment.activity_type === "COMMENT" ? (
|
||||
<IssueCommentCard
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
commentId={activityComment.id}
|
||||
activityOperations={activityOperations}
|
||||
|
@ -18,6 +18,7 @@ import { TActivityOperations } from "../root";
|
||||
const fileService = new FileService();
|
||||
|
||||
type TIssueCommentCard = {
|
||||
projectId: string;
|
||||
workspaceSlug: string;
|
||||
commentId: string;
|
||||
activityOperations: TActivityOperations;
|
||||
@ -26,13 +27,16 @@ type TIssueCommentCard = {
|
||||
};
|
||||
|
||||
export const IssueCommentCard: FC<TIssueCommentCard> = (props) => {
|
||||
const { workspaceSlug, commentId, activityOperations, ends, showAccessSpecifier = false } = props;
|
||||
const { workspaceSlug, projectId, commentId, activityOperations, ends, showAccessSpecifier = false } = props;
|
||||
// hooks
|
||||
const {
|
||||
comment: { getCommentById },
|
||||
} = useIssueDetail();
|
||||
const { currentUser } = useUser();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
// refs
|
||||
const editorRef = useRef<any>(null);
|
||||
const showEditorRef = useRef<any>(null);
|
||||
|
@ -15,6 +15,7 @@ import { useMention, useWorkspace } from "hooks/store";
|
||||
const fileService = new FileService();
|
||||
|
||||
type TIssueCommentCreate = {
|
||||
projectId: string;
|
||||
workspaceSlug: string;
|
||||
activityOperations: TActivityOperations;
|
||||
showAccessSpecifier?: boolean;
|
||||
@ -39,11 +40,14 @@ const commentAccess: commentAccessType[] = [
|
||||
];
|
||||
|
||||
export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
||||
const { workspaceSlug, activityOperations, showAccessSpecifier = false } = props;
|
||||
const { workspaceSlug, projectId, activityOperations, showAccessSpecifier = false } = props;
|
||||
const workspaceStore = useWorkspace();
|
||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
||||
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
|
||||
// refs
|
||||
const editorRef = useRef<any>(null);
|
||||
|
@ -8,6 +8,7 @@ import { IssueCommentCard } from "./comment-card";
|
||||
import { TActivityOperations } from "../root";
|
||||
|
||||
type TIssueCommentRoot = {
|
||||
projectId: string;
|
||||
workspaceSlug: string;
|
||||
issueId: string;
|
||||
activityOperations: TActivityOperations;
|
||||
@ -15,7 +16,7 @@ type TIssueCommentRoot = {
|
||||
};
|
||||
|
||||
export const IssueCommentRoot: FC<TIssueCommentRoot> = observer((props) => {
|
||||
const { workspaceSlug, issueId, activityOperations, showAccessSpecifier } = props;
|
||||
const { workspaceSlug, projectId, issueId, activityOperations, showAccessSpecifier } = props;
|
||||
// hooks
|
||||
const {
|
||||
comment: { getCommentsByIssueId },
|
||||
@ -28,6 +29,7 @@ export const IssueCommentRoot: FC<TIssueCommentRoot> = observer((props) => {
|
||||
<div>
|
||||
{commentIds.map((commentId, index) => (
|
||||
<IssueCommentCard
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
commentId={commentId}
|
||||
ends={index === 0 ? "top" : index === commentIds.length - 1 ? "bottom" : undefined}
|
||||
|
@ -141,12 +141,14 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||
{activityTab === "all" ? (
|
||||
<div className="space-y-3">
|
||||
<IssueActivityCommentRoot
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={issueId}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
<IssueCommentCreate
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
@ -157,12 +159,14 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<IssueCommentRoot
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={issueId}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
<IssueCommentCreate
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
|
@ -96,7 +96,11 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
} = useApplication();
|
||||
const { getProjectById } = useProject();
|
||||
const { areEstimatesEnabledForProject } = useEstimate();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention();
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: defaultProjectId,
|
||||
});
|
||||
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
} = useIssueDetail();
|
||||
|
@ -1,23 +1,27 @@
|
||||
import useSWR from "swr";
|
||||
|
||||
import { ProjectMemberService } from "services/project";
|
||||
import { IProjectMember } from "@plane/types";
|
||||
import { UserService } from "services/user.service";
|
||||
import { useRef, useEffect } from "react";
|
||||
import { ProjectMemberService } from "services/project";
|
||||
import { IProjectMember, IUser } from "@plane/types";
|
||||
import { UserService } from "services/user.service";
|
||||
|
||||
export const useMention = ({ workspaceSlug, projectId }: { workspaceSlug: string; projectId: string }) => {
|
||||
const userService = new UserService();
|
||||
const projectMemberService = new ProjectMemberService();
|
||||
|
||||
const { data: projectMembers } = useSWR(["projectMembers", workspaceSlug, projectId], async () => {
|
||||
const members = await projectMemberService.fetchProjectMembers(workspaceSlug, projectId);
|
||||
const detailedMembers = await Promise.all(
|
||||
members.map(async (member) => projectMemberService.getProjectMember(workspaceSlug, projectId, member.id))
|
||||
);
|
||||
return detailedMembers;
|
||||
});
|
||||
const { data: projectMembers, isLoading: projectMembersLoading } = useSWR(
|
||||
["projectMembers", workspaceSlug, projectId],
|
||||
async () => {
|
||||
const members = await projectMemberService.fetchProjectMembers(workspaceSlug, projectId);
|
||||
const detailedMembers = await Promise.all(
|
||||
members.map(async (member) => projectMemberService.getProjectMember(workspaceSlug, projectId, member.id))
|
||||
);
|
||||
return detailedMembers;
|
||||
}
|
||||
);
|
||||
const { data: user, isLoading: userDataLoading } = useSWR("currentUser", async () => userService.currentUser());
|
||||
|
||||
const projectMembersRef = useRef<IProjectMember[] | undefined>();
|
||||
const userRef = useRef<IUser | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (projectMembers) {
|
||||
@ -25,13 +29,51 @@ export const useMention = ({ workspaceSlug, projectId }: { workspaceSlug: string
|
||||
}
|
||||
}, [projectMembers]);
|
||||
|
||||
const { data: user } = useSWR("currentUser", async () => userService.currentUser());
|
||||
useEffect(() => {
|
||||
if (userRef) {
|
||||
userRef.current = user;
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const mentionHighlights = user ? [user.id] : [];
|
||||
const waitForUserDate = async () =>
|
||||
new Promise<IUser>((resolve) => {
|
||||
const checkData = () => {
|
||||
if (userRef.current) {
|
||||
resolve(userRef.current);
|
||||
} else {
|
||||
setTimeout(checkData, 100);
|
||||
}
|
||||
};
|
||||
checkData();
|
||||
});
|
||||
|
||||
const getMentionSuggestions = () => () => {
|
||||
const mentionSuggestions =
|
||||
projectMembersRef.current?.map((memberDetails) => ({
|
||||
const mentionHighlights = async () => {
|
||||
console.log("isme aaya highlights");
|
||||
if (!userDataLoading && userRef.current) {
|
||||
return [userRef.current.id];
|
||||
} else {
|
||||
const user = await waitForUserDate();
|
||||
return [user.id];
|
||||
}
|
||||
};
|
||||
|
||||
// Polling function to wait for projectMembersRef.current to be populated
|
||||
const waitForData = async () =>
|
||||
new Promise<IProjectMember[]>((resolve) => {
|
||||
const checkData = () => {
|
||||
if (projectMembersRef.current && projectMembersRef.current.length > 0) {
|
||||
resolve(projectMembersRef.current);
|
||||
} else {
|
||||
setTimeout(checkData, 100); // Check every 100ms
|
||||
}
|
||||
};
|
||||
checkData();
|
||||
});
|
||||
|
||||
const mentionSuggestions = async () => {
|
||||
if (!projectMembersLoading && projectMembersRef.current && projectMembersRef.current.length > 0) {
|
||||
// If data is already available, return it immediately
|
||||
return projectMembersRef.current.map((memberDetails) => ({
|
||||
entity_name: "user_mention",
|
||||
entity_identifier: `${memberDetails?.member?.id}`,
|
||||
type: "User",
|
||||
@ -39,12 +81,25 @@ export const useMention = ({ workspaceSlug, projectId }: { workspaceSlug: string
|
||||
subtitle: memberDetails?.member?.email ?? "",
|
||||
avatar: `${memberDetails?.member?.avatar}`,
|
||||
redirect_uri: `/${workspaceSlug}/profile/${memberDetails?.member?.id}`,
|
||||
})) || [];
|
||||
|
||||
return mentionSuggestions;
|
||||
}));
|
||||
} else {
|
||||
// Wait for data to be available
|
||||
const members = await waitForData();
|
||||
console.log("isme aaya", members);
|
||||
return members.map((memberDetails) => ({
|
||||
entity_name: "user_mention",
|
||||
entity_identifier: `${memberDetails?.member?.id}`,
|
||||
type: "User",
|
||||
title: `${memberDetails?.member?.display_name}`,
|
||||
subtitle: memberDetails?.member?.email ?? "",
|
||||
avatar: `${memberDetails?.member?.avatar}`,
|
||||
redirect_uri: `/${workspaceSlug}/profile/${memberDetails?.member?.id}`,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getMentionSuggestions,
|
||||
mentionSuggestions,
|
||||
mentionHighlights,
|
||||
};
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Sparkle } from "lucide-react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import { useRouter } from "next/router";
|
||||
import { ReactElement, useEffect, useRef, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import useSWR from "swr";
|
||||
// hooks
|
||||
|
||||
import { useApplication, useMention, usePage, useUser, useWorkspace } from "hooks/store";
|
||||
@ -26,11 +26,9 @@ import { IPage } from "@plane/types";
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
// fetch-keys
|
||||
// constants
|
||||
import { IssuePeekOverview } from "components/issues";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useProjectPages } from "hooks/store/use-project-specific-pages";
|
||||
import { IssuePeekOverview } from "components/issues";
|
||||
import { ProjectMemberService } from "services/project";
|
||||
import { UserService } from "services/user.service";
|
||||
|
||||
// services
|
||||
const fileService = new FileService();
|
||||
@ -78,6 +76,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
? () => fetchProjectPages(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
// fetching archived pages from API
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `ALL_ARCHIVED_PAGES_LIST_${projectId}` : null,
|
||||
@ -86,21 +85,12 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
: null
|
||||
);
|
||||
|
||||
const projectMemberService = new ProjectMemberService();
|
||||
|
||||
const { data: projectMembers } = useSWR(["projectMembers", workspaceSlug, projectId], async () => {
|
||||
const members = await projectMemberService.fetchProjectMembers(workspaceSlug, projectId);
|
||||
const detailedMembers = await Promise.all(
|
||||
members.map(async (member) => projectMemberService.getProjectMember(workspaceSlug, projectId, member.id))
|
||||
);
|
||||
console.log("ye toh chal", detailedMembers);
|
||||
return detailedMembers;
|
||||
});
|
||||
|
||||
const pageStore = usePage(pageId as string);
|
||||
|
||||
// store hooks
|
||||
const { getMentionSuggestions, mentionHighlights, mentionSuggestions } = useMention({ workspaceSlug, projectId });
|
||||
const { mentionHighlights, mentionSuggestions } = useMention({
|
||||
workspaceSlug: workspaceSlug as string,
|
||||
projectId: projectId as string,
|
||||
});
|
||||
|
||||
const { setShowAlert } = useReloadConfirmations(pageStore?.isSubmitting === "submitting");
|
||||
|
||||
@ -317,7 +307,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
last_updated_at: updated_at,
|
||||
last_updated_by: updated_by,
|
||||
}}
|
||||
mentionSuggestions={getMentionSuggestions(projectMembers)}
|
||||
mentionSuggestions={mentionSuggestions}
|
||||
mentionHighlights={mentionHighlights}
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||
deleteFile={fileService.getDeleteImageFunction(workspaceId)}
|
||||
|
@ -6,6 +6,7 @@ import "styles/globals.css";
|
||||
import "styles/command-pallette.css";
|
||||
import "styles/nprogress.css";
|
||||
import "styles/react-datepicker.css";
|
||||
|
||||
// constants
|
||||
import { SITE_TITLE } from "constants/seo-variables";
|
||||
// mobx store provider
|
||||
|
Loading…
Reference in New Issue
Block a user