Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/M11 | Rich Commenting #622

Open
wants to merge 73 commits into
base: tdb1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
99f1d2c
chore(tdb1): add .trigger to gitignore
rrojan Feb 4, 2025
cb3c181
chore(OUT-1363): add trigger.dev sdk & build packages
rrojan Feb 3, 2025
ff1628f
chore(OUT-1363): add and configure trigger.dev for project
rrojan Feb 3, 2025
1f58fc4
feat(OUT-1363): add health-check task to check that trigger workers a…
rrojan Feb 3, 2025
c96786b
refactor: format code
rrojan Feb 3, 2025
6df30d0
feat(OUT-1363): modify health-check endpoint to check db & trigger.de…
rrojan Feb 3, 2025
82ad74e
feat(OUT-1363): add clever delay to check for trigger / triggerAndWai…
rrojan Feb 3, 2025
6e38367
tryfix(OUT-1363): bump up node-version for @trigger.dev/sdk
rrojan Feb 3, 2025
9218bc7
chore(OUT-1363): add benchmark for db & trigger connections
rrojan Feb 4, 2025
234cb7b
chore: rename triggers/ folder to jobs/
rrojan Feb 4, 2025
8174a24
fix(OUT-1363): fix dependency version issues between trigger cloud & …
rrojan Feb 5, 2025
4f5d8e6
fix(OUT-1363): reduce long-running health-check task to 5 seconds
rrojan Feb 5, 2025
d6195b2
refactor(OUT-1363): format tsconfig w prettier
rrojan Feb 5, 2025
1dae782
chore(tdb1): add .trigger to gitignore
rrojan Feb 4, 2025
bc750f8
chore(OUT-1363): add trigger.dev sdk & build packages
rrojan Feb 3, 2025
2759ca1
chore(tdb1): add .trigger to gitignore
rrojan Feb 4, 2025
03d1640
feat(OUT-1366): add build script to auto-deploy trigger jobs to envir…
rrojan Feb 5, 2025
208c264
docs: add script logs
rrojan Feb 5, 2025
1d6df12
fix(OUT-1366): typo in environment
rrojan Feb 5, 2025
1c67ddd
docs(OUT-1366): add note about future release of Vercel trigger integ…
rrojan Feb 6, 2025
bff35d6
refactor(OUT-1366): remove scripts for trigger deployment from packag…
rrojan Feb 6, 2025
6de92c3
perf(OUT-1366): tweak build script to run in parallel
rrojan Feb 6, 2025
d538016
chore(OUT-1366): use yarn runner instead of npx
rrojan Feb 6, 2025
f67dd09
fix(OUT-1366): add missing chain
rrojan Feb 6, 2025
480c7f1
feat(OUT-1366): implement trigger redeploy only on main or feature br…
rrojan Feb 6, 2025
509a8ae
feat(OUT-1351): implement task create notifications job
rrojan Feb 6, 2025
fe9ad66
chore(OUT-1351): add script to deploy latest trigger jobs to cloud
rrojan Feb 6, 2025
acd0cf6
chore(OUT-1351): add basic trigger scripts
rrojan Feb 6, 2025
964bc83
fix(OUT-1351): fix prisma not working on trigger
rrojan Feb 6, 2025
06d4160
fix(OUT-1351): fix prisma usage with trigger.dev
rrojan Feb 6, 2025
649086a
chore(OUT-1351): bump machine to small-2x
rrojan Feb 6, 2025
9eef824
feat(OUT-1351): remove wait from health-check
rrojan Feb 6, 2025
90b6b05
feat(OUT-1352): implement task update notifications
rrojan Feb 7, 2025
5739282
docs(OUT-1352): update task create notification response message
rrojan Feb 7, 2025
173c342
feat(OUT-1352): barrel task update function to notifications jobs
rrojan Feb 7, 2025
e85bf47
feat(OUT-1352): use trigger function for tasks update
rrojan Feb 7, 2025
c49f296
fix(OUT-1352): typo in trigger task id
rrojan Feb 7, 2025
55e0c17
chore(OUT-1352): add logs on notification create / bulk create
rrojan Feb 7, 2025
53bf3df
feat(OUT-1376): implement notification deletion on task delete
rrojan Feb 7, 2025
8acbbc2
chore(OUT-1376): bump machine to medium-1x
rrojan Feb 7, 2025
c3a44ba
hotfix: disable automatic deploy for preview environment
rrojan Feb 7, 2025
86c6be4
hotfix: disable retries to prevent duplicate notifications
rrojan Feb 7, 2025
9ec065c
hotfix: add concurrency limit
rrojan Feb 9, 2025
6ad79d9
feat: add a keep-alive cron for workers that runs every minute
rrojan Feb 9, 2025
d6ab62f
hotfix: use branch from ref_name
rrojan Feb 9, 2025
6e3ebfb
feat(tdb1): dynamically set TRIGGER_SECRET_KEY for prod / staging
rrojan Feb 9, 2025
a2f2233
chore: deprecate deployment from build script
rrojan Feb 9, 2025
c2c7a9a
tryfix: debug prisma issue
rrojan Feb 10, 2025
af657e9
refactor: remove console.log
rrojan Feb 10, 2025
3347c5f
chore: schedule keep-alive to run every 5 mins
rrojan Feb 10, 2025
c20c510
fix: aggressive warming to fix trigger cold starts
rrojan Feb 10, 2025
8a2b5e5
refactor(tdb1): organize imports
rrojan Feb 10, 2025
5b3e9b1
refactor(tdb1): organize imports
rrojan Feb 10, 2025
a27d762
hotfix(tdb1): offload client update notifications to trigger
rrojan Feb 10, 2025
d7951ab
hotfix: fix job id for client task update
rrojan Feb 10, 2025
a5e2c6e
chore: fix node-version to 20.17
rrojan Feb 11, 2025
367f282
chore: bump checkout and node base actions
rrojan Feb 11, 2025
198e069
test: use pnpm instead of npm/yarn for package runner
rrojan Feb 11, 2025
d81c8c5
test: use pnpm instead of npm/yarn for package runner
rrojan Feb 11, 2025
de0f89f
tryfix: pin trigger cli version to 3.3.12
rrojan Feb 11, 2025
b1a911a
tryfix: pin trigger cli version to 3.3.13
rrojan Feb 11, 2025
762c38a
feat: pin trigger sdk & build versions to 3.3.13
rrojan Feb 11, 2025
d1ceb96
feat: install only necessary packages for workflow
rrojan Feb 11, 2025
2761f50
feat: install only necessary packages for workflow
rrojan Feb 11, 2025
6f58cef
fix: add TRIGGER_PROJECT to action env
rrojan Feb 11, 2025
e87758a
fix(OUT-1390): unique constraint error in internalUserNotifications
rrojan Feb 10, 2025
8aae1ee
fix(OUT-1390): cast taskId as uuid in query
rrojan Feb 10, 2025
cb580de
chore: add example env vars for trigger
rrojan Feb 11, 2025
cbf831b
refactor(OUT-1386): clean code
rrojan Feb 11, 2025
c378351
feat(OUT-1386): security fixes for comment update
rrojan Feb 11, 2025
cf0ca95
feat(OUT-1381) : enabled images and attachments in comment card and c…
arpandhakal Feb 10, 2025
5a9f406
feat(OUT-1400) : transferred comment create notification to a trigger…
arpandhakal Feb 13, 2025
a399ba8
fix(OUT-1400) : removed unnecessary package addition
arpandhakal Feb 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,9 @@ NGROK_AUTHTOKEN=

