updated comments
Signed-off-by: Max <ai-agent@topdoglabs.com>
This commit is contained in:
parent
92adbef66e
commit
8aaca14e3a
@ -215,7 +215,7 @@ cmd_task_comment() {
|
|||||||
--arg id "$comment_id" \
|
--arg id "$comment_id" \
|
||||||
--arg text "$text" \
|
--arg text "$text" \
|
||||||
--arg createdAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
--arg createdAt "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||||
'{id: $id, text: $text, createdAt: $createdAt, author: "assistant"}')
|
'{id: $id, text: $text, createdAt: $createdAt, commentAuthorId: "assistant", replies: []}')
|
||||||
|
|
||||||
local updated_task
|
local updated_task
|
||||||
updated_task=$(echo "$task" | jq --argjson comment "$new_comment" '.comments += [$comment]')
|
updated_task=$(echo "$task" | jq --argjson comment "$new_comment" '.comments += [$comment]')
|
||||||
|
|||||||
@ -231,7 +231,7 @@ function mapTaskRow(row: Record<string, unknown>, usersById: Map<string, UserPro
|
|||||||
throw new HttpError(500, "Invalid tasks.tags value in database", { taskId: row.id, value: row.tags });
|
throw new HttpError(500, "Invalid tasks.tags value in database", { taskId: row.id, value: row.tags });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeFullData && row.comments !== undefined && !Array.isArray(row.comments)) {
|
if (row.comments !== undefined && !Array.isArray(row.comments)) {
|
||||||
throw new HttpError(500, "Invalid tasks.comments value in database", { taskId: row.id, value: row.comments });
|
throw new HttpError(500, "Invalid tasks.comments value in database", { taskId: row.id, value: row.comments });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ function mapTaskRow(row: Record<string, unknown>, usersById: Map<string, UserPro
|
|||||||
assigneeEmail: assigneeUser?.email,
|
assigneeEmail: assigneeUser?.email,
|
||||||
assigneeAvatarUrl: assigneeUser?.avatarUrl,
|
assigneeAvatarUrl: assigneeUser?.avatarUrl,
|
||||||
dueDate: toNonEmptyString(row.due_date),
|
dueDate: toNonEmptyString(row.due_date),
|
||||||
comments: includeFullData ? (row.comments as unknown[] | undefined) ?? [] : [],
|
comments: (row.comments as unknown[] | undefined) ?? [],
|
||||||
tags: (row.tags as unknown[]).filter((tag): tag is string => typeof tag === "string"),
|
tags: (row.tags as unknown[]).filter((tag): tag is string => typeof tag === "string"),
|
||||||
attachments: includeFullData ? (row.attachments as unknown[] | undefined) ?? [] : [],
|
attachments: includeFullData ? (row.attachments as unknown[] | undefined) ?? [] : [],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import {
|
|||||||
markdownPreviewObjectUrl,
|
markdownPreviewObjectUrl,
|
||||||
textPreviewObjectUrl,
|
textPreviewObjectUrl,
|
||||||
} from "@/lib/attachments"
|
} from "@/lib/attachments"
|
||||||
import { useTaskStore, Task, TaskType, TaskStatus, Priority, TaskAttachment, type CommentAuthor } from "@/stores/useTaskStore"
|
import { useTaskStore, Task, TaskType, TaskStatus, Priority, TaskAttachment } from "@/stores/useTaskStore"
|
||||||
import { BacklogSkeleton, SearchSkeleton } from "@/components/LoadingSkeletons"
|
import { BacklogSkeleton, SearchSkeleton } from "@/components/LoadingSkeletons"
|
||||||
import { Plus, MessageSquare, Calendar, Trash2, X, LayoutGrid, ListTodo, GripVertical, Paperclip, Download, Search, Archive } from "lucide-react"
|
import { Plus, MessageSquare, Calendar, Trash2, X, LayoutGrid, ListTodo, GripVertical, Paperclip, Download, Search, Archive } from "lucide-react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
@ -422,27 +422,8 @@ export default function Home() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCommentAuthor = (value: unknown): CommentAuthor => {
|
const getCommentAuthorId = (value: unknown): string | null =>
|
||||||
if (value === "assistant") {
|
typeof value === "string" && value.trim().length > 0 ? value.trim() : null
|
||||||
return { id: "assistant", name: "Assistant", type: "assistant" }
|
|
||||||
}
|
|
||||||
if (value === "user") {
|
|
||||||
return { id: "legacy-user", name: "User", type: "human" }
|
|
||||||
}
|
|
||||||
if (!value || typeof value !== "object") {
|
|
||||||
return { id: "legacy-user", name: "User", type: "human" }
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidate = value as Partial<CommentAuthor>
|
|
||||||
const type = candidate.type === "assistant" || candidate.id === "assistant" ? "assistant" : "human"
|
|
||||||
return {
|
|
||||||
id: typeof candidate.id === "string" && candidate.id ? candidate.id : type === "assistant" ? "assistant" : "legacy-user",
|
|
||||||
name: typeof candidate.name === "string" && candidate.name ? candidate.name : type === "assistant" ? "Assistant" : "User",
|
|
||||||
email: typeof candidate.email === "string" && candidate.email ? candidate.email : undefined,
|
|
||||||
avatarUrl: typeof candidate.avatarUrl === "string" && candidate.avatarUrl ? candidate.avatarUrl : undefined,
|
|
||||||
type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatBytes = (bytes: number) => {
|
const formatBytes = (bytes: number) => {
|
||||||
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B"
|
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B"
|
||||||
@ -872,7 +853,12 @@ export default function Home() {
|
|||||||
|
|
||||||
const handleAddComment = () => {
|
const handleAddComment = () => {
|
||||||
if (newComment.trim() && selectedTaskId) {
|
if (newComment.trim() && selectedTaskId) {
|
||||||
addComment(selectedTaskId, newComment.trim())
|
const commentAuthorId = getCommentAuthorId(currentUser.id)
|
||||||
|
if (!commentAuthorId) {
|
||||||
|
toast.error("You must be signed in to add a comment.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addComment(selectedTaskId, newComment.trim(), commentAuthorId)
|
||||||
setNewComment("")
|
setNewComment("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1697,10 +1683,15 @@ export default function Home() {
|
|||||||
<p className="text-slate-500 text-sm">No comments yet. Add the first one.</p>
|
<p className="text-slate-500 text-sm">No comments yet. Add the first one.</p>
|
||||||
) : (
|
) : (
|
||||||
editedTask.comments.map((comment) => {
|
editedTask.comments.map((comment) => {
|
||||||
const author = getCommentAuthor(comment.author)
|
const authorId = comment.commentAuthorId
|
||||||
const isAssistant = author.type === "assistant"
|
const isAssistant = authorId === "assistant"
|
||||||
const displayName = author.id === currentUser.id ? "You" : author.name
|
const resolvedAuthor = authorId === currentUser.id ? currentUser : resolveAssignee(authorId)
|
||||||
const resolvedAuthorAvatar = author.avatarUrl || resolveAssignee(author.id)?.avatarUrl
|
const displayName = isAssistant
|
||||||
|
? "Assistant"
|
||||||
|
: authorId === currentUser.id
|
||||||
|
? "You"
|
||||||
|
: resolvedAuthor?.name || "Unknown user"
|
||||||
|
const resolvedAuthorAvatar = resolvedAuthor?.avatarUrl
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={comment.id}
|
key={comment.id}
|
||||||
@ -1713,7 +1704,7 @@ export default function Home() {
|
|||||||
AI
|
AI
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<AvatarCircle name={displayName} avatarUrl={resolvedAuthorAvatar} seed={author.id} sizeClass="h-8 w-8" />
|
<AvatarCircle name={displayName} avatarUrl={resolvedAuthorAvatar} seed={authorId} sizeClass="h-8 w-8" />
|
||||||
)}
|
)}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
|||||||
@ -21,7 +21,6 @@ import { parseSprintStart } from "@/lib/utils"
|
|||||||
import {
|
import {
|
||||||
useTaskStore,
|
useTaskStore,
|
||||||
type Comment as TaskComment,
|
type Comment as TaskComment,
|
||||||
type CommentAuthor,
|
|
||||||
type Priority,
|
type Priority,
|
||||||
type Task,
|
type Task,
|
||||||
type TaskAttachment,
|
type TaskAttachment,
|
||||||
@ -89,13 +88,15 @@ const getComments = (value: unknown): TaskComment[] => {
|
|||||||
for (const entry of value) {
|
for (const entry of value) {
|
||||||
if (!entry || typeof entry !== "object") continue
|
if (!entry || typeof entry !== "object") continue
|
||||||
const comment = entry as Partial<TaskComment>
|
const comment = entry as Partial<TaskComment>
|
||||||
if (typeof comment.id !== "string" || typeof comment.text !== "string") continue
|
if (typeof comment.id !== "string" || typeof comment.text !== "string" || typeof comment.createdAt !== "string") continue
|
||||||
|
const commentAuthorId = getCommentAuthorId(comment.commentAuthorId)
|
||||||
|
if (!commentAuthorId) continue
|
||||||
|
|
||||||
normalized.push({
|
normalized.push({
|
||||||
id: comment.id,
|
id: comment.id,
|
||||||
text: comment.text,
|
text: comment.text,
|
||||||
createdAt: typeof comment.createdAt === "string" ? comment.createdAt : new Date().toISOString(),
|
createdAt: comment.createdAt,
|
||||||
author: getCommentAuthor(comment.author),
|
commentAuthorId,
|
||||||
replies: getComments(comment.replies),
|
replies: getComments(comment.replies),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -103,41 +104,17 @@ const getComments = (value: unknown): TaskComment[] => {
|
|||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCommentAuthor = (value: unknown): CommentAuthor => {
|
const getCommentAuthorId = (value: unknown): string | null =>
|
||||||
if (value === "assistant") {
|
typeof value === "string" && value.trim().length > 0 ? value.trim() : null
|
||||||
return { id: "assistant", name: "Assistant", type: "assistant" }
|
|
||||||
}
|
|
||||||
if (value === "user") {
|
|
||||||
return { id: "legacy-user", name: "User", type: "human" }
|
|
||||||
}
|
|
||||||
if (!value || typeof value !== "object") {
|
|
||||||
return { id: "legacy-user", name: "User", type: "human" }
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidate = value as Partial<CommentAuthor>
|
const getCurrentUserCommentAuthorId = (profile: UserProfile): string | null =>
|
||||||
const type = candidate.type === "assistant" || candidate.id === "assistant" ? "assistant" : "human"
|
getCommentAuthorId(profile.id)
|
||||||
return {
|
|
||||||
id: typeof candidate.id === "string" && candidate.id ? candidate.id : type === "assistant" ? "assistant" : "legacy-user",
|
|
||||||
name: typeof candidate.name === "string" && candidate.name ? candidate.name : type === "assistant" ? "Assistant" : "User",
|
|
||||||
email: typeof candidate.email === "string" && candidate.email ? candidate.email : undefined,
|
|
||||||
avatarUrl: typeof candidate.avatarUrl === "string" && candidate.avatarUrl ? candidate.avatarUrl : undefined,
|
|
||||||
type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const profileToAuthor = (profile: UserProfile): CommentAuthor => ({
|
const buildComment = (text: string, commentAuthorId: string): TaskComment => ({
|
||||||
id: profile.id,
|
|
||||||
name: profile.name,
|
|
||||||
email: profile.email,
|
|
||||||
avatarUrl: profile.avatarUrl,
|
|
||||||
type: "human",
|
|
||||||
})
|
|
||||||
|
|
||||||
const buildComment = (text: string, author: CommentAuthor): TaskComment => ({
|
|
||||||
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||||
text,
|
text,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
author,
|
commentAuthorId,
|
||||||
replies: [],
|
replies: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -400,10 +377,14 @@ export default function TaskDetailPage() {
|
|||||||
const handleAddComment = () => {
|
const handleAddComment = () => {
|
||||||
if (!editedTask || !newComment.trim()) return
|
if (!editedTask || !newComment.trim()) return
|
||||||
|
|
||||||
const actor = profileToAuthor(currentUser)
|
const commentAuthorId = getCurrentUserCommentAuthorId(currentUser)
|
||||||
|
if (!commentAuthorId) {
|
||||||
|
toast.error("You must be signed in to add a comment.")
|
||||||
|
return
|
||||||
|
}
|
||||||
setEditedTask({
|
setEditedTask({
|
||||||
...editedTask,
|
...editedTask,
|
||||||
comments: [...getComments(editedTask.comments), buildComment(newComment.trim(), actor)],
|
comments: [...getComments(editedTask.comments), buildComment(newComment.trim(), commentAuthorId)],
|
||||||
})
|
})
|
||||||
setNewComment("")
|
setNewComment("")
|
||||||
}
|
}
|
||||||
@ -413,10 +394,14 @@ export default function TaskDetailPage() {
|
|||||||
const text = replyDrafts[parentId]?.trim()
|
const text = replyDrafts[parentId]?.trim()
|
||||||
if (!text) return
|
if (!text) return
|
||||||
|
|
||||||
const actor = profileToAuthor(currentUser)
|
const commentAuthorId = getCurrentUserCommentAuthorId(currentUser)
|
||||||
|
if (!commentAuthorId) {
|
||||||
|
toast.error("You must be signed in to reply.")
|
||||||
|
return
|
||||||
|
}
|
||||||
setEditedTask({
|
setEditedTask({
|
||||||
...editedTask,
|
...editedTask,
|
||||||
comments: addReplyToThread(getComments(editedTask.comments), parentId, buildComment(text, actor)),
|
comments: addReplyToThread(getComments(editedTask.comments), parentId, buildComment(text, commentAuthorId)),
|
||||||
})
|
})
|
||||||
|
|
||||||
setReplyDrafts((prev) => ({ ...prev, [parentId]: "" }))
|
setReplyDrafts((prev) => ({ ...prev, [parentId]: "" }))
|
||||||
@ -586,10 +571,11 @@ export default function TaskDetailPage() {
|
|||||||
const replies = getComments(comment.replies)
|
const replies = getComments(comment.replies)
|
||||||
const isReplying = !!openReplyEditors[comment.id]
|
const isReplying = !!openReplyEditors[comment.id]
|
||||||
const replyDraft = replyDrafts[comment.id] || ""
|
const replyDraft = replyDrafts[comment.id] || ""
|
||||||
const author = getCommentAuthor(comment.author)
|
const authorId = comment.commentAuthorId
|
||||||
const isAssistant = author.type === "assistant"
|
const isAssistant = authorId === "assistant"
|
||||||
const displayName = isAssistant ? "Assistant" : author.id === currentUser.id ? "You" : author.name
|
const resolvedAuthor = authorId === currentUser.id ? currentUser : resolveAssignee(authorId)
|
||||||
const resolvedAuthorAvatar = author.avatarUrl || resolveAssignee(author.id)?.avatarUrl
|
const displayName = isAssistant ? "Assistant" : authorId === currentUser.id ? "You" : resolvedAuthor?.name || "Unknown user"
|
||||||
|
const resolvedAuthorAvatar = resolvedAuthor?.avatarUrl
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={comment.id} className="space-y-2" style={{ marginLeft: depth * 20 }}>
|
<div key={comment.id} className="space-y-2" style={{ marginLeft: depth * 20 }}>
|
||||||
@ -601,7 +587,7 @@ export default function TaskDetailPage() {
|
|||||||
AI
|
AI
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<AvatarCircle name={displayName} avatarUrl={resolvedAuthorAvatar} seed={author.id} />
|
<AvatarCircle name={displayName} avatarUrl={resolvedAuthorAvatar} seed={authorId} />
|
||||||
)}
|
)}
|
||||||
<span className="text-sm text-slate-300 font-medium">{displayName}</span>
|
<span className="text-sm text-slate-300 font-medium">{displayName}</span>
|
||||||
<span className="text-xs text-slate-500">{new Date(comment.createdAt).toLocaleString()}</span>
|
<span className="text-xs text-slate-500">{new Date(comment.createdAt).toLocaleString()}</span>
|
||||||
|
|||||||
@ -22,18 +22,10 @@ export interface Comment {
|
|||||||
id: string
|
id: string
|
||||||
text: string
|
text: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
author: CommentAuthor | 'user' | 'assistant'
|
commentAuthorId: string
|
||||||
replies?: Comment[]
|
replies?: Comment[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentAuthor {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
email?: string
|
|
||||||
avatarUrl?: string
|
|
||||||
type: 'human' | 'assistant'
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserProfile {
|
export interface UserProfile {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@ -121,7 +113,7 @@ interface TaskStore {
|
|||||||
getTasksBySprint: (sprintId: string) => Task[]
|
getTasksBySprint: (sprintId: string) => Task[]
|
||||||
|
|
||||||
// Comment actions
|
// Comment actions
|
||||||
addComment: (taskId: string, text: string, author?: CommentAuthor | 'user' | 'assistant') => void
|
addComment: (taskId: string, text: string, commentAuthorId: string) => void
|
||||||
deleteComment: (taskId: string, commentId: string) => void
|
deleteComment: (taskId: string, commentId: string) => void
|
||||||
|
|
||||||
// Filters
|
// Filters
|
||||||
@ -145,57 +137,14 @@ const normalizeUserProfile = (value: unknown, fallback: UserProfile = defaultCur
|
|||||||
return { id, name, email, avatarUrl }
|
return { id, name, email, avatarUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileToCommentAuthor = (profile: UserProfile): CommentAuthor => ({
|
const profileToCommentAuthor = (profile: UserProfile) => ({
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
name: profile.name,
|
name: profile.name,
|
||||||
email: profile.email,
|
|
||||||
avatarUrl: profile.avatarUrl,
|
avatarUrl: profile.avatarUrl,
|
||||||
type: 'human',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const assistantAuthor: CommentAuthor = {
|
const normalizeCommentAuthorId = (value: unknown): string | null =>
|
||||||
id: 'assistant',
|
typeof value === 'string' && value.trim().length > 0 ? value.trim() : null
|
||||||
name: 'Assistant',
|
|
||||||
type: 'assistant',
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizeCommentAuthor = (value: unknown): CommentAuthor => {
|
|
||||||
if (value === 'assistant') return assistantAuthor
|
|
||||||
if (value === 'user') {
|
|
||||||
return {
|
|
||||||
id: 'legacy-user',
|
|
||||||
name: 'User',
|
|
||||||
type: 'human',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value || typeof value !== 'object') {
|
|
||||||
return {
|
|
||||||
id: 'legacy-user',
|
|
||||||
name: 'User',
|
|
||||||
type: 'human',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidate = value as Partial<CommentAuthor>
|
|
||||||
const type: CommentAuthor['type'] =
|
|
||||||
candidate.type === 'assistant' || candidate.id === 'assistant' ? 'assistant' : 'human'
|
|
||||||
|
|
||||||
const id = typeof candidate.id === 'string' && candidate.id.trim().length > 0
|
|
||||||
? candidate.id
|
|
||||||
: type === 'assistant'
|
|
||||||
? 'assistant'
|
|
||||||
: 'legacy-user'
|
|
||||||
const name = typeof candidate.name === 'string' && candidate.name.trim().length > 0
|
|
||||||
? candidate.name.trim()
|
|
||||||
: type === 'assistant'
|
|
||||||
? 'Assistant'
|
|
||||||
: 'User'
|
|
||||||
const email = typeof candidate.email === 'string' && candidate.email.trim().length > 0 ? candidate.email.trim() : undefined
|
|
||||||
const avatarUrl = typeof candidate.avatarUrl === 'string' && candidate.avatarUrl.trim().length > 0 ? candidate.avatarUrl : undefined
|
|
||||||
|
|
||||||
return { id, name, email, avatarUrl, type }
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizeComments = (value: unknown): Comment[] => {
|
const normalizeComments = (value: unknown): Comment[] => {
|
||||||
if (!Array.isArray(value)) return []
|
if (!Array.isArray(value)) return []
|
||||||
@ -205,13 +154,15 @@ const normalizeComments = (value: unknown): Comment[] => {
|
|||||||
for (const entry of value) {
|
for (const entry of value) {
|
||||||
if (!entry || typeof entry !== 'object') continue
|
if (!entry || typeof entry !== 'object') continue
|
||||||
const candidate = entry as Partial<Comment>
|
const candidate = entry as Partial<Comment>
|
||||||
if (typeof candidate.id !== 'string' || typeof candidate.text !== 'string') continue
|
if (typeof candidate.id !== 'string' || typeof candidate.text !== 'string' || typeof candidate.createdAt !== 'string') continue
|
||||||
|
const commentAuthorId = normalizeCommentAuthorId(candidate.commentAuthorId)
|
||||||
|
if (!commentAuthorId) continue
|
||||||
|
|
||||||
comments.push({
|
comments.push({
|
||||||
id: candidate.id,
|
id: candidate.id,
|
||||||
text: candidate.text,
|
text: candidate.text,
|
||||||
createdAt: typeof candidate.createdAt === 'string' ? candidate.createdAt : new Date().toISOString(),
|
createdAt: candidate.createdAt,
|
||||||
author: normalizeCommentAuthor(candidate.author),
|
commentAuthorId,
|
||||||
replies: normalizeComments(candidate.replies),
|
replies: normalizeComments(candidate.replies),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -646,13 +597,19 @@ export const useTaskStore = create<TaskStore>()(
|
|||||||
return get().tasks.filter((t) => t.sprintId === sprintId)
|
return get().tasks.filter((t) => t.sprintId === sprintId)
|
||||||
},
|
},
|
||||||
|
|
||||||
addComment: (taskId, text, author) => {
|
addComment: (taskId, text, commentAuthorId) => {
|
||||||
const actor = normalizeCommentAuthor(author ?? profileToCommentAuthor(get().currentUser))
|
const normalizedCommentAuthorId = normalizeCommentAuthorId(commentAuthorId)
|
||||||
|
if (!normalizedCommentAuthorId) {
|
||||||
|
const message = 'Failed to add comment: invalid comment author id'
|
||||||
|
console.error(message, { taskId, commentAuthorId })
|
||||||
|
set({ syncError: message })
|
||||||
|
return
|
||||||
|
}
|
||||||
const newComment: Comment = {
|
const newComment: Comment = {
|
||||||
id: Date.now().toString(),
|
id: Date.now().toString(),
|
||||||
text,
|
text,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
author: actor,
|
commentAuthorId: normalizedCommentAuthorId,
|
||||||
replies: [],
|
replies: [],
|
||||||
}
|
}
|
||||||
set((state) => {
|
set((state) => {
|
||||||
|
|||||||
1177
supabase/tasks.json
Normal file
1177
supabase/tasks.json
Normal file
File diff suppressed because one or more lines are too long
22
supabase/update_task_comments_only.sql
Normal file
22
supabase/update_task_comments_only.sql
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user