1
1
import { Box , Flex , Stack , Text } from '@sanity/ui'
2
2
import { uuid } from '@sanity/uuid'
3
3
import { AnimatePresence , motion , type Variants } from 'framer-motion'
4
- import { Fragment , useCallback , useMemo } from 'react'
5
- import { type FormPatch , LoadingBlock , type PatchEvent , type Path , useCurrentUser } from 'sanity'
4
+ import { useCallback , useEffect , useMemo , useState } from 'react'
5
+ import {
6
+ type FormPatch ,
7
+ getPublishedId ,
8
+ LoadingBlock ,
9
+ type PatchEvent ,
10
+ type Path ,
11
+ type TransactionLogEventWithEffects ,
12
+ useClient ,
13
+ useCurrentUser ,
14
+ } from 'sanity'
6
15
import styled from 'styled-components'
7
16
17
+ import { getJsonStream } from '../../../../../core/store/_legacy/history/history/getJsonStream'
8
18
import {
9
19
type CommentBaseCreatePayload ,
10
20
type CommentCreatePayload ,
@@ -16,13 +26,77 @@ import {
16
26
type CommentUpdatePayload ,
17
27
useComments ,
18
28
} from '../../../../../structure/comments'
29
+ import { API_VERSION } from '../../constants/API_VERSION'
19
30
import { type TaskDocument } from '../../types'
31
+ import { CurrentWorkspaceProvider } from '../form/CurrentWorkspaceProvider'
32
+ import { type FieldChange , trackFieldChanges } from './helpers/parseTransactions'
20
33
import { EditedAt } from './TaskActivityEditedAt'
21
34
import { TasksActivityCommentInput } from './TasksActivityCommentInput'
22
35
import { TasksActivityCreatedAt } from './TasksActivityCreatedAt'
23
36
import { ActivityItem } from './TasksActivityItem'
24
37
import { TasksSubscribers } from './TasksSubscribers'
25
38
39
+ function useActivityLog ( task : TaskDocument ) {
40
+ const [ changes , setChanges ] = useState < FieldChange [ ] > ( [ ] )
41
+ const client = useClient ( { apiVersion : API_VERSION } )
42
+ const { dataset, token} = client . config ( )
43
+
44
+ const queryParams = `tag=sanity.studio.tasks.history&effectFormat=mendoza&excludeContent=true&includeIdentifiedDocumentsOnly=true&reverse=true`
45
+ const transactionsUrl = client . getUrl (
46
+ `/data/history/${ dataset } /transactions/${ getPublishedId ( task . _id ) } ?${ queryParams } ` ,
47
+ )
48
+
49
+ const fetchAndParse = useCallback (
50
+ async ( newestTaskDocument : TaskDocument ) => {
51
+ try {
52
+ const transactions : TransactionLogEventWithEffects [ ] = [ ]
53
+
54
+ const stream = await getJsonStream ( transactionsUrl , token )
55
+ const reader = stream . getReader ( )
56
+ let result
57
+ for ( ; ; ) {
58
+ result = await reader . read ( )
59
+ if ( result . done ) {
60
+ break
61
+ }
62
+ if ( 'error' in result . value ) {
63
+ throw new Error ( result . value . error . description || result . value . error . type )
64
+ }
65
+ transactions . push ( result . value )
66
+ }
67
+
68
+ const fieldsToTrack : ( keyof Omit < TaskDocument , '_rev' > ) [ ] = [
69
+ 'createdByUser' ,
70
+ 'title' ,
71
+ 'description' ,
72
+ 'dueBy' ,
73
+ 'assignedTo' ,
74
+ 'status' ,
75
+ 'target' ,
76
+ ]
77
+
78
+ const parsedChanges = await trackFieldChanges (
79
+ newestTaskDocument ,
80
+ [ ...transactions ] ,
81
+ fieldsToTrack ,
82
+ )
83
+
84
+ setChanges ( parsedChanges )
85
+ } catch ( error ) {
86
+ console . error ( 'Failed to fetch and parse activity log' , error )
87
+ }
88
+ } ,
89
+ [ transactionsUrl , token ] ,
90
+ )
91
+
92
+ useEffect ( ( ) => {
93
+ fetchAndParse ( task )
94
+ // Task is updated on every change, wait until the revision changes to update the activity log.
95
+ // eslint-disable-next-line react-hooks/exhaustive-deps
96
+ } , [ fetchAndParse , task . _rev ] )
97
+ return { changes}
98
+ }
99
+
26
100
const EMPTY_ARRAY : [ ] = [ ]
27
101
28
102
const VARIANTS : Variants = {
@@ -45,14 +119,6 @@ interface TasksActivityLogProps {
45
119
value : TaskDocument
46
120
}
47
121
48
- interface ActivityLogItem {
49
- author : string
50
- field : string
51
- from : string
52
- timestamp : string
53
- to ?: string
54
- }
55
-
56
122
type Activity =
57
123
| {
58
124
_type : 'comment'
@@ -61,7 +127,7 @@ type Activity =
61
127
}
62
128
| {
63
129
_type : 'activity'
64
- payload : ActivityLogItem
130
+ payload : FieldChange
65
131
timestamp : string
66
132
}
67
133
@@ -148,13 +214,12 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
148
214
[ operation ] ,
149
215
)
150
216
151
- // TODO: Get the task real activity.
152
- const activityData : ActivityLogItem [ ] = EMPTY_ARRAY
217
+ const activityData = useActivityLog ( value ) . changes
153
218
154
219
const activity : Activity [ ] = useMemo ( ( ) => {
155
220
const taskActivity : Activity [ ] = activityData . map ( ( item ) => ( {
156
221
_type : 'activity' as const ,
157
- payload : item as ActivityLogItem ,
222
+ payload : item ,
158
223
timestamp : item . timestamp ,
159
224
} ) )
160
225
const commentsActivity : Activity [ ] = taskComments . map ( ( comment ) => ( {
@@ -199,48 +264,45 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
199
264
) }
200
265
201
266
{ currentUser && (
202
- < Stack space = { 4 } marginTop = { 1 } >
203
- { taskComments . length > 0 && (
204
- < Fragment >
205
- { activity . map ( ( item ) => {
206
- if ( item . _type === 'activity' ) {
207
- return < EditedAt key = { item . timestamp } activity = { item . payload } />
208
- }
209
-
210
- return (
211
- < ActivityItem
267
+ < CurrentWorkspaceProvider >
268
+ < Stack space = { 4 } marginTop = { 1 } >
269
+ { activity . map ( ( item ) => {
270
+ if ( item . _type === 'activity' ) {
271
+ return < EditedAt key = { item . timestamp } activity = { item . payload } />
272
+ }
273
+ return (
274
+ < ActivityItem
275
+ key = { item . payload . parentComment . _id }
276
+ userId = { item . payload . parentComment . authorId }
277
+ >
278
+ < CommentsListItem
279
+ avatarConfig = { COMMENTS_LIST_ITEM_AVATAR_CONFIG }
280
+ canReply
281
+ currentUser = { currentUser }
282
+ innerPadding = { 1 }
283
+ isSelected = { false }
212
284
key = { item . payload . parentComment . _id }
213
- userId = { item . payload . parentComment . authorId }
214
- >
215
- < CommentsListItem
216
- avatarConfig = { COMMENTS_LIST_ITEM_AVATAR_CONFIG }
217
- canReply
218
- currentUser = { currentUser }
219
- innerPadding = { 1 }
220
- isSelected = { false }
221
- key = { item . payload . parentComment . _id }
222
- mentionOptions = { mentionOptions }
223
- mode = "default" // TODO: set dynamic mode?
224
- onCreateRetry = { handleCommentCreateRetry }
225
- onDelete = { handleCommentRemove }
226
- onEdit = { handleCommentEdit }
227
- onReactionSelect = { handleCommentReact }
228
- onReply = { handleCommentReply }
229
- parentComment = { item . payload . parentComment }
230
- replies = { item . payload . replies }
231
- />
232
- </ ActivityItem >
233
- )
234
- } ) }
235
- </ Fragment >
236
- ) }
237
-
238
- < TasksActivityCommentInput
239
- currentUser = { currentUser }
240
- mentionOptions = { mentionOptions }
241
- onSubmit = { handleCommentCreate }
242
- />
243
- </ Stack >
285
+ mentionOptions = { mentionOptions }
286
+ mode = "default" // TODO: set dynamic mode?
287
+ onCreateRetry = { handleCommentCreateRetry }
288
+ onDelete = { handleCommentRemove }
289
+ onEdit = { handleCommentEdit }
290
+ onReactionSelect = { handleCommentReact }
291
+ onReply = { handleCommentReply }
292
+ parentComment = { item . payload . parentComment }
293
+ replies = { item . payload . replies }
294
+ />
295
+ </ ActivityItem >
296
+ )
297
+ } ) }
298
+
299
+ < TasksActivityCommentInput
300
+ currentUser = { currentUser }
301
+ mentionOptions = { mentionOptions }
302
+ onSubmit = { handleCommentCreate }
303
+ />
304
+ </ Stack >
305
+ </ CurrentWorkspaceProvider >
244
306
) }
245
307
</ MotionStack >
246
308
) }
0 commit comments