# --- Enable/disable logging prisma's generated queries in dev environments
PRISMA_SHOW_QUERIES=1

# --- Add trigger env vars
TRIGGER_PROJECT=
TRIGGER_SECRET_KEY=
# Only required if you are planning to deploy trigger jobs from external sources, like Github Action, build pipelines, etc
TRIGGER_ACCESS_TOKEN=
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18.18.2
node-version: 18.20.0
cache: yarn
cache-dependency-path: './yarn.lock'

Expand Down
14 changes: 9 additions & 5 deletions .github/workflows/trigger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Automatically checkout the same branch/tag from "Use workflow from" dropdown
ref: ${{ github.ref_name }}

- name: Setup Node.js
uses: actions/setup-node@v3
- name: Setup Node.JS v20
uses: actions/setup-node@v4
with:
node-version: 20
node-version: '20.x'

# Dynamically set TRIGGER_SECRET_KEY from the correct secrets based on whether 'prod' or 'staging' was chosen.
- name: Configure environment variables
Expand All @@ -38,6 +38,10 @@ jobs:

# TRIGGER_ACCESS_TOKEN (same for both)
echo "TRIGGER_ACCESS_TOKEN=${{ secrets.TRIGGER_ACCESS_TOKEN }}" >> $GITHUB_ENV
echo "TRIGGER_PROJECT=proj_gaxcpkutbhhxzjvnevvi" >> $GITHUB_ENV

