Skip to content

Commit

Permalink
fix(rich-text): better handling for status [] (#1643)
Browse files Browse the repository at this point in the history
* refactor(rich-text): better handling for status []

* fix(rich-text): better handle missing content type []

* chore: removed unused type []
  • Loading branch information
chrishelgert authored Apr 23, 2024
1 parent 0d8d60d commit 00cd60b
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 96 deletions.
2 changes: 0 additions & 2 deletions packages/reference/src/common/EntityStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ type UseEntityResult<E> =
| { status: 'error'; data: never }
| { status: 'success'; data: E };

export type UseEntityStatus = 'idle' | 'loading' | 'error' | 'success';

type FetchFunction<TQueryData> = (context: { cmaClient: PlainClientAPI }) => Promise<TQueryData>;
type FetchServiceOptions<
TQueryFnData = unknown,
Expand Down
2 changes: 1 addition & 1 deletion packages/reference/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type {
export { SortableLinkList } from './common/SortableLinkList';
export { EntityProvider, useEntityLoader, useEntity, useResource } from './common/EntityStore';
export { SharedQueryClientProvider as EntityCacheProvider } from './common/queryClient';
export type { ResourceInfo, UseEntityStatus } from './common/EntityStore';
export type { ResourceInfo } from './common/EntityStore';
export {
SingleResourceReferenceEditor,
MultipleResourceReferenceEditor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,81 @@ const styles = {
}),
};

type InternalFetchingWrappedInlineEntryCardProps = Pick<
FetchingWrappedInlineEntryCardProps,
'onEdit' | 'onRemove' | 'isDisabled' | 'isSelected'
> & {
locale: string;
defaultLocale: string;
entry: Entry;
allContentTypes: ReturnType<FieldAppSDK['space']['getCachedContentTypes']>;
getEntityScheduledActions: React.ComponentProps<
typeof ScheduledIconWithTooltip
>['getEntityScheduledActions'];
entryStatus: ReturnType<typeof getEntryStatus>;
};

function InternalFetchingWrappedInlineEntryCard({
entry,
allContentTypes,
locale,
defaultLocale,
isSelected,
entryStatus,
getEntityScheduledActions,
onEdit,
onRemove,
isDisabled,
}: InternalFetchingWrappedInlineEntryCardProps) {
const contentType = React.useMemo(() => {
if (!allContentTypes) {
return undefined;
}

return allContentTypes.find(
(contentType) => contentType.sys.id === entry.sys.contentType.sys.id
);
}, [allContentTypes, entry]);

const title = React.useMemo(
() =>
getEntryTitle({
entry,
contentType,
localeCode: locale,
defaultLocaleCode: defaultLocale,
defaultTitle: 'Untitled',
}),
[entry, contentType, locale, defaultLocale]
);

return (
<InlineEntryCard
testId={INLINES.EMBEDDED_ENTRY}
isSelected={isSelected}
title={contentType ? `${contentType.name}: ${title}` : title}
status={entryStatus}
actions={[
<MenuItem key="edit" onClick={onEdit}>
Edit
</MenuItem>,
<MenuItem key="remove" onClick={onRemove} disabled={isDisabled} testId="delete">
Remove
</MenuItem>,
]}
>
<ScheduledIconWithTooltip
getEntityScheduledActions={getEntityScheduledActions}
entityType="Entry"
entityId={entry.sys.id}
>
<ClockIcon className={styles.scheduledIcon} variant="muted" testId="scheduled-icon" />
</ScheduledIconWithTooltip>
<Text>{title}</Text>
</InlineEntryCard>
);
}

interface FetchingWrappedInlineEntryCardProps {
entryId: string;
sdk: FieldAppSDK;
Expand All @@ -34,19 +109,8 @@ interface FetchingWrappedInlineEntryCardProps {
export function FetchingWrappedInlineEntryCard(props: FetchingWrappedInlineEntryCardProps) {
const { data: entry, status: requestStatus } = useEntity<Entry>('Entry', props.entryId);
const { getEntityScheduledActions } = useEntityLoader();
const loadEntityScheduledActions = () => getEntityScheduledActions('Entry', props.entryId);

const allContentTypes = props.sdk.space.getCachedContentTypes();
const { onEntityFetchComplete } = props;
const contentType = React.useMemo(() => {
if (requestStatus !== 'success' || !entry || !allContentTypes) {
return undefined;
}

return allContentTypes.find(
(contentType) => contentType.sys.id === entry.sys.contentType.sys.id
);
}, [allContentTypes, entry, requestStatus]);

React.useEffect(() => {
if (requestStatus !== 'success') {
Expand All @@ -55,20 +119,9 @@ export function FetchingWrappedInlineEntryCard(props: FetchingWrappedInlineEntry
onEntityFetchComplete?.();
}, [requestStatus, onEntityFetchComplete]);

const contentTypeName = contentType ? contentType.name : '';

const title = React.useMemo(
() =>
requestStatus === 'success' &&
getEntryTitle({
entry,
contentType,
localeCode: props.sdk.field.locale,
defaultLocaleCode: props.sdk.locales.default,
defaultTitle: 'Untitled',
}),
[entry, requestStatus, contentType, props.sdk.field.locale, props.sdk.locales.default]
);
if (requestStatus === 'loading' || requestStatus === 'idle') {
return <InlineEntryCard isLoading />;
}

if (requestStatus === 'error') {
return (
Expand All @@ -80,10 +133,6 @@ export function FetchingWrappedInlineEntryCard(props: FetchingWrappedInlineEntry
);
}

if (requestStatus === 'loading') {
return <InlineEntryCard isLoading />;
}

const entryStatus = getEntryStatus(entry.sys);
if (entryStatus === 'deleted') {
return (
Expand All @@ -101,28 +150,17 @@ export function FetchingWrappedInlineEntryCard(props: FetchingWrappedInlineEntry
}

return (
<InlineEntryCard
testId={INLINES.EMBEDDED_ENTRY}
<InternalFetchingWrappedInlineEntryCard
allContentTypes={props.sdk.space.getCachedContentTypes()}
getEntityScheduledActions={() => getEntityScheduledActions('Entry', props.entryId)}
locale={props.sdk.field.locale}
defaultLocale={props.sdk.locales.default}
entry={entry}
entryStatus={entryStatus}
isDisabled={props.isDisabled}
isSelected={props.isSelected}
title={`${contentTypeName}: ${title}`}
status={entryStatus}
actions={[
<MenuItem key="edit" onClick={props.onEdit}>
Edit
</MenuItem>,
<MenuItem key="remove" onClick={props.onRemove} disabled={props.isDisabled} testId="delete">
Remove
</MenuItem>,
]}
>
<ScheduledIconWithTooltip
getEntityScheduledActions={loadEntityScheduledActions}
entityType="Entry"
entityId={entry.sys.id}
>
<ClockIcon className={styles.scheduledIcon} variant="muted" testId="scheduled-icon" />
</ScheduledIconWithTooltip>
<Text>{title}</Text>
</InlineEntryCard>
onEdit={props.onEdit}
onRemove={props.onRemove}
/>
);
}
44 changes: 21 additions & 23 deletions packages/rich-text/src/plugins/shared/FetchingWrappedAssetCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import {
useEntityLoader,
MissingEntityCard,
WrappedAssetCard,
type UseEntityStatus,
} from '@contentful/field-editor-reference';
import areEqual from 'fast-deep-equal';

interface InternalAssetCardProps {
status: UseEntityStatus;
asset?: Asset;
asset: Asset;
isDisabled: boolean;
isSelected: boolean;
locale: string;
Expand All @@ -23,22 +21,8 @@ interface InternalAssetCardProps {
loadEntityScheduledActions: (entityType: string, entityId: string) => Promise<ScheduledAction[]>;
}

const InternalAssetCard = React.memo((props: InternalAssetCardProps) => {
if (props.status === 'error') {
return (
<MissingEntityCard
isDisabled={props.isDisabled}
onRemove={props.onRemove}
providerName="Contentful"
/>
);
}

if (!props.asset) {
return <AssetCard size="default" isLoading />;
}

return (
const InternalAssetCard = React.memo(
(props: InternalAssetCardProps) => (
<WrappedAssetCard
getEntityScheduledActions={props.loadEntityScheduledActions}
size="small"
Expand All @@ -51,8 +35,9 @@ const InternalAssetCard = React.memo((props: InternalAssetCardProps) => {
onRemove={props.isDisabled ? undefined : props.onRemove}
isClickable={false}
/>
);
}, areEqual);
),
areEqual
);

InternalAssetCard.displayName = 'InternalAssetCard';

Expand Down Expand Up @@ -82,10 +67,23 @@ export function FetchingWrappedAssetCard(props: FetchingWrappedAssetCardProps) {
}
}, [onEntityFetchComplete, status]);

if (status === 'loading' || status === 'idle') {
return <AssetCard size="default" isLoading />;
}

if (status === 'error') {
return (
<MissingEntityCard
isDisabled={props.isDisabled}
onRemove={props.onRemove}
providerName="Contentful"
/>
);
}

return (
<InternalAssetCard
asset={asset as Asset | undefined}
status={status}
asset={asset}
sdk={props.sdk}
isDisabled={props.isDisabled}
isSelected={props.isSelected}
Expand Down
35 changes: 16 additions & 19 deletions packages/rich-text/src/plugins/shared/FetchingWrappedEntryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,22 @@ import {
MissingEntityCard,
WrappedEntryCard,
useEntityLoader,
type UseEntityStatus,
} from '@contentful/field-editor-reference';
import areEqual from 'fast-deep-equal';

interface InternalEntryCard {
status: UseEntityStatus;
isDisabled: boolean;
isSelected: boolean;
locale: string;
sdk: FieldAppSDK;
loadEntityScheduledActions: (entityType: string, entityId: string) => Promise<ScheduledAction[]>;
entry?: Entry;
entry: Entry;
onEdit?: VoidFunction;
onRemove?: VoidFunction;
}

const InternalEntryCard = React.memo((props: InternalEntryCard) => {
const { entry, sdk, loadEntityScheduledActions, status } = props;

if (status === 'error') {
return (
<MissingEntityCard
isDisabled={props.isDisabled}
onRemove={props.onRemove}
providerName="Contentful"
/>
);
}

if (entry === undefined) {
return <EntryCard isLoading />;
}
const { entry, sdk, loadEntityScheduledActions } = props;

const contentType = sdk.space
.getCachedContentTypes()
Expand Down Expand Up @@ -91,9 +75,22 @@ export const FetchingWrappedEntryCard = (props: FetchingWrappedEntryCardProps) =
}
}, [onEntityFetchComplete, status]);

if (status === 'loading' || status === 'idle') {
return <EntryCard isLoading />;
}

if (status === 'error') {
return (
<MissingEntityCard
isDisabled={props.isDisabled}
onRemove={props.onRemove}
providerName="Contentful"
/>
);
}

return (
<InternalEntryCard
status={status}
entry={entry}
sdk={props.sdk}
locale={props.locale}
Expand Down

0 comments on commit 00cd60b

Please sign in to comment.