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:
Cássio Souza
2016-10-10 15:34:21 -03:00
committed by GitHub
parent 45d810a25f
commit 2815a86e0c
25 changed files with 493 additions and 111 deletions

View File

@ -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:

View File

@ -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;

View File

@ -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);

View 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;