diff --git a/apps/web/components/markdown/Markdown.tsx b/apps/web/components/markdown/Markdown.tsx
index 00038dad..d09f31a5 100644
--- a/apps/web/components/markdown/Markdown.tsx
+++ b/apps/web/components/markdown/Markdown.tsx
@@ -3,7 +3,6 @@ import ReactMarkdown, { type Components } from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import remarkGfm from 'remark-gfm'
import { cn } from '@/lib/utils'
-import { MentionHoverCard } from '@/components/common/mention-hover-card'
import { CodeBlock, InlineCode } from './CodeBlock'
import { preprocessLinks } from './linkify'
@@ -61,15 +60,10 @@ function createComponents(
a: ({ href, children }) => {
// Mention links: mention://member/id or mention://agent/id
if (href?.startsWith('mention://')) {
- const parts = href.replace('mention://', '').split('/')
- const mentionType = parts[0] ?? 'member'
- const mentionId = parts[1] ?? ''
return (
-
-
- {children}
-
-
+
+ {children}
+
)
}
diff --git a/apps/web/features/issues/components/comment-input.tsx b/apps/web/features/issues/components/comment-input.tsx
index 6e2b8052..6aaa2041 100644
--- a/apps/web/features/issues/components/comment-input.tsx
+++ b/apps/web/features/issues/components/comment-input.tsx
@@ -1,7 +1,7 @@
"use client";
import { useRef, useState } from "react";
-import { ArrowUp } from "lucide-react";
+import { ArrowUp, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { RichTextEditor, type RichTextEditorRef } from "@/components/common/rich-text-editor";
@@ -44,7 +44,7 @@ function CommentInput({ onSubmit }: CommentInputProps) {
disabled={isEmpty || submitting}
onClick={handleSubmit}
>
-
+ {submitting ? : }
diff --git a/apps/web/features/issues/components/reply-input.tsx b/apps/web/features/issues/components/reply-input.tsx
index b95662c4..d58c8443 100644
--- a/apps/web/features/issues/components/reply-input.tsx
+++ b/apps/web/features/issues/components/reply-input.tsx
@@ -1,7 +1,7 @@
"use client";
import { useRef, useState } from "react";
-import { ArrowUp } from "lucide-react";
+import { ArrowUp, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { RichTextEditor, type RichTextEditorRef } from "@/components/common/rich-text-editor";
import { ActorAvatar } from "@/components/common/actor-avatar";
@@ -83,7 +83,7 @@ function ReplyInput({
onClick={handleSubmit}
tabIndex={isEmpty ? -1 : 0}
>
-
+ {submitting ? : }
diff --git a/apps/web/features/issues/hooks/use-issue-timeline.ts b/apps/web/features/issues/hooks/use-issue-timeline.ts
index 98530d9a..63555d0c 100644
--- a/apps/web/features/issues/hooks/use-issue-timeline.ts
+++ b/apps/web/features/issues/hooks/use-issue-timeline.ts
@@ -176,27 +176,14 @@ export function useIssueTimeline(issueId: string, userId?: string) {
const submitComment = useCallback(
async (content: string) => {
if (!content.trim() || submitting || !userId) return;
- const tempId = "temp-" + Date.now();
- const tempEntry: TimelineEntry = {
- type: "comment",
- id: tempId,
- actor_type: "member",
- actor_id: userId,
- content,
- parent_id: null,
- created_at: new Date().toISOString(),
- updated_at: new Date().toISOString(),
- comment_type: "comment",
- };
- setTimeline((prev) => [...prev, tempEntry]);
setSubmitting(true);
try {
const comment = await api.createComment(issueId, content);
- setTimeline((prev) =>
- prev.map((e) => (e.id === tempId ? commentToTimelineEntry(comment) : e)),
- );
+ setTimeline((prev) => {
+ if (prev.some((e) => e.id === comment.id)) return prev;
+ return [...prev, commentToTimelineEntry(comment)];
+ });
} catch {
- setTimeline((prev) => prev.filter((e) => e.id !== tempId));
toast.error("Failed to send comment");
} finally {
setSubmitting(false);
@@ -208,26 +195,13 @@ export function useIssueTimeline(issueId: string, userId?: string) {
const submitReply = useCallback(
async (parentId: string, content: string) => {
if (!content.trim() || !userId) return;
- const tempId = "temp-" + Date.now();
- const tempEntry: TimelineEntry = {
- type: "comment",
- id: tempId,
- actor_type: "member",
- actor_id: userId,
- content,
- parent_id: parentId,
- created_at: new Date().toISOString(),
- updated_at: new Date().toISOString(),
- comment_type: "comment",
- };
- setTimeline((prev) => [...prev, tempEntry]);
try {
const comment = await api.createComment(issueId, content, "comment", parentId);
- setTimeline((prev) =>
- prev.map((e) => (e.id === tempId ? commentToTimelineEntry(comment) : e)),
- );
+ setTimeline((prev) => {
+ if (prev.some((e) => e.id === comment.id)) return prev;
+ return [...prev, commentToTimelineEntry(comment)];
+ });
} catch {
- setTimeline((prev) => prev.filter((e) => e.id !== tempId));
toast.error("Failed to send reply");
}
},