@digifi-los/reactapp
Version:
258 lines (244 loc) • 12.2 kB
JavaScript
// import React from 'react';
import utilities from './index';
import { _invokeWebhooks } from './webhooks';
// import AppError404 from '../components/AppError404';
/**
* Because these dynamic data fetching functions are used in multiple locations this function standardizes access to the getState function
* @return {Function} Returns the getState function that is either on this.props or on this directly
*/
var _getState = function () {
return (this.props && typeof this.props.getState === 'function') ? this.props.getState : this.getState;
};
/**
* Sets parameterized values derived from window location to their respective resource path counterparts
* @param {string} pathname The dynamic path that parameters should be set in
* @param {Object} resources Contains resource paths
* @param {string} [current] The actual current window path. If this argument is not passed the window path will be pulled from the window object or from this.props
* @return {Object} Returns the resource object with populated dynamic routes
*/
export const _handleDynamicParams = function (pathname, resources, current) {
// console.log('_handleDynamicParams',{ pathname, resources, current });
let currentPathname;
if (typeof current === 'string') currentPathname = current;
else currentPathname = (typeof window !== 'undefined' && window.location.pathname) ? window.location.pathname : this.props.location.pathname;
return Object.keys(resources).reduce((result, key) => {
let updatedPath = utilities.setParameters({
route: pathname,
location: currentPathname,
query: (/\?[^\s]+$/.test(currentPathname)) ? currentPathname.replace(/\?([^\s]+)$/g, '$1') : undefined,
resource: resources[ key ],
});
result[ key ] = updatedPath;
return result;
}, {});
};
/**
* Handles making fetch requests for resource paths
* @param {Object} layout Configuration for dynamic page, component or modal
* @param {Object} [resources={}] Dynamically loaded resources stored as resource name and resource path key value pairs
* @param {Object} [options={}] Configurable options
* @param {Function} [options.onSuccess] Optional success function
* @param {Function} [options.onError] Optional error function
*/
export const _handleFetchPaths = function (layout, resources = {}, options = {}) {
let state = _getState.call(this)();
let headers = (state.settings && state.settings.userprofile && state.settings.userprofile.options && state.settings.userprofile.options.headers)
? state.settings.userprofile.options.headers
: {};
delete headers.clientid_default;
if (state.user && state.user.jwt_token) {
headers[ 'x-access-token' ] = state.user.jwt_token;
}
return utilities.fetchPaths.call(this, state.settings.basename, resources, headers)
.then((typeof options.onSuccess === 'function') ? options.onSuccess : _resources => {
if (!_resources || (_resources && !_resources.__hasError)) {
this.uiLayout = this.getRenderedComponent(layout, Object.assign({}, _resources, this.uiResources));
this.setState({ ui_is_loaded: true, async_data_is_loaded: true, });
if (options.callbacks) _invokeWebhooks.call(this, options.callbacks);
}
})
.catch((typeof options.onError === 'function') ? e => options.onError(e, 'fetchResources', resources) : e => {
if (this.props && this.props.errorNotification) this.props.errorNotification(e);
else console.error(e);
this.setState({ ui_is_loaded: true, async_data_is_loaded: true, });
});
};
/**
* Sets a configurable 404 error component or sets a default 404 component
*/
export const fetchErrorContent = function _fetchErrorContent(e, type, resources) {
console.debug('fetchErrorContent', e, { type, resources, });
let getState = _getState.call(this);
let state = getState();
let custom404Error;
let componentData;
let windowTitle;
let navLabel;
let get404Error = utilities.get404Error.bind(this);
let errorComponents = (state.ui && state.ui.components && state.ui.components.error) ? state.ui.components.error : false;
let errorCode = (type === 'fetchResources')
? '400'
: '404';
// console.debug({ errorComponents });
get404Error({
getState, _handleFetchPaths, state, custom404Error, componentData, windowTitle, navLabel, errorComponents, errorCode, resources, e,
});
};
/**
* Gets a dynamic page element and handles resolving async props if resources exist
* @param {string} pathname Dynamic page manifest pathname
* @param {Boolean} hasParams If true will attempt to assign dynamic params to resource path
*/
export const fetchSuccessContent = function _fetchSuccessContent(pathname, hasParams) {
try {
let getState = _getState.call(this);
let state = getState();
let containers = state.manifest.containers;
let layout = Object.assign({}, containers[ pathname ].layout);
if (typeof window.customOnChangeLocation === 'function') {
window.customOnChangeLocation(window.location.pathname);
}
if (containers[ pathname ].dynamic && typeof containers[ pathname ].dynamic === 'object') {
Object.keys(containers[ pathname ].dynamic).forEach(dynamicProp => {
this.props.setDynamicData(dynamicProp, containers[ pathname ].dynamic[ dynamicProp ]);
});
}
if (containers[ pathname ].clearDynamicOnLoad) {
this.props.clearDynamicData();
}
if (containers[ pathname ].resources && typeof containers[ pathname ].resources === 'object') {
let container = containers[ pathname ];
let resources = container.resources;
if (hasParams) resources = _handleDynamicParams.call(this, pathname, resources, (typeof this.props.pathname === 'string') ? this.props.pathname : undefined);
if (container.pageData && container.pageData.title) window.document.title = container.pageData.title;
if (container.pageData && container.pageData.navLabel && this.props && this.props.setNavLabel) this.props.setNavLabel(container.pageData.navLabel);
else if (this.props && this.props.setNavLabel) this.props.setNavLabel('');
return _handleFetchPaths.call(this, layout, resources, {
getState,
onError: fetchErrorContent.bind(this),
callbacks: containers[ pathname ].callbacks,
});
} else {
if (containers[ pathname ].callbacks) _invokeWebhooks.call(this, containers[ pathname ].callbacks);
this.uiLayout = this.getRenderedComponent(containers[ pathname ].layout, this.uiResources);
this.setState({ ui_is_loaded: true, async_data_is_loaded: true, });
if (window && window.scrollTo) {
window.scrollTo(0, 0);
}
if (document && document.querySelector && document.querySelector('.reactapp__app_div_content')) {
document.querySelector('.reactapp__app_div_content').scrollIntoView(true)
}
}
} catch (e) {
if (this.props && this.props.errorNotification) this.props.errorNotification(e);
else console.error(e);
this.setState({ ui_is_loaded: true, async_data_is_loaded: true, });
if (window && window.scrollTo) {
window.scrollTo(0, 0);
}
if (document && document.querySelector && document.querySelector('.reactapp__app_div_content')) {
document.querySelector('.reactapp__app_div_content').scrollIntoView(true)
}
}
};
/**
* Gets dynamic content for a given page, component, modal
* @param {string} [_pathname] The window path that should content is being fetched for. If the argument is not passed it will defaul to window.location.pathname
* @param {Function} [onSuccess] Optional success function override. If this isnt passed resource paths will be fetched for async props
* @param {Function} onError Optional error function override. If this isnt passed 404 error page will be rendered
*/
export const fetchDynamicContent = function _fetchDynamicContent(_pathname, onSuccess, onError) {
let pathname;
let getState = _getState.call(this);
let state = getState();
if (typeof _pathname === 'string') {
pathname = _pathname;
} else {
pathname = (window.location.pathname)
? window.location.pathname
: this.props.location.pathname;
}
onSuccess = (typeof onSuccess === 'function')
? onSuccess
: fetchSuccessContent.bind(this);
onError = (typeof onError === 'function')
? onError
: fetchErrorContent.bind(this);
// console.log({pathname}, onSuccess, onError)
if (state.manifest.containers[ pathname ]) {
return onSuccess(pathname);
} else if (state.manifest.containers[ pathname.replace(state.settings.auth.admin_path, '') ]) {
let adminPathname = pathname.replace(state.settings.auth.admin_path, '');
return onSuccess(adminPathname);
} else {
let dynamicPathname = utilities.findMatchingRoute(state.manifest.containers, pathname.replace(state.settings.auth.admin_path, ''));
if (!dynamicPathname) return onError();
return onSuccess(dynamicPathname, true);
}
};
const FUNCTION_NAME_REGEXP = /func:(?:this\.props|window)(?:\.reduxRouter)?\.(\D.+)*/;
export const getDynamicFunctionName = function _getDynamicFunctionName(function_name) {
return function_name.replace(FUNCTION_NAME_REGEXP, '$1');
};
export const fetchAction = function _fetchAction(pathname, fetchOptions, success) {
// let pathname, fetchOptions, success;
if (typeof pathname === 'object') {
pathname = pathname.pathname;
fetchOptions = pathname.fetchOptions;
success = pathname.success;
}
let setUILoadedStateOnFinish = (success && success.setUILoadedState) ? true : false;
// console.debug('in fetch action this', this,{ pathname, fetchOptions, success, customThis, });
let state = _getState.call(this)();
let headers = (state.settings && state.settings.userprofile && state.settings.userprofile.options && state.settings.userprofile.options.headers)
? state.settings.userprofile.options.headers
: {};
let successCallback = console.debug;
delete headers.clientid_default;
if (state.user && state.user.jwt_token) {
headers[ 'x-access-token' ] = state.user.jwt_token;
}
fetchOptions.headers = Object.assign({}, fetchOptions.headers, headers);
fetch(pathname, fetchOptions)
.then(utilities.checkStatus)
.then(res => {
if (success.success) {
if (success.success.modal) {
this.props.createModal(success.success.modal);
} else if (success.success.notification) {
this.props.createNotification(success.success.notification);
} else {
this.props.createNotification({ text: 'Saved', timeout: 4000, type: 'success', });
}
}
if (success.successCallback) {
res.json()
.then(successData => {
let successCallbackProp = success.successCallback;
if (typeof successCallbackProp === 'string' && successCallbackProp.indexOf('func:this.props.reduxRouter') !== -1) {
successCallback = this.props.reduxRouter[ successCallbackProp.replace('func:this.props.reduxRouter.', '') ];
} else if (typeof successCallbackProp === 'string' && successCallbackProp.indexOf('func:this.props') !== -1) {
successCallback = this.props[ success.successCallback.replace('func:this.props.', '') ];
} else if (typeof successCallbackProp === 'string' && successCallbackProp.indexOf('func:window') !== -1 && typeof window[ success.successCallback.replace('func:window.', '') ] === 'function') {
successCallback = window[ success.successCallback.replace('func:window.', '') ].bind(this);
}
if (fetchOptions.successCallback === 'func:this.props.setDynamicData') {
this.props.setDynamicData(success.dynamicField, success.successProps || successData);
} else {
successCallback(success.successProps || successData);
}
if (setUILoadedStateOnFinish) {
this.props.setUILoadedState(true);
}
});
} else {
return res.json();
}
})
.catch((e) => {
this.props.errorNotification(e);
if (setUILoadedStateOnFinish) {
this.props.setUILoadedState(true);
}
});
};