fix: media not displaying with local backend (#324)
This commit is contained in:
parent
a9e0770fd0
commit
9dba066ca4
@ -1,17 +1,18 @@
|
|||||||
backend:
|
backend:
|
||||||
name: github
|
name: github
|
||||||
branch: main
|
branch: main
|
||||||
repo: owner/repo
|
repo: staticjscms/static-cms-github
|
||||||
|
|
||||||
media_folder: static/media
|
|
||||||
public_folder: /media
|
|
||||||
|
|
||||||
local_backend: true
|
local_backend: true
|
||||||
|
|
||||||
|
media_folder: assets/upload
|
||||||
|
public_folder: /assets/upload
|
||||||
collections:
|
collections:
|
||||||
- name: posts
|
- name: posts
|
||||||
label: Posts
|
label: Posts
|
||||||
label_singular: Post
|
label_singular: Post
|
||||||
|
media_folder: /assets/posts
|
||||||
|
public_folder: /assets/posts
|
||||||
description: >
|
description: >
|
||||||
The description is a great place for tone setting, high level information,
|
The description is a great place for tone setting, high level information,
|
||||||
and editing guidelines that are specific to a collection.
|
and editing guidelines that are specific to a collection.
|
||||||
@ -94,8 +95,6 @@ collections:
|
|||||||
- name: settings
|
- name: settings
|
||||||
label: Settings
|
label: Settings
|
||||||
delete: false
|
delete: false
|
||||||
editor:
|
|
||||||
preview: false
|
|
||||||
files:
|
files:
|
||||||
- name: general
|
- name: general
|
||||||
label: Site Settings
|
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 title = entry.data.site_title;
|
||||||
const posts = entry.data.posts;
|
const posts = entry.data.posts;
|
||||||
const thumb = posts && posts.thumb;
|
const thumb = posts && posts.thumb;
|
||||||
|
|
||||||
const [thumbUrl, setThumbUrl] = useState('');
|
const thumbUrl = useMediaAsset(thumb, collection, undefined, entry);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let alive = true;
|
|
||||||
|
|
||||||
const loadThumb = async () => {
|
|
||||||
const thumbAsset = await getAsset(thumb);
|
|
||||||
if (alive) {
|
|
||||||
setThumbUrl(thumbAsset.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadThumb();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
alive = false;
|
|
||||||
};
|
|
||||||
}, [thumb]);
|
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
'div',
|
'div',
|
||||||
@ -96,7 +79,6 @@ const CustomPage = () => {
|
|||||||
return h('div', {}, 'I am a custom page!');
|
return h('div', {}, 'I am a custom page!');
|
||||||
};
|
};
|
||||||
|
|
||||||
CMS.registerPreviewStyle('.toastui-editor-contents h1 { color: blue }', { raw: true });
|
|
||||||
CMS.registerPreviewTemplate('posts', PostPreview);
|
CMS.registerPreviewTemplate('posts', PostPreview);
|
||||||
CMS.registerPreviewTemplate('general', GeneralPreview);
|
CMS.registerPreviewTemplate('general', GeneralPreview);
|
||||||
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
CMS.registerPreviewTemplate('authors', AuthorsPreview);
|
||||||
|
@ -93,10 +93,6 @@ export function getAsset<F extends BaseField = UnknownField>(
|
|||||||
dispatch: ThunkDispatch<RootState, {}, AnyAction>,
|
dispatch: ThunkDispatch<RootState, {}, AnyAction>,
|
||||||
getState: () => RootState,
|
getState: () => RootState,
|
||||||
): Promise<AssetProxy> => {
|
): Promise<AssetProxy> => {
|
||||||
if (!collection || !entry || !path) {
|
|
||||||
return Promise.resolve(emptyAsset);
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
if (!state.config.config) {
|
if (!state.config.config) {
|
||||||
return Promise.resolve(emptyAsset);
|
return Promise.resolve(emptyAsset);
|
||||||
|
@ -166,8 +166,12 @@ export default class ProxyBackend implements BackendClass {
|
|||||||
return deserializeMediaFile(file);
|
return deserializeMediaFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMediaDisplayURL(displayURL: DisplayURL) {
|
getMediaDisplayURL(displayURL: DisplayURL): Promise<string> {
|
||||||
return Promise.resolve(typeof displayURL === 'string' ? displayURL : displayURL.id);
|
if (typeof displayURL === 'string') {
|
||||||
|
return Promise.resolve(displayURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(displayURL.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
async persistMedia(assetProxy: AssetProxy, options: PersistOptions) {
|
async persistMedia(assetProxy: AssetProxy, options: PersistOptions) {
|
||||||
|
@ -3,19 +3,20 @@ import CardActionArea from '@mui/material/CardActionArea';
|
|||||||
import CardContent from '@mui/material/CardContent';
|
import CardContent from '@mui/material/CardContent';
|
||||||
import CardMedia from '@mui/material/CardMedia';
|
import CardMedia from '@mui/material/CardMedia';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { getAsset as getAssetAction } from '@staticcms/core/actions/media';
|
import { getAsset as getAssetAction } from '@staticcms/core/actions/media';
|
||||||
import { VIEW_STYLE_GRID, VIEW_STYLE_LIST } from '@staticcms/core/constants/collectionViews';
|
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 { selectEntryCollectionTitle } from '@staticcms/core/lib/util/collection.util';
|
||||||
import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias';
|
import { selectIsLoadingAsset } from '@staticcms/core/reducers/medias';
|
||||||
|
|
||||||
import type { ConnectedProps } from 'react-redux';
|
|
||||||
import type { CollectionViewStyle } from '@staticcms/core/constants/collectionViews';
|
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 { RootState } from '@staticcms/core/store';
|
||||||
|
import type { ConnectedProps } from 'react-redux';
|
||||||
|
|
||||||
const EntryCard = ({
|
const EntryCard = ({
|
||||||
collection,
|
collection,
|
||||||
@ -25,22 +26,10 @@ const EntryCard = ({
|
|||||||
imageField,
|
imageField,
|
||||||
collectionLabel,
|
collectionLabel,
|
||||||
viewStyle = VIEW_STYLE_LIST,
|
viewStyle = VIEW_STYLE_LIST,
|
||||||
getAsset,
|
|
||||||
}: NestedCollectionProps) => {
|
}: NestedCollectionProps) => {
|
||||||
const summary = useMemo(() => selectEntryCollectionTitle(collection, entry), [collection, entry]);
|
const summary = useMemo(() => selectEntryCollectionTitle(collection, entry), [collection, entry]);
|
||||||
|
|
||||||
const [imageUrl, setImageUrl] = useState<string>();
|
const imageUrl = useMediaAsset(image, collection, imageField, entry);
|
||||||
useEffect(() => {
|
|
||||||
if (!image) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getImage = async () => {
|
|
||||||
setImageUrl((await getAsset(collection, entry, image, imageField)).toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
getImage();
|
|
||||||
}, [collection, entry, getAsset, image, imageField]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
@ -84,8 +73,9 @@ function mapStateToProps(state: RootState, ownProps: EntryCardOwnProps) {
|
|||||||
let image = inferedFields.imageField
|
let image = inferedFields.imageField
|
||||||
? (entryData?.[inferedFields.imageField] as string | undefined)
|
? (entryData?.[inferedFields.imageField] as string | undefined)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (image) {
|
if (image) {
|
||||||
image = encodeURI(image);
|
image = encodeURI(image.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoadingAsset = selectIsLoadingAsset(state.medias);
|
const isLoadingAsset = selectIsLoadingAsset(state.medias);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { styled } from '@mui/material/styles';
|
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 { 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';
|
import type { MediaLibraryDisplayURL } from '@staticcms/core/reducers/mediaLibrary';
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ const MediaLibraryCard = ({
|
|||||||
isDraft,
|
isDraft,
|
||||||
loadDisplayURL,
|
loadDisplayURL,
|
||||||
}: MediaLibraryCardProps) => {
|
}: MediaLibraryCardProps) => {
|
||||||
const url = useMemo(() => displayURL.url, [displayURL.url]);
|
const url = useMediaAsset(displayURL.url);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!displayURL.url) {
|
if (!displayURL.url) {
|
||||||
|
@ -2,15 +2,18 @@ import createReactClass from 'create-react-class';
|
|||||||
import { createElement, useCallback, useEffect, useMemo, useState } from 'react';
|
import { createElement, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import bootstrap from './bootstrap';
|
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';
|
import Registry from './lib/registry';
|
||||||
|
|
||||||
export * from './backends';
|
export * from './backends';
|
||||||
export * from './widgets';
|
|
||||||
export * from './media-libraries';
|
|
||||||
export { default as locales } from './locales';
|
|
||||||
export * from './lib';
|
|
||||||
|
|
||||||
export * from './interface';
|
export * from './interface';
|
||||||
|
export * from './lib';
|
||||||
|
export { default as locales } from './locales';
|
||||||
|
export * from './media-libraries';
|
||||||
|
export * from './widgets';
|
||||||
|
|
||||||
const CMS = {
|
const CMS = {
|
||||||
...Registry,
|
...Registry,
|
||||||
@ -25,6 +28,10 @@ if (typeof window !== 'undefined') {
|
|||||||
window.useEffect = window.useEffect || useEffect;
|
window.useEffect = window.useEffect || useEffect;
|
||||||
window.useCallback = window.useCallback || useCallback;
|
window.useCallback = window.useCallback || useCallback;
|
||||||
window.h = window.h || createElement;
|
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;
|
export default CMS;
|
||||||
|
@ -235,6 +235,9 @@ export interface DisplayURLState {
|
|||||||
|
|
||||||
export type TranslatedProps<T> = T & ReactPolyglotTranslateProps;
|
export type TranslatedProps<T> = T & ReactPolyglotTranslateProps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Should use `useMediaAsset` React hook instead
|
||||||
|
*/
|
||||||
export type GetAssetFunction<F extends BaseField = UnknownField> = (
|
export type GetAssetFunction<F extends BaseField = UnknownField> = (
|
||||||
path: string,
|
path: string,
|
||||||
field?: F,
|
field?: F,
|
||||||
@ -248,6 +251,9 @@ export interface WidgetControlProps<T, F extends BaseField = UnknownField> {
|
|||||||
fieldsErrors: FieldsErrors;
|
fieldsErrors: FieldsErrors;
|
||||||
submitted: boolean;
|
submitted: boolean;
|
||||||
forList: boolean;
|
forList: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Should use `useMediaAsset` React hook instead
|
||||||
|
*/
|
||||||
getAsset: GetAssetFunction<F>;
|
getAsset: GetAssetFunction<F>;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
isFieldDuplicate: EditorControlProps['isFieldDuplicate'];
|
isFieldDuplicate: EditorControlProps['isFieldDuplicate'];
|
||||||
@ -273,6 +279,9 @@ export interface WidgetPreviewProps<T = unknown, F extends BaseField = UnknownFi
|
|||||||
collection: Collection<F>;
|
collection: Collection<F>;
|
||||||
entry: Entry;
|
entry: Entry;
|
||||||
field: RenderedField<F>;
|
field: RenderedField<F>;
|
||||||
|
/**
|
||||||
|
* @deprecated Should use `useMediaAsset` React hook instead
|
||||||
|
*/
|
||||||
getAsset: GetAssetFunction<F>;
|
getAsset: GetAssetFunction<F>;
|
||||||
value: T | undefined | null;
|
value: T | undefined | null;
|
||||||
}
|
}
|
||||||
@ -300,6 +309,9 @@ export interface TemplatePreviewProps<T = EntryData, EF extends BaseField = Unkn
|
|||||||
entry: Entry<T>;
|
entry: Entry<T>;
|
||||||
document: Document | undefined | null;
|
document: Document | undefined | null;
|
||||||
window: Window | undefined | null;
|
window: Window | undefined | null;
|
||||||
|
/**
|
||||||
|
* @deprecated Should use `useMediaAsset` React hook instead
|
||||||
|
*/
|
||||||
getAsset: GetAssetFunction<Field<EF>>;
|
getAsset: GetAssetFunction<Field<EF>>;
|
||||||
widgetFor: (name: T extends EntryData ? string : keyof T) => ReactNode;
|
widgetFor: (name: T extends EntryData ? string : keyof T) => ReactNode;
|
||||||
widgetsFor: WidgetsFor<T>;
|
widgetsFor: WidgetsFor<T>;
|
||||||
|
@ -127,7 +127,7 @@ export function summaryFormatter(summaryTemplate: string, entry: Entry, collecti
|
|||||||
|
|
||||||
export function folderFormatter(
|
export function folderFormatter(
|
||||||
folderTemplate: string,
|
folderTemplate: string,
|
||||||
entry: Entry | undefined,
|
entry: Entry | null | undefined,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
defaultFolder: string,
|
defaultFolder: string,
|
||||||
folderKey: string,
|
folderKey: string,
|
||||||
|
@ -4,18 +4,22 @@ import { getAsset } from '@staticcms/core/actions/media';
|
|||||||
import { useAppDispatch } from '@staticcms/core/store/hooks';
|
import { useAppDispatch } from '@staticcms/core/store/hooks';
|
||||||
import { isNotEmpty } from '../util/string.util';
|
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>(
|
export default function useMediaAsset<T extends Field>(
|
||||||
url: string,
|
url: string | undefined,
|
||||||
collection: Collection<T>,
|
collection?: Collection<T>,
|
||||||
field: T,
|
field?: T,
|
||||||
entry: Entry,
|
entry?: Entry,
|
||||||
): string {
|
): string {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [assetSource, setAssetSource] = useState(url);
|
const [assetSource, setAssetSource] = useState(url);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const fetchMedia = async () => {
|
const fetchMedia = async () => {
|
||||||
const asset = (await dispatch(getAsset<T>(collection, entry, url, field)))?.toString() ?? '';
|
const asset = (await dispatch(getAsset<T>(collection, entry, url, field)))?.toString() ?? '';
|
||||||
setAssetSource(asset);
|
setAssetSource(asset);
|
||||||
@ -24,5 +28,5 @@ export default function useMediaAsset<T extends FileOrImageField | MarkdownField
|
|||||||
fetchMedia();
|
fetchMedia();
|
||||||
}, [collection, dispatch, entry, field, url]);
|
}, [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',
|
folderKey: 'media_folder' | 'public_folder',
|
||||||
config: Config,
|
config: Config,
|
||||||
c: Collection,
|
c: Collection,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | null | undefined,
|
||||||
field: FileOrImageField | MarkdownField,
|
field: FileOrImageField | MarkdownField,
|
||||||
) {
|
) {
|
||||||
let currentFolder = config[folderKey]!;
|
let currentFolder = config[folderKey]!;
|
||||||
@ -158,7 +158,7 @@ function traverseFields(
|
|||||||
folderKey: 'media_folder' | 'public_folder',
|
folderKey: 'media_folder' | 'public_folder',
|
||||||
config: Config,
|
config: Config,
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | null | undefined,
|
||||||
field: FileOrImageField | MarkdownField | ListField | ObjectField,
|
field: FileOrImageField | MarkdownField | ListField | ObjectField,
|
||||||
fields: Field[],
|
fields: Field[],
|
||||||
currentFolder: string,
|
currentFolder: string,
|
||||||
@ -227,7 +227,7 @@ function traverseFields(
|
|||||||
export function selectMediaFolder(
|
export function selectMediaFolder(
|
||||||
config: Config,
|
config: Config,
|
||||||
collection: Collection | undefined | null,
|
collection: Collection | undefined | null,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | null | undefined,
|
||||||
field: Field | undefined,
|
field: Field | undefined,
|
||||||
) {
|
) {
|
||||||
const name = 'media_folder';
|
const name = 'media_folder';
|
||||||
@ -279,7 +279,7 @@ export function selectMediaFilePublicPath(
|
|||||||
export function selectMediaFilePath(
|
export function selectMediaFilePath(
|
||||||
config: Config,
|
config: Config,
|
||||||
collection: Collection | null,
|
collection: Collection | null,
|
||||||
entryMap: Entry | undefined,
|
entryMap: Entry | null | undefined,
|
||||||
mediaPath: string,
|
mediaPath: string,
|
||||||
field: Field | undefined,
|
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 CmsAPI from '../index';
|
||||||
import type createReactClass from 'create-react-class';
|
import type createReactClass from 'create-react-class';
|
||||||
import type { createElement, useEffect, useState, useMemo, useCallback } from 'react';
|
import type { createElement, useEffect, useState, useMemo, useCallback } from 'react';
|
||||||
|
import type { useIsMediaAsset, useMediaAsset, useMediaInsert, useUUID } from '../lib/hooks';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -19,5 +20,9 @@ declare global {
|
|||||||
useMemo: useMemo;
|
useMemo: useMemo;
|
||||||
useEffect: useEffect;
|
useEffect: useEffect;
|
||||||
useCallback: useCallback;
|
useCallback: useCallback;
|
||||||
|
useIsMediaAsset: useIsMediaAsset;
|
||||||
|
useMediaAsset: useMediaAsset;
|
||||||
|
useMediaInsert: useMediaInsert;
|
||||||
|
useUUID: useUUID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user