From 7f7916514748dcadc74f253d97a0c62d509094d3 Mon Sep 17 00:00:00 2001 From: Amine Ben hammou Date: Mon, 30 Mar 2020 06:15:27 +0200 Subject: [PATCH] Refactor to use WP Redux store and actions [MAILPOET-2681] --- .../settings/pages/key_activation/index.tsx | 196 ++++-------------- .../key_activation/messages/key_messages.tsx | 4 +- .../key_activation/messages/mss_messages.tsx | 4 +- .../messages/premium_messages.tsx | 14 +- assets/js/src/settings/store/actions.ts | 148 ++++++++++++- .../js/src/settings/store/create_reducer.ts | 13 +- .../src/settings/store/make_default_state.ts | 68 ++++-- assets/js/src/settings/store/selectors.ts | 30 +-- assets/js/src/settings/store/types.ts | 61 +++--- 9 files changed, 300 insertions(+), 238 deletions(-) diff --git a/assets/js/src/settings/pages/key_activation/index.tsx b/assets/js/src/settings/pages/key_activation/index.tsx index f50c1ddce6..76924127c0 100644 --- a/assets/js/src/settings/pages/key_activation/index.tsx +++ b/assets/js/src/settings/pages/key_activation/index.tsx @@ -1,137 +1,37 @@ -import React, { useState } from 'react'; +import React, { useContext } from 'react'; import MailPoet from 'mailpoet'; -import { useSetting, useSelector } from 'settings/store/hooks'; +import { useSelector, useAction } from 'settings/store/hooks'; +import { GlobalContext } from 'context'; +import { t } from 'common/functions'; import { KeyMessages, MssMessages, PremiumMessages } from './messages'; -const getSettings = async () => MailPoet.Ajax.post({ - api_version: window.mailpoet_api_version, - endpoint: 'settings', - action: 'get', -}); +export default function KeyActivation() { + const { notices } = useContext(GlobalContext); + const { + key, isKeyValid, premiumStatus, premiumMessage, mssStatus, mssMessage, premiumInstallationStatus, + } = useSelector('getKeyActivationState')(); + const setState = useAction('updateKeyActivationState'); + const verifyMssKey = useAction('verifyMssKey'); + const verifyPremiumKey = useAction('verifyPremiumKey'); + const installPremiumPlugin = useAction('installPremiumPlugin'); + const activatePremiumPlugin = useAction('activatePremiumPlugin'); -const requestServicesApi = async (key, action) => MailPoet.Ajax.post({ - api_version: window.mailpoet_api_version, - endpoint: 'services', - action, - data: { key }, -}); - -const requestPremiumApi = async (action) => MailPoet.Ajax.post({ - api_version: window.mailpoet_api_version, - endpoint: 'premium', - action, -}); - -const activateMss = async (key) => MailPoet.Ajax.post({ - api_version: window.mailpoet_api_version, - endpoint: 'settings', - action: 'set', - data: { - mta_group: 'mailpoet', - mta: { - method: 'MailPoet', - mailpoet_api_key: key, - }, - signup_confirmation: { - enabled: '1', - }, - }, -}); - -const PremiumTab = () => { - const [premiumKey] = useSetting('premium', 'premium_key'); - const [mssKey] = useSetting('mta', 'mailpoet_api_key'); - const [key, setKey] = useState(premiumKey || mssKey); - const getMssStatus = useSelector('getMssStatus'); - const getPremiumStatus = useSelector('getPremiumStatus'); - const [premiumStatus, setPremiumStatus] = useState(key ? getPremiumStatus() : null); - const [premiumMessage, setPremiumMessage] = useState(null); - const [premiumInstallationStatus, setPremiumInstallationStatus] = useState(null); - const [mssStatus, setMssStatus] = useState(key ? getMssStatus() : null); - const [mssKeyMessage, setMssKeyMessage] = useState(null); - const keyValid = useSelector('hasValidKey')(); - - const activatePremiumPlugin = async (isAfterInstall = false) => { - const activateStatus = isAfterInstall ? 'install_activating' : 'activate_activating'; - const doneStatus = isAfterInstall ? 'install_done' : 'activate_done'; - const errorStatus = isAfterInstall ? 'install_activating_error' : 'activate_error'; - - setPremiumInstallationStatus(activateStatus); - try { - await requestPremiumApi('activatePlugin'); - } catch (error) { - setPremiumInstallationStatus(errorStatus); - return false; + const verifyKey = async (event) => { + if (!key) { + notices.error(

{t('premiumTabNoKeyNotice')}

, { scroll: true }); + return; } - setPremiumInstallationStatus(doneStatus); - return true; + await setState({ + mssStatus: null, + premiumStatus: null, + premiumInstallationStatus: null, + }); + MailPoet.Modal.loading(true); + await verifyMssKey(key, event.isTrusted); + await verifyPremiumKey(key); + MailPoet.Modal.loading(false); }; - const installPremiumPlugin = async () => { - setPremiumInstallationStatus('install_installing'); - try { - await requestPremiumApi('installPlugin'); - } catch (error) { - setPremiumInstallationStatus('install_installing_error'); - return false; - } - return activatePremiumPlugin(true); - }; - - const verifyMailPoetPremiumKey = async () => { - try { - const response = await requestServicesApi(key, 'checkPremiumKey'); - setPremiumMessage(null); - - // install/activate Premium plugin - let pluginActive = response.meta.premium_plugin_active; - if (!response.meta.premium_plugin_installed) { - setPremiumStatus('valid_premium_plugin_being_installed'); - pluginActive = await installPremiumPlugin(); - } else if (!response.meta.premium_plugin_active) { - setPremiumStatus('valid_premium_plugin_being_activated'); - pluginActive = await activatePremiumPlugin(); - } else { - setPremiumStatus('valid_premium_plugin_active'); - } - - MailPoet.trackEvent( - 'User has validated a Premium key', - { - 'MailPoet Free version': MailPoet.version, - 'Premium plugin is active': pluginActive, - } - ); - } catch (error) { - setPremiumStatus('invalid'); - setPremiumMessage(error.errors.map((e) => e.message).join(' ') || null); - MailPoet.trackEvent( - 'User has failed to validate a Premium key', - { - 'MailPoet Free version': MailPoet.version, - 'Premium plugin is active': !!MailPoet.premiumVersion, - } - ); - } - }; - - async function verifyMailPoetSendingServiceKey(activateMssIfKeyValid) { - try { - const response = await requestServicesApi(key, 'checkMSSKey'); - setMssKeyMessage(response.data.message || null); - if (activateMssIfKeyValid) { - await activateMss(key); - setMssStatus('valid_mss_active'); - } else { - setMssStatus('valid_mss_not_active'); - } - } catch (error) { - setMssStatus('invalid'); - setMssKeyMessage(error.errors.map((e) => e.message).join(' ') || null); - } - window.updateMSSActivationUI(); - } - return ( @@ -152,47 +52,29 @@ const PremiumTab = () => { className="regular-text" name="premium[premium_key]" value={key || ''} - onChange={(event) => { - setKey(event.target.value.trim() || null); - setPremiumStatus(null); - setPremiumInstallationStatus(null); - setMssStatus(null); - }} + onChange={(event) => setState({ + mssStatus: null, + premiumStatus: null, + premiumInstallationStatus: null, + key: event.target.value.trim() || null, + })} /> - {keyValid !== null && ( + {isKeyValid !== null && (
{mssStatus !== null && ( verifyMailPoetSendingServiceKey(true)} + keyMessage={mssMessage} + activationCallback={() => verifyMssKey(key, true)} /> )} {premiumStatus !== null && ( @@ -200,7 +82,7 @@ const PremiumTab = () => { keyMessage={premiumMessage} installationStatus={premiumInstallationStatus} installationCallback={installPremiumPlugin} - activationCallback={() => activatePremiumPlugin()} + activationCallback={() => activatePremiumPlugin(false)} /> )}
@@ -238,4 +120,4 @@ const PremiumTab = () => { } ); -}; +} diff --git a/assets/js/src/settings/pages/key_activation/messages/key_messages.tsx b/assets/js/src/settings/pages/key_activation/messages/key_messages.tsx index 2bee9f93aa..0ce3136afc 100644 --- a/assets/js/src/settings/pages/key_activation/messages/key_messages.tsx +++ b/assets/js/src/settings/pages/key_activation/messages/key_messages.tsx @@ -15,6 +15,6 @@ const KeyNotValidMessage = () => ( ); export default function KeyMessages() { - const hasValidKey = useSelector('hasValidKey')(); - return hasValidKey ? : ; + const { isKeyValid } = useSelector('getKeyActivationState')(); + return isKeyValid ? : ; } diff --git a/assets/js/src/settings/pages/key_activation/messages/mss_messages.tsx b/assets/js/src/settings/pages/key_activation/messages/mss_messages.tsx index 39d2a8988b..b58fe7b136 100644 --- a/assets/js/src/settings/pages/key_activation/messages/mss_messages.tsx +++ b/assets/js/src/settings/pages/key_activation/messages/mss_messages.tsx @@ -32,8 +32,8 @@ type Props = { activationCallback: () => any, } export default function MssMessages(props: Props) { - const mssKeyStatus = useSelector('getMssStatus')(); - switch (mssKeyStatus) { + const { mssStatus } = useSelector('getKeyActivationState')(); + switch (mssStatus) { case 'valid_mss_active': return ; case 'valid_mss_not_active': diff --git a/assets/js/src/settings/pages/key_activation/messages/premium_messages.tsx b/assets/js/src/settings/pages/key_activation/messages/premium_messages.tsx index 4a9a3322f4..0e1b914897 100644 --- a/assets/js/src/settings/pages/key_activation/messages/premium_messages.tsx +++ b/assets/js/src/settings/pages/key_activation/messages/premium_messages.tsx @@ -58,19 +58,19 @@ type Props = { installationStatus: PremiumInstallationStatus } export default function PremiumMessages(props: Props) { - const keyStatus = useSelector('getPremiumStatus')(); + const { premiumStatus: status } = useSelector('getKeyActivationState')(); return ( <> - {keyStatus === 'valid_premium_plugin_active' && } - {keyStatus === 'valid_premium_plugin_not_active' && ( + {status === 'valid_premium_plugin_active' && } + {status === 'valid_premium_plugin_not_active' && ( )} - {keyStatus === 'valid_premium_plugin_not_installed' && ( + {status === 'valid_premium_plugin_not_installed' && ( )} - {keyStatus === 'valid_premium_plugin_being_installed' && } - {keyStatus === 'valid_premium_plugin_being_activated' && } - {keyStatus === 'invalid' && } + {status === 'valid_premium_plugin_being_installed' && } + {status === 'valid_premium_plugin_being_activated' && } + {status === 'invalid' && } ); diff --git a/assets/js/src/settings/store/actions.ts b/assets/js/src/settings/store/actions.ts index afe8e5556f..6fdef992e8 100644 --- a/assets/js/src/settings/store/actions.ts +++ b/assets/js/src/settings/store/actions.ts @@ -1,6 +1,7 @@ import { select } from '@wordpress/data'; +import MailPoet from 'mailpoet'; import { STORE_NAME } from '.'; -import { Action } from './types'; +import { Action, KeyActivationState } from './types'; export function setSetting(path: string[], value: any): Action { return { type: 'SET_SETTING', path, value }; @@ -28,6 +29,10 @@ export function* openWoocommerceCustomizer(newsletterId?: string) { return null; } +export function updateKeyActivationState(fields: Partial): Action { + return { type: 'UPDATE_KEY_ACTIVATION_STATE', fields }; +} + export function* saveSettings() { yield { type: 'SAVE_STARTED' }; const data = select(STORE_NAME).getSettings(); @@ -43,3 +48,144 @@ export function* saveSettings() { yield { type: 'TRACK_SETTINGS_SAVED' }; return { type: 'SAVE_DONE' }; } + +export function* verifyMssKey(key: string, activateMssIfKeyValid: boolean) { + const { success, error, res } = yield { + type: 'CALL_API', + endpoint: 'services', + action: 'checkMSSKey', + data: { key }, + }; + if (!success) { + return updateKeyActivationState({ + mssStatus: 'invalid', + mssMessage: error.join(' ') || null, + }); + } + const fields: Partial = { + mssMessage: res.data.message || null, + }; + if (activateMssIfKeyValid) { + const call = yield { + type: 'CALL_API', + endpoint: 'settings', + action: 'set', + data: { + mta_group: 'mailpoet', + mta: { method: 'MailPoet', mailpoet_api_key: key }, + signup_confirmation: { enabled: '1' }, + }, + }; + if (!call.success) { + fields.mssStatus = 'valid_mss_not_active'; + } else { + yield setSetting(['mta_group'], 'mailpoet'); + yield setSetting(['mta', 'method'], 'MailPoet'); + yield setSetting(['mta', 'mailpoet_api_key'], key); + yield setSetting(['signup_confirmation', 'enabled'], '1'); + fields.mssStatus = 'valid_mss_active'; + } + } else { + fields.mssStatus = 'valid_mss_not_active'; + } + return updateKeyActivationState(fields); +} + +export function* verifyPremiumKey(key: string) { + const { res, success, error } = yield { + type: 'CALL_API', + endpoint: 'services', + action: 'checkPremiumKey', + data: { key }, + }; + if (!success) { + MailPoet.trackEvent( + 'User has failed to validate a Premium key', + { + 'MailPoet Free version': MailPoet.version, + 'Premium plugin is active': !!MailPoet.premiumVersion, + } + ); + return updateKeyActivationState({ + premiumStatus: 'invalid', + premiumMessage: error.join(' ') || null, + }); + } + + yield updateKeyActivationState({ premiumMessage: null }); + // install/activate Premium plugin + let pluginInstalled = res.meta.premium_plugin_installed; + let pluginActive = res.meta.premium_plugin_active; + + if (!pluginInstalled) { + const actions = installPremiumPlugin(); + let action = actions.next(); + while (!action.done) { + yield action.value; + action = actions.next(); + } + pluginInstalled = action.value; + } + + if (pluginInstalled && !pluginActive) { + const isAfterInstall = !res.meta.premium_plugin_installed; + const actions = activatePremiumPlugin(isAfterInstall); + let action = actions.next(); + while (!action.done) { + yield action.value; + action = actions.next(); + } + pluginActive = action.value; + } + + if (pluginInstalled && pluginActive) { + yield updateKeyActivationState({ premiumStatus: 'valid_premium_plugin_active' }); + } + + MailPoet.trackEvent( + 'User has validated a Premium key', + { + 'MailPoet Free version': MailPoet.version, + 'Premium plugin is active': pluginActive, + } + ); + + return null; +} + +export function* activatePremiumPlugin(isAfterInstall) { + const doneStatus = isAfterInstall ? 'install_done' : 'activate_done'; + const errorStatus = isAfterInstall ? 'install_activating_error' : 'activate_error'; + yield updateKeyActivationState({ + premiumStatus: 'valid_premium_plugin_being_activated', + premiumInstallationStatus: isAfterInstall ? 'install_activating' : 'activate_activating', + }); + const call = yield { + type: 'CALL_API', + endpoint: 'premium', + action: 'activatePlugin', + }; + if (call && !call.success) { + yield updateKeyActivationState({ premiumInstallationStatus: errorStatus }); + return false; + } + yield updateKeyActivationState({ premiumInstallationStatus: doneStatus }); + return true; +} + +export function* installPremiumPlugin() { + yield updateKeyActivationState({ + premiumStatus: 'valid_premium_plugin_being_installed', + premiumInstallationStatus: 'install_installing', + }); + const call = yield { + type: 'CALL_API', + endpoint: 'premium', + action: 'installPlugin', + }; + if (call && !call.success) { + yield updateKeyActivationState({ premiumInstallationStatus: 'install_installing_error' }); + return false; + } + return true; +} diff --git a/assets/js/src/settings/store/create_reducer.ts b/assets/js/src/settings/store/create_reducer.ts index df7ab34b2c..9d272def51 100644 --- a/assets/js/src/settings/store/create_reducer.ts +++ b/assets/js/src/settings/store/create_reducer.ts @@ -1,7 +1,8 @@ import _ from 'lodash'; -import { State, Action } from './types'; +import { State, Action, KeyActivationState } from './types'; export default function createReducer(defaultValue: State) { + let keyActivation: KeyActivationState; return (state: State = defaultValue, action: Action): State => { switch (action.type) { case 'SET_SETTING': @@ -14,6 +15,16 @@ export default function createReducer(defaultValue: State) { return { ...state, save: { inProgress: false, error: null } }; case 'SAVE_FAILED': return { ...state, save: { inProgress: false, error: action.error } }; + case 'UPDATE_KEY_ACTIVATION_STATE': + keyActivation = { ...state.keyActivation, ...action.fields }; + keyActivation.isKeyValid = null; + if (keyActivation.mssStatus !== null || keyActivation.premiumStatus !== null) { + keyActivation.isKeyValid = ( + keyActivation.mssStatus !== 'invalid' + || keyActivation.premiumStatus !== 'invalid' + ); + } + return { ...state, keyActivation }; default: return state; } diff --git a/assets/js/src/settings/store/make_default_state.ts b/assets/js/src/settings/store/make_default_state.ts index 734ecd4eb7..8b37576a6a 100644 --- a/assets/js/src/settings/store/make_default_state.ts +++ b/assets/js/src/settings/store/make_default_state.ts @@ -1,22 +1,58 @@ -import { State } from './types'; +import MailPoet from 'mailpoet'; +import { State, PremiumStatus, MssStatus } from './types'; import normalizeSettings from './normalize_settings'; export default function makeDefaultState(window: any): State { + const pages = window.mailpoet_pages; + const segments = window.mailpoet_segments; + const save = { inProgress: false, error: null }; + const data = normalizeSettings(window.mailpoet_settings); + const flags = { + error: false, + woocommerce: !!window.mailpoet_woocommerce_active, + newUser: !!window.mailpoet_is_new_user, + mssKeyValid: window.mailpoet_mss_key_valid, + premiumKeyValid: window.mailpoet_premium_key_valid, + premiumPluginInstalled: window.mailpoet_premium_plugin_installed, + }; + const premiumStatus = getPremiumStatus(flags); + const mssStatus = getMssStatus(flags, data); + let isKeyValid = null; + if (mssStatus !== null || premiumStatus !== null) { + isKeyValid = mssStatus !== 'invalid' || premiumStatus !== 'invalid'; + } + const keyActivation = { + mssStatus, + isKeyValid, + premiumStatus, + mssMessage: null, + premiumMessage: null, + premiumInstallationStatus: null, + key: data.premium.premium_key || data.mta.mailpoet_api_key, + }; return { - save: { - inProgress: false, - error: null, - }, - flags: { - error: false, - woocommerce: !!window.mailpoet_woocommerce_active, - newUser: !!window.mailpoet_is_new_user, - mssKeyValid: window.mailpoet_mss_key_valid, - premiumKeyValid: window.mailpoet_premium_key_valid, - premiumPluginInstalled: window.mailpoet_premium_plugin_installed, - }, - data: normalizeSettings(window.mailpoet_settings), - segments: window.mailpoet_segments, - pages: window.mailpoet_pages, + data, flags, save, keyActivation, segments, pages, }; } + +function getPremiumStatus(flags): PremiumStatus { + const keyValid = flags.premiumKeyValid; + const pluginInstalled = flags.premiumPluginInstalled; + const pluginActive = !!MailPoet.premiumVersion; + if (!keyValid) { + return 'invalid'; + } + if (pluginActive) { + return 'valid_premium_plugin_active'; + } + return pluginInstalled + ? 'valid_premium_plugin_not_active' + : 'valid_premium_plugin_not_installed'; +} + +function getMssStatus(flags, data): MssStatus { + const keyValid = flags.mssKeyValid; + if (!keyValid) return 'invalid'; + const mssActive = data.mta.method === 'MailPoet'; + return mssActive ? 'valid_mss_active' : 'valid_mss_not_active'; +} diff --git a/assets/js/src/settings/store/selectors.ts b/assets/js/src/settings/store/selectors.ts index 5cd32cf0c1..d5df3a7ec3 100644 --- a/assets/js/src/settings/store/selectors.ts +++ b/assets/js/src/settings/store/selectors.ts @@ -48,32 +48,6 @@ export function getPages(state: State) { return state.pages; } -export function getPremiumStatus(state: State): PremiumStatus { - const keyValid = state.flags.premiumKeyValid; - const pluginInstalled = state.flags.premiumPluginInstalled; - const pluginActive = !!MailPoet.premiumVersion; - if (!keyValid) { - return 'invalid'; - } - if (pluginActive) { - return 'valid_premium_plugin_active'; - } - return pluginInstalled - ? 'valid_premium_plugin_not_active' - : 'valid_premium_plugin_not_installed'; -} - -export function getMssStatus(state: State): MssStatus { - const keyValid = state.flags.mssKeyValid; - const mssActive = isMssActive(state); - if (!keyValid) { - return 'invalid'; - } - return mssActive ? 'valid_mss_active' : 'valid_mss_not_active'; -} - -export function hasValidKey(state: State) { - const hasValidMssKey = getMssStatus(state) !== 'invalid'; - const hasValidPremiumKey = getPremiumStatus(state) !== 'invalid'; - return hasValidMssKey || hasValidPremiumKey; +export function getKeyActivationState(state: State) { + return state.keyActivation; } diff --git a/assets/js/src/settings/store/types.ts b/assets/js/src/settings/store/types.ts index b57da12fc7..ba32915932 100644 --- a/assets/js/src/settings/store/types.ts +++ b/assets/js/src/settings/store/types.ts @@ -177,30 +177,6 @@ type Page = { confirm: string } } -export type State = { - data: Settings - segments: Segment[] - pages: Page[] - flags: { - woocommerce: boolean - newUser: boolean - error: boolean - mssKeyValid: boolean - premiumKeyValid: boolean - premiumPluginInstalled: boolean - } - save: { - inProgress: boolean - error: any - } -} - -export type Action = - | { type: 'SET_SETTING'; value: any; path: string[] } - | { type: 'SET_ERROR_FLAG'; value: boolean } - | { type: 'SAVE_STARTED' } - | { type: 'SAVE_DONE' } - | { type: 'SAVE_FAILED'; error: any } export type PremiumStatus = | 'invalid' @@ -224,3 +200,40 @@ export type PremiumInstallationStatus = | 'activate_activating' | 'activate_done' | 'activate_error' + +export type KeyActivationState = { + key: string + isKeyValid: boolean + premiumStatus: PremiumStatus + premiumMessage: string + mssStatus: MssStatus + mssMessage: string + premiumInstallationStatus: PremiumInstallationStatus +} + +export type State = { + data: Settings + segments: Segment[] + pages: Page[] + flags: { + woocommerce: boolean + newUser: boolean + error: boolean + mssKeyValid: boolean + premiumKeyValid: boolean + premiumPluginInstalled: boolean + } + save: { + inProgress: boolean + error: any + } + keyActivation: KeyActivationState +} + +export type Action = + | { type: 'SET_SETTING'; value: any; path: string[] } + | { type: 'SET_ERROR_FLAG'; value: boolean } + | { type: 'SAVE_STARTED' } + | { type: 'SAVE_DONE' } + | { type: 'SAVE_FAILED'; error: any } + | { type: 'UPDATE_KEY_ACTIVATION_STATE', fields: Partial }