-
-
Save edtoken/34f5f060a56678cf5443a6bcf5282afc to your computer and use it in GitHub Desktop.
redux-shelf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createAction } from 'redux-actions'; | |
import { identity, isArray } from 'lodash'; | |
import { SubmissionError } from 'redux-form'; | |
export const PREFIX = '@@shelf'; | |
export const statuses = ['START', 'SUCCESS', 'ERROR']; | |
const getType = (status:string) => (name):string => `${PREFIX}/${status} -> ${name.toString()}` | |
const createApiAction = (name, status) => createAction( | |
getType(status)(name), | |
identity, | |
() => ({ | |
name, | |
status, | |
time: new Date().getTime() | |
}) | |
); | |
const defaultMapper = data => isArray(data) ? ({ entities: data }) : ({ entity: data }); | |
export const create = (actionName, apiFunction, responseMapper = defaultMapper) => { | |
const action = createAction(actionName, responseMapper); | |
const [start, success, error] = statuses.map(status => createApiAction(actionName, status)); | |
const apiAction = (...params) => async dispatch => { | |
dispatch(start()); | |
try { | |
const { data } = await apiFunction(...params); | |
dispatch(success(data)); | |
dispatch(action(data)); | |
return data; | |
} catch (e) { | |
console.error(e); | |
dispatch(error(e)); | |
throw new SubmissionError({ _error: e }); | |
} | |
} | |
apiAction.toString = ():string => actionName; | |
return apiAction; | |
}; | |
export const success = getType(statuses[1]); | |
export const error = getType(statuses[2]); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios from 'axios'; | |
import * as jsonp from 'jsonp'; | |
import session from 'lib/session'; | |
import { isFunction } from 'lodash'; | |
import * as config from 'config'; | |
import { create } from './actions'; | |
import { stringify } from 'qs'; | |
const jsonpRequest = (path, query, params) => new Promise((resolve, reject) => | |
jsonp( | |
`${config.searchApi.url}/v3/${path}${query && `?${stringify(query)}` || ''}`, | |
params, | |
(err, data) => err && reject(err) || resolve({ data })) | |
); | |
const createUrl = (strings, keys) => strings.map((s, i) => | |
keys[i] | |
? s + (isFunction(keys[i]) ? keys[i]() : keys[i]) | |
: s | |
).join(''); | |
const replaceParams = function(pattern, params) { | |
return pattern.replace(/:[a-z|A-Z]+/g, match => { | |
const matchedParam = match.substr(1); | |
const value = params[matchedParam]; | |
if (typeof value === 'undefined') { | |
throw new Error(`Matched param "${matchedParam}" is not presented at given object`); | |
} | |
return value; | |
}); | |
} | |
const instance = axios.create({ | |
baseURL: `${config.adminApi.url}/${config.adminApi.version}`, | |
}); | |
const createApiCall = (method:string, strings:string[], keys: any[]) => (data, params) => { | |
const normalizedUrl: string = replaceParams(createUrl(strings, keys), {...data, ...params}); | |
if (method === 'json') { | |
const user = {uid: 1, sid:1}; | |
const t_client = (new Date).getTime(); | |
const log = false; | |
const normalizeData = {...(data && data || {}), log, user, t_client }; | |
return jsonpRequest(normalizedUrl, normalizeData, params); | |
} | |
return instance.request({ | |
url: session.merchant && !normalizedUrl.startsWith('/') ? `merchants/${session.getMerchant()}/${normalizedUrl}` : normalizedUrl, | |
headers: { | |
[config.adminApi.token]: session.token | |
}, | |
method, | |
data, | |
params | |
}); | |
} | |
const createRequest = method => (strings, ...keys) => (responseMapper?) => { | |
const path = strings.join(''); | |
return create(`[${method}]${path}`, createApiCall(method, strings, keys), responseMapper); | |
} | |
export const get = createRequest('get'); | |
export const put = createRequest('put'); | |
export const post = createRequest('post'); | |
export const del = createRequest('delete'); | |
export const json = createRequest('json'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component } from "react"; | |
import { createEagerFactory, createEagerElement } from 'recompose'; | |
import { isArray, isFunction } from 'lodash'; | |
import Spinner from 'components/Spinner'; | |
const createPromise = (handler, props) => { | |
if (!props[handler]) { | |
console.warn(`Property [${handler}] not found`); | |
return false; | |
} | |
return props[handler](); | |
}; | |
export const asyncConnect = (...handlers) => BaseComponent => { | |
const factory = createEagerFactory(BaseComponent); | |
return class Connector extends Component<any, any>{ | |
state = { | |
loaded: false, | |
hasError: false, | |
error: void 0, | |
} | |
componentWillMount() { | |
Promise.all( | |
handlers.map((handler) => isFunction(handler) | |
? handler(this.props) | |
: createPromise(handler, this.props) | |
) | |
) | |
.then(() => this.setState({ loaded: true })) | |
.catch(e => { | |
console.log(e); | |
return this.setState({ loaded: true, hasError: true, error: e }) | |
}); | |
} | |
render() { | |
return this.state.loaded | |
? factory({ ...this.state, ...this.props }) | |
: createEagerElement(Spinner); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const field = name => data => ({ [name]: data }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export * from './actions'; | |
export * from './selectors'; | |
export * from './helpers'; | |
export { createReducer } from './reducer'; | |
export { asyncConnect } from './asyncConnect'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { handleActions, combineActions } from 'redux-actions'; | |
import { isArray } from 'lodash'; | |
import { PREFIX, statuses } from './actions'; | |
const applyState = (s, { payload }) => ({ ...s, ...payload }); | |
const emptyObject = {}; | |
const emptyArray = {}; | |
export const createReducer = ( | |
actions?: any, | |
reducer = emptyObject, | |
initialState = emptyObject | |
) => handleActions({ | |
...(actions && { | |
[combineActions( | |
...(isArray(actions) ? actions : Object.keys(actions).map(k => actions[k])) | |
)]: applyState | |
}), | |
...reducer | |
}, initialState); | |
export const reducer = (state = {}, { type, meta, payload }) => { | |
if (!type.includes(PREFIX)) return state; | |
return { | |
...state, | |
[meta.name]: { | |
...meta, | |
payload | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createSelector } from 'reselect'; | |
import { statuses } from './actions'; | |
import { path, identity } from 'ramda'; | |
const [ start, success, error ] = statuses; | |
const getStatus = name => s => s.shelf[name] && s.shelf[name].status; | |
export const isFetching = action => { | |
const name = action.toString(); | |
return createSelector( | |
getStatus(name), | |
status => status === start | |
); | |
}; | |
export const hasError = action => { | |
const name = action.toString(); | |
return createSelector( | |
getStatus(name), | |
status => status && status === error | |
); | |
}; | |
export const getList = reducerName => createSelector( | |
s => s[reducerName].entities, | |
list => list | |
); | |
export const getItem = reducerName => { | |
const [name, field] = reducerName.split('.'); | |
return createSelector( | |
s => s[name][field || 'entity'], | |
item => item | |
) | |
}; | |
export const get = (...deep) => createSelector(path(deep), identity); | |
export const routeParams = createSelector( | |
s => s.match && s.match.params, | |
params => params | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment