Skip to content

Commit

Permalink
fix(tasks): update info passed to the tasks comments notification con…
Browse files Browse the repository at this point in the history
…text (#6087)
  • Loading branch information
pedrobonamin authored and hermanwikner committed Mar 21, 2024
1 parent a8f12ba commit 9f01651
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import {Box, Flex, Stack, Text} from '@sanity/ui'
import {uuid} from '@sanity/uuid'
import {AnimatePresence, motion, type Variants} from 'framer-motion'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {useCallback, useMemo, useState} from 'react'
import {
type FormPatch,
getPublishedId,
LoadingBlock,
type PatchEvent,
type Path,
set,
type TransactionLogEventWithEffects,
useClient,
useCurrentUser,
useTranslation,
useWorkspace,
} from 'sanity'
import styled from 'styled-components'

import {getJsonStream} from '../../../../../core/store/_legacy/history/history/getJsonStream'
import {
type CommentBaseCreatePayload,
type CommentCreatePayload,
Expand All @@ -29,78 +25,15 @@ import {
useComments,
} from '../../../../../structure/comments'
import {tasksLocaleNamespace} from '../../../../i18n'
import {API_VERSION} from '../../constants/API_VERSION'
import {type TaskDocument} from '../../types'
import {CurrentWorkspaceProvider} from '../form/CurrentWorkspaceProvider'
import {getMentionedUsers} from '../form/utils'
import {type FieldChange, trackFieldChanges} from './helpers/parseTransactions'
import {type FieldChange} from './helpers/parseTransactions'
import {EditedAt} from './TaskActivityEditedAt'
import {TasksActivityCommentInput} from './TasksActivityCommentInput'
import {TasksActivityCommentItem} from './TasksActivityCommentItem'
import {TasksActivityCreatedAt} from './TasksActivityCreatedAt'
import {TasksSubscribers} from './TasksSubscribers'

function useActivityLog(task: TaskDocument) {
const [changes, setChanges] = useState<FieldChange[]>([])
const client = useClient({apiVersion: API_VERSION})
const {dataset, token} = client.config()

const queryParams = `tag=sanity.studio.tasks.history&effectFormat=mendoza&excludeContent=true&includeIdentifiedDocumentsOnly=true&reverse=true`
const transactionsUrl = client.getUrl(
`/data/history/${dataset}/transactions/${getPublishedId(task._id)}?${queryParams}`,
)

const fetchAndParse = useCallback(
async (newestTaskDocument: TaskDocument) => {
try {
const transactions: TransactionLogEventWithEffects[] = []

const stream = await getJsonStream(transactionsUrl, token)
const reader = stream.getReader()
let result
for (;;) {
result = await reader.read()
if (result.done) {
break
}
if ('error' in result.value) {
throw new Error(result.value.error.description || result.value.error.type)
}
transactions.push(result.value)
}

const fieldsToTrack: (keyof Omit<TaskDocument, '_rev'>)[] = [
'createdByUser',
'title',
'description',
'dueBy',
'assignedTo',
'status',
'target',
]

const parsedChanges = await trackFieldChanges(
newestTaskDocument,
[...transactions],
fieldsToTrack,
)

setChanges(parsedChanges)
} catch (error) {
console.error('Failed to fetch and parse activity log', error)
}
},
[transactionsUrl, token],
)

useEffect(() => {
fetchAndParse(task)
// Task is updated on every change, wait until the revision changes to update the activity log.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchAndParse, task._rev])
return {changes}
}

const EMPTY_ARRAY: [] = []

const VARIANTS: Variants = {
Expand All @@ -114,6 +47,7 @@ interface TasksActivityLogProps {
onChange: (patch: FormPatch | PatchEvent | FormPatch[]) => void
path?: Path
value: TaskDocument
activityData: FieldChange[]
}

type Activity =
Expand All @@ -129,21 +63,21 @@ type Activity =
}

export function TasksActivityLog(props: TasksActivityLogProps) {
const {value, onChange, path} = props
const {value, onChange, path, activityData = []} = props
const currentUser = useCurrentUser()
const {title: workspaceTitle, basePath} = useWorkspace()

const {title: workspaceTitle, basePath} = useWorkspace()
const {comments, mentionOptions, operation, getComment} = useComments()
const [commentToDeleteId, setCommentToDeleteId] = useState<string | null>(null)
const [commentDeleteError, setCommentDeleteError] = useState<Error | null>(null)
const [commentDeleteLoading, setCommentDeleteLoading] = useState(false)

const loading = comments.loading
const taskComments = comments.data.open

const handleGetNotificationValue = useCallback(
(message: CommentInputProps['value'], commentId: string) => {
const studioUrl = new URL(`${window.location.origin}${basePath}/`)
const studioUrl = new URL(`${window.location.origin}${basePath ? `${basePath}/` : ''}`)

studioUrl.searchParams.set('sidebar', 'tasks')
studioUrl.searchParams.set('selectedTask', value?._id)
studioUrl.searchParams.set('viewMode', 'edit')
Expand Down Expand Up @@ -271,8 +205,6 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
[operation],
)

const activityData = useActivityLog(value).changes

const activity: Activity[] = useMemo(() => {
const taskActivity: Activity[] = activityData.map((item) => ({
_type: 'activity' as const,
Expand Down Expand Up @@ -344,36 +276,34 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
)}

{currentUser && (
<CurrentWorkspaceProvider>
<Stack space={4} marginTop={1}>
{activity.map((item) => {
if (item._type === 'activity') {
return <EditedAt key={item.timestamp} activity={item.payload} />
}

return (
<TasksActivityCommentItem
currentUser={currentUser}
key={item.payload.parentComment._id}
mentionOptions={mentionOptions}
onCreateRetry={handleCommentCreateRetry}
onDelete={handleDeleteCommentStart}
onEdit={handleCommentEdit}
onReactionSelect={handleCommentReact}
onReply={handleCommentReply}
parentComment={item.payload.parentComment}
replies={item.payload.replies}
/>
)
})}

<TasksActivityCommentInput
currentUser={currentUser}
mentionOptions={mentionOptions}
onSubmit={handleCommentCreate}
/>
</Stack>
</CurrentWorkspaceProvider>
<Stack space={4} marginTop={1}>
{activity.map((item) => {
if (item._type === 'activity') {
return <EditedAt key={item.timestamp} activity={item.payload} />
}

return (
<TasksActivityCommentItem
currentUser={currentUser}
key={item.payload.parentComment._id}
mentionOptions={mentionOptions}
onCreateRetry={handleCommentCreateRetry}
onDelete={handleDeleteCommentStart}
onEdit={handleCommentEdit}
onReactionSelect={handleCommentReact}
onReply={handleCommentReply}
parentComment={item.payload.parentComment}
replies={item.payload.replies}
/>
)
})}

<TasksActivityCommentInput
currentUser={currentUser}
mentionOptions={mentionOptions}
onSubmit={handleCommentCreate}
/>
</Stack>
)}
</MotionStack>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import {CommentsProvider} from '../../../../../../structure/comments'
import {MenuButton, MenuItem} from '../../../../../../ui-components'
import {tasksLocaleNamespace} from '../../../../../i18n'
import {useTasksNavigation} from '../../../context'
import {useActivityLog} from '../../../hooks/useActivityLog'
import {useRemoveTask} from '../../../hooks/useRemoveTask'
import {type TaskDocument} from '../../../types'
import {TasksActivityLog} from '../../activity'
import {CurrentWorkspaceProvider} from '../CurrentWorkspaceProvider'
import {AssigneeEditFormField} from '../fields/assignee'
import {DateEditFormField} from '../fields/DateEditFormField'
import {StatusSelector} from '../fields/StatusSelector'
Expand Down Expand Up @@ -95,6 +97,7 @@ function FormEditInner(props: ObjectInputProps) {
const value = props.value as TaskDocument
const currentUser = useCurrentUser()
const {t} = useTranslation(tasksLocaleNamespace)
const activityData = useActivityLog(value).changes
const handleChangeAndSubscribe = useCallback(
(patch: FormPatch | PatchEvent | FormPatch[]) => {
const subscribers = value.subscribers || []
Expand Down Expand Up @@ -155,16 +158,22 @@ function FormEditInner(props: ObjectInputProps) {
</Card>

{props.renderDefault(props)}

<CommentsProvider
documentId={value._id}
documentType="tasks.task"
sortOrder="asc"
type="task"
>
<Card borderTop paddingTop={4} marginTop={4} paddingBottom={6}>
<TasksActivityLog value={value} onChange={props.onChange} path={['subscribers']} />
</Card>
<CurrentWorkspaceProvider>
<Card borderTop paddingTop={4} marginTop={4} paddingBottom={6}>
<TasksActivityLog
value={value}
onChange={props.onChange}
path={['subscribers']}
activityData={activityData}
/>
</Card>
</CurrentWorkspaceProvider>
</CommentsProvider>
</>
)
Expand Down
70 changes: 70 additions & 0 deletions packages/sanity/src/tasks/src/tasks/hooks/useActivityLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {useCallback, useEffect, useState} from 'react'
import {getPublishedId, type TransactionLogEventWithEffects, useClient} from 'sanity'

import {getJsonStream} from '../../../../core/store/_legacy/history/history/getJsonStream'
import {type FieldChange, trackFieldChanges} from '../components/activity/helpers/parseTransactions'
import {API_VERSION} from '../constants/API_VERSION'
import {type TaskDocument} from '../types'

export function useActivityLog(task: TaskDocument): {
changes: FieldChange[]
} {
const [changes, setChanges] = useState<FieldChange[]>([])
const client = useClient({apiVersion: API_VERSION})
const {dataset, token} = client.config()

const queryParams = `tag=sanity.studio.tasks.history&effectFormat=mendoza&excludeContent=true&includeIdentifiedDocumentsOnly=true&reverse=true`
const transactionsUrl = client.getUrl(
`/data/history/${dataset}/transactions/${getPublishedId(task._id)}?${queryParams}`,
)

const fetchAndParse = useCallback(
async (newestTaskDocument: TaskDocument) => {
try {
const transactions: TransactionLogEventWithEffects[] = []

const stream = await getJsonStream(transactionsUrl, token)
const reader = stream.getReader()
let result
for (;;) {
result = await reader.read()
if (result.done) {
break
}
if ('error' in result.value) {
throw new Error(result.value.error.description || result.value.error.type)
}
transactions.push(result.value)
}

const fieldsToTrack: (keyof Omit<TaskDocument, '_rev'>)[] = [
'createdByUser',
'title',
'description',
'dueBy',
'assignedTo',
'status',
'target',
]

const parsedChanges = await trackFieldChanges(
newestTaskDocument,
[...transactions],
fieldsToTrack,
)

setChanges(parsedChanges)
} catch (error) {
console.error('Failed to fetch and parse activity log', error)
}
},
[transactionsUrl, token],
)

useEffect(() => {
fetchAndParse(task)
// Task is updated on every change, wait until the revision changes to update the activity log.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetchAndParse, task._rev])
return {changes}
}

0 comments on commit 9f01651

Please sign in to comment.