Skip to content

Commit

Permalink
feat: add reference count to asset list (#148)
Browse files Browse the repository at this point in the history
* feat: add reference count to asset list

* fix: artifically delay rendering of reference counts to reduce overfetching on scroll

* fix: display unique count of referred documents in rows, eagerly display count in edit dialog tabs

---------

Co-authored-by: Robin Pyon <[email protected]>
  • Loading branch information
nkgentile and robinpyon authored Jul 5, 2023
1 parent a4397c7 commit c12d1e5
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 174 deletions.
230 changes: 126 additions & 104 deletions src/components/DialogAssetEdit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import groq from 'groq'
import React, {ReactNode, useCallback, useEffect, useRef, useState} from 'react'
import {SubmitHandler, useForm} from 'react-hook-form'
import {useDispatch} from 'react-redux'
import {WithReferringDocuments, useDocumentStore} from 'sanity'
import {assetFormSchema} from '../../formSchema'
import useTypedSelector from '../../hooks/useTypedSelector'
import useVersionedClient from '../../hooks/useVersionedClient'
import {assetsActions, selectAssetById} from '../../modules/assets'
import {dialogActions} from '../../modules/dialog'
import {selectTags, selectTagSelectOptions, tagsActions} from '../../modules/tags'
import getTagSelectOptions from '../../utils/getTagSelectOptions'
import {getUniqueDocuments} from '../../utils/getUniqueDocuments'
import imageDprUrl from '../../utils/imageDprUrl'
import sanitizeFormData from '../../utils/sanitizeFormData'
import {isFileAsset, isImageAsset} from '../../utils/typeGuards'
Expand All @@ -39,6 +41,8 @@ const DialogAssetEdit = (props: Props) => {

const client = useVersionedClient()

const documentStore = useDocumentStore()

const dispatch = useDispatch()
const assetItem = useTypedSelector(state => selectAssetById(state, String(assetId))) // TODO: check casting
const tags = useTypedSelector(selectTags)
Expand Down Expand Up @@ -238,110 +242,128 @@ const DialogAssetEdit = (props: Props) => {
*/}
<Flex direction={['column-reverse', 'column-reverse', 'row-reverse']}>
<Box flex={1} marginTop={[5, 5, 0]} padding={4}>
{/* Tabs */}
<TabList space={2}>
<Tab
aria-controls="details-panel"
disabled={formUpdating}
id="details-tab"
label="Details"
onClick={() => setTabSection('details')}
selected={tabSection === 'details'}
size={2}
/>
<Tab
aria-controls="references-panel"
disabled={formUpdating}
id="references-tab"
label="References"
onClick={() => setTabSection('references')}
selected={tabSection === 'references'}
size={2}
/>
</TabList>

{/* Form fields */}
<Box as="form" marginTop={4} onSubmit={handleSubmit(onSubmit)}>
{/* Deleted notification */}
{!assetItem && (
<Card marginBottom={3} padding={3} radius={2} shadow={1} tone="critical">
<Text size={1}>This file cannot be found – it may have been deleted.</Text>
</Card>
)}

{/* Hidden button to enable enter key submissions */}
<button style={{display: 'none'}} tabIndex={-1} type="submit" />

{/* Panel: details */}
<TabPanel
aria-labelledby="details"
hidden={tabSection !== 'details'}
id="details-panel"
>
<Stack space={3}>
{/* Tags */}
<FormFieldInputTags
control={control}
disabled={formUpdating}
error={errors?.opt?.media?.tags?.message}
label="Tags"
name="opt.media.tags"
onCreateTag={handleCreateTag}
options={allTagOptions}
placeholder="Select or create..."
value={assetTagOptions}
/>
{/* Filename */}
<FormFieldInputText
{...register('originalFilename')}
disabled={formUpdating}
error={errors?.originalFilename?.message}
label="Filename"
name="originalFilename"
value={currentAsset?.originalFilename}
/>
{/* Title */}
<FormFieldInputText
{...register('title')}
disabled={formUpdating}
error={errors?.title?.message}
label="Title"
name="title"
value={currentAsset?.title}
/>
{/* Alt text */}
<FormFieldInputText
{...register('altText')}
disabled={formUpdating}
error={errors?.altText?.message}
label="Alt Text"
name="altText"
value={currentAsset?.altText}
/>
{/* Description */}
<FormFieldInputTextarea
{...register('description')}
disabled={formUpdating}
error={errors?.description?.message}
label="Description"
name="description"
rows={3}
value={currentAsset?.description}
/>
</Stack>
</TabPanel>

{/* Panel: References */}
<TabPanel
aria-labelledby="references"
hidden={tabSection !== 'references'}
id="references-panel"
>
<Box marginTop={5}>
{assetItem?.asset && <DocumentList assetId={assetItem?.asset._id} />}
</Box>
</TabPanel>
</Box>
<WithReferringDocuments documentStore={documentStore} id={assetItem.asset._id}>
{({isLoading, referringDocuments}) => {
const uniqueReferringDocuments = getUniqueDocuments(referringDocuments)
return (
<>
{/* Tabs */}
<TabList space={2}>
<Tab
aria-controls="details-panel"
disabled={formUpdating}
id="details-tab"
label="Details"
onClick={() => setTabSection('details')}
selected={tabSection === 'details'}
size={2}
/>
<Tab
aria-controls="references-panel"
disabled={formUpdating}
id="references-tab"
label={`References${
!isLoading && Array.isArray(uniqueReferringDocuments)
? ` (${uniqueReferringDocuments.length})`
: ''
}`}
onClick={() => setTabSection('references')}
selected={tabSection === 'references'}
size={2}
/>
</TabList>

{/* Form fields */}
<Box as="form" marginTop={4} onSubmit={handleSubmit(onSubmit)}>
{/* Deleted notification */}
{!assetItem && (
<Card marginBottom={3} padding={3} radius={2} shadow={1} tone="critical">
<Text size={1}>This file cannot be found – it may have been deleted.</Text>
</Card>
)}

{/* Hidden button to enable enter key submissions */}
<button style={{display: 'none'}} tabIndex={-1} type="submit" />

{/* Panel: details */}
<TabPanel
aria-labelledby="details"
hidden={tabSection !== 'details'}
id="details-panel"
>
<Stack space={3}>
{/* Tags */}
<FormFieldInputTags
control={control}
disabled={formUpdating}
error={errors?.opt?.media?.tags?.message}
label="Tags"
name="opt.media.tags"
onCreateTag={handleCreateTag}
options={allTagOptions}
placeholder="Select or create..."
value={assetTagOptions}
/>
{/* Filename */}
<FormFieldInputText
{...register('originalFilename')}
disabled={formUpdating}
error={errors?.originalFilename?.message}
label="Filename"
name="originalFilename"
value={currentAsset?.originalFilename}
/>
{/* Title */}
<FormFieldInputText
{...register('title')}
disabled={formUpdating}
error={errors?.title?.message}
label="Title"
name="title"
value={currentAsset?.title}
/>
{/* Alt text */}
<FormFieldInputText
{...register('altText')}
disabled={formUpdating}
error={errors?.altText?.message}
label="Alt Text"
name="altText"
value={currentAsset?.altText}
/>
{/* Description */}
<FormFieldInputTextarea
{...register('description')}
disabled={formUpdating}
error={errors?.description?.message}
label="Description"
name="description"
rows={3}
value={currentAsset?.description}
/>
</Stack>
</TabPanel>

{/* Panel: References */}
<TabPanel
aria-labelledby="references"
hidden={tabSection !== 'references'}
id="references-panel"
>
<Box marginTop={5}>
{assetItem?.asset && (
<DocumentList
documents={uniqueReferringDocuments}
isLoading={isLoading}
/>
)}
</Box>
</TabPanel>
</Box>
</>
)
}}
</WithReferringDocuments>
</Box>

<Box flex={1} padding={4}>
Expand Down
49 changes: 16 additions & 33 deletions src/components/DocumentList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,37 @@
import type {SanityDocument} from '@sanity/client'
import {Box, Button, Card, Stack, Text} from '@sanity/ui'
import React from 'react'
import {Preview, SchemaType, useDocumentStore, useSchema, WithReferringDocuments} from 'sanity'
import {Preview, SchemaType, useSchema} from 'sanity'
import {useIntentLink} from 'sanity/router'

type Props = {
assetId: string
documents: SanityDocument[]
isLoading: boolean
}

const DocumentList = (props: Props) => {
const {assetId} = props

const documentStore = useDocumentStore()

return (
<WithReferringDocuments documentStore={documentStore} id={assetId}>
{({isLoading, referringDocuments}) => (
<ReferringDocuments isLoading={isLoading} referringDocuments={referringDocuments} />
)}
</WithReferringDocuments>
)
}

const ReferringDocuments = (props: {isLoading: boolean; referringDocuments: SanityDocument[]}) => {
const {isLoading, referringDocuments} = props

const DocumentList = ({documents, isLoading}: Props) => {
const schema = useSchema()

const draftIds = referringDocuments.reduce(
(acc: string[], doc: SanityDocument) =>
doc._id.startsWith('drafts.') ? acc.concat(doc._id.slice(7)) : acc,
[]
)

const filteredDocuments: SanityDocument[] = referringDocuments.filter(
(doc: SanityDocument) => !draftIds.includes(doc._id)
)

if (isLoading) {
return <Text size={1}>Loading...</Text>
return (
<Text muted size={1}>
Loading...
</Text>
)
}

if (filteredDocuments.length === 0) {
return <Text size={1}>No documents are referencing this asset</Text>
if (documents.length === 0) {
return (
<Text muted size={1}>
No documents are referencing this asset
</Text>
)
}

return (
<Card flex={1} marginBottom={2} padding={2} radius={2} shadow={1}>
<Stack space={2}>
{filteredDocuments?.map(doc => (
{documents?.map(doc => (
<ReferringDocument doc={doc} key={doc._id} schemaType={schema.get(doc._type)} />
))}
</Stack>
Expand Down
1 change: 1 addition & 0 deletions src/components/TableHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const TableHeader = () => {
<TableHeaderItem field="mimeType" title="MIME type" />
<TableHeaderItem field="size" title="Size" />
<TableHeaderItem field="_updatedAt" title="Last updated" />
<TableHeaderItem title="References" />
<TableHeaderItem />
</Grid>
)
Expand Down
Loading

0 comments on commit c12d1e5

Please sign in to comment.