- name: Install build dependencies
run: yarn add "@trigger.dev/[email protected]" "@trigger.dev/[email protected]"

- name: Run deploy command
run: npx trigger.dev@latest deploy -e ${{ github.event.inputs.environment }}
run: npx trigger.dev@3.3.13 deploy -e ${{ github.event.inputs.environment }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ next-env.d.ts

# Test routes
**/test-route-*

# Trigger
.trigger
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
"lint-staged": "npx lint-staged",
"postinstall": "prisma generate",
"test": "jest",
"trigger": "npx [email protected]",
"trigger:dev": "yarn trigger dev",
"trigger:deploy-staging": "yarn trigger deploy -e staging",
"loadtest": "tsx ./src/cmd/load-testing",
"loadtest:delete-clients": "tsx ./src/cmd/load-testing/deleteClients.ts",
"seed:activity-logs": "tsx ./src/cmd/fill-activity-logs",
Expand All @@ -42,6 +45,7 @@
"@reduxjs/toolkit": "^2.2.3",
"@sentry/nextjs": "^8",
"@supabase/supabase-js": "^2.47.5",
"@trigger.dev/sdk": "3.3.13",
"@types/date-fns": "^2.6.0",
"@vercel/blob": "^0.23.2",
"@vercel/postgres": "^0.8.0",
Expand Down Expand Up @@ -71,6 +75,7 @@
"@faker-js/faker": "^8.4.1",
"@ngrok/ngrok": "^1.4.1",
"@svgr/webpack": "^8.1.0",
"@trigger.dev/build": "3.3.13",
"@types/jest": "^29.5.12",
"@types/jsdom": "^21.1.7",
"@types/node": "^20",
Expand All @@ -93,5 +98,6 @@
"yarn lint:fix",
"yarn prettier:fix"
]
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
2 changes: 2 additions & 0 deletions prisma/schema/main.prisma
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["prismaSchemaFolder", "relationJoins"]
// debian-openssl-3.0.x is for trigger.dev's runtime
binaryTargets = ["native", "debian-openssl-3.0.x"]
}

