fix: persistent view style (#4138)

This commit is contained in:
andreascm 2020-08-13 19:21:47 +08:00 committed by GitHub
parent 5baa20bf67
commit 017883f0dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 36 deletions

View File

@ -73,6 +73,8 @@ export const ENTRY_DELETE_FAILURE = 'ENTRY_DELETE_FAILURE';
export const ADD_DRAFT_ENTRY_MEDIA_FILE = 'ADD_DRAFT_ENTRY_MEDIA_FILE'; export const ADD_DRAFT_ENTRY_MEDIA_FILE = 'ADD_DRAFT_ENTRY_MEDIA_FILE';
export const REMOVE_DRAFT_ENTRY_MEDIA_FILE = 'REMOVE_DRAFT_ENTRY_MEDIA_FILE'; export const REMOVE_DRAFT_ENTRY_MEDIA_FILE = 'REMOVE_DRAFT_ENTRY_MEDIA_FILE';
export const CHANGE_VIEW_STYLE = 'CHANGE_VIEW_STYLE';
/* /*
* Simple Action Creators (Internal) * Simple Action Creators (Internal)
* We still need to export them for tests * We still need to export them for tests
@ -240,6 +242,15 @@ export function filterByField(collection: Collection, filter: ViewFilter) {
}; };
} }
export function changeViewStyle(viewStyle: string) {
return {
type: CHANGE_VIEW_STYLE,
payload: {
style: viewStyle,
},
};
}
export function entryPersisting(collection: Collection, entry: EntryMap) { export function entryPersisting(collection: Collection, entry: EntryMap) {
return { return {
type: ENTRY_PERSIST_REQUEST, type: ENTRY_PERSIST_REQUEST,

View File

@ -11,10 +11,9 @@ import CollectionTop from './CollectionTop';
import EntriesCollection from './Entries/EntriesCollection'; import EntriesCollection from './Entries/EntriesCollection';
import EntriesSearch from './Entries/EntriesSearch'; import EntriesSearch from './Entries/EntriesSearch';
import CollectionControls from './CollectionControls'; import CollectionControls from './CollectionControls';
import { sortByField, filterByField } from '../../actions/entries'; import { sortByField, filterByField, changeViewStyle } from '../../actions/entries';
import { selectSortableFields, selectViewFilters } from '../../reducers/collections'; import { selectSortableFields, selectViewFilters } from '../../reducers/collections';
import { selectEntriesSort, selectEntriesFilter } from '../../reducers/entries'; import { selectEntriesSort, selectEntriesFilter, selectViewStyle } from '../../reducers/entries';
import { VIEW_STYLE_LIST } from '../../constants/collectionViews';
const CollectionContainer = styled.div` const CollectionContainer = styled.div`
margin: ${lengths.pageMargin}; margin: ${lengths.pageMargin};
@ -46,18 +45,10 @@ export class Collection extends React.Component {
onSortClick: PropTypes.func.isRequired, onSortClick: PropTypes.func.isRequired,
}; };
state = {
viewStyle: VIEW_STYLE_LIST,
};
renderEntriesCollection = () => { renderEntriesCollection = () => {
const { collection, filterTerm } = this.props; const { collection, filterTerm, viewStyle } = this.props;
return ( return (
<EntriesCollection <EntriesCollection collection={collection} viewStyle={viewStyle} filterTerm={filterTerm} />
collection={collection}
viewStyle={this.state.viewStyle}
filterTerm={filterTerm}
/>
); );
}; };
@ -71,12 +62,6 @@ export class Collection extends React.Component {
); );
}; };
handleChangeViewStyle = viewStyle => {
if (this.state.viewStyle !== viewStyle) {
this.setState({ viewStyle });
}
};
render() { render() {
const { const {
collection, collection,
@ -93,6 +78,8 @@ export class Collection extends React.Component {
t, t,
onFilterClick, onFilterClick,
filter, filter,
onChangeViewStyle,
viewStyle,
} = this.props; } = this.props;
let newEntryUrl = collection.get('create') ? getNewEntryUrl(collectionName) : ''; let newEntryUrl = collection.get('create') ? getNewEntryUrl(collectionName) : '';
@ -125,8 +112,8 @@ export class Collection extends React.Component {
<> <>
<CollectionTop collection={collection} newEntryUrl={newEntryUrl} /> <CollectionTop collection={collection} newEntryUrl={newEntryUrl} />
<CollectionControls <CollectionControls
viewStyle={this.state.viewStyle} viewStyle={viewStyle}
onChangeViewStyle={this.handleChangeViewStyle} onChangeViewStyle={onChangeViewStyle}
sortableFields={sortableFields} sortableFields={sortableFields}
onSortClick={onSortClick} onSortClick={onSortClick}
sort={sort} sort={sort}
@ -153,6 +140,7 @@ function mapStateToProps(state, ownProps) {
const sortableFields = selectSortableFields(collection, t); const sortableFields = selectSortableFields(collection, t);
const viewFilters = selectViewFilters(collection); const viewFilters = selectViewFilters(collection);
const filter = selectEntriesFilter(state.entries, collection.get('name')); const filter = selectEntriesFilter(state.entries, collection.get('name'));
const viewStyle = selectViewStyle(state.entries);
return { return {
collection, collection,
@ -165,12 +153,14 @@ function mapStateToProps(state, ownProps) {
sortableFields, sortableFields,
viewFilters, viewFilters,
filter, filter,
viewStyle,
}; };
} }
const mapDispatchToProps = { const mapDispatchToProps = {
sortByField, sortByField,
filterByField, filterByField,
changeViewStyle,
}; };
const mergeProps = (stateProps, dispatchProps, ownProps) => { const mergeProps = (stateProps, dispatchProps, ownProps) => {
@ -180,6 +170,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
onSortClick: (key, direction) => onSortClick: (key, direction) =>
dispatchProps.sortByField(stateProps.collection, key, direction), dispatchProps.sortByField(stateProps.collection, key, direction),
onFilterClick: filter => dispatchProps.filterByField(stateProps.collection, filter), onFilterClick: filter => dispatchProps.filterByField(stateProps.collection, filter),
onChangeViewStyle: viewStyle => dispatchProps.changeViewStyle(viewStyle),
}; };
}; };

View File

@ -30,12 +30,10 @@ exports[`Collection should render connected component 1`] = `
filter="Map {}" filter="Map {}"
sortablefields="" sortablefields=""
viewfilters="" viewfilters=""
viewstyle="VIEW_STYLE_LIST"
/> />
<mock-entries-collection <mock-entries-collection
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [] }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [] }"
filterterm="" filterterm=""
viewstyle="VIEW_STYLE_LIST"
/> />
</main> </main>
</div> </div>
@ -66,12 +64,9 @@ exports[`Collection should render with collection with create url 1`] = `
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
newentryurl="/collections/pages/new" newentryurl="/collections/pages/new"
/> />
<mock-collection-controls <mock-collection-controls />
viewstyle="VIEW_STYLE_LIST"
/>
<mock-entries-collection <mock-entries-collection
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
viewstyle="VIEW_STYLE_LIST"
/> />
</main> </main>
</div> </div>
@ -103,13 +98,10 @@ exports[`Collection should render with collection with create url and path 1`] =
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
newentryurl="/collections/pages/new?path=dir1/dir2" newentryurl="/collections/pages/new?path=dir1/dir2"
/> />
<mock-collection-controls <mock-collection-controls />
viewstyle="VIEW_STYLE_LIST"
/>
<mock-entries-collection <mock-entries-collection
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
filterterm="dir1/dir2" filterterm="dir1/dir2"
viewstyle="VIEW_STYLE_LIST"
/> />
</main> </main>
</div> </div>
@ -140,12 +132,9 @@ exports[`Collection should render with collection without create url 1`] = `
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
newentryurl="" newentryurl=""
/> />
<mock-collection-controls <mock-collection-controls />
viewstyle="VIEW_STYLE_LIST"
/>
<mock-entries-collection <mock-entries-collection
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }" collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
viewstyle="VIEW_STYLE_LIST"
/> />
</main> </main>
</div> </div>

View File

@ -14,6 +14,7 @@ import {
FILTER_ENTRIES_REQUEST, FILTER_ENTRIES_REQUEST,
FILTER_ENTRIES_SUCCESS, FILTER_ENTRIES_SUCCESS,
FILTER_ENTRIES_FAILURE, FILTER_ENTRIES_FAILURE,
CHANGE_VIEW_STYLE,
} from '../actions/entries'; } from '../actions/entries';
import { SEARCH_ENTRIES_SUCCESS } from '../actions/search'; import { SEARCH_ENTRIES_SUCCESS } from '../actions/search';
import { import {
@ -42,12 +43,14 @@ import {
FilterMap, FilterMap,
EntriesFilterRequestPayload, EntriesFilterRequestPayload,
EntriesFilterFailurePayload, EntriesFilterFailurePayload,
ChangeViewStylePayload,
} from '../types/redux'; } from '../types/redux';
import { folderFormatter } from '../lib/formatters'; import { folderFormatter } from '../lib/formatters';
import { isAbsolutePath, basename } from 'netlify-cms-lib-util'; import { isAbsolutePath, basename } from 'netlify-cms-lib-util';
import { trim, once, sortBy, set, orderBy } from 'lodash'; import { trim, once, sortBy, set, orderBy } from 'lodash';
import { selectSortDataPath } from './collections'; import { selectSortDataPath } from './collections';
import { stringTemplate } from 'netlify-cms-lib-widgets'; import { stringTemplate } from 'netlify-cms-lib-widgets';
import { VIEW_STYLE_LIST } from '../constants/collectionViews';
const { keyToPathArray } = stringTemplate; const { keyToPathArray } = stringTemplate;
@ -58,6 +61,7 @@ let page: number;
let slug: string; let slug: string;
const storageSortKey = 'netlify-cms.entries.sort'; const storageSortKey = 'netlify-cms.entries.sort';
const viewStyleKey = 'netlify-cms.entries.viewStyle';
type StorageSortObject = SortObject & { index: number }; type StorageSortObject = SortObject & { index: number };
type StorageSort = { [collection: string]: { [key: string]: StorageSortObject } }; type StorageSort = { [collection: string]: { [key: string]: StorageSortObject } };
@ -107,8 +111,30 @@ const persistSort = (sort: Sort | undefined) => {
} }
}; };
const loadViewStyle = once(() => {
const viewStyle = localStorage.getItem(viewStyleKey);
if (viewStyle) {
return viewStyle;
}
localStorage.setItem(viewStyleKey, VIEW_STYLE_LIST);
return VIEW_STYLE_LIST;
});
const clearViewStyle = () => {
localStorage.removeItem(viewStyleKey);
};
const persistViewStyle = (viewStyle: string | undefined) => {
if (viewStyle) {
localStorage.setItem(viewStyleKey, viewStyle);
} else {
clearViewStyle();
}
};
const entries = ( const entries = (
state = Map({ entities: Map(), pages: Map(), sort: loadSort() }), state = Map({ entities: Map(), pages: Map(), sort: loadSort(), viewStyle: loadViewStyle() }),
action: EntriesAction, action: EntriesAction,
) => { ) => {
switch (action.type) { switch (action.type) {
@ -272,6 +298,16 @@ const entries = (
return newState; return newState;
} }
case CHANGE_VIEW_STYLE: {
const payload = (action.payload as unknown) as ChangeViewStylePayload;
const { style } = payload;
const newState = state.withMutations(map => {
map.setIn(['viewStyle'], style);
});
persistViewStyle(newState.get('viewStyle') as string);
return newState;
}
default: default:
return state; return state;
} }
@ -308,6 +344,10 @@ export const selectEntriesFilterFields = (entries: Entries, collection: string)
return values; return values;
}; };
export const selectViewStyle = (entries: Entries) => {
return entries.get('viewStyle');
};
export const selectEntry = (state: Entries, collection: string, slug: string) => export const selectEntry = (state: Entries, collection: string, slug: string) =>
state.getIn(['entities', `${collection}.${slug}`]); state.getIn(['entities', `${collection}.${slug}`]);

View File

@ -75,6 +75,7 @@ export type Entries = StaticallyTypedRecord<{
entities: Entities & EntitiesObject; entities: Entities & EntitiesObject;
sort: Sort; sort: Sort;
filter: Filter; filter: Filter;
viewStyle: string;
}>; }>;
export type Deploys = StaticallyTypedRecord<{}>; export type Deploys = StaticallyTypedRecord<{}>;
@ -348,6 +349,10 @@ export interface EntriesFilterFailurePayload {
error: Error; error: Error;
} }
export interface ChangeViewStylePayload {
style: string;
}
export interface EntriesMoveSuccessPayload extends EntryPayload { export interface EntriesMoveSuccessPayload extends EntryPayload {
entries: EntryObject[]; entries: EntryObject[];
} }