GitLab backend built with cursor API (#1343)
This commit is contained in:
committed by
Shawn Erquhart
parent
1f94e3123d
commit
b65f68efd4
@ -11,7 +11,9 @@ const Entries = ({
|
||||
page,
|
||||
onPaginate,
|
||||
isFetching,
|
||||
viewStyle
|
||||
viewStyle,
|
||||
cursor,
|
||||
handleCursorActions,
|
||||
}) => {
|
||||
const loadingMessages = [
|
||||
'Loading Entries',
|
||||
@ -25,9 +27,9 @@ const Entries = ({
|
||||
collections={collections}
|
||||
entries={entries}
|
||||
publicFolder={publicFolder}
|
||||
page={page}
|
||||
onPaginate={onPaginate}
|
||||
viewStyle={viewStyle}
|
||||
cursor={cursor}
|
||||
handleCursorActions={handleCursorActions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -46,6 +48,8 @@ Entries.propTypes = {
|
||||
page: PropTypes.number,
|
||||
isFetching: PropTypes.bool,
|
||||
viewStyle: PropTypes.string,
|
||||
cursor: PropTypes.any.isRequired,
|
||||
handleCursorActions: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Entries;
|
||||
|
@ -2,18 +2,26 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import { loadEntries as actionLoadEntries } from 'Actions/entries';
|
||||
import { partial } from 'lodash';
|
||||
import {
|
||||
loadEntries as actionLoadEntries,
|
||||
traverseCollectionCursor as actionTraverseCollectionCursor,
|
||||
} from 'Actions/entries';
|
||||
import { selectEntries } from 'Reducers';
|
||||
import { selectCollectionEntriesCursor } from 'Reducers/cursors';
|
||||
import Cursor from 'ValueObjects/Cursor';
|
||||
import Entries from './Entries';
|
||||
|
||||
class EntriesCollection extends React.Component {
|
||||
static propTypes = {
|
||||
collection: ImmutablePropTypes.map.isRequired,
|
||||
publicFolder: PropTypes.string.isRequired,
|
||||
page: PropTypes.number,
|
||||
entries: ImmutablePropTypes.list,
|
||||
isFetching: PropTypes.bool.isRequired,
|
||||
viewStyle: PropTypes.string,
|
||||
cursor: PropTypes.object.isRequired,
|
||||
loadEntries: PropTypes.func.isRequired,
|
||||
traverseCollectionCursor: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -30,31 +38,31 @@ class EntriesCollection extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = page => {
|
||||
const { collection, loadEntries } = this.props;
|
||||
loadEntries(collection, page);
|
||||
}
|
||||
handleCursorActions = (cursor, action) => {
|
||||
const { collection, traverseCollectionCursor } = this.props;
|
||||
traverseCollectionCursor(collection, action);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { collection, entries, publicFolder, page, isFetching, viewStyle } = this.props;
|
||||
const { collection, entries, publicFolder, isFetching, viewStyle, cursor } = this.props;
|
||||
|
||||
return (
|
||||
<Entries
|
||||
collections={collection}
|
||||
entries={entries}
|
||||
publicFolder={publicFolder}
|
||||
page={page}
|
||||
onPaginate={this.handleLoadMore}
|
||||
isFetching={isFetching}
|
||||
collectionName={collection.get('label')}
|
||||
viewStyle={viewStyle}
|
||||
cursor={cursor}
|
||||
handleCursorActions={partial(this.handleCursorActions, cursor)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const { name, collection, viewStyle } = ownProps;
|
||||
const { collection, viewStyle } = ownProps;
|
||||
const { config } = state;
|
||||
const publicFolder = config.get('public_folder');
|
||||
const page = state.entries.getIn(['pages', collection.get('name'), 'page']);
|
||||
@ -62,11 +70,15 @@ function mapStateToProps(state, ownProps) {
|
||||
const entries = selectEntries(state, collection.get('name'));
|
||||
const isFetching = state.entries.getIn(['pages', collection.get('name'), 'isFetching'], false);
|
||||
|
||||
return { publicFolder, collection, page, entries, isFetching, viewStyle };
|
||||
const rawCursor = selectCollectionEntriesCursor(state.cursors, collection.get("name"));
|
||||
const cursor = Cursor.create(rawCursor).clearData();
|
||||
|
||||
return { publicFolder, collection, page, entries, isFetching, viewStyle, cursor };
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loadEntries: actionLoadEntries,
|
||||
traverseCollectionCursor: actionTraverseCollectionCursor,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(EntriesCollection);
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
searchEntries as actionSearchEntries,
|
||||
clearSearch as actionClearSearch
|
||||
} from 'Actions/search';
|
||||
import Cursor from 'ValueObjects/Cursor';
|
||||
import Entries from './Entries';
|
||||
|
||||
class EntriesSearch extends React.Component {
|
||||
@ -36,15 +37,27 @@ class EntriesSearch extends React.Component {
|
||||
this.props.clearSearch();
|
||||
}
|
||||
|
||||
handleLoadMore = (page) => {
|
||||
const { searchTerm, searchEntries } = this.props;
|
||||
if (!isNaN(page)) searchEntries(searchTerm, page);
|
||||
getCursor = () => {
|
||||
const { page } = this.props;
|
||||
return Cursor.create({
|
||||
actions: isNaN(page) ? [] : ["append_next"],
|
||||
});
|
||||
};
|
||||
|
||||
handleCursorActions = (action) => {
|
||||
const { page, searchTerm, searchEntries } = this.props;
|
||||
if (action === "append_next") {
|
||||
const nextPage = page + 1;
|
||||
searchEntries(searchTerm, nextPage);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { collections, entries, publicFolder, page, isFetching } = this.props;
|
||||
return (
|
||||
<Entries
|
||||
cursor={this.getCursor()}
|
||||
handleCursorActions={this.handleCursorActions}
|
||||
collections={collections}
|
||||
entries={entries}
|
||||
publicFolder={publicFolder}
|
||||
@ -59,8 +72,8 @@ class EntriesSearch extends React.Component {
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const { searchTerm } = ownProps;
|
||||
const collections = ownProps.collections.toIndexedSeq();
|
||||
const isFetching = state.entries.getIn(['search', 'isFetching']);
|
||||
const page = state.entries.getIn(['search', 'page']);
|
||||
const isFetching = state.search.get('isFetching');
|
||||
const page = state.search.get('page');
|
||||
const entries = selectSearchedEntries(state);
|
||||
const publicFolder = state.config.get('public_folder');
|
||||
|
||||
|
@ -5,6 +5,7 @@ import Waypoint from 'react-waypoint';
|
||||
import { Map } from 'immutable';
|
||||
import { selectFields, selectInferedField } from 'Reducers/collections';
|
||||
import EntryCard from './EntryCard';
|
||||
import Cursor from 'ValueObjects/Cursor';
|
||||
|
||||
export default class EntryListing extends React.Component {
|
||||
static propTypes = {
|
||||
@ -14,13 +15,14 @@ export default class EntryListing extends React.Component {
|
||||
ImmutablePropTypes.iterable,
|
||||
]).isRequired,
|
||||
entries: ImmutablePropTypes.list,
|
||||
onPaginate: PropTypes.func.isRequired,
|
||||
page: PropTypes.number,
|
||||
viewStyle: PropTypes.string,
|
||||
};
|
||||
|
||||
handleLoadMore = () => {
|
||||
this.props.onPaginate(this.props.page + 1);
|
||||
const { cursor, handleCursorActions } = this.props;
|
||||
if (Cursor.create(cursor).actions.has("append_next")) {
|
||||
handleCursorActions("append_next");
|
||||
}
|
||||
};
|
||||
|
||||
inferFields = collection => {
|
||||
@ -48,12 +50,12 @@ export default class EntryListing extends React.Component {
|
||||
const collectionLabel = collection.get('label');
|
||||
const inferedFields = this.inferFields(collection);
|
||||
const entryCardProps = { collection, entry, inferedFields, publicFolder, key: idx, collectionLabel };
|
||||
return <EntryCard {...entryCardProps}/>;
|
||||
return <EntryCard {...entryCardProps} />;
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { collections, entries, publicFolder } = this.props;
|
||||
const { collections } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -1,35 +1,36 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
const ErrorComponent = () => {
|
||||
const issueUrl = "https://github.com/netlify/netlify-cms/issues/new";
|
||||
return (
|
||||
<div className="nc-errorBoundary">
|
||||
<h1 className="nc-errorBoundary-heading">Sorry!</h1>
|
||||
<p>
|
||||
<span>There's been an error - please </span>
|
||||
<a href={issueUrl} target="_blank" className="nc-errorBoundary-link">report it</a>!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
const DefaultErrorComponent = () => {
|
||||
};
|
||||
|
||||
export class ErrorBoundary extends React.Component {
|
||||
static propTypes = {
|
||||
render: PropTypes.element,
|
||||
};
|
||||
const ISSUE_URL = "https://github.com/netlify/netlify-cms/issues/new";
|
||||
|
||||
export class ErrorBoundary extends React.Component {
|
||||
state = {
|
||||
hasError: false,
|
||||
errorMessage: '',
|
||||
};
|
||||
|
||||
componentDidCatch(error) {
|
||||
console.error(error);
|
||||
this.setState({ hasError: true });
|
||||
this.setState({ hasError: true, errorMessage: error.toString() });
|
||||
}
|
||||
|
||||
render() {
|
||||
const errorComponent = this.props.errorComponent || <ErrorComponent/>;
|
||||
return this.state.hasError ? errorComponent : this.props.children;
|
||||
const { hasError, errorMessage } = this.state;
|
||||
if (!hasError) {
|
||||
return this.props.children;
|
||||
}
|
||||
return (
|
||||
<div className="nc-errorBoundary">
|
||||
<h1 className="nc-errorBoundary-heading">Sorry!</h1>
|
||||
<p>
|
||||
<span>There's been an error - please </span>
|
||||
<a href={ISSUE_URL} target="_blank" className="nc-errorBoundary-link">report it</a>!
|
||||
</p>
|
||||
<p>{errorMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import iconDragHandle from './drag-handle.svg';
|
||||
import iconEye from './eye.svg';
|
||||
import iconFolder from './folder.svg';
|
||||
import iconGithub from './github.svg';
|
||||
import iconGitlab from './gitlab.svg';
|
||||
import iconGrid from './grid.svg';
|
||||
import iconH1 from './h1.svg';
|
||||
import iconH2 from './h2.svg';
|
||||
@ -55,6 +56,7 @@ const images = {
|
||||
'eye': iconEye,
|
||||
'folder': iconFolder,
|
||||
'github': iconGithub,
|
||||
'gitlab': iconGitlab,
|
||||
'grid': iconGrid,
|
||||
'h1': iconH1,
|
||||
'h2': iconH2,
|
||||
|
1
src/components/UI/Icon/images/gitlab.svg
Normal file
1
src/components/UI/Icon/images/gitlab.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="26" height="26" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M22.616 14.971L21.52 11.5l-2.173-6.882a.37.37 0 0 0-.71 0l-2.172 6.882H9.252L7.079 4.617a.37.37 0 0 0-.71 0l-2.172 6.882L3.1 14.971c-.1.317.01.664.27.86l9.487 7.094 9.487-7.094a.781.781 0 0 0 .27-.86" fill="#FC6D26"/><path d="M12.858 22.925L16.465 11.5H9.251z" fill="#E24329"/><path d="M12.858 22.925L9.251 11.5H4.197z" fill="#FC6D26"/><path d="M4.197 11.499L3.1 14.971c-.1.317.01.664.27.86l9.487 7.094L4.197 11.5z" fill="#FCA326"/><path d="M4.197 11.499H9.25L7.08 4.617a.37.37 0 0 0-.71 0l-2.172 6.882z" fill="#E24329"/><path d="M12.858 22.925L16.465 11.5h5.055z" fill="#FC6D26"/><path d="M21.52 11.499l1.096 3.472c.1.317-.01.664-.271.86l-9.487 7.094L21.52 11.5z" fill="#FCA326"/><path d="M21.52 11.499h-5.055l2.172-6.882a.37.37 0 0 1 .71 0l2.173 6.882z" fill="#E24329"/></g></svg>
|
After Width: | Height: | Size: 889 B |
Reference in New Issue
Block a user