diff --git a/assets/js/src/settings/index.tsx b/assets/js/src/settings/index.tsx index 488ba23a35..2b8250701b 100644 --- a/assets/js/src/settings/index.tsx +++ b/assets/js/src/settings/index.tsx @@ -1,14 +1,44 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { initStore, STORE_NAME } from './store'; -const App = () => ( - <> -

Settings

-
{JSON.stringify((window as any).mailpoet_settings)}
- -); +const App = () => { + const isSaving = useSelect( + (sel) => sel(STORE_NAME).isSaving(), + [] + ); + const error = useSelect( + (sel) => sel(STORE_NAME).getError(), + [] + ); + const settings = useSelect( + (sel) => sel(STORE_NAME).getSettings(), + [] + ); + const email = useSelect( + (sel) => sel(STORE_NAME).getSetting(['sender', 'address']), + [] + ); + const actions = useDispatch(STORE_NAME); + const setEmail = (event) => { + actions.setSetting(['sender', 'address'], event.target.value); + }; + const save = () => { + actions.saveSettings(settings); + }; + return ( + <> +

Settings

+

{JSON.stringify({ email, isSaving, error })}

+ + + + ); +}; const container = document.getElementById('settings_container'); if (container) { + initStore((window as any).mailpoet_settings); ReactDOM.render(, container); } diff --git a/assets/js/src/settings/store/actions.ts b/assets/js/src/settings/store/actions.ts new file mode 100644 index 0000000000..0fe47cf4fe --- /dev/null +++ b/assets/js/src/settings/store/actions.ts @@ -0,0 +1,15 @@ +import { Action, Settings } from './types'; + +export function setSetting(path: string[], value: any): Action { + return { type: 'SET_SETTING', path, value }; +} + +export function* saveSettings(data: Settings) { + yield { type: 'SAVE_STARTED' }; + const error = yield { type: 'SEND_DATA_TO_API', data }; + if (error) { + return { type: 'SAVE_FAILED', error }; + } + yield { type: 'TRACK_SETTINGS_SAVED', data }; + return { type: 'SAVE_DONE' }; +} diff --git a/assets/js/src/settings/store/controls.ts b/assets/js/src/settings/store/controls.ts new file mode 100644 index 0000000000..410f171b88 --- /dev/null +++ b/assets/js/src/settings/store/controls.ts @@ -0,0 +1,19 @@ +import MailPoet from 'mailpoet'; + +export async function SEND_DATA_TO_API({ data }) { + try { + await MailPoet.Ajax.post({ + api_version: (window as any).mailpoet_api_version, + endpoint: 'settings', + action: 'set', + data, + }); + return false; + } catch (res) { + return res.errors.map((e) => e.message); + } +} + +export function TRACK_SETTINGS_SAVED({ data }) { + // ... +} diff --git a/assets/js/src/settings/store/create_reducer.ts b/assets/js/src/settings/store/create_reducer.ts new file mode 100644 index 0000000000..dec9d65051 --- /dev/null +++ b/assets/js/src/settings/store/create_reducer.ts @@ -0,0 +1,19 @@ +import _ from 'lodash'; +import { State, Action } from './types'; + +export default function createReducer(defaultValue: State) { + return (state: State = defaultValue, action: Action): State => { + switch (action.type) { + case 'SET_SETTING': + return _.setWith(_.clone(state), ['data', ...action.path], action.value, _.clone); + case 'SAVE_STARTED': + return { ...state, save: { inProgress: true, error: null } }; + case 'SAVE_DONE': + return { ...state, save: { inProgress: false, error: null } }; + case 'SAVE_FAILED': + return { ...state, save: { inProgress: false, error: action.error } }; + default: + return state; + } + }; +} diff --git a/assets/js/src/settings/store/index.ts b/assets/js/src/settings/store/index.ts new file mode 100644 index 0000000000..03df0849c5 --- /dev/null +++ b/assets/js/src/settings/store/index.ts @@ -0,0 +1,17 @@ +import { registerStore } from '@wordpress/data'; +import { Settings } from './types'; +import * as actions from './actions'; +import * as selectors from './selectors'; +import * as controls from './controls'; +import createReducer from './create_reducer'; +import makeDefaultState from './make_default_state'; + +export const STORE_NAME = 'mailpoet-settings'; + +export const initStore = (data: Settings) => registerStore(STORE_NAME, { + reducer: createReducer(makeDefaultState(data)), + actions, + selectors, + controls, + resolvers: {}, +}); diff --git a/assets/js/src/settings/store/make_default_state.ts b/assets/js/src/settings/store/make_default_state.ts new file mode 100644 index 0000000000..a912017e3a --- /dev/null +++ b/assets/js/src/settings/store/make_default_state.ts @@ -0,0 +1,11 @@ +import { State, Settings } from './types'; + +export default function makeDefaultState(data: Settings): State { + return { + save: { + inProgress: false, + error: null, + }, + data, + }; +} diff --git a/assets/js/src/settings/store/selectors.ts b/assets/js/src/settings/store/selectors.ts new file mode 100644 index 0000000000..724bf2d270 --- /dev/null +++ b/assets/js/src/settings/store/selectors.ts @@ -0,0 +1,22 @@ +import _ from 'lodash'; +import { State, Settings } from './types'; + +export function getSetting(state: State, path: string[]): any { + return _.get(state.data, path); +} + +export function getSettings(state: State): Settings { + return state.data; +} + +export function isSaving(state: State): boolean { + return state.save.inProgress; +} + +export function hasError(state: State): boolean { + return state.save.error !== null; +} + +export function getError(state: State): any { + return state.save.error; +} diff --git a/assets/js/src/settings/store/types.ts b/assets/js/src/settings/store/types.ts new file mode 100644 index 0000000000..9ed103eb6a --- /dev/null +++ b/assets/js/src/settings/store/types.ts @@ -0,0 +1,55 @@ +export type Settings = { + sender: { + name: string + address: string + } + reply_to: { + name: string + address: string + } + subscribe: { + on_comment: { + enabled: boolean + label: string + segments: string[] + } + on_register: { + enabled: boolean + label: string + segments: string[] + } + } + subscription: { + pages: { + manage: string + unsubscribe: string + confirmation: string + captcha: string + } + segments: string[] + } + stats_notifications: { + enabled: boolean + automated: boolean + address: string + } + subscriber_email_notification: { + enabled: boolean + address: string + } + // ... +} + +export type State = { + data: Settings + save: { + inProgress: boolean + error: any + } +} + +export type Action = + | { type: 'SET_SETTING'; value: any; path: string[] } + | { type: 'SAVE_STARTED' } + | { type: 'SAVE_DONE' } + | { type: 'SAVE_FAILED'; error: any } diff --git a/package-lock.json b/package-lock.json index 656fae229d..d5fdbb6ef3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3968,6 +3968,12 @@ "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", "dev": true }, + "@types/lodash": { + "version": "4.14.149", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", diff --git a/package.json b/package.json index 2f580a3336..8de7bdb47f 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "@babel/preset-react": "^7.8.3", "@babel/preset-typescript": "^7.8.3", "@babel/register": "^7.8.3", + "@types/lodash": "^4.14.149", "@types/react": "^16.9.22", "@typescript-eslint/eslint-plugin": "^2.21.0", "@typescript-eslint/parser": "^2.21.0",