Merge branch 'main' into next
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@staticcms/core",
|
||||
"version": "1.2.10",
|
||||
"version": "1.2.12",
|
||||
"license": "MIT",
|
||||
"description": "Static CMS core application.",
|
||||
"repository": "https://github.com/StaticJsCMS/static-cms",
|
||||
@ -54,7 +54,7 @@
|
||||
"@codemirror/language": "6.6.0",
|
||||
"@codemirror/language-data": "6.1.0",
|
||||
"@codemirror/legacy-modes": "6.3.1",
|
||||
"@codemirror/lint": "6.1.1",
|
||||
"@codemirror/lint": "6.2.0",
|
||||
"@codemirror/search": "6.2.3",
|
||||
"@codemirror/state": "6.2.0",
|
||||
"@codemirror/theme-one-dark": "6.1.1",
|
||||
@ -76,9 +76,9 @@
|
||||
"@reduxjs/toolkit": "1.9.3",
|
||||
"@styled-icons/fluentui-system-regular": "10.47.0",
|
||||
"@styled-icons/remix-editor": "10.46.0",
|
||||
"@udecode/plate": "19.6.0",
|
||||
"@udecode/plate-juice": "19.5.0",
|
||||
"@udecode/plate-serializer-md": "19.5.0",
|
||||
"@udecode/plate": "19.7.0",
|
||||
"@udecode/plate-juice": "19.7.0",
|
||||
"@udecode/plate-serializer-md": "19.7.0",
|
||||
"@uiw/codemirror-extensions-langs": "4.19.9",
|
||||
"@uiw/react-codemirror": "4.19.9",
|
||||
"ajv": "8.12.0",
|
||||
@ -144,7 +144,7 @@
|
||||
"slate": "0.91.4",
|
||||
"slate-history": "0.86.0",
|
||||
"slate-hyperscript": "0.77.0",
|
||||
"slate-react": "0.91.5",
|
||||
"slate-react": "0.91.6",
|
||||
"stream-browserify": "3.0.0",
|
||||
"styled-components": "5.3.6",
|
||||
"symbol-observable": "4.0.0",
|
||||
@ -193,7 +193,7 @@
|
||||
"@types/jwt-decode": "2.2.1",
|
||||
"@types/lodash": "4.14.191",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@types/node": "16.18.12",
|
||||
"@types/node": "16.18.13",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-color": "3.0.6",
|
||||
@ -203,8 +203,8 @@
|
||||
"@types/styled-components": "5.1.26",
|
||||
"@types/url-join": "4.0.1",
|
||||
"@types/uuid": "9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.53.0",
|
||||
"@typescript-eslint/parser": "5.53.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
||||
"@typescript-eslint/parser": "5.54.0",
|
||||
"axios": "1.3.4",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-loader": "9.1.2",
|
||||
|
@ -1,2 +0,0 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const createHashHistory = jest.fn();
|
@ -1,18 +0,0 @@
|
||||
import { history } from '../routing/history';
|
||||
import { getCollectionUrl, getNewEntryUrl } from '../lib/urlHelper';
|
||||
|
||||
export function searchCollections(query: string, collection?: string) {
|
||||
if (collection) {
|
||||
history.push(`/collections/${collection}/search/${query}`);
|
||||
} else {
|
||||
history.push(`/search/${query}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function showCollection(collectionName: string) {
|
||||
history.push(getCollectionUrl(collectionName));
|
||||
}
|
||||
|
||||
export function createNewEntry(collectionName: string) {
|
||||
history.push(getNewEntryUrl(collectionName));
|
||||
}
|
@ -48,7 +48,6 @@ import {
|
||||
selectIsFetching,
|
||||
selectPublishedSlugs,
|
||||
} from '../reducers/selectors/entries';
|
||||
import { navigateToEntry } from '../routing/history';
|
||||
import { addSnackbar } from '../store/slices/snackbars';
|
||||
import { createAssetProxy } from '../valueObjects/AssetProxy';
|
||||
import createEntry from '../valueObjects/createEntry';
|
||||
@ -56,6 +55,7 @@ import { addAssets, getAsset } from './media';
|
||||
import { loadMedia, waitForMediaLibraryToLoad } from './mediaLibrary';
|
||||
import { waitUntil } from './waitUntil';
|
||||
|
||||
import type { NavigateFunction } from 'react-router-dom';
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { ThunkDispatch } from 'redux-thunk';
|
||||
import type { Backend } from '../backend';
|
||||
@ -950,7 +950,7 @@ export function getSerializedEntry(collection: Collection, entry: Entry): Entry
|
||||
return serializedEntry;
|
||||
}
|
||||
|
||||
export function persistEntry(collection: Collection) {
|
||||
export function persistEntry(collection: Collection, navigate: NavigateFunction) {
|
||||
return async (dispatch: ThunkDispatch<RootState, {}, AnyAction>, getState: () => RootState) => {
|
||||
const state = getState();
|
||||
const entryDraft = state.entryDraft;
|
||||
@ -1039,7 +1039,7 @@ export function persistEntry(collection: Collection) {
|
||||
}
|
||||
if (entry.slug !== newSlug) {
|
||||
await dispatch(loadEntry(collection, newSlug));
|
||||
navigateToEntry(collection.name, newSlug);
|
||||
navigate(`/collections/${collection.name}/entries/${newSlug}`);
|
||||
} else {
|
||||
await dispatch(loadEntry(collection, newSlug, true));
|
||||
}
|
||||
|
@ -4,15 +4,23 @@ import { styled } from '@mui/material/styles';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { connect } from 'react-redux';
|
||||
import { Navigate, Route, Routes, useLocation, useParams } from 'react-router-dom';
|
||||
import {
|
||||
Navigate,
|
||||
Route,
|
||||
Routes,
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useParams,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom';
|
||||
import { ScrollSync } from 'react-scroll-sync';
|
||||
import TopBarProgress from 'react-topbar-progress-indicator';
|
||||
|
||||
import { loginUser as loginUserAction } from '@staticcms/core/actions/auth';
|
||||
import { discardDraft as discardDraftAction } from '@staticcms/core/actions/entries';
|
||||
import { discardDraft } from '@staticcms/core/actions/entries';
|
||||
import { currentBackend } from '@staticcms/core/backend';
|
||||
import { colors, GlobalStyles } from '@staticcms/core/components/UI/styles';
|
||||
import { history } from '@staticcms/core/routing/history';
|
||||
import { useAppDispatch } from '@staticcms/core/store/hooks';
|
||||
import { getDefaultPath } from '../../lib/util/collection.util';
|
||||
import CollectionRoute from '../collection/CollectionRoute';
|
||||
import EditorRoute from '../editor/EditorRoute';
|
||||
@ -82,8 +90,10 @@ const App = ({
|
||||
useMediaLibrary,
|
||||
t,
|
||||
scrollSyncEnabled,
|
||||
discardDraft,
|
||||
}: TranslatedProps<AppProps>) => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const configError = useCallback(
|
||||
(error?: string) => {
|
||||
return (
|
||||
@ -140,22 +150,28 @@ const App = ({
|
||||
base_url={config.config.backend.base_url}
|
||||
authEndpoint={config.config.backend.auth_endpoint}
|
||||
config={config.config}
|
||||
clearHash={() => history.replace('/')}
|
||||
clearHash={() => navigate('/', { replace: true })}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}, [AuthComponent, auth.error, auth.isFetching, config.config, handleLogin, t]);
|
||||
}, [AuthComponent, auth.error, auth.isFetching, config.config, handleLogin, navigate, t]);
|
||||
|
||||
const defaultPath = useMemo(() => getDefaultPath(collections), [collections]);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const [searchParams] = useSearchParams();
|
||||
useEffect(() => {
|
||||
if (!/\/collections\/[a-zA-Z0-9_-]+\/entries\/[a-zA-Z0-9_-]+/g.test(pathname)) {
|
||||
discardDraft();
|
||||
if (
|
||||
/\/collections\/[a-zA-Z0-9_-]+\/entries\/[a-zA-Z0-9_-]+/g.test(pathname) ||
|
||||
(/\/collections\/[a-zA-Z0-9_-]+\/new/g.test(pathname) &&
|
||||
searchParams.get('duplicate') === 'true')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pathname]);
|
||||
|
||||
dispatch(discardDraft());
|
||||
}, [dispatch, pathname, searchParams]);
|
||||
|
||||
const content = useMemo(() => {
|
||||
if (!user) {
|
||||
@ -265,7 +281,6 @@ function mapStateToProps(state: RootState) {
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loginUser: loginUserAction,
|
||||
discardDraft: discardDraftAction,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
@ -12,13 +12,13 @@ import Toolbar from '@mui/material/Toolbar';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { connect } from 'react-redux';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { logoutUser as logoutUserAction } from '@staticcms/core/actions/auth';
|
||||
import { createNewEntry } from '@staticcms/core/actions/collections';
|
||||
import { openMediaLibrary as openMediaLibraryAction } from '@staticcms/core/actions/mediaLibrary';
|
||||
import { checkBackendStatus as checkBackendStatusAction } from '@staticcms/core/actions/status';
|
||||
import { buttons, colors } from '@staticcms/core/components/UI/styles';
|
||||
import { stripProtocol } from '@staticcms/core/lib/urlHelper';
|
||||
import { stripProtocol, getNewEntryUrl } from '@staticcms/core/lib/urlHelper';
|
||||
import NavLink from '../UI/NavLink';
|
||||
import SettingsDropdown from '../UI/SettingsDropdown';
|
||||
|
||||
@ -82,9 +82,7 @@ const Header = ({
|
||||
setAnchorEl(null);
|
||||
}, []);
|
||||
|
||||
const handleCreatePostClick = useCallback((collectionName: string) => {
|
||||
createNewEntry(collectionName);
|
||||
}, []);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const creatableCollections = useMemo(
|
||||
() =>
|
||||
@ -148,7 +146,7 @@ const Header = ({
|
||||
{creatableCollections.map(collection => (
|
||||
<MenuItem
|
||||
key={collection.name}
|
||||
onClick={() => handleCreatePostClick(collection.name)}
|
||||
onClick={() => navigate(getNewEntryUrl(collection.name))}
|
||||
>
|
||||
{collection.label_singular || collection.label}
|
||||
</MenuItem>
|
||||
|
@ -83,6 +83,10 @@ const CollectionView = ({
|
||||
}, [collection]);
|
||||
|
||||
const newEntryUrl = useMemo(() => {
|
||||
if (!collectionName || !collection) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let url = 'fields' in collection && collection.create ? getNewEntryUrl(collectionName) : '';
|
||||
if (url && filterTerm) {
|
||||
url = getNewEntryUrl(collectionName);
|
||||
@ -90,6 +94,7 @@ const CollectionView = ({
|
||||
url = `${newEntryUrl}?path=${filterTerm}`;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}, [collection, collectionName, filterTerm]);
|
||||
|
||||
@ -120,6 +125,10 @@ const CollectionView = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (!collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EntriesCollection
|
||||
collection={collection}
|
||||
@ -142,21 +151,21 @@ const CollectionView = ({
|
||||
|
||||
const onSortClick = useCallback(
|
||||
async (key: string, direction?: SortDirection) => {
|
||||
await sortByField(collection, key, direction);
|
||||
collection && (await sortByField(collection, key, direction));
|
||||
},
|
||||
[collection, sortByField],
|
||||
);
|
||||
|
||||
const onFilterClick = useCallback(
|
||||
async (filter: ViewFilter) => {
|
||||
await filterByField(collection, filter);
|
||||
collection && (await filterByField(collection, filter));
|
||||
},
|
||||
[collection, filterByField],
|
||||
);
|
||||
|
||||
const onGroupClick = useCallback(
|
||||
async (group: ViewGroup) => {
|
||||
await groupByField(collection, group);
|
||||
collection && (await groupByField(collection, group));
|
||||
},
|
||||
[collection, groupByField],
|
||||
);
|
||||
@ -176,7 +185,7 @@ const CollectionView = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultSort = collection.sortable_fields?.default;
|
||||
const defaultSort = collection?.sortable_fields?.default;
|
||||
if (!defaultSort || !defaultSort.field) {
|
||||
if (!readyToLoad) {
|
||||
setReadyToLoad(true);
|
||||
@ -220,7 +229,7 @@ const CollectionView = ({
|
||||
<>
|
||||
<SearchResultContainer>
|
||||
<SearchResultHeading>
|
||||
{t(searchResultKey, { searchTerm, collection: collection.label })}
|
||||
{t(searchResultKey, { searchTerm, collection: collection?.label })}
|
||||
</SearchResultHeading>
|
||||
</SearchResultContainer>
|
||||
<CollectionControls viewStyle={viewStyle} onChangeViewStyle={changeViewStyle} t={t} />
|
||||
@ -254,7 +263,7 @@ const CollectionView = ({
|
||||
interface CollectionViewOwnProps {
|
||||
isSearchResults?: boolean;
|
||||
isSingleSearchResult?: boolean;
|
||||
name: string;
|
||||
name?: string;
|
||||
searchTerm?: string;
|
||||
filterTerm?: string;
|
||||
}
|
||||
@ -270,13 +279,13 @@ function mapStateToProps(state: RootState, ownProps: TranslatedProps<CollectionV
|
||||
filterTerm = '',
|
||||
t,
|
||||
} = ownProps;
|
||||
const collection: Collection = name ? collections[name] : collections[0];
|
||||
const sort = selectEntriesSort(state, collection.name);
|
||||
const collection = (name ? collections[name] : collections[0]) as Collection | undefined;
|
||||
const sort = selectEntriesSort(state, collection?.name);
|
||||
const sortableFields = selectSortableFields(collection, t);
|
||||
const viewFilters = selectViewFilters(collection);
|
||||
const viewGroups = selectViewGroups(collection);
|
||||
const filter = selectEntriesFilter(state, collection.name);
|
||||
const group = selectEntriesGroup(state, collection.name);
|
||||
const filter = selectEntriesFilter(state, collection?.name);
|
||||
const group = selectEntriesGroup(state, collection?.name);
|
||||
const viewStyle = selectViewStyle(state);
|
||||
|
||||
return {
|
||||
|
@ -28,11 +28,11 @@ const CollectionRoute = ({
|
||||
|
||||
const defaultPath = useMemo(() => getDefaultPath(collections), [collections]);
|
||||
|
||||
if (!name || !collection) {
|
||||
if (!searchTerm && (!name || !collection)) {
|
||||
return <Navigate to={defaultPath} />;
|
||||
}
|
||||
|
||||
if ('files' in collection && collection.files?.length === 1) {
|
||||
if (collection && 'files' in collection && collection.files?.length === 1) {
|
||||
return <Navigate to={`/collections/${collection.name}/entries/${collection.files[0].name}`} />;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ const EntriesSearch = ({
|
||||
searchEntries,
|
||||
clearSearch,
|
||||
}: EntriesSearchProps) => {
|
||||
console.log('collections', collections);
|
||||
const collectionNames = useMemo(() => Object.keys(collections), [collections]);
|
||||
|
||||
const getCursor = useCallback(() => {
|
||||
@ -80,6 +81,7 @@ function mapStateToProps(state: RootState, ownProps: EntriesSearchOwnProps) {
|
||||
const isFetching = state.search.isFetching;
|
||||
const page = state.search.page;
|
||||
const entries = selectSearchedEntries(state, collectionNames);
|
||||
console.log('searched entries', entries);
|
||||
return { isFetching, page, collections, viewStyle, entries, searchTerm };
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ import ListItemText from '@mui/material/ListItemText';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import React, { useMemo } from 'react';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { searchCollections } from '@staticcms/core/actions/collections';
|
||||
import { colors } from '@staticcms/core/components/UI/styles';
|
||||
import { getAdditionalLinks, getIcon } from '@staticcms/core/lib/registry';
|
||||
import NavLink from '../UI/NavLink';
|
||||
@ -48,6 +48,15 @@ const Sidebar = ({
|
||||
t,
|
||||
filterTerm,
|
||||
}: TranslatedProps<SidebarProps>) => {
|
||||
const navigate = useNavigate();
|
||||
function searchCollections(query: string, collection?: string) {
|
||||
if (collection) {
|
||||
navigate(`/collections/${collection}/search/${query}`);
|
||||
} else {
|
||||
navigate(`/search/${query}`);
|
||||
}
|
||||
}
|
||||
|
||||
const collectionLinks = useMemo(
|
||||
() =>
|
||||
Object.values(collections)
|
||||
|
@ -1,31 +1,28 @@
|
||||
import { createHashHistory } from 'history';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { connect } from 'react-redux';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { logoutUser as logoutUserAction } from '@staticcms/core/actions/auth';
|
||||
import { logoutUser } from '@staticcms/core/actions/auth';
|
||||
import {
|
||||
createDraftDuplicateFromEntry as createDraftDuplicateFromEntryAction,
|
||||
createEmptyDraft as createEmptyDraftAction,
|
||||
deleteDraftLocalBackup as deleteDraftLocalBackupAction,
|
||||
deleteEntry as deleteEntryAction,
|
||||
deleteLocalBackup as deleteLocalBackupAction,
|
||||
discardDraft as discardDraftAction,
|
||||
loadEntries as loadEntriesAction,
|
||||
loadEntry as loadEntryAction,
|
||||
loadLocalBackup as loadLocalBackupAction,
|
||||
persistEntry as persistEntryAction,
|
||||
persistLocalBackup as persistLocalBackupAction,
|
||||
retrieveLocalBackup as retrieveLocalBackupAction,
|
||||
createDraftDuplicateFromEntry,
|
||||
createEmptyDraft,
|
||||
deleteDraftLocalBackup,
|
||||
deleteEntry,
|
||||
deleteLocalBackup,
|
||||
loadEntry,
|
||||
loadLocalBackup,
|
||||
persistEntry,
|
||||
persistLocalBackup,
|
||||
retrieveLocalBackup,
|
||||
} from '@staticcms/core/actions/entries';
|
||||
import {
|
||||
loadScroll as loadScrollAction,
|
||||
toggleScroll as toggleScrollAction,
|
||||
} from '@staticcms/core/actions/scroll';
|
||||
import { loadScroll, toggleScroll } from '@staticcms/core/actions/scroll';
|
||||
import { selectFields } from '@staticcms/core/lib/util/collection.util';
|
||||
import { useWindowEvent } from '@staticcms/core/lib/util/window.util';
|
||||
import { selectEntry } from '@staticcms/core/reducers/selectors/entries';
|
||||
import { history, navigateToCollection, navigateToNewEntry } from '@staticcms/core/routing/history';
|
||||
import { useAppDispatch } from '@staticcms/core/store/hooks';
|
||||
import confirm from '../UI/Confirm';
|
||||
import Loader from '../UI/Loader';
|
||||
import EditorInterface from './EditorInterface';
|
||||
@ -38,10 +35,10 @@ import type {
|
||||
} from '@staticcms/core/interface';
|
||||
import type { RootState } from '@staticcms/core/store';
|
||||
import type { Blocker } from 'history';
|
||||
import type { ComponentType } from 'react';
|
||||
import type { ComponentType, FC } from 'react';
|
||||
import type { ConnectedProps } from 'react-redux';
|
||||
|
||||
const Editor = ({
|
||||
const Editor: FC<TranslatedProps<EditorProps>> = ({
|
||||
entry,
|
||||
entryDraft,
|
||||
fields,
|
||||
@ -50,34 +47,25 @@ const Editor = ({
|
||||
hasChanged,
|
||||
displayUrl,
|
||||
isModification,
|
||||
logoutUser,
|
||||
draftKey,
|
||||
t,
|
||||
editorBackLink,
|
||||
toggleScroll,
|
||||
scrollSyncEnabled,
|
||||
loadScroll,
|
||||
showDelete,
|
||||
slug,
|
||||
localBackup,
|
||||
persistLocalBackup,
|
||||
loadEntry,
|
||||
persistEntry,
|
||||
deleteEntry,
|
||||
loadLocalBackup,
|
||||
retrieveLocalBackup,
|
||||
deleteLocalBackup,
|
||||
deleteDraftLocalBackup,
|
||||
createDraftDuplicateFromEntry,
|
||||
createEmptyDraft,
|
||||
discardDraft,
|
||||
}: TranslatedProps<EditorProps>) => {
|
||||
t,
|
||||
}) => {
|
||||
const [version, setVersion] = useState(0);
|
||||
|
||||
const history = createHashHistory();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const createBackup = useMemo(
|
||||
() =>
|
||||
debounce(function (entry: Entry, collection: Collection) {
|
||||
persistLocalBackup(entry, collection);
|
||||
dispatch(persistLocalBackup(entry, collection));
|
||||
}, 2000),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
@ -86,9 +74,9 @@ const Editor = ({
|
||||
const deleteBackup = useCallback(() => {
|
||||
createBackup.cancel();
|
||||
if (slug) {
|
||||
deleteLocalBackup(collection, slug);
|
||||
dispatch(deleteLocalBackup(collection, slug));
|
||||
}
|
||||
deleteDraftLocalBackup();
|
||||
dispatch(deleteDraftLocalBackup());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [collection, createBackup, slug]);
|
||||
|
||||
@ -105,29 +93,29 @@ const Editor = ({
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await persistEntry(collection);
|
||||
await dispatch(persistEntry(collection, navigate));
|
||||
setVersion(version + 1);
|
||||
|
||||
deleteBackup();
|
||||
|
||||
if (createNew) {
|
||||
navigateToNewEntry(collection.name);
|
||||
if (duplicate && entryDraft.entry) {
|
||||
createDraftDuplicateFromEntry(entryDraft.entry);
|
||||
dispatch(createDraftDuplicateFromEntry(entryDraft.entry));
|
||||
navigate(`/collections/${collection.name}/new`, { replace: true });
|
||||
} else {
|
||||
setSubmitted(false);
|
||||
setTimeout(() => {
|
||||
dispatch(createEmptyDraft(collection, location.search));
|
||||
setVersion(version + 1);
|
||||
navigate(`/collections/${collection.name}/new`, { replace: true });
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
}, 100);
|
||||
},
|
||||
[
|
||||
collection,
|
||||
createDraftDuplicateFromEntry,
|
||||
deleteBackup,
|
||||
entryDraft.entry,
|
||||
persistEntry,
|
||||
version,
|
||||
],
|
||||
[collection, deleteBackup, dispatch, entryDraft.entry, navigate, version],
|
||||
);
|
||||
|
||||
const handleDuplicateEntry = useCallback(() => {
|
||||
@ -135,9 +123,9 @@ const Editor = ({
|
||||
return;
|
||||
}
|
||||
|
||||
navigateToNewEntry(collection.name);
|
||||
createDraftDuplicateFromEntry(entryDraft.entry);
|
||||
}, [collection.name, createDraftDuplicateFromEntry, entryDraft.entry]);
|
||||
dispatch(createDraftDuplicateFromEntry(entryDraft.entry));
|
||||
navigate(`/collections/${collection.name}/new?duplicate=true`, { replace: true });
|
||||
}, [collection.name, dispatch, entryDraft.entry, navigate]);
|
||||
|
||||
const handleDeleteEntry = useCallback(async () => {
|
||||
if (entryDraft.hasChanged) {
|
||||
@ -161,15 +149,15 @@ const Editor = ({
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
return navigateToCollection(collection.name);
|
||||
return navigate(`/collections/${collection.name}`);
|
||||
}
|
||||
|
||||
setTimeout(async () => {
|
||||
await deleteEntry(collection, slug);
|
||||
await dispatch(deleteEntry(collection, slug));
|
||||
deleteBackup();
|
||||
return navigateToCollection(collection.name);
|
||||
return navigate(`/collections/${collection.name}`);
|
||||
}, 0);
|
||||
}, [collection, deleteBackup, deleteEntry, entryDraft.hasChanged, slug]);
|
||||
}, [collection, deleteBackup, dispatch, entryDraft.hasChanged, navigate, slug]);
|
||||
|
||||
const [prevLocalBackup, setPrevLocalBackup] = useState<
|
||||
| {
|
||||
@ -187,7 +175,7 @@ const Editor = ({
|
||||
});
|
||||
|
||||
if (confirmLoadBackupBody) {
|
||||
loadLocalBackup();
|
||||
dispatch(loadLocalBackup());
|
||||
setVersion(version + 1);
|
||||
} else {
|
||||
deleteBackup();
|
||||
@ -198,7 +186,7 @@ const Editor = ({
|
||||
}
|
||||
|
||||
setPrevLocalBackup(localBackup);
|
||||
}, [deleteBackup, loadLocalBackup, localBackup, prevLocalBackup, version]);
|
||||
}, [deleteBackup, dispatch, localBackup, prevLocalBackup, version]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasChanged && entryDraft.entry) {
|
||||
@ -211,32 +199,22 @@ const Editor = ({
|
||||
}, [collection, createBackup, entryDraft.entry, hasChanged]);
|
||||
|
||||
const [prevCollection, setPrevCollection] = useState<Collection | null>(null);
|
||||
const [preSlug, setPrevSlug] = useState<string | undefined | null>(null);
|
||||
const [prevSlug, setPrevSlug] = useState<string | undefined | null>(null);
|
||||
useEffect(() => {
|
||||
if (!slug && preSlug !== slug) {
|
||||
if (!slug && prevSlug !== slug) {
|
||||
setTimeout(() => {
|
||||
createEmptyDraft(collection, location.search);
|
||||
dispatch(createEmptyDraft(collection, location.search));
|
||||
});
|
||||
} else if (slug && (prevCollection !== collection || preSlug !== slug)) {
|
||||
} else if (slug && (prevCollection !== collection || prevSlug !== slug)) {
|
||||
setTimeout(() => {
|
||||
retrieveLocalBackup(collection, slug);
|
||||
loadEntry(collection, slug);
|
||||
dispatch(retrieveLocalBackup(collection, slug));
|
||||
dispatch(loadEntry(collection, slug));
|
||||
});
|
||||
}
|
||||
|
||||
setPrevCollection(collection);
|
||||
setPrevSlug(slug);
|
||||
}, [
|
||||
collection,
|
||||
createEmptyDraft,
|
||||
discardDraft,
|
||||
entryDraft.entry,
|
||||
loadEntry,
|
||||
preSlug,
|
||||
prevCollection,
|
||||
retrieveLocalBackup,
|
||||
slug,
|
||||
]);
|
||||
}, [collection, entryDraft.entry, prevSlug, prevCollection, slug, dispatch]);
|
||||
|
||||
const leaveMessage = useMemo(() => t('editor.editor.onLeavePage'), [t]);
|
||||
|
||||
@ -284,7 +262,19 @@ const Editor = ({
|
||||
return () => {
|
||||
unblock();
|
||||
};
|
||||
}, [collection.name, deleteBackup, discardDraft, navigationBlocker]);
|
||||
}, [collection.name, history, navigationBlocker]);
|
||||
|
||||
const handleLogout = useCallback(() => {
|
||||
dispatch(logoutUser());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleToggleScroll = useCallback(async () => {
|
||||
await dispatch(toggleScroll());
|
||||
}, [dispatch]);
|
||||
|
||||
const handleLoadScroll = useCallback(async () => {
|
||||
await dispatch(loadScroll());
|
||||
}, [dispatch]);
|
||||
|
||||
if (entry && entry.error) {
|
||||
return (
|
||||
@ -313,11 +303,11 @@ const Editor = ({
|
||||
displayUrl={displayUrl}
|
||||
isNewEntry={!slug}
|
||||
isModification={isModification}
|
||||
onLogoutClick={logoutUser}
|
||||
onLogoutClick={handleLogout}
|
||||
editorBackLink={editorBackLink}
|
||||
toggleScroll={toggleScroll}
|
||||
toggleScroll={handleToggleScroll}
|
||||
scrollSyncEnabled={scrollSyncEnabled}
|
||||
loadScroll={loadScroll}
|
||||
loadScroll={handleLoadScroll}
|
||||
submitted={submitted}
|
||||
t={t}
|
||||
/>
|
||||
@ -378,25 +368,7 @@ function mapStateToProps(state: RootState, ownProps: CollectionViewOwnProps) {
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loadEntry: loadEntryAction,
|
||||
loadEntries: loadEntriesAction,
|
||||
loadLocalBackup: loadLocalBackupAction,
|
||||
deleteDraftLocalBackup: deleteDraftLocalBackupAction,
|
||||
retrieveLocalBackup: retrieveLocalBackupAction,
|
||||
persistLocalBackup: persistLocalBackupAction,
|
||||
deleteLocalBackup: deleteLocalBackupAction,
|
||||
createDraftDuplicateFromEntry: createDraftDuplicateFromEntryAction,
|
||||
createEmptyDraft: createEmptyDraftAction,
|
||||
discardDraft: discardDraftAction,
|
||||
persistEntry: persistEntryAction,
|
||||
deleteEntry: deleteEntryAction,
|
||||
logoutUser: logoutUserAction,
|
||||
toggleScroll: toggleScrollAction,
|
||||
loadScroll: loadScrollAction,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
const connector = connect(mapStateToProps);
|
||||
export type EditorProps = ConnectedProps<typeof connector>;
|
||||
|
||||
export default connector(translate()(Editor) as ComponentType<EditorProps>);
|
||||
|
@ -137,7 +137,7 @@ interface EditorInterfaceProps {
|
||||
isModification: boolean;
|
||||
onLogoutClick: () => void;
|
||||
editorBackLink: string;
|
||||
toggleScroll: () => Promise<{ readonly type: 'TOGGLE_SCROLL' }>;
|
||||
toggleScroll: () => Promise<void>;
|
||||
scrollSyncEnabled: boolean;
|
||||
loadScroll: () => void;
|
||||
submitted: boolean;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { styled } from '@mui/material/styles';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createPortal } from 'react-dom';
|
||||
import Frame from 'react-frame-component';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { connect } from 'react-redux';
|
||||
@ -132,7 +132,7 @@ const PreviewPane = (props: TranslatedProps<EditorPreviewPaneProps>) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
return createPortal(
|
||||
<StyledPreviewContent className="preview-content">
|
||||
{!entry || !entry.data ? null : (
|
||||
<ErrorBoundary config={config}>
|
||||
|
@ -156,9 +156,13 @@ export function selectDefaultSortableFields(collection: Collection, backend: Bac
|
||||
}
|
||||
|
||||
export function selectSortableFields(
|
||||
collection: Collection,
|
||||
collection: Collection | undefined,
|
||||
t: (key: string) => string,
|
||||
): SortableField[] {
|
||||
if (!collection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const fields = (collection.sortable_fields?.fields ?? [])
|
||||
.map(key => {
|
||||
if (key === COMMIT_DATE) {
|
||||
@ -177,12 +181,12 @@ export function selectSortableFields(
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function selectViewFilters(collection: Collection) {
|
||||
return collection.view_filters;
|
||||
export function selectViewFilters(collection?: Collection) {
|
||||
return collection?.view_filters;
|
||||
}
|
||||
|
||||
export function selectViewGroups(collection: Collection) {
|
||||
return collection.view_groups;
|
||||
export function selectViewGroups(collection?: Collection) {
|
||||
return collection?.view_groups;
|
||||
}
|
||||
|
||||
export function selectFieldsComments(collection: Collection, entryMap: Entry) {
|
||||
|
@ -21,7 +21,7 @@ const de: LocalePhrasesRoot = {
|
||||
content: 'Inhalt',
|
||||
workflow: 'Arbeitsablauf',
|
||||
media: 'Medien',
|
||||
quickAdd: 'Schnell-Erstellung',
|
||||
quickAdd: 'Schnellerstellung',
|
||||
},
|
||||
app: {
|
||||
errorHeader: 'Fehler beim Laden der CMS-Konfiguration.',
|
||||
@ -36,8 +36,8 @@ const de: LocalePhrasesRoot = {
|
||||
},
|
||||
collection: {
|
||||
sidebar: {
|
||||
collections: 'Inhaltstypen',
|
||||
allCollections: 'Allen Inhaltstypen',
|
||||
collections: 'Bereiche',
|
||||
allCollections: 'Allen Bereichen',
|
||||
searchAll: 'Alles durchsuchen',
|
||||
searchIn: 'Suchen in',
|
||||
},
|
||||
@ -231,6 +231,10 @@ const de: LocalePhrasesRoot = {
|
||||
},
|
||||
},
|
||||
ui: {
|
||||
common: {
|
||||
yes: 'Ja',
|
||||
no: 'Nein',
|
||||
},
|
||||
default: {
|
||||
goBackToSite: 'Zurück zur Seite',
|
||||
},
|
||||
|
@ -6,16 +6,28 @@ import type { CollectionViewStyle } from '@staticcms/core/constants/collectionVi
|
||||
import type { Entry, Group, GroupMap, Sort } from '@staticcms/core/interface';
|
||||
import type { RootState } from '@staticcms/core/store';
|
||||
|
||||
export function selectEntriesSort(entries: RootState, collection: string) {
|
||||
export function selectEntriesSort(entries: RootState, collection?: string) {
|
||||
if (!collection) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sort = entries.entries.sort as Sort | undefined;
|
||||
return sort?.[collection];
|
||||
}
|
||||
|
||||
export const selectEntriesFilter = (collectionName: string) => (entries: RootState) => {
|
||||
return entries.entries.filter?.[collectionName];
|
||||
export const selectEntriesFilter = (collectionName?: string) => (entries: RootState) => {
|
||||
if (!collectionName) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return entries.entries.filter?.[collectionName] ?? {};
|
||||
};
|
||||
|
||||
export function selectEntriesGroup(entries: RootState, collection: string) {
|
||||
export function selectEntriesGroup(entries: RootState, collection?: string) {
|
||||
if (!collection) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const group = entries.entries.group as Group | undefined;
|
||||
return group?.[collection] || {};
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import { createHashHistory } from 'history';
|
||||
|
||||
const history = createHashHistory();
|
||||
|
||||
export function navigateToCollection(collectionName: string) {
|
||||
return history.push(`/collections/${collectionName}`);
|
||||
}
|
||||
|
||||
export function navigateToNewEntry(collectionName: string) {
|
||||
return history.replace(`/collections/${collectionName}/new`);
|
||||
}
|
||||
|
||||
export function navigateToEntry(collectionName: string, slug: string) {
|
||||
return history.replace(`/collections/${collectionName}/entries/${slug}`);
|
||||
}
|
||||
|
||||
export { history };
|
Reference in New Issue
Block a user