From 9dba066ca4ad19165493fd4dbe2fe3c62b829313 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Tue, 10 Jan 2023 12:49:12 -0500 Subject: [PATCH] fix: media not displaying with local backend (#324) --- .../core/dev-test/backends/proxy/config.yml | 11 ++++----- packages/core/dev-test/index.js | 22 ++--------------- packages/core/src/actions/media.ts | 4 ---- .../core/src/backends/proxy/implementation.ts | 8 +++++-- .../Collection/Entries/EntryCard.tsx | 24 ++++++------------- .../MediaLibrary/MediaLibraryCard.tsx | 7 +++--- packages/core/src/index.ts | 17 +++++++++---- packages/core/src/interface.ts | 12 ++++++++++ packages/core/src/lib/formatters.ts | 2 +- packages/core/src/lib/hooks/useMediaAsset.ts | 18 ++++++++------ packages/core/src/lib/util/media.util.ts | 8 +++---- packages/core/src/types/global.d.ts | 5 ++++ 12 files changed, 69 insertions(+), 69 deletions(-) diff --git a/packages/core/dev-test/backends/proxy/config.yml b/packages/core/dev-test/backends/proxy/config.yml index 5d54289b..0c9eb2a8 100644 --- a/packages/core/dev-test/backends/proxy/config.yml +++ b/packages/core/dev-test/backends/proxy/config.yml @@ -1,17 +1,18 @@ backend: name: github branch: main - repo: owner/repo - -media_folder: static/media -public_folder: /media + repo: staticjscms/static-cms-github local_backend: true +media_folder: assets/upload +public_folder: /assets/upload collections: - name: posts label: Posts label_singular: Post + media_folder: /assets/posts + public_folder: /assets/posts description: > The description is a great place for tone setting, high level information, and editing guidelines that are specific to a collection. @@ -94,8 +95,6 @@ collections: - name: settings label: Settings delete: false - editor: - preview: false files: - name: general label: Site Settings diff --git a/packages/core/dev-test/index.js b/packages/core/dev-test/index.js index 0428dfd3..4c29576e 100644 --- a/packages/core/dev-test/index.js +++ b/packages/core/dev-test/index.js @@ -11,29 +11,12 @@ const PostPreview = ({ entry, widgetFor, widgetsFor }) => { ); }; -const GeneralPreview = ({ widgetsFor, getAsset, entry }) => { +const GeneralPreview = ({ widgetsFor, entry, collection }) => { const title = entry.data.site_title; const posts = entry.data.posts; const thumb = posts && posts.thumb; - const [thumbUrl, setThumbUrl] = useState(''); - - useEffect(() => { - let alive = true; - - const loadThumb = async () => { - const thumbAsset = await getAsset(thumb); - if (alive) { - setThumbUrl(thumbAsset.toString()); - } - }; - - loadThumb(); - - return () => { - alive = false; - }; - }, [thumb]); + const thumbUrl = useMediaAsset(thumb, collection, undefined, entry); return h( 'div', @@ -96,7 +79,6 @@ const CustomPage = () => { return h('div', {}, 'I am a custom page!'); }; -CMS.registerPreviewStyle('.toastui-editor-contents h1 { color: blue }', { raw: true }); CMS.registerPreviewTemplate('posts', PostPreview); CMS.registerPreviewTemplate('general', GeneralPreview); CMS.registerPreviewTemplate('authors', AuthorsPreview); diff --git a/packages/core/src/actions/media.ts b/packages/core/src/actions/media.ts index b89ef552..215dcba0 100644 --- a/packages/core/src/actions/media.ts +++ b/packages/core/src/actions/media.ts @@ -93,10 +93,6 @@ export function getAsset( dispatch: ThunkDispatch, getState: () => RootState, ): Promise => { - if (!collection || !entry || !path) { - return Promise.resolve(emptyAsset); - } - const state = getState(); if (!state.config.config) { return Promise.resolve(emptyAsset); diff --git a/packages/core/src/backends/proxy/implementation.ts b/packages/core/src/backends/proxy/implementation.ts index c4e88015..6aafe7bf 100644 --- a/packages/core/src/backends/proxy/implementation.ts +++ b/packages/core/src/backends/proxy/implementation.ts @@ -166,8 +166,12 @@ export default class ProxyBackend implements BackendClass { return deserializeMediaFile(file); } - getMediaDisplayURL(displayURL: DisplayURL) { - return Promise.resolve(typeof displayURL === 'string' ? displayURL : displayURL.id); + getMediaDisplayURL(displayURL: DisplayURL): Promise { + if (typeof displayURL === 'string') { + return Promise.resolve(displayURL); + } + + return Promise.resolve(displayURL.path); } async persistMedia(assetProxy: AssetProxy, options: PersistOptions) { diff --git a/packages/core/src/components/Collection/Entries/EntryCard.tsx b/packages/core/src/components/Collection/Entries/EntryCard.tsx index 0b63b961..b9d69875 100644 --- a/packages/core/src/components/Collection/Entries/EntryCard.tsx +++ b/packages/core/src/components/Collection/Entries/EntryCard.tsx @@ -3,19 +3,20 @@ import CardActionArea from '@mui/material/CardActionArea'; import CardContent from '@mui/material/CardContent'; import CardMedia from '@mui/material/CardMedia'; import Typography from '@mui/material/Typography'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router-dom'; import { getAsset as getAssetAction } from '@staticcms/core/actions/media'; import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '@staticcms/core/constants/collectionViews'; +import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset'; import { selectEntryCollectionTitle } from '@staticcms/core/lib/util/collection.util'; import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias'; -import type { ConnectedProps } from 'react-redux'; import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews'; -import type { Field, Collection, Entry } from '@staticcms/core/interface'; +import type { Collection, Entry, Field } from '@staticcms/core/interface'; import type { RootState } from '@staticcms/core/store'; +import type { ConnectedProps } from 'react-redux'; const EntryCard = ({ collection, @@ -25,22 +26,10 @@ const EntryCard = ({ imageField, collectionLabel, viewStyle = VIEW_STYLE_LIST, - getAsset, }: NestedCollectionProps) => { const summary = useMemo(() => selectEntryCollectionTitle(collection, entry), [collection, entry]); - const [imageUrl, setImageUrl] = useState(); - useEffect(() => { - if (!image) { - return; - } - - const getImage = async () => { - setImageUrl((await getAsset(collection, entry, image, imageField)).toString()); - }; - - getImage(); - }, [collection, entry, getAsset, image, imageField]); + const imageUrl = useMediaAsset(image, collection, imageField, entry); return ( @@ -84,8 +73,9 @@ function mapStateToProps(state: RootState, ownProps: EntryCardOwnProps) { let image = inferedFields.imageField ? (entryData?.[inferedFields.imageField] as string | undefined) : undefined; + if (image) { - image = encodeURI(image); + image = encodeURI(image.trim()); } const isLoadingAsset = selectIsLoadingAsset(state.medias); diff --git a/packages/core/src/components/MediaLibrary/MediaLibraryCard.tsx b/packages/core/src/components/MediaLibrary/MediaLibraryCard.tsx index 1ef97f92..7b0ae97d 100644 --- a/packages/core/src/components/MediaLibrary/MediaLibraryCard.tsx +++ b/packages/core/src/components/MediaLibrary/MediaLibraryCard.tsx @@ -1,8 +1,9 @@ import { styled } from '@mui/material/styles'; -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect } from 'react'; import { borders, colors, effects, lengths, shadows } from '@staticcms/core/components/UI/styles'; -import { transientOptions } from '@staticcms/core/lib'; +import useMediaAsset from '@staticcms/core/lib/hooks/useMediaAsset'; +import transientOptions from '@staticcms/core/lib/util/transientOptions'; import type { MediaLibraryDisplayURL } from '@staticcms/core/reducers/mediaLibrary'; @@ -104,7 +105,7 @@ const MediaLibraryCard = ({ isDraft, loadDisplayURL, }: MediaLibraryCardProps) => { - const url = useMemo(() => displayURL.url, [displayURL.url]); + const url = useMediaAsset(displayURL.url); useEffect(() => { if (!displayURL.url) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index ea935848..361a195b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,15 +2,18 @@ import createReactClass from 'create-react-class'; import { createElement, useCallback, useEffect, useMemo, useState } from 'react'; import bootstrap from './bootstrap'; +import useIsMediaAsset from './lib/hooks/useIsMediaAsset'; +import useMediaAsset from './lib/hooks/useMediaAsset'; +import useMediaInsert from './lib/hooks/useMediaInsert'; +import useUUID from './lib/hooks/useUUID'; import Registry from './lib/registry'; export * from './backends'; -export * from './widgets'; -export * from './media-libraries'; -export { default as locales } from './locales'; -export * from './lib'; - export * from './interface'; +export * from './lib'; +export { default as locales } from './locales'; +export * from './media-libraries'; +export * from './widgets'; const CMS = { ...Registry, @@ -25,6 +28,10 @@ if (typeof window !== 'undefined') { window.useEffect = window.useEffect || useEffect; window.useCallback = window.useCallback || useCallback; window.h = window.h || createElement; + window.useIsMediaAsset = window.useIsMediaAsset || useIsMediaAsset; + window.useMediaAsset = window.useMediaAsset || useMediaAsset; + window.useMediaInsert = window.useMediaInsert || useMediaInsert; + window.useUUID = window.useUUID || useUUID; } export default CMS; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index dfee55d8..b3730107 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -235,6 +235,9 @@ export interface DisplayURLState { export type TranslatedProps = T & ReactPolyglotTranslateProps; +/** + * @deprecated Should use `useMediaAsset` React hook instead + */ export type GetAssetFunction = ( path: string, field?: F, @@ -248,6 +251,9 @@ export interface WidgetControlProps { fieldsErrors: FieldsErrors; submitted: boolean; forList: boolean; + /** + * @deprecated Should use `useMediaAsset` React hook instead + */ getAsset: GetAssetFunction; isDisabled: boolean; isFieldDuplicate: EditorControlProps['isFieldDuplicate']; @@ -273,6 +279,9 @@ export interface WidgetPreviewProps; entry: Entry; field: RenderedField; + /** + * @deprecated Should use `useMediaAsset` React hook instead + */ getAsset: GetAssetFunction; value: T | undefined | null; } @@ -300,6 +309,9 @@ export interface TemplatePreviewProps; document: Document | undefined | null; window: Window | undefined | null; + /** + * @deprecated Should use `useMediaAsset` React hook instead + */ getAsset: GetAssetFunction>; widgetFor: (name: T extends EntryData ? string : keyof T) => ReactNode; widgetsFor: WidgetsFor; diff --git a/packages/core/src/lib/formatters.ts b/packages/core/src/lib/formatters.ts index 9c0e25c7..7f8bc074 100644 --- a/packages/core/src/lib/formatters.ts +++ b/packages/core/src/lib/formatters.ts @@ -127,7 +127,7 @@ export function summaryFormatter(summaryTemplate: string, entry: Entry, collecti export function folderFormatter( folderTemplate: string, - entry: Entry | undefined, + entry: Entry | null | undefined, collection: Collection, defaultFolder: string, folderKey: string, diff --git a/packages/core/src/lib/hooks/useMediaAsset.ts b/packages/core/src/lib/hooks/useMediaAsset.ts index df17b025..97445072 100644 --- a/packages/core/src/lib/hooks/useMediaAsset.ts +++ b/packages/core/src/lib/hooks/useMediaAsset.ts @@ -4,18 +4,22 @@ import { getAsset } from '@staticcms/core/actions/media'; import { useAppDispatch } from '@staticcms/core/store/hooks'; import { isNotEmpty } from '../util/string.util'; -import type { Collection, Entry, FileOrImageField, MarkdownField } from '@staticcms/core/interface'; +import type { Field, Collection, Entry } from '@staticcms/core/interface'; -export default function useMediaAsset( - url: string, - collection: Collection, - field: T, - entry: Entry, +export default function useMediaAsset( + url: string | undefined, + collection?: Collection, + field?: T, + entry?: Entry, ): string { const dispatch = useAppDispatch(); const [assetSource, setAssetSource] = useState(url); useEffect(() => { + if (!url) { + return; + } + const fetchMedia = async () => { const asset = (await dispatch(getAsset(collection, entry, url, field)))?.toString() ?? ''; setAssetSource(asset); @@ -24,5 +28,5 @@ export default function useMediaAsset