/** * External dependencies */ import { has } from 'lodash'; import { decodeHtmlEntities, isUrl } from '@nelio-content/utils'; import type { AnyAction, ImageId, Url } from '@nelio-content/types'; /** * Internal dependencies */ import { INIT_STATE } from '../config'; import type { State as FullState } from '../types'; import type { PostAction as Action } from '../actions/post'; type State = FullState[ 'post' ]; export function post( state = INIT_STATE.post, action: AnyAction ): State { return actualReducer( state, action as Action ) ?? state; } function actualReducer( state: State, action: Action ): State { switch ( action.type ) { case 'SET_POST_ID': return state.id ? state : { ...state, id: state.id }; case 'SET_POST': { const { imageSrc, ...p } = action.post; return { ...INIT_STATE.post, ...p, imageSrc: imageSrc || false, ...extractImages( p?.content ), automationSources: { ...INIT_STATE.post.automationSources, ...p?.automationSources, }, }; } case 'SET_QUERY_ARGS': return { ...state, permalinkQueryArgs: action.args, }; case 'SET_CUSTOM_FIELD': return { ...state, customFields: has( state.customFields, action.key ) ? { ...state.customFields, [ action.key ]: { ...state.customFields[ action.key ], value: action.value, }, } : state.customFields, }; case 'REMOVE_CUSTOM_FIELD': return { ...state, customFields: has( state.customFields, action.key ) ? { ...state.customFields, [ action.key ]: { ...state.customFields[ action.key ], value: '', }, } : state.customFields, }; case 'SET_TITLE': return { ...state, title: action.title, }; case 'SET_EXCERPT': return { ...state, excerpt: action.excerpt, }; case 'SET_CONTENT': return { ...state, content: action.content, ...extractImages( action.content ), }; case 'SET_DATE': return { ...state, date: action.date ?? false, }; case 'SET_AUTHOR': return { ...state, author: action.authorId, authorName: action.authorName, }; case 'SET_PERMALINK': return { ...state, permalink: action.permalink, }; case 'SET_FEATURED_IMAGE': return { ...state, imageId: action.imageId, imageSrc: action.imageSrc, thumbnailSrc: action.thumbnailSrc, imageAltText: action.imageAltText ?? '', }; case 'REMOVE_FEATURED_IMAGE': return { ...state, imageId: 0, imageSrc: false, thumbnailSrc: false, }; case 'SET_FOLLOWERS': return { ...state, followers: action.followers, }; case 'SET_STATUS': return { ...state, status: action.status, }; case 'SET_TERMS': return { ...state, taxonomies: { ...state.taxonomies, [ action.taxonomy ]: action.terms, }, }; case 'ENABLE_AUTO_SHARE': return { ...state, isAutoShareEnabled: action.isAutoShareEnabled, }; case 'SET_AUTO_SHARE_END_MODE': return { ...state, autoShareEndMode: action.autoShareEndMode, }; case 'SET_AUTOMATION_SOURCES': return { ...state, automationSources: { ...state.automationSources, ...action.sources, }, }; case 'SET_NETWORK_IMAGE': return { ...state, networkImages: { ...state.networkImages, [ action.network ]: action.imageUrl, }, networkImageIds: { ...state.networkImageIds, [ action.network ]: action.imageId, }, networkImageAltTexts: { ...state.networkImageAltTexts, [ action.network ]: action.imageAltText, }, }; case 'SET_SERIES': return { ...state, series: action.series, }; case 'MARK_AS_LOADING_POST_ITEMS': return state; } } // ======= // HELPERS // ======= function extractImages( content = '' ): { images: Url[]; imageIds: ImageId[]; imageAltTexts: string[]; } { const imgTags = content.match( /]*>/gi ) || []; const images: Url[] = []; const imageIds: ImageId[] = []; const imageAltTexts: string[] = []; imgTags.forEach( ( tag ) => { // Extract URL from src or data-src const srcMatch = tag.match( /(?:src|data-src)=["']([^"']+)["']/i ); const imageUrl = srcMatch && isUrl( srcMatch[ 1 ] ) ? srcMatch[ 1 ] : null; if ( imageUrl && ! images.includes( imageUrl ) ) { images.push( imageUrl ); // Extract image ID from wp-image-XXXXX class. const idMatch = tag.match( /wp-image-(\d+)/ ); imageIds.push( idMatch ? ( parseInt( idMatch[ 1 ] ?? '0', 10 ) as ImageId ) : ( 0 as ImageId ) ); // Extract alt text. const altMatch = tag.match( /alt=["']([^"']*)["']/i ); imageAltTexts.push( altMatch ? decodeHtmlEntities( altMatch[ 1 ] ?? '' ) : '' ); } } ); return { images, imageIds, imageAltTexts, }; }