fix: media not displaying with local backend (#324)
This commit is contained in:
parent
a9e0770fd0
commit
9dba066ca4
@ -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
|
||||
|
@ -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);
|
||||
|
@ -93,10 +93,6 @@ export function getAsset<F extends BaseField = UnknownField>(
|
||||
dispatch: ThunkDispatch<RootState, {}, AnyAction>,
|
||||
getState: () => RootState,
|
||||
): Promise<AssetProxy> => {
|
||||
if (!collection || !entry || !path) {
|
||||
return Promise.resolve(emptyAsset);
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
if (!state.config.config) {
|
||||
return Promise.resolve(emptyAsset);
|
||||
|
@ -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<string> {
|
||||
if (typeof displayURL === 'string') {
|
||||
return Promise.resolve(displayURL);
|
||||
}
|
||||
|
||||
return Promise.resolve(displayURL.path);
|
||||
}
|
||||
|
||||
async persistMedia(assetProxy: AssetProxy, options: PersistOptions) {
|
||||
|
@ -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<string>();
|
||||
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 (
|
||||
<Card>
|
||||
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -235,6 +235,9 @@ export interface DisplayURLState {
|
||||
|
||||
export type TranslatedProps<T> = T & ReactPolyglotTranslateProps;
|
||||
|
||||
/**
|
||||
* @deprecated Should use `useMediaAsset` React hook instead
|
||||
*/
|
||||
export type GetAssetFunction<F extends BaseField = UnknownField> = (
|
||||
path: string,
|
||||
field?: F,
|
||||
@ -248,6 +251,9 @@ export interface WidgetControlProps<T, F extends BaseField = UnknownField> {
|
||||
fieldsErrors: FieldsErrors;
|
||||
submitted: boolean;
|
||||
forList: boolean;
|
||||
/**
|
||||
* @deprecated Should use `useMediaAsset` React hook instead
|
||||
*/
|
||||
getAsset: GetAssetFunction<F>;
|
||||
isDisabled: boolean;
|
||||
isFieldDuplicate: EditorControlProps['isFieldDuplicate'];
|
||||
@ -273,6 +279,9 @@ export interface WidgetPreviewProps<T = unknown, F extends BaseField = UnknownFi
|
||||
collection: Collection<F>;
|
||||
entry: Entry;
|
||||
field: RenderedField<F>;
|
||||
/**
|
||||
* @deprecated Should use `useMediaAsset` React hook instead
|
||||
*/
|
||||
getAsset: GetAssetFunction<F>;
|
||||
value: T | undefined | null;
|
||||
}
|
||||
@ -300,6 +309,9 @@ export interface TemplatePreviewProps<T = EntryData, EF extends BaseField = Unkn
|
||||
entry: Entry<T>;
|
||||
document: Document | undefined | null;
|
||||
window: Window | undefined | null;
|
||||
/**
|
||||
* @deprecated Should use `useMediaAsset` React hook instead
|
||||
*/
|
||||
getAsset: GetAssetFunction<Field<EF>>;
|
||||
widgetFor: (name: T extends EntryData ? string : keyof T) => ReactNode;
|
||||
widgetsFor: WidgetsFor<T>;
|
||||
|
@ -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,
|
||||
|
@ -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<T extends FileOrImageField | MarkdownField>(
|
||||
url: string,
|
||||
collection: Collection<T>,
|
||||
field: T,
|
||||
entry: Entry,
|
||||
export default function useMediaAsset<T extends Field>(
|
||||
url: string | undefined,
|
||||
collection?: Collection<T>,
|
||||
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<T>(collection, entry, url, field)))?.toString() ?? '';
|
||||
setAssetSource(asset);
|
||||
@ -24,5 +28,5 @@ export default function useMediaAsset<T extends FileOrImageField | MarkdownField
|
||||
fetchMedia();
|
||||
}, [collection, dispatch, entry, field, url]);
|
||||
|
||||
return isNotEmpty(assetSource) ? assetSource : url;
|
||||
return isNotEmpty(assetSource) ? assetSource : url ?? '';
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ function evaluateFolder(
|
||||
folderKey: 'media_folder' | 'public_folder',
|
||||
config: Config,
|
||||
c: Collection,
|
||||
entryMap: Entry | undefined,
|
||||
entryMap: Entry | null | undefined,
|
||||
field: FileOrImageField | MarkdownField,
|
||||
) {
|
||||
let currentFolder = config[folderKey]!;
|
||||
@ -158,7 +158,7 @@ function traverseFields(
|
||||
folderKey: 'media_folder' | 'public_folder',
|
||||
config: Config,
|
||||
collection: Collection,
|
||||
entryMap: Entry | undefined,
|
||||
entryMap: Entry | null | undefined,
|
||||
field: FileOrImageField | MarkdownField | ListField | ObjectField,
|
||||
fields: Field[],
|
||||
currentFolder: string,
|
||||
@ -227,7 +227,7 @@ function traverseFields(
|
||||
export function selectMediaFolder(
|
||||
config: Config,
|
||||
collection: Collection | undefined | null,
|
||||
entryMap: Entry | undefined,
|
||||
entryMap: Entry | null | undefined,
|
||||
field: Field | undefined,
|
||||
) {
|
||||
const name = 'media_folder';
|
||||
@ -279,7 +279,7 @@ export function selectMediaFilePublicPath(
|
||||
export function selectMediaFilePath(
|
||||
config: Config,
|
||||
collection: Collection | null,
|
||||
entryMap: Entry | undefined,
|
||||
entryMap: Entry | null | undefined,
|
||||
mediaPath: string,
|
||||
field: Field | undefined,
|
||||
) {
|
||||
|
5
packages/core/src/types/global.d.ts
vendored
5
packages/core/src/types/global.d.ts
vendored
@ -4,6 +4,7 @@ import type { Config } from '../interface';
|
||||
import type CmsAPI from '../index';
|
||||
import type createReactClass from 'create-react-class';
|
||||
import type { createElement, useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import type { useIsMediaAsset, useMediaAsset, useMediaInsert, useUUID } from '../lib/hooks';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@ -19,5 +20,9 @@ declare global {
|
||||
useMemo: useMemo;
|
||||
useEffect: useEffect;
|
||||
useCallback: useCallback;
|
||||
useIsMediaAsset: useIsMediaAsset;
|
||||
useMediaAsset: useMediaAsset;
|
||||
useMediaInsert: useMediaInsert;
|
||||
useUUID: useUUID;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user