Search integration (React Version) (#84)
* algolia integration skeleton * Configuration Defaults * Implemented partial entries with lazy loading of complete file * Moved backend selection logic to actioncreators * basic pagination for entries * general search skeleton * Basic search result listing * Redo search for different search terms * search results pagination * Changing integration config & handling * Changing integration config & handling * new integration config model
This commit is contained in:
@ -1,12 +1,28 @@
|
||||
import Immutable from 'immutable';
|
||||
import _ from 'lodash';
|
||||
import * as publishModes from '../constants/publishModes';
|
||||
import { CONFIG_REQUEST, CONFIG_SUCCESS, CONFIG_FAILURE } from '../actions/config';
|
||||
|
||||
const defaults = {
|
||||
publish_mode: publishModes.SIMPLE
|
||||
};
|
||||
|
||||
const applyDefaults = (config) => {
|
||||
// Make sure there is a public folder
|
||||
_.set(defaults,
|
||||
'public_folder',
|
||||
config.media_folder.charAt(0) === '/' ? config.media_folder : '/' + config.media_folder);
|
||||
|
||||
return _.defaultsDeep(config, defaults);
|
||||
};
|
||||
|
||||
const config = (state = null, action) => {
|
||||
switch (action.type) {
|
||||
case CONFIG_REQUEST:
|
||||
return Immutable.Map({ isFetching: true });
|
||||
case CONFIG_SUCCESS:
|
||||
return Immutable.fromJS(action.payload);
|
||||
const config = applyDefaults(action.payload);
|
||||
return Immutable.fromJS(config);
|
||||
case CONFIG_FAILURE:
|
||||
return Immutable.Map({ error: action.payload.toString() });
|
||||
default:
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { Map, List, fromJS } from 'immutable';
|
||||
import {
|
||||
ENTRY_REQUEST, ENTRY_SUCCESS, ENTRIES_REQUEST, ENTRIES_SUCCESS
|
||||
ENTRY_REQUEST, ENTRY_SUCCESS, ENTRIES_REQUEST, ENTRIES_SUCCESS, SEARCH_ENTRIES_REQUEST, SEARCH_ENTRIES_SUCCESS
|
||||
} from '../actions/entries';
|
||||
|
||||
let collection, loadedEntries, page, searchTerm;
|
||||
|
||||
const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
||||
switch (action.type) {
|
||||
case ENTRY_REQUEST:
|
||||
@ -18,14 +20,45 @@ const entries = (state = Map({ entities: Map(), pages: Map() }), action) => {
|
||||
return state.setIn(['pages', action.payload.collection, 'isFetching'], true);
|
||||
|
||||
case ENTRIES_SUCCESS:
|
||||
const { collection, entries, pages } = action.payload;
|
||||
collection = action.payload.collection;
|
||||
loadedEntries = action.payload.entries;
|
||||
page = action.payload.page;
|
||||
return state.withMutations((map) => {
|
||||
entries.forEach((entry) => (
|
||||
loadedEntries.forEach((entry) => (
|
||||
map.setIn(['entities', `${collection}.${entry.slug}`], fromJS(entry).set('isFetching', false))
|
||||
));
|
||||
|
||||
const ids = List(loadedEntries.map((entry) => entry.slug));
|
||||
|
||||
map.setIn(['pages', collection], Map({
|
||||
...pages,
|
||||
ids: List(entries.map((entry) => entry.slug))
|
||||
page: page,
|
||||
ids: page === 0 ? ids : map.getIn(['pages', collection, 'ids'], List()).concat(ids)
|
||||
}));
|
||||
});
|
||||
|
||||
case SEARCH_ENTRIES_REQUEST:
|
||||
if (action.payload.searchTerm !== state.getIn(['search', 'term'])) {
|
||||
return state.withMutations((map) => {
|
||||
map.setIn(['search', 'isFetching'], true);
|
||||
map.setIn(['search', 'term'], action.payload.searchTerm);
|
||||
});
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
|
||||
case SEARCH_ENTRIES_SUCCESS:
|
||||
loadedEntries = action.payload.entries;
|
||||
page = action.payload.page;
|
||||
searchTerm = action.payload.searchTerm;
|
||||
return state.withMutations((map) => {
|
||||
loadedEntries.forEach((entry) => (
|
||||
map.setIn(['entities', `${entry.collection}.${entry.slug}`], fromJS(entry).set('isFetching', false))
|
||||
));
|
||||
const ids = List(loadedEntries.map(entry => ({ collection: entry.collection, slug: entry.slug })));
|
||||
map.set('search', Map({
|
||||
page: page,
|
||||
term: searchTerm,
|
||||
ids: page === 0 ? ids : map.getIn(['search', 'ids'], List()).concat(ids)
|
||||
}));
|
||||
});
|
||||
|
||||
@ -43,4 +76,9 @@ export const selectEntries = (state, collection) => {
|
||||
return slugs && slugs.map((slug) => selectEntry(state, collection, slug));
|
||||
};
|
||||
|
||||
export const selectSearchedEntries = (state) => {
|
||||
const searchItems = state.getIn(['search', 'ids']);
|
||||
return searchItems && searchItems.map(({ collection, slug }) => selectEntry(state, collection, slug));
|
||||
};
|
||||
|
||||
export default entries;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import auth from './auth';
|
||||
import config from './config';
|
||||
import editor from './editor';
|
||||
import integrations, * as fromIntegrations from './integrations';
|
||||
import entries, * as fromEntries from './entries';
|
||||
import editorialWorkflow, * as fromEditorialWorkflow from './editorialWorkflow';
|
||||
import editorialWorkflow, * as fromEditorialWorkflow from './editorialWorkflow';
|
||||
import entryDraft from './entryDraft';
|
||||
import collections from './collections';
|
||||
import medias, * as fromMedias from './medias';
|
||||
@ -11,6 +12,7 @@ const reducers = {
|
||||
auth,
|
||||
config,
|
||||
collections,
|
||||
integrations,
|
||||
editor,
|
||||
entries,
|
||||
editorialWorkflow,
|
||||
@ -29,11 +31,17 @@ export const selectEntry = (state, collection, slug) =>
|
||||
export const selectEntries = (state, collection) =>
|
||||
fromEntries.selectEntries(state.entries, collection);
|
||||
|
||||
export const selectSearchedEntries = (state) =>
|
||||
fromEntries.selectSearchedEntries(state.entries);
|
||||
|
||||
export const selectUnpublishedEntry = (state, status, slug) =>
|
||||
fromEditorialWorkflow.selectUnpublishedEntry(state.editorialWorkflow, status, slug);
|
||||
|
||||
export const selectUnpublishedEntries = (state, status) =>
|
||||
fromEditorialWorkflow.selectUnpublishedEntries(state.editorialWorkflow, status);
|
||||
|
||||
export const selectIntegration = (state, collection, hook) =>
|
||||
fromIntegrations.selectIntegration(state.integrations, collection, hook);
|
||||
|
||||
export const getMedia = (state, path) =>
|
||||
fromMedias.getMedia(state.medias, path);
|
||||
|
29
src/reducers/integrations.js
Normal file
29
src/reducers/integrations.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { fromJS } from 'immutable';
|
||||
import { CONFIG_SUCCESS } from '../actions/config';
|
||||
|
||||
const integrations = (state = null, action) => {
|
||||
switch (action.type) {
|
||||
case CONFIG_SUCCESS:
|
||||
const integrations = action.payload.integrations || [];
|
||||
const newState = integrations.reduce((acc, integration) => {
|
||||
const { hooks, collections, provider, ...providerData } = integration;
|
||||
acc.providers[provider] = { ...providerData };
|
||||
collections.forEach(collection => {
|
||||
hooks.forEach(hook => {
|
||||
acc.hooks[collection] ? acc.hooks[collection][hook] = provider : acc.hooks[collection] = { [hook]: provider };
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, { providers:{}, hooks: {} });
|
||||
return fromJS(newState);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const selectIntegration = (state, collection, hook) => {
|
||||
return state.getIn(['hooks', collection, hook], false);
|
||||
};
|
||||
|
||||
|
||||
export default integrations;
|
Reference in New Issue
Block a user