Skip to content

Commit 177bc79

Browse files
ninaandalpedrobonamin
andauthoredMar 12, 2024
fix(tasks): show pending tasks in document footer (#5894)
* fix(tasks): show pending tasks in document footer * fix(tasks): add button to open tasks * fix(tasks): remove comment * fix(tasks): move out of document * fix(tasks): add conditional to tasks footer * fix(tasks): fix ts error * fix(tasks): update pr with comments * fix(tasks): keep task sidebar open if clicked on badge * fix(tasks): pluralize task text * fix(tasks): change React.ReactNode for ReactNode and remove data-as from button --------- Co-authored-by: Pedro Bonamin <pedrobonamin@gmail.com>
1 parent 9254565 commit 177bc79

File tree

9 files changed

+105
-37
lines changed

9 files changed

+105
-37
lines changed
 

‎packages/sanity/src/core/config/configPropertyReducers.ts

+26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {type AssetSource, type SchemaTypeDefinition} from '@sanity/types'
2+
import {type ReactNode} from 'react'
23

34
import {type LocaleConfigContext, type LocaleDefinition, type LocaleResourceBundle} from '../i18n'
45
import {type Template, type TemplateItem} from '../templates'
@@ -308,6 +309,31 @@ export const documentCommentsEnabledReducer = (opts: {
308309
return result
309310
}
310311

312+
export const internalTasksReducer = (opts: {
313+
config: PluginOptions
314+
}): {footerAction: ReactNode} | undefined => {
315+
const {config} = opts
316+
const flattenedConfig = flattenConfig(config, [])
317+
318+
const result = flattenedConfig.reduce(
319+
(acc: {footerAction: ReactNode} | undefined, {config: innerConfig}) => {
320+
const resolver = innerConfig.__internal_tasks
321+
322+
if (!resolver) return acc
323+
if (typeof resolver === 'object' && resolver.footerAction) return resolver
324+
325+
throw new Error(
326+
`Expected \`__internal__tasks\` to be an object with footerAction, but received ${getPrintableType(
327+
resolver,
328+
)}`,
329+
)
330+
},
331+
undefined,
332+
)
333+
334+
return result
335+
}
336+
311337
export const partialIndexingEnabledReducer = (opts: {
312338
config: PluginOptions
313339
initialValue: boolean

‎packages/sanity/src/core/config/prepareConfig.ts

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
initialDocumentActions,
3030
initialDocumentBadges,
3131
initialLanguageFilter,
32+
internalTasksReducer,
3233
newDocumentOptionsResolver,
3334
newSearchEnabledReducer,
3435
partialIndexingEnabledReducer,
@@ -472,6 +473,10 @@ function resolveSource({
472473
templates,
473474
auth,
474475
i18n: i18n.source,
476+
// eslint-disable-next-line camelcase
477+
__internal_tasks: internalTasksReducer({
478+
config,
479+
}),
475480
document: {
476481
actions: (partialContext) =>
477482
resolveConfigProperty({
@@ -533,6 +538,7 @@ function resolveSource({
533538
},
534539
},
535540
},
541+
536542
form: {
537543
file: {
538544
assetSources: resolveConfigProperty({

‎packages/sanity/src/core/config/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ export interface PluginOptions {
358358
tools?: Tool[] | ComposableOption<Tool[], ConfigContext>
359359
form?: SanityFormConfig
360360

361+
__internal_tasks?: {
362+
footerAction: ReactNode
363+
}
364+
361365
studio?: {
362366
/**
363367
* Components for the studio.
@@ -653,6 +657,9 @@ export interface Source {
653657
}
654658
}
655659

660+
/** @internal */
661+
__internal_tasks?: {footerAction: ReactNode}
662+
656663
/**
657664
* Form-related functionality.
658665
* @hidden

‎packages/sanity/src/structure/panes/document/DocumentPaneContext.ts

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export interface DocumentPaneContextValue {
8181
isDeleted: boolean
8282
isPermissionsLoading: boolean
8383
unstable_languageFilter: DocumentLanguageFilterComponent[]
84+
__internal_tasks?: {
85+
footerAction: React.ReactNode
86+
}
8487
}
8588

8689
/** @internal */

‎packages/sanity/src/structure/panes/document/DocumentPaneProvider.tsx

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable camelcase */
12
import {isActionEnabled} from '@sanity/schema/_internal'
23
import {
34
type ObjectSchemaType,
@@ -62,12 +63,15 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
6263
const schema = useSchema()
6364
const templates = useTemplates()
6465
const {
65-
actions: documentActions,
66-
badges: documentBadges,
67-
unstable_fieldActions: fieldActionsResolver,
68-
unstable_languageFilter: languageFilterResolver,
69-
inspectors: inspectorsResolver,
70-
} = useSource().document
66+
__internal_tasks,
67+
document: {
68+
actions: documentActions,
69+
badges: documentBadges,
70+
unstable_fieldActions: fieldActionsResolver,
71+
unstable_languageFilter: languageFilterResolver,
72+
inspectors: inspectorsResolver,
73+
},
74+
} = useSource()
7175
const presenceStore = usePresenceStore()
7276
const paneRouter = usePaneRouter()
7377
const setPaneParams = paneRouter.setParams
@@ -583,6 +587,7 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
583587
focusPath,
584588
inspector: currentInspector || null,
585589
inspectors,
590+
__internal_tasks,
586591
onBlur: handleBlur,
587592
onChange: handleChange,
588593
onFocus: handleFocus,
@@ -622,6 +627,7 @@ export const DocumentPaneProvider = memo((props: DocumentPaneProviderProps) => {
622627
unstable_languageFilter: languageFilter,
623628
}),
624629
[
630+
__internal_tasks,
625631
actions,
626632
activeViewId,
627633
badges,

‎packages/sanity/src/structure/panes/document/statusBar/DocumentStatusBarActions.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable camelcase */
12
import {Flex, Hotkeys, LayerProvider, Stack, Text} from '@sanity/ui'
23
import {memo, useMemo, useState} from 'react'
34
import {type DocumentActionDescription, useTimelineSelector} from 'sanity'
@@ -17,6 +18,7 @@ interface DocumentStatusBarActionsInnerProps {
1718

1819
function DocumentStatusBarActionsInner(props: DocumentStatusBarActionsInnerProps) {
1920
const {disabled, showMenu, states} = props
21+
const {__internal_tasks} = useDocumentPane()
2022
const [firstActionState, ...menuActionStates] = states
2123
const [buttonElement, setButtonElement] = useState<HTMLButtonElement | null>(null)
2224

@@ -42,6 +44,7 @@ function DocumentStatusBarActionsInner(props: DocumentStatusBarActionsInnerProps
4244

4345
return (
4446
<Flex align="center" gap={1}>
47+
{__internal_tasks && __internal_tasks.footerAction}
4548
{firstActionState && (
4649
<LayerProvider zOffset={200}>
4750
<Tooltip disabled={!tooltipContent} content={tooltipContent} placement="top">

‎packages/sanity/src/tasks/plugin/TasksBadge.tsx

-26
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import {useCallback, useMemo} from 'react'
2+
3+
import {Button} from '../../ui-components'
4+
import {useTasks, useTasksEnabled} from '../src'
5+
6+
/**
7+
* Button that shows how many pending tasks are assigned to the current document.
8+
* Clicking it will open the task sidebar, showing the open tasks related to the document.
9+
*
10+
* todo: just show the tab with tasks related to the document
11+
* @internal
12+
*/
13+
export function TasksFooterOpenTasks() {
14+
const {data, activeDocument, toggleOpen, isOpen} = useTasks()
15+
const {enabled} = useTasksEnabled()
16+
17+
const pendingTasks = useMemo(
18+
() =>
19+
data.filter((item) => {
20+
return item.target?.document._ref === activeDocument?.documentId && item.status === 'open'
21+
}),
22+
[activeDocument, data],
23+
)
24+
25+
const handleOnClick = useCallback(() => {
26+
if (isOpen) {
27+
return
28+
}
29+
toggleOpen()
30+
}, [isOpen, toggleOpen])
31+
32+
if (pendingTasks.length === 0 || !enabled) return null
33+
34+
const pluralizedTask = `task${pendingTasks.length > 1 ? 's' : ''}`
35+
36+
return (
37+
<Button
38+
mode="bleed"
39+
tooltipProps={{content: `Open ${pluralizedTask}`}}
40+
text={`${pendingTasks.length} open ${pluralizedTask}`}
41+
onClick={handleOnClick}
42+
/>
43+
)
44+
}

‎packages/sanity/src/tasks/plugin/index.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {definePlugin, type ObjectInputProps} from 'sanity'
22

3-
import {DocumentBadge} from './TasksBadge'
43
import {TasksDocumentInputLayout} from './TasksDocumentInputLayout'
4+
import {TasksFooterOpenTasks} from './TasksFooterOpenTasks'
55
import {TasksStudioActiveToolLayout} from './TasksStudioActiveToolLayout'
66
import {TasksStudioLayout} from './TasksStudioLayout'
77
import {TasksStudioNavbar} from './TasksStudioNavbar'
@@ -12,10 +12,9 @@ import {TasksStudioNavbar} from './TasksStudioNavbar'
1212
*/
1313
export const tasks = definePlugin({
1414
name: 'sanity/tasks',
15-
document: {
16-
badges: (prev) => {
17-
return (prev || []).concat(DocumentBadge)
18-
},
15+
// eslint-disable-next-line camelcase
16+
__internal_tasks: {
17+
footerAction: <TasksFooterOpenTasks />,
1918
},
2019
studio: {
2120
components: {

0 commit comments

Comments
 (0)
Please sign in to comment.