| 1 |
/** |
| 2 |
* WordPress dependencies |
| 3 |
*/ |
| 4 |
import apiFetch from '@wordpress/api-fetch'; |
| 5 |
import { createReduxStore } from '@wordpress/data'; |
| 6 |
|
| 7 |
// `voices`/`videoSizes`/`project` are keyed by resolver argument: resolution is |
| 8 |
// cached per selector-args, so a shared slot would go stale yet report fresh. |
| 9 |
export const DEFAULT_STATE = { |
| 10 |
settings: {}, |
| 11 |
languages: [], |
| 12 |
voices: {}, // languageCode → voice list |
| 13 |
scriptTemplates: [], |
| 14 |
videoTemplates: [], |
| 15 |
videoSizes: {}, // projectId → size list |
| 16 |
project: {}, // projectId → project record |
| 17 |
}; |
| 18 |
|
| 19 |
const reducer = ( state = DEFAULT_STATE, action ) => { |
| 20 |
// Replace a session-wide key. |
| 21 |
if ( action.type === 'SET' && action.key in state ) { |
| 22 |
const empty = Array.isArray( DEFAULT_STATE[ action.key ] ) ? [] : {}; |
| 23 |
return { ...state, [ action.key ]: action.value || empty }; |
| 24 |
} |
| 25 |
// Merge one argument's result, leaving other arguments' entries untouched. |
| 26 |
if ( action.type === 'SET_BY' && action.key in state ) { |
| 27 |
return { |
| 28 |
...state, |
| 29 |
[ action.key ]: { |
| 30 |
...state[ action.key ], |
| 31 |
[ action.arg ]: action.value, |
| 32 |
}, |
| 33 |
}; |
| 34 |
} |
| 35 |
return state; |
| 36 |
}; |
| 37 |
|
| 38 |
const selectors = { |
| 39 |
getSettings: ( state ) => state.settings, |
| 40 |
getLanguages: ( state ) => state.languages, |
| 41 |
getVoices: ( state, languageCode ) => state.voices[ languageCode ] ?? [], |
| 42 |
getScriptTemplates: ( state ) => state.scriptTemplates, |
| 43 |
getVideoTemplates: ( state ) => state.videoTemplates, |
| 44 |
getVideoSizes: ( state, projectId ) => state.videoSizes[ projectId ] ?? [], |
| 45 |
getProject: ( state, projectId ) => state.project[ projectId ] ?? {}, |
| 46 |
}; |
| 47 |
|
| 48 |
// Private to this store — no registered `actions`; only resolvers dispatch. |
| 49 |
const set = ( key, value ) => ( { type: 'SET', key, value } ); |
| 50 |
const setBy = ( key, arg, value ) => ( { type: 'SET_BY', key, arg, value } ); |
| 51 |
|
| 52 |
const resolvers = { |
| 53 |
async getSettings() { |
| 54 |
const value = await apiFetch( { path: '/beyondwords/v1/settings' } ); |
| 55 |
return set( 'settings', value ); |
| 56 |
}, |
| 57 |
async getLanguages() { |
| 58 |
const value = await apiFetch( { path: '/beyondwords/v1/languages' } ); |
| 59 |
return set( 'languages', value ); |
| 60 |
}, |
| 61 |
async getVoices( languageCode ) { |
| 62 |
const value = await apiFetch( { |
| 63 |
path: `/beyondwords/v1/languages/${ languageCode }/voices`, |
| 64 |
} ); |
| 65 |
return setBy( 'voices', languageCode, value || [] ); |
| 66 |
}, |
| 67 |
async getScriptTemplates() { |
| 68 |
const value = await apiFetch( { |
| 69 |
path: '/beyondwords/v1/summarization-settings-templates', |
| 70 |
} ); |
| 71 |
return set( 'scriptTemplates', value ); |
| 72 |
}, |
| 73 |
async getVideoTemplates() { |
| 74 |
const value = await apiFetch( { |
| 75 |
path: '/beyondwords/v1/video-settings-templates', |
| 76 |
} ); |
| 77 |
return set( 'videoTemplates', value ); |
| 78 |
}, |
| 79 |
async getVideoSizes( projectId ) { |
| 80 |
if ( ! projectId ) { |
| 81 |
return setBy( 'videoSizes', projectId, [] ); |
| 82 |
} |
| 83 |
const r = await apiFetch( { |
| 84 |
path: `/beyondwords/v1/projects/${ projectId }/video-settings`, |
| 85 |
} ); |
| 86 |
return setBy( 'videoSizes', projectId, r?.sizes ?? [] ); |
| 87 |
}, |
| 88 |
// The project's default `language` pre-selects the Language dropdown. |
| 89 |
async getProject( projectId ) { |
| 90 |
if ( ! projectId ) { |
| 91 |
return setBy( 'project', projectId, {} ); |
| 92 |
} |
| 93 |
const value = await apiFetch( { |
| 94 |
path: `/beyondwords/v1/projects/${ projectId }`, |
| 95 |
} ); |
| 96 |
return setBy( 'project', projectId, value || {} ); |
| 97 |
}, |
| 98 |
}; |
| 99 |
|
| 100 |
export default createReduxStore( 'beyondwords/settings', { |
| 101 |
reducer, |
| 102 |
selectors, |
| 103 |
resolvers, |
| 104 |
} ); |
| 105 |
|