Skip to content

Commit 8e63552

Browse files
authoredMar 14, 2024
feat(tasks): add notification data for tasks document and tasks comments (#5998)
* feat(tasks): add notification target to task document * feat(tasks): add notification context to tasks comments * fix(tasks): add comment id to the comments notification url * fix(tasks): add TasksNotificationTarget by the tasks schema * fix(tasks): remove onChange action from handleGetNotificationValue function * fix(tasks): update deep equal comparison
1 parent 96bc72b commit 8e63552

File tree

6 files changed

+174
-5
lines changed

6 files changed

+174
-5
lines changed
 

‎packages/sanity/src/structure/comments/src/hooks/use-comment-operations/createOperation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export async function createOperation(props: CreateOperationProps): Promise<void
7878
payload: {
7979
workspace,
8080
},
81-
notification: undefined, // TODO: add task notification data
81+
notification: comment.context.notification,
8282
tool: activeTool?.name || '',
8383
},
8484

‎packages/sanity/src/structure/comments/src/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export interface CommentContext {
101101
url: string
102102
workspaceTitle: string
103103
currentThreadLength?: number
104+
// Used in task comments, list of users that are subscribed to the task.
105+
subscribers?: string[]
104106
}
105107
intent?: {
106108
title: string
@@ -260,6 +262,9 @@ export interface CommentBaseCreatePayload {
260262
export interface CommentTaskCreatePayload extends CommentBaseCreatePayload {
261263
// ...
262264
type: 'task'
265+
context: {
266+
notification: CommentContext['notification']
267+
}
263268
}
264269

265270
/**

‎packages/sanity/src/tasks/src/tasks/components/activity/TasksActivityLog.tsx

+54-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
LoadingBlock,
99
type PatchEvent,
1010
type Path,
11+
set,
1112
type TransactionLogEventWithEffects,
1213
useClient,
1314
useCurrentUser,
15+
useWorkspace,
1416
} from 'sanity'
1517
import styled from 'styled-components'
1618

@@ -29,6 +31,7 @@ import {
2931
import {API_VERSION} from '../../constants/API_VERSION'
3032
import {type TaskDocument} from '../../types'
3133
import {CurrentWorkspaceProvider} from '../form/CurrentWorkspaceProvider'
34+
import {getMentionedUsers} from '../form/utils'
3235
import {type FieldChange, trackFieldChanges} from './helpers/parseTransactions'
3336
import {EditedAt} from './TaskActivityEditedAt'
3437
import {TasksActivityCommentInput} from './TasksActivityCommentInput'
@@ -134,39 +137,80 @@ type Activity =
134137
export function TasksActivityLog(props: TasksActivityLogProps) {
135138
const {value, onChange, path} = props
136139
const currentUser = useCurrentUser()
140+
const {title: workspaceTitle, basePath} = useWorkspace()
137141

138142
const {comments, mentionOptions, operation, getComment} = useComments()
139143
const loading = comments.loading
140144
const taskComments = comments.data.open
141145

146+
const handleGetNotificationValue = useCallback(
147+
(message: CommentInputProps['value'], commentId: string) => {
148+
const studioUrl = new URL(`${window.location.origin}${basePath}/`)
149+
studioUrl.searchParams.set('sidebar', 'tasks')
150+
studioUrl.searchParams.set('selectedTask', value?._id)
151+
studioUrl.searchParams.set('viewMode', 'edit')
152+
studioUrl.searchParams.set('commentId', commentId)
153+
154+
const mentionedUsers = getMentionedUsers(message)
155+
const subscribers = Array.from(new Set([...(value.subscribers || []), ...mentionedUsers]))
156+
157+
return {
158+
documentTitle: value.title || 'Sanity task',
159+
url: studioUrl.toString(),
160+
workspaceTitle: workspaceTitle,
161+
subscribers: subscribers,
162+
}
163+
},
164+
[basePath, value?._id, value.title, workspaceTitle, value.subscribers],
165+
)
166+
142167
const handleCommentCreate = useCallback(
143168
(message: CommentInputProps['value']) => {
169+
const commentId = uuid()
170+
const notification = handleGetNotificationValue(message, commentId)
171+
144172
const nextComment: CommentCreatePayload = {
173+
id: commentId,
145174
type: 'task',
146175
message,
147176
parentCommentId: undefined,
148177
reactions: EMPTY_ARRAY,
149178
status: 'open',
150179
threadId: uuid(),
180+
context: {
181+
notification,
182+
},
151183
}
152184

185+
onChange(set(notification.subscribers, ['subscribers']))
186+
153187
operation.create(nextComment)
154188
},
155-
[operation],
189+
[operation, handleGetNotificationValue, onChange],
156190
)
157191

158192
const handleCommentReply = useCallback(
159193
(nextComment: CommentBaseCreatePayload) => {
194+
const commentId = uuid()
195+
196+
const notification = handleGetNotificationValue(nextComment.message, commentId)
197+
198+
onChange(set(notification.subscribers, ['subscribers']))
199+
160200
operation.create({
201+
id: commentId,
161202
type: 'task',
162203
message: nextComment.message,
163204
parentCommentId: nextComment.parentCommentId,
164205
reactions: EMPTY_ARRAY,
165206
status: 'open',
166207
threadId: nextComment.threadId,
208+
context: {
209+
notification,
210+
},
167211
})
168212
},
169-
[operation],
213+
[operation, handleGetNotificationValue, onChange],
170214
)
171215

172216
const handleCommentCreateRetry = useCallback(
@@ -176,6 +220,10 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
176220
const comment = getComment(id)
177221
if (!comment) return
178222

223+
const notification = handleGetNotificationValue(comment.message, comment._id)
224+
225+
onChange(set(notification.subscribers, ['subscribers']))
226+
179227
operation.create({
180228
type: 'task',
181229
id: comment._id,
@@ -184,9 +232,12 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
184232
reactions: comment.reactions || EMPTY_ARRAY,
185233
status: comment.status,
186234
threadId: comment.threadId,
235+
context: {
236+
notification,
237+
},
187238
})
188239
},
189-
[getComment, operation],
240+
[getComment, operation, handleGetNotificationValue, onChange],
190241
)
191242

192243
const handleCommentReact = useCallback(

‎packages/sanity/src/tasks/src/tasks/components/form/addonWorkspace/taskSchema.tsx

+32
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from '../fields'
1212
import {FormCreate} from '../tasksFormBuilder/FormCreate'
1313
import {FormEdit} from '../tasksFormBuilder/FormEdit'
14+
import {TasksNotificationTarget} from '../tasksFormBuilder/TasksNotificationTarget'
1415

1516
const targetContentField = (mode: FormMode) =>
1617
defineField({
@@ -144,5 +145,36 @@ export const taskSchema = (mode: FormMode) =>
144145
},
145146
hidden: true,
146147
},
148+
{
149+
type: 'object',
150+
name: 'context',
151+
components: {
152+
field: TasksNotificationTarget,
153+
},
154+
fields: [
155+
{
156+
type: 'object',
157+
name: 'notification',
158+
fields: [
159+
{
160+
type: 'string',
161+
name: 'url',
162+
},
163+
{
164+
type: 'string',
165+
name: 'workspaceTitle',
166+
},
167+
{
168+
type: 'string',
169+
name: 'targetContentImageUrl',
170+
},
171+
{
172+
type: 'string',
173+
name: 'targetContentTitle',
174+
},
175+
],
176+
},
177+
],
178+
},
147179
],
148180
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import {isImageSource} from '@sanity/asset-utils'
2+
import imageUrlBuilder from '@sanity/image-url'
3+
import {useEffect, useMemo} from 'react'
4+
import deepEquals from 'react-fast-compare'
5+
import {
6+
DEFAULT_STUDIO_CLIENT_OPTIONS,
7+
type ObjectFieldProps,
8+
set,
9+
useClient,
10+
useFormValue,
11+
useWorkspace,
12+
} from 'sanity'
13+
14+
import {useDocumentPreviewValues} from '../../../hooks/useDocumentPreviewValues'
15+
import {type TaskContext, type TaskDocument} from '../../../types'
16+
import {CurrentWorkspaceProvider} from '../CurrentWorkspaceProvider'
17+
18+
function TasksNotificationTargetInner(props: ObjectFieldProps<TaskDocument>) {
19+
const {inputProps} = props
20+
const {onChange} = inputProps
21+
const {target, _id, context} = useFormValue([]) as TaskDocument
22+
const {title: workspaceTitle, basePath} = useWorkspace()
23+
const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)
24+
const imageBuilder = useMemo(() => imageUrlBuilder(client), [client])
25+
const documentId = target?.document?._ref ?? ''
26+
const documentType = target?.documentType ?? ''
27+
28+
const {isLoading: previewValuesLoading, value} = useDocumentPreviewValues({
29+
documentId,
30+
documentType,
31+
})
32+
const targetContentTitle = value?.title || null
33+
const imageUrl = isImageSource(value?.media)
34+
? imageBuilder.image(value.media).width(96).height(96).url()
35+
: null
36+
37+
useEffect(() => {
38+
if (documentId && documentType && previewValuesLoading) {
39+
// Wait until the preview values are loaded
40+
return
41+
}
42+
43+
const studioUrl = new URL(`${window.location.origin}${basePath}/`)
44+
studioUrl.searchParams.set('sidebar', 'tasks')
45+
studioUrl.searchParams.set('selectedTask', _id)
46+
studioUrl.searchParams.set('viewMode', 'edit')
47+
48+
const notificationTarget: TaskContext['notification'] = {
49+
url: studioUrl.toString(),
50+
workspaceTitle,
51+
targetContentImageUrl: imageUrl,
52+
targetContentTitle: targetContentTitle,
53+
}
54+
if (deepEquals(notificationTarget, context?.notification)) return
55+
56+
onChange(set(notificationTarget, ['notification']))
57+
}, [
58+
_id,
59+
basePath,
60+
workspaceTitle,
61+
documentId,
62+
documentType,
63+
previewValuesLoading,
64+
targetContentTitle,
65+
imageUrl,
66+
onChange,
67+
context,
68+
])
69+
70+
return null
71+
}
72+
73+
// This component is listening to the changes to the form value and will update the notification target in the task document.
74+
export function TasksNotificationTarget(props: ObjectFieldProps<TaskDocument>) {
75+
return (
76+
<CurrentWorkspaceProvider>
77+
<TasksNotificationTargetInner {...props} />
78+
</CurrentWorkspaceProvider>
79+
)
80+
}

‎packages/sanity/src/tasks/src/tasks/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ export interface TaskContext {
3030
tool?: string
3131
payload?: Record<string, unknown>
3232
notification?: {
33-
documentTitle: string
3433
url: string
3534
workspaceTitle: string
35+
targetContentTitle: string | null
36+
targetContentImageUrl: string | null
3637
}
3738
}
3839

0 commit comments

Comments
 (0)