Skip to content

Commit

Permalink
feat(tasks): update tasks activity log to display values
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrobonamin committed Mar 12, 2024
1 parent 2c6d9a9 commit 0f3fdac
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 187 deletions.
Original file line number Diff line number Diff line change
@@ -1,44 +1,19 @@
import {DotIcon} from '@sanity/icons'
import {Box, Flex, Text} from '@sanity/ui'
import {memo} from 'react'

import {Tooltip} from '../../../../../ui-components'
import {getStringForKey, UpdatedTimeAgo} from './helpers'
import {getChangeDetails, UpdatedTimeAgo, UserName} from './helpers'
import {type FieldChange} from './helpers/parseTransactions'

interface EditedAtProps {
activity: {
author: string
field: string
from?: string | null
to?: string | null
timestamp: string
}
activity: FieldChange
}

export const EditedAt = memo(
function EditedAt(props: EditedAtProps) {
const {activity} = props
let key: string = activity.field
let showToValue: boolean = key === 'dueDate' || key === 'status' || key === 'targetContent'

//If the status is changed to be done
if (activity.field === 'status' && activity.to === 'done') {
key = 'statusDone'
showToValue = true
}
//If a task is unassigned - it goes from having a assignee to be unassigned
if (activity.field === 'assignedTo' && !!activity.to && activity.from) {
key = 'unassigned'
}

//Set the due date for the first time
if (activity.field === 'dueDate' && (activity.from === null || undefined) && activity.to) {
key = 'dueDateSet'
showToValue = true
}

const {formattedDate, timeAgo} = UpdatedTimeAgo(activity.timestamp)
const {icon, string} = getStringForKey(key) || {icon: null, string: ''}
const {icon, text, changeTo} = getChangeDetails(activity)

return (
<Flex gap={1}>
Expand All @@ -48,9 +23,7 @@ export const EditedAt = memo(
</Box>
</Box>
<Text muted size={1}>
<strong style={{fontWeight: 600}}>{activity.author} </strong>
{string} {showToValue && <strong style={{fontWeight: 600}}>{activity.to}</strong>}{' '}
<DotIcon />{' '}
<UserName userId={activity.author} /> {text} {changeTo}{' '}
<Tooltip content={formattedDate} placement="top-end">
<time dateTime={formattedDate}>{timeAgo}</time>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {DotIcon} from '@sanity/icons'
import {Flex, Text, TextSkeleton} from '@sanity/ui'
import {memo} from 'react'
import {useUser} from 'sanity'
Expand Down Expand Up @@ -31,7 +30,7 @@ export const TasksActivityCreatedAt = memo(
<strong style={{fontWeight: 600}}>
{loading ? <UserSkeleton /> : user?.displayName ?? 'Unknown user'}{' '}
</strong>
created this task <DotIcon />{' '}
created this task {' '}
<Tooltip content={formattedDate} placement="top-end">
<time dateTime={createdAt}>{timeAgo}</time>
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import {Box, Flex, Stack, Text} from '@sanity/ui'
import {uuid} from '@sanity/uuid'
import {AnimatePresence, motion, type Variants} from 'framer-motion'
import {Fragment, useCallback, useEffect, useMemo, useState} from 'react'
import {useCallback, useEffect, useMemo, useState} from 'react'
import {
type FormPatch,
LoadingBlock,
type PatchEvent,
type Path,
useCurrentUser,
type TransactionLogEventWithEffects,
useClient,
TransactionLogEventWithEffects,
useCurrentUser,
} from 'sanity'
import styled from 'styled-components'

import {getJsonStream} from '../../../../../core/store/_legacy/history/history/getJsonStream'
import {
type CommentBaseCreatePayload,
type CommentCreatePayload,
Expand All @@ -25,27 +26,29 @@ import {
useComments,
} from '../../../../../structure/comments'
import {type TaskDocument} from '../../types'
import {CurrentWorkspaceProvider} from '../form/CurrentWorkspaceProvider'
import {type FieldChange, trackFieldChanges} from './helpers/parseTransactions'
import {EditedAt} from './TaskActivityEditedAt'
import {TasksActivityCommentInput} from './TasksActivityCommentInput'
import {TasksActivityCreatedAt} from './TasksActivityCreatedAt'
import {ActivityItem} from './TasksActivityItem'
import {TasksSubscribers} from './TasksSubscribers'
import {getJsonStream} from '../../../../../core/store/_legacy/history/history/getJsonStream'
import {FieldChange, trackFieldChanges} from './helpers/parseTransactions'

function useActivityLog(task: TaskDocument) {
const [changes, setChanges] = useState<FieldChange[]>([])
const client = useClient()
const clientConfig = client.config()
let queryParams = `tag=sanity.studio.tasks.history&effectFormat=mendoza&excludeContent=true&includeIdentifiedDocumentsOnly=true&reverse=true`
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/${task._id}?${queryParams}`,
)

const fetchAndParse = useCallback(
async (newestTaskDocument: TaskDocument) => {
const transactions: TransactionLogEventWithEffects[] = []
const transactionsUrl = client.getUrl(
`/data/history/${clientConfig.dataset}/transactions/${newestTaskDocument._id}?${queryParams}`,
)
const stream = await getJsonStream(transactionsUrl, clientConfig.token)

const stream = await getJsonStream(transactionsUrl, token)
const reader = stream.getReader()
let result
for (;;) {
Expand All @@ -60,9 +63,13 @@ function useActivityLog(task: TaskDocument) {
}

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

const parsedChanges = await trackFieldChanges(
Expand All @@ -73,14 +80,14 @@ function useActivityLog(task: TaskDocument) {

setChanges(parsedChanges)
},
[client, clientConfig, queryParams],
[transactionsUrl, token],
)

// TODO: Probably don't want this to fire every time the task updates
useEffect(() => {
fetchAndParse(task)
}, [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}
}

Expand All @@ -106,14 +113,6 @@ interface TasksActivityLogProps {
value: TaskDocument
}

interface ActivityLogItem {
author: string
field: string
from: string
timestamp: string
to?: string
}

type Activity =
| {
_type: 'comment'
Expand All @@ -122,7 +121,7 @@ type Activity =
}
| {
_type: 'activity'
payload: ActivityLogItem
payload: FieldChange
timestamp: string
}

Expand Down Expand Up @@ -209,13 +208,12 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
[operation],
)

// TODO: Get the task real activity.
const activityData: ActivityLogItem[] = useActivityLog(value)
const activityData = useActivityLog(value).changes

const activity: Activity[] = useMemo(() => {
const taskActivity: Activity[] = activityData.map((item) => ({
_type: 'activity' as const,
payload: item as ActivityLogItem,
payload: item,
timestamp: item.timestamp,
}))
const commentsActivity: Activity[] = taskComments.map((comment) => ({
Expand Down Expand Up @@ -260,48 +258,45 @@ export function TasksActivityLog(props: TasksActivityLogProps) {
)}

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

return (
<ActivityItem
<CurrentWorkspaceProvider>
<Stack space={4} marginTop={1}>
{activity.map((item) => {
if (item._type === 'activity') {
return <EditedAt key={item.timestamp} activity={item.payload} />
}
return (
<ActivityItem
key={item.payload.parentComment._id}
userId={item.payload.parentComment.authorId}
>
<CommentsListItem
avatarConfig={COMMENTS_LIST_ITEM_AVATAR_CONFIG}
canReply
currentUser={currentUser}
innerPadding={1}
isSelected={false}
key={item.payload.parentComment._id}
userId={item.payload.parentComment.authorId}
>
<CommentsListItem
avatarConfig={COMMENTS_LIST_ITEM_AVATAR_CONFIG}
canReply
currentUser={currentUser}
innerPadding={1}
isSelected={false}
key={item.payload.parentComment._id}
mentionOptions={mentionOptions}
mode="default" // TODO: set dynamic mode?
onCreateRetry={handleCommentCreateRetry}
onDelete={handleCommentRemove}
onEdit={handleCommentEdit}
onReactionSelect={handleCommentReact}
onReply={handleCommentReply}
parentComment={item.payload.parentComment}
replies={item.payload.replies}
/>
</ActivityItem>
)
})}
</Fragment>
)}

<TasksActivityCommentInput
currentUser={currentUser}
mentionOptions={mentionOptions}
onSubmit={handleCommentCreate}
/>
</Stack>
mentionOptions={mentionOptions}
mode="default" // TODO: set dynamic mode?
onCreateRetry={handleCommentCreateRetry}
onDelete={handleCommentRemove}
onEdit={handleCommentEdit}
onReactionSelect={handleCommentReact}
onReply={handleCommentReply}
parentComment={item.payload.parentComment}
replies={item.payload.replies}
/>
</ActivityItem>
)
})}

<TasksActivityCommentInput
currentUser={currentUser}
mentionOptions={mentionOptions}
onSubmit={handleCommentCreate}
/>
</Stack>
</CurrentWorkspaceProvider>
)}
</MotionStack>
)}
Expand Down
Loading

0 comments on commit 0f3fdac

Please sign in to comment.