import $ from 'jquery'; import { createAction } from 'redux-actions'; import { batchActions } from 'redux-batched-actions'; import createAjaxRequest from 'Utilities/createAjaxRequest'; import requestAction from 'Utilities/requestAction'; import getSectionState from 'Utilities/State/getSectionState'; import updateSectionState from 'Utilities/State/updateSectionState'; import { createThunk, handleThunks } from 'Store/thunks'; import { set } from 'Store/Actions/baseActions'; import createHandleActions from './Creators/createHandleActions'; // // Variables export const section = 'oAuth'; const callbackUrl = `${window.location.origin}${window.Readarr.urlBase}/oauth.html`; // // State export const defaultState = { authorizing: false, result: null, error: null }; // // Actions Types export const START_OAUTH = 'oAuth/startOAuth'; export const SET_OAUTH_VALUE = 'oAuth/setOAuthValue'; export const RESET_OAUTH = 'oAuth/resetOAuth'; // // Action Creators export const startOAuth = createThunk(START_OAUTH); export const setOAuthValue = createAction(SET_OAUTH_VALUE); export const resetOAuth = createAction(RESET_OAUTH); // // Helpers function showOAuthWindow(url, payload) { const deferred = $.Deferred(); const selfWindow = window; const newWindow = window.open(url); if ( !newWindow || newWindow.closed || typeof newWindow.closed == 'undefined' ) { // A fake validation error to mimic a 400 response from the API. const error = { status: 400, responseJSON: [ { propertyName: payload.name, errorMessage: 'Pop-ups are being blocked by your browser' } ] }; return deferred.reject(error).promise(); } selfWindow.onCompleteOauth = function(query, onComplete) { delete selfWindow.onCompleteOauth; const queryParams = {}; const splitQuery = query.substring(1).split('&'); splitQuery.forEach((param) => { if (param) { const paramSplit = param.split('='); queryParams[paramSplit[0]] = paramSplit[1]; } }); onComplete(); deferred.resolve(queryParams); }; return deferred.promise(); } function executeIntermediateRequest(payload, ajaxOptions) { return createAjaxRequest(ajaxOptions).request.then((data) => { return requestAction({ action: 'continueOAuth', queryParams: { ...data, callbackUrl }, ...payload }); }); } // // Action Handlers export const actionHandlers = handleThunks({ [START_OAUTH]: function(getState, payload, dispatch) { const { name, section: actionSection, ...otherPayload } = payload; const actionPayload = { action: 'startOAuth', queryParams: { callbackUrl }, ...otherPayload }; dispatch(setOAuthValue({ authorizing: true })); let startResponse = {}; const promise = requestAction(actionPayload) .then((response) => { startResponse = response; if (response.oauthUrl) { return showOAuthWindow(response.oauthUrl, payload); } return executeIntermediateRequest(otherPayload, response).then((intermediateResponse) => { startResponse = intermediateResponse; return showOAuthWindow(intermediateResponse.oauthUrl, payload); }); }) .then((queryParams) => { return requestAction({ action: 'getOAuthToken', queryParams: { ...startResponse, ...queryParams }, ...otherPayload }); }) .then((response) => { dispatch(setOAuthValue({ authorizing: false, result: response, error: null })); }); promise.done(() => { // Clear any previously set save error. dispatch(set({ section: actionSection, saveError: null })); }); promise.fail((xhr) => { const actions = [ setOAuthValue({ authorizing: false, result: null, error: xhr }) ]; if (xhr.status === 400) { // Set a save error so the UI can display validation errors to the user. actions.splice(0, 0, set({ section: actionSection, saveError: xhr })); } dispatch(batchActions(actions)); }); } }); // // Reducers export const reducers = createHandleActions({ [SET_OAUTH_VALUE]: function(state, { payload }) { const newState = Object.assign(getSectionState(state, section), payload); return updateSectionState(state, section, newState); }, [RESET_OAUTH]: function(state) { return updateSectionState(state, section, defaultState); } }, defaultState, section);