Merge branch 'main' into next
This commit is contained in:
commit
7ca5f6d1e6
@ -4,6 +4,7 @@ import flatten from 'lodash/flatten';
|
||||
import get from 'lodash/get';
|
||||
import isError from 'lodash/isError';
|
||||
import uniq from 'lodash/uniq';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import { resolveFormat } from './formats/formats';
|
||||
import { commitMessageFormatter, slugFormatter } from './lib/formatters';
|
||||
@ -40,12 +41,9 @@ import {
|
||||
selectMediaFolders,
|
||||
} from './lib/util/collection.util';
|
||||
import filterEntries from './lib/util/filter.util';
|
||||
import {
|
||||
DRAFT_MEDIA_FILES,
|
||||
selectMediaFilePath,
|
||||
selectMediaFilePublicPath,
|
||||
} from './lib/util/media.util';
|
||||
import { DRAFT_MEDIA_FILES, selectMediaFilePublicPath } from './lib/util/media.util';
|
||||
import { selectCustomPath, slugFromCustomPath } from './lib/util/nested.util';
|
||||
import { isNullish } from './lib/util/null.util';
|
||||
import { set } from './lib/util/object.util';
|
||||
import { dateParsers, expandPath, extractTemplateVars } from './lib/widgets/stringTemplate';
|
||||
import createEntry from './valueObjects/createEntry';
|
||||
@ -63,46 +61,70 @@ import type {
|
||||
DisplayURL,
|
||||
Entry,
|
||||
EntryData,
|
||||
EntryDraft,
|
||||
EventData,
|
||||
FilterRule,
|
||||
ImplementationEntry,
|
||||
MediaField,
|
||||
ObjectValue,
|
||||
PersistArgs,
|
||||
SearchQueryResponse,
|
||||
SearchResponse,
|
||||
UnknownField,
|
||||
User,
|
||||
ValueOrNestedValue,
|
||||
} from './interface';
|
||||
import type { AsyncLock } from './lib/util';
|
||||
import type { RootState } from './store';
|
||||
import type AssetProxy from './valueObjects/AssetProxy';
|
||||
|
||||
function updateAssetProxies(
|
||||
assetProxies: AssetProxy[],
|
||||
config: Config,
|
||||
collection: Collection,
|
||||
entryDraft: EntryDraft,
|
||||
path: string,
|
||||
) {
|
||||
assetProxies.map(asset => {
|
||||
// update media files path based on entry path
|
||||
const oldPath = asset.path;
|
||||
entryDraft.entry.path = path;
|
||||
function updatePath(entryPath: string, assetPath: string): string | null {
|
||||
const pathDir = dirname(entryPath);
|
||||
|
||||
const folderPath = joinUrlPath(
|
||||
collection && 'folder' in collection ? collection.folder : '',
|
||||
DRAFT_MEDIA_FILES,
|
||||
);
|
||||
const pathParts = assetPath.split(DRAFT_MEDIA_FILES);
|
||||
const restOfPath = pathParts.length > 1 ? pathParts[1] : null;
|
||||
if (restOfPath === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const newPath = selectMediaFilePath(
|
||||
config,
|
||||
collection,
|
||||
entryDraft.entry,
|
||||
oldPath.replace(folderPath, ''),
|
||||
asset.field,
|
||||
);
|
||||
asset.path = newPath;
|
||||
return joinUrlPath(pathDir, restOfPath).replace(/\/\//g, '');
|
||||
}
|
||||
|
||||
function updateAssetFields(data: ValueOrNestedValue, path: string): ValueOrNestedValue {
|
||||
if (
|
||||
isNullish(data) ||
|
||||
typeof data === 'number' ||
|
||||
typeof data === 'boolean' ||
|
||||
data instanceof Date
|
||||
) {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(child => updateAssetFields(child, path));
|
||||
}
|
||||
|
||||
if (typeof data === 'object') {
|
||||
return Object.keys(data).reduce((acc, key) => {
|
||||
acc[key] = updateAssetFields(data[key], path);
|
||||
|
||||
return acc;
|
||||
}, {} as ObjectValue);
|
||||
}
|
||||
|
||||
const newPath = updatePath(path, data);
|
||||
if (!newPath) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
function updateAssetProxies(assetProxies: AssetProxy[], path: string) {
|
||||
assetProxies.forEach(asset => {
|
||||
const newPath = updatePath(path, asset.path);
|
||||
if (newPath) {
|
||||
asset.path = newPath;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -865,13 +887,16 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
|
||||
customPath,
|
||||
);
|
||||
const path = customPath || (selectEntryPath(collection, slug) ?? '');
|
||||
|
||||
entryDraft.entry.path = path;
|
||||
entryDraft.entry.data = updateAssetFields(entryDraft.entry.data, path) as ObjectValue;
|
||||
updateAssetProxies(assetProxies, path);
|
||||
|
||||
dataFile = {
|
||||
path,
|
||||
slug,
|
||||
raw: this.entryToRaw(collection, entryDraft.entry),
|
||||
};
|
||||
|
||||
updateAssetProxies(assetProxies, config, collection, entryDraft, path);
|
||||
} else {
|
||||
const slug = entryDraft.entry.slug;
|
||||
dataFile = {
|
||||
|
@ -96,14 +96,23 @@ const EditorInterface = ({
|
||||
slug,
|
||||
}: TranslatedProps<EditorInterfaceProps>) => {
|
||||
const { locales, defaultLocale } = useMemo(() => getI18nInfo(collection), [collection]) ?? {};
|
||||
const [selectedLocale, setSelectedLocale] = useState<string>(locales?.[1] ?? 'en');
|
||||
const translatedLocales = useMemo(
|
||||
() => locales?.filter(locale => locale !== defaultLocale) ?? [],
|
||||
[locales, defaultLocale],
|
||||
);
|
||||
|
||||
const [previewActive, setPreviewActive] = useState(
|
||||
localStorage.getItem(PREVIEW_VISIBLE) !== 'false',
|
||||
);
|
||||
|
||||
const i18nEnabled = useMemo(() => locales && locales.length > 0, [locales]);
|
||||
|
||||
const [i18nActive, setI18nActive] = useState(
|
||||
Boolean(localStorage.getItem(I18N_VISIBLE) !== 'false' && locales && locales.length > 0),
|
||||
Boolean(localStorage.getItem(I18N_VISIBLE) !== 'false' && i18nEnabled),
|
||||
);
|
||||
|
||||
const [selectedLocale, setSelectedLocale] = useState<string>(
|
||||
(i18nActive ? translatedLocales?.[0] : defaultLocale) ?? 'en',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -132,8 +141,11 @@ const EditorInterface = ({
|
||||
const handleToggleI18n = useCallback(() => {
|
||||
const newI18nActive = !i18nActive;
|
||||
setI18nActive(newI18nActive);
|
||||
setSelectedLocale(selectedLocale =>
|
||||
newI18nActive && selectedLocale === defaultLocale ? translatedLocales?.[0] : selectedLocale,
|
||||
);
|
||||
localStorage.setItem(I18N_VISIBLE, `${newI18nActive}`);
|
||||
}, [i18nActive]);
|
||||
}, [i18nActive, setSelectedLocale, translatedLocales, defaultLocale]);
|
||||
|
||||
const handleLocaleChange = useCallback((locale: string) => {
|
||||
setSelectedLocale(locale);
|
||||
@ -174,40 +186,60 @@ const EditorInterface = ({
|
||||
setShowMobilePreview(old => !old);
|
||||
}, []);
|
||||
|
||||
const editor = (
|
||||
<div
|
||||
key={defaultLocale}
|
||||
id="control-pane"
|
||||
className={classNames(
|
||||
`
|
||||
w-full
|
||||
`,
|
||||
(finalPreviewActive || i18nActive) &&
|
||||
const editor = useMemo(
|
||||
() => (
|
||||
<div
|
||||
key={defaultLocale}
|
||||
id="control-pane"
|
||||
className={classNames(
|
||||
`
|
||||
overflow-y-auto
|
||||
styled-scrollbars
|
||||
h-main-mobile
|
||||
md:h-main
|
||||
w-full
|
||||
`,
|
||||
showMobilePreview &&
|
||||
`
|
||||
hidden
|
||||
lg:block
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<EditorControlPane
|
||||
collection={collection}
|
||||
entry={entry}
|
||||
fields={fields}
|
||||
fieldsErrors={fieldsErrors}
|
||||
locale={defaultLocale}
|
||||
submitted={submitted}
|
||||
hideBorder={!finalPreviewActive && !i18nActive}
|
||||
slug={slug}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
(finalPreviewActive || i18nActive) &&
|
||||
`
|
||||
overflow-y-auto
|
||||
styled-scrollbars
|
||||
h-main-mobile
|
||||
md:h-main
|
||||
`,
|
||||
showMobilePreview &&
|
||||
`
|
||||
hidden
|
||||
lg:block
|
||||
`,
|
||||
)}
|
||||
>
|
||||
<EditorControlPane
|
||||
collection={collection}
|
||||
entry={entry}
|
||||
fields={fields}
|
||||
fieldsErrors={fieldsErrors}
|
||||
locale={i18nActive ? defaultLocale : selectedLocale}
|
||||
submitted={submitted}
|
||||
hideBorder={!finalPreviewActive && !i18nActive}
|
||||
canChangeLocale={i18nEnabled && !i18nActive}
|
||||
onLocaleChange={handleLocaleChange}
|
||||
slug={slug}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
[
|
||||
defaultLocale,
|
||||
finalPreviewActive,
|
||||
i18nActive,
|
||||
showMobilePreview,
|
||||
collection,
|
||||
entry,
|
||||
fields,
|
||||
fieldsErrors,
|
||||
selectedLocale,
|
||||
submitted,
|
||||
i18nEnabled,
|
||||
handleLocaleChange,
|
||||
slug,
|
||||
t,
|
||||
],
|
||||
);
|
||||
|
||||
const editorLocale = useMemo(
|
||||
@ -232,6 +264,7 @@ const EditorInterface = ({
|
||||
onLocaleChange={handleLocaleChange}
|
||||
submitted={submitted}
|
||||
canChangeLocale
|
||||
context="i18nSplit"
|
||||
hideBorder
|
||||
t={t}
|
||||
/>
|
||||
|
@ -31,6 +31,7 @@ export interface EditorControlPaneProps {
|
||||
slug?: string;
|
||||
onLocaleChange?: (locale: string) => void;
|
||||
allowDefaultLocale?: boolean;
|
||||
context?: 'default' | 'i18nSplit';
|
||||
}
|
||||
|
||||
const EditorControlPane = ({
|
||||
@ -45,6 +46,7 @@ const EditorControlPane = ({
|
||||
slug,
|
||||
onLocaleChange,
|
||||
allowDefaultLocale = false,
|
||||
context = 'default',
|
||||
t,
|
||||
}: TranslatedProps<EditorControlPaneProps>) => {
|
||||
const pathField = useMemo(
|
||||
@ -120,7 +122,9 @@ const EditorControlPane = ({
|
||||
})}
|
||||
canChangeLocale={canChangeLocale}
|
||||
onLocaleChange={onLocaleChange}
|
||||
allowDefaultLocale={allowDefaultLocale}
|
||||
excludeLocales={
|
||||
!allowDefaultLocale && context === 'i18nSplit' ? [i18n.defaultLocale] : []
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -9,8 +9,8 @@ interface LocaleDropdownProps {
|
||||
defaultLocale: string;
|
||||
dropdownText: string;
|
||||
canChangeLocale: boolean;
|
||||
allowDefaultLocale: boolean;
|
||||
onLocaleChange?: (locale: string) => void;
|
||||
excludeLocales?: string[];
|
||||
}
|
||||
|
||||
const LocaleDropdown = ({
|
||||
@ -18,8 +18,8 @@ const LocaleDropdown = ({
|
||||
defaultLocale,
|
||||
dropdownText,
|
||||
canChangeLocale,
|
||||
allowDefaultLocale,
|
||||
onLocaleChange,
|
||||
excludeLocales = [defaultLocale],
|
||||
}: LocaleDropdownProps) => {
|
||||
if (!canChangeLocale) {
|
||||
return (
|
||||
@ -41,7 +41,7 @@ const LocaleDropdown = ({
|
||||
<Menu label={dropdownText}>
|
||||
<MenuGroup>
|
||||
{locales
|
||||
.filter(locale => allowDefaultLocale || locale !== defaultLocale)
|
||||
.filter(locale => !excludeLocales.includes(locale))
|
||||
.map(locale => (
|
||||
<MenuItemButton key={locale} onClick={() => onLocaleChange?.(locale)}>
|
||||
{locale}
|
||||
|
@ -34,7 +34,7 @@ const Login = ({
|
||||
<div className="flex flex-col h-screen items-center justify-center bg-slate-50 dark:bg-slate-900">
|
||||
{config?.logo_url ? (
|
||||
<div
|
||||
className="h-40 w-80 mb-4 bg-cover bg-no-repeat bg-center object-cover"
|
||||
className="h-40 w-80 mb-4 bg-contain bg-no-repeat bg-center object-cover"
|
||||
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
||||
/>
|
||||
) : (
|
||||
|
@ -87,7 +87,7 @@ const Navbar = ({
|
||||
>
|
||||
{config?.logo_url ? (
|
||||
<div
|
||||
className="h-10 w-10 bg-cover bg-no-repeat bg-center object-cover"
|
||||
className="h-10 w-10 bg-contain bg-no-repeat bg-center object-cover"
|
||||
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
||||
/>
|
||||
) : (
|
||||
|
@ -320,7 +320,6 @@ export function selectMediaFilePath(
|
||||
}
|
||||
|
||||
let mediaFolder = selectMediaFolder(config, collection, entryMap, field, currentFolder);
|
||||
|
||||
if (!currentFolder) {
|
||||
let publicFolder = trim(config['public_folder'] ?? mediaFolder, '/');
|
||||
let mediaPathDir = trim(dirname(mediaPath), '/');
|
||||
@ -341,10 +340,14 @@ export function selectMediaFilePath(
|
||||
collection,
|
||||
entryMap,
|
||||
field,
|
||||
mediaPathDir.replace(publicFolder, mediaFolder),
|
||||
publicFolder === '' && mediaPathDir.startsWith(mediaFolder)
|
||||
? mediaPathDir
|
||||
: mediaPathDir.replace(publicFolder, mediaFolder),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return joinUrlPath(mediaFolder, basename(mediaPath));
|
||||
return mediaPath.startsWith(mediaFolder)
|
||||
? mediaPath
|
||||
: joinUrlPath(mediaFolder, basename(mediaPath));
|
||||
}
|
||||
|
@ -109,8 +109,17 @@ function mediaLibrary(
|
||||
return {
|
||||
...state,
|
||||
isVisible: false,
|
||||
insertOptions: undefined,
|
||||
forImage: false,
|
||||
forFolder: false,
|
||||
controlID: undefined,
|
||||
config: undefined,
|
||||
collection: undefined,
|
||||
collectionFile: undefined,
|
||||
field: undefined,
|
||||
value: undefined,
|
||||
alt: undefined,
|
||||
replaceIndex: undefined,
|
||||
insertOptions: undefined,
|
||||
};
|
||||
|
||||
case MEDIA_INSERT: {
|
||||
|
@ -1,5 +1,11 @@
|
||||
{
|
||||
"releases": [
|
||||
{
|
||||
"date": "2023-06-13T10:00:00.000Z",
|
||||
"version": "v2.5.0",
|
||||
"type": "minor",
|
||||
"description": "Switch i18n locales while in preview mode"
|
||||
},
|
||||
{
|
||||
"date": "2023-05-29T10:00:00.000Z",
|
||||
"version": "v2.4.4",
|
||||
|
Loading…
x
Reference in New Issue
Block a user