datasource db {
Expand Down
42 changes: 42 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Script to build Tasks App on Vercel

echo "👷 Running build script for environment: $VERCEL_ENV"

# Build and deploy latest trigger jobs to trigger cloud
# DEPRECATED: Building from Github Actions now instead
# (
# echo "💼 [1/4] Build and deploy latest trigger jobs"
#
# if [ "$VERCEL_ENV" = "production" ]; then
# echo "🚀 Deploying trigger jobs for production environment..."
# npx trigger.dev@latest deploy
# elif [ "$VERCEL_ENV" = "preview" ]; then
# Check if the branch name contains 'feature' OR 'tdb'
# VERCEL_GIT_COMMIT_REF is the branch name in Vercel, e.g. "feature/some-branch"
# if [[ "$VERCEL_GIT_COMMIT_REF" =~ (feature/|tdb) ]]; then
# echo "🚀 Deploying trigger jobs for staging environment (branch is '$VERCEL_GIT_COMMIT_REF')..."
# npx trigger.dev@latest deploy -e staging
# else
# echo "🔒 Skip deploying trigger jobs for preview branch '$VERCEL_GIT_COMMIT_REF' as it isn't a feature branch (checking 'feature/' or 'tdb' prefix"
# fi
# else
# echo "🔒 Skip deploying trigger jobs for dev environment"
# fi
# ) &

(
echo "🔧 [1/3] Applying latest prisma migrations"
yarn prisma migrate deploy
) &

(
echo "🛠️ [2/3] Building Next app"
yarn next build
) &
wait

# Grant anon privileges so realtime channel can work using only Copilot token
echo "🏃 [3/3] Running grant-supabase-privileges"
yarn db:grant-supabase-privileges

echo "🥳 Deployment completed! 🎉🎉🎉"
5 changes: 2 additions & 3 deletions src/app/api/comment/comment.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ export const updateComment = async (req: NextRequest, { params: { id } }: IdPara
const data = UpdateCommentSchema.parse(await req.json())

const commentService = new CommentService(user)
const comment = await commentService.update(id, data)

const updatedComment = await commentService.update(id, data)

return NextResponse.json({ comment: updatedComment }, { status: httpStatus.OK })
return NextResponse.json({ comment })
}
58 changes: 12 additions & 46 deletions src/app/api/comment/comment.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { sendCommentCreateNotifications } from '@/jobs/notifications'
import { CreateComment, UpdateComment } from '@/types/dto/comment.dto'
import { CopilotAPI } from '@/utils/CopilotAPI'
import { CommentAddedSchema } from '@api/activity-logs/schemas/CommentAddedSchema'
Expand All @@ -15,45 +16,6 @@ import httpStatus from 'http-status'
import { z } from 'zod'

export class CommentService extends BaseService {
private async sendCommentCreatedNotifications(task: Task, comment: Comment) {
// If task is unassigned, there's nobody to send notifications to
if (!task.assigneeId || !task.assigneeType) return

const notificationService = new NotificationService(this.user)
await this.handleCommentNotifications(task, comment, notificationService)
}

private handleCommentNotifications = async (task: Task, comment: Comment, notificationService: NotificationService) => {
const copilot = new CopilotAPI(this.user.token)
const { recipientIds: clientRecipientIds } = await notificationService.getNotificationParties(
copilot,
task,
NotificationTaskActions.CommentToCU,
)

const filteredCUIds = clientRecipientIds.filter((id: string) => id !== comment.initiatorId)
console.info('creating notifications for CUS', filteredCUIds)
await notificationService.createBulkNotification(NotificationTaskActions.Commented, task, filteredCUIds, {
email: true,
disableInProduct: true,
commentId: comment.id,
})

const { recipientIds: iuRecipientIds } = await notificationService.getNotificationParties(
copilot,
task,
NotificationTaskActions.CommentToIU,
)

const filteredIUIds = iuRecipientIds.filter((id: string) => id !== comment.initiatorId)
console.info('creating notifications for IUs', filteredIUIds)
await notificationService.createBulkNotification(NotificationTaskActions.Commented, task, filteredIUIds, {
email: false,
disableInProduct: false,
commentId: comment.id,
})
}

async create(data: CreateComment) {
const policyGate = new PoliciesService(this.user)
policyGate.authorize(UserAction.Create, Resource.Comment)
Expand Down Expand Up @@ -88,9 +50,8 @@ export class CommentService extends BaseService {
}),
)

const sendCommentCreatedNotifications = this.sendCommentCreatedNotifications(task, comment)

await Promise.all([logActivity, sendCommentCreatedNotifications])
await sendCommentCreateNotifications.trigger({ user: this.user, task: task, comment: comment })
await logActivity
// if (data.mentions) {
// await notificationService.createBulkNotification(NotificationTaskActions.Mentioned, task, data.mentions, {
// commentId: comment.id,
Expand All @@ -115,14 +76,19 @@ export class CommentService extends BaseService {
const policyGate = new PoliciesService(this.user)
policyGate.authorize(UserAction.Update, Resource.Comment)

const filters = { id, workspaceId: this.user.workspaceId, initiatorId: this.user.internalUserId }
const prevComment = await this.db.comment.findFirst({
where: filters,
})
if (!prevComment) throw new APIError(httpStatus.NOT_FOUND, 'The comment to update was not found')

const comment = await this.db.comment.update({
where: { id },
data: {
...data,
},
where: filters,
data,
})
const tasksService = new TasksService(this.user)
await tasksService.setNewLastActivityLogUpdated(comment.taskId)
return comment
}

async getCommentsByIds(commentIds: string[]) {
Expand Down
30 changes: 29 additions & 1 deletion src/app/api/health-check/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
import DBClient from '@/lib/db'
import { healthCheck } from '@/jobs/health-check'
import { tasks } from '@trigger.dev/sdk/v3'
import { NextResponse } from 'next/server'

export async function GET() {
let dbConnection: boolean = false
let triggerConnection: boolean = false
let triggerRunId: string | undefined = undefined

// Check database connection
try {
console.time('db')
const client = DBClient.getInstance()
await client.$queryRaw`SELECT 1`
console.timeEnd('db')
dbConnection = true
} catch {}

// Check trigger.dev workers connection
try {
console.time('trigger')
const queueRunHandler = await tasks.trigger<typeof healthCheck>('health-check', {})
console.timeEnd('trigger')
triggerConnection = !!queueRunHandler?.id.startsWith('run')
triggerRunId = queueRunHandler.id
} catch {}

return NextResponse.json({
message: 'Copilot Tasks app API is rolling 🔥',
message: 'Copilot Tasks App API is rolling 🔥',
dbConnection,
triggerConnection,
triggerRunId,
})
}
9 changes: 8 additions & 1 deletion src/app/api/notification/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class NotificationService extends BaseService {
// If any of the given action is not present in details obj, that type of notification is not sent
deliveryTargets: { inProduct, email },
}
console.info('NotificationService#create | Creating single notification:', notificationDetails)

const notification = await copilot.createNotification(notificationDetails)
// NOTE: There are cases where task.assigneeType does not account for IU notification!
Expand Down Expand Up @@ -94,6 +95,8 @@ export class NotificationService extends BaseService {
recipientId,
deliveryTargets: { inProduct, email },
}
console.info('NotificationService#bulkCreate | Creating single notification:', notificationDetails)

notifications.push(await copilot.createNotification(notificationDetails))
} catch (err: unknown) {
console.error(`Failed to send notifications to ${recipientId}:`, err)
Expand Down Expand Up @@ -137,7 +140,11 @@ export class NotificationService extends BaseService {
}
console.info(`Deleting all notifications triggerd by task ${taskId}`)
await Promise.all(markAsReadPromises)
await this.db.internalUserNotification.deleteMany({ where: { taskId } })
// Hard delete this since we are not marking these as read, but deleting them
await this.db.$executeRaw`
DELETE FROM "InternalUserNotifications"
WHERE "taskId" = ${taskId}::uuid
`
}

/**
Expand Down
16 changes: 8 additions & 8 deletions src/app/api/tasks/tasks.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { MAX_FETCH_ASSIGNEE_COUNT } from '@/constants/users'
import { deleteTaskNotifications, sendTaskCreateNotifications, sendTaskUpdateNotifications } from '@/jobs/notifications'
import { sendClientUpdateTaskNotifications } from '@/jobs/notifications/send-client-task-update-notifications'
import { ClientResponse, CompanyResponse, InternalUsers } from '@/types/common'
import { CreateTaskRequest, UpdateTaskRequest } from '@/types/dto/tasks.dto'
import { CopilotAPI } from '@/utils/CopilotAPI'
Expand Down Expand Up @@ -179,8 +181,9 @@ export class TasksService extends BaseService {
}
}

const taskNotificationsSevice = new TaskNotificationsService(this.user)
await taskNotificationsSevice.sendTaskCreateNotifications(newTask)
// Send task created notifications to users
await sendTaskCreateNotifications.trigger({ user: this.user, task: newTask })

return newTask
}

Expand Down Expand Up @@ -269,8 +272,7 @@ export class TasksService extends BaseService {
await activityLogger.logTaskUpdated(prevTask)
}

const taskNotificationsSevice = new TaskNotificationsService(this.user)
await taskNotificationsSevice.sendTaskUpdateNotifications(prevTask, updatedTask)
await sendTaskUpdateNotifications.trigger({ prevTask, updatedTask, user: this.user })

return updatedTask
}
Expand All @@ -288,8 +290,7 @@ export class TasksService extends BaseService {

if (!task) throw new APIError(httpStatus.NOT_FOUND, 'The requested task to delete was not found')

const taskNotificationsSevice = new TaskNotificationsService(this.user)
await taskNotificationsSevice.removeDeletedTaskNotifications(task)
await deleteTaskNotifications.trigger({ user: this.user, task })

//delete the associated label
const labelMappingService = new LabelMappingService(this.user)
Expand Down Expand Up @@ -443,8 +444,7 @@ export class TasksService extends BaseService {
await activityLogger.logTaskUpdated(prevTask)
}

const taskNotificationsSevice = new TaskNotificationsService(this.user)
await taskNotificationsSevice.sendClientUpdateTaskNotifications(prevTask, updatedTask, updatedWorkflowState)
await sendClientUpdateTaskNotifications.trigger({ user: this.user, prevTask, updatedTask, updatedWorkflowState })
return updatedTask
}
}
1 change: 0 additions & 1 deletion src/components/AttachmentLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const AttachmentLayout: React.FC<AttachmentLayoutProps> = ({ selected, src, file
padding: { sm: '4px 8px', md: '4px 12px 4px 8px' },
border: (theme) => `1px solid ${theme.color.gray[selected ? 600 : 150]}`,
background: '#fff',
boxShadow: '0px 3px 4px 0px rgba(16, 17, 19, 0.06), 0px 4px 11px 0px rgba(20, 21, 24, 0.18)',

'@media (max-width: 600px)': {
'&:active': {
Expand Down
4 changes: 4 additions & 0 deletions src/components/cards/CommentCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { DotSeparator } from '@/app/detail/ui/DotSeparator'
import { BoldTypography, CommentCardContainer, StyledModal, StyledTypography } from '@/app/detail/ui/styledComponent'
import AttachmentLayout from '@/components/AttachmentLayout'
import { ListBtn } from '@/components/buttons/ListBtn'
import { PrimaryBtn } from '@/components/buttons/PrimaryBtn'
import { MenuBox } from '@/components/inputs/MenuBox'
Expand Down Expand Up @@ -127,6 +128,7 @@ export const CommentCard = ({
getContent={() => {}}
readonly
editorClass="tapwrite-comment"
attachmentLayout={AttachmentLayout}
/>

{Array.isArray((comment as LogResponse).details?.replies) &&
Expand All @@ -150,6 +152,7 @@ export const CommentCard = ({
getContent={() => {}}
readonly
editorClass="tapwrite-comment"
attachmentLayout={AttachmentLayout}
/>
</Stack>
)
Expand All @@ -168,6 +171,7 @@ export const CommentCard = ({
placeholder="Leave a reply..."
suggestions={assigneeSuggestions}
editorClass="tapwrite-reply-input"
attachmentLayout={AttachmentLayout}
/>
<InputAdornment
position="end"
Expand Down
Loading
Loading