EmailFormPopupUtils.js
120 lines
| 1 | /** |
| 2 | * Shared constants and pure helpers for EmailFormPopup. |
| 3 | */ |
| 4 | import { __ } from '@wordpress/i18n'; |
| 5 | import { format as formatDateFns } from 'date-fns'; |
| 6 | |
| 7 | const pad2 = ( n ) => String( n ).padStart( 2, '0' ); |
| 8 | |
| 9 | export const SELECT_TIME_OPTIONS = ( () => { |
| 10 | const opts = [ { value: '', label: __( 'None', 'presto-player' ) } ]; |
| 11 | for ( let m = 0; m < 720; m += 10 ) { |
| 12 | const h = Math.floor( m / 60 ); |
| 13 | opts.push( { value: pad2( h ) + ':' + pad2( m % 60 ), label: pad2( h ) + ':' + pad2( m % 60 ) } ); |
| 14 | } |
| 15 | return opts; |
| 16 | } )(); |
| 17 | |
| 18 | export const PERIOD_OPTIONS = [ |
| 19 | { value: '-1', label: __( 'None', 'presto-player' ) }, |
| 20 | { value: '0', label: __( 'AM', 'presto-player' ) }, |
| 21 | { value: '12', label: __( 'PM', 'presto-player' ) }, |
| 22 | ]; |
| 23 | |
| 24 | export const STATUS_OPTIONS = [ |
| 25 | { value: 'publish', label: __( 'Published', 'presto-player' ) }, |
| 26 | { value: 'future', label: __( 'Scheduled', 'presto-player' ) }, |
| 27 | { value: 'pending', label: __( 'Pending Review', 'presto-player' ) }, |
| 28 | { value: 'draft', label: __( 'Draft', 'presto-player' ) }, |
| 29 | ]; |
| 30 | |
| 31 | export const VISIBILITY_OPTIONS = [ |
| 32 | { value: 'public', label: __( 'Public', 'presto-player' ) }, |
| 33 | { value: 'private', label: __( 'Private', 'presto-player' ) }, |
| 34 | ]; |
| 35 | |
| 36 | export const DEFAULT_STATE = { |
| 37 | email: '', |
| 38 | status: 'publish', |
| 39 | visibility: 'public', |
| 40 | publishedDate: null, |
| 41 | publishedTimeValue: '', |
| 42 | publishedPeriod: '-1', |
| 43 | }; |
| 44 | |
| 45 | export const getOptionLabel = ( opts, val ) => |
| 46 | opts.find( ( o ) => o.value === val )?.label || opts[ 0 ]?.label; |
| 47 | |
| 48 | function getLocalDateOnly( dateOrIso ) { |
| 49 | if ( ! dateOrIso ) return null; |
| 50 | const d = typeof dateOrIso === 'string' ? new Date( dateOrIso ) : dateOrIso; |
| 51 | return isNaN( d.getTime() ) ? null : new Date( d.getFullYear(), d.getMonth(), d.getDate() ); |
| 52 | } |
| 53 | |
| 54 | function toHHMM( timeValue, period ) { |
| 55 | if ( [ -1, '-1', '', null, undefined ].includes( period ) || ! timeValue ) return '00:00'; |
| 56 | let h12, min; |
| 57 | if ( typeof timeValue === 'string' && timeValue.includes( ':' ) ) { |
| 58 | [ h12, min ] = timeValue.split( ':' ).map( ( x ) => parseInt( x, 10 ) || 0 ); |
| 59 | } else { |
| 60 | const t = parseFloat( timeValue ); |
| 61 | h12 = Math.floor( t ); |
| 62 | min = Math.round( ( t % 1 ) * 60 ); |
| 63 | } |
| 64 | const p = Number( period ); |
| 65 | const h24 = p === 0 ? ( h12 === 0 ? 0 : h12 ) : h12 === 0 ? 12 : 12 + h12; |
| 66 | return pad2( h24 ) + ':' + pad2( min ); |
| 67 | } |
| 68 | |
| 69 | function fromHHMM( hhmm ) { |
| 70 | if ( ! hhmm ) return { timeValue: '', period: '-1' }; |
| 71 | const [ hStr, mStr ] = hhmm.split( ':' ); |
| 72 | const h = parseInt( hStr, 10 ); |
| 73 | if ( isNaN( h ) ) return { timeValue: '', period: '-1' }; |
| 74 | const m = parseInt( mStr, 10 ) || 0; |
| 75 | const period = h < 12 ? '0' : '12'; |
| 76 | const h12 = h === 0 || h === 12 ? 0 : h > 12 ? h - 12 : h; |
| 77 | const snap = Math.round( ( h12 * 60 + m ) / 10 ) * 10; |
| 78 | return { timeValue: pad2( Math.floor( snap / 60 ) % 12 ) + ':' + pad2( snap % 60 ), period }; |
| 79 | } |
| 80 | |
| 81 | function fromDate( date ) { |
| 82 | if ( ! date || isNaN( date.getTime() ) ) return { timeValue: '', period: '-1' }; |
| 83 | return fromHHMM( pad2( date.getHours() ) + ':' + pad2( date.getMinutes() ) ); |
| 84 | } |
| 85 | |
| 86 | function buildDate( date, timeValue, period ) { |
| 87 | const local = getLocalDateOnly( date ); |
| 88 | if ( ! local ) return null; |
| 89 | const [ h, m ] = toHHMM( timeValue, period ).split( ':' ).map( Number ); |
| 90 | return new Date( local.getFullYear(), local.getMonth(), local.getDate(), h, m, 0, 0 ); |
| 91 | } |
| 92 | |
| 93 | export function toPublishedDateTime( date, timeValue, period ) { |
| 94 | const d = buildDate( date, timeValue, period ); |
| 95 | return d ? d.toISOString() : new Date().toISOString(); |
| 96 | } |
| 97 | |
| 98 | export function formatPublishedDisplay( date, timeValue, period ) { |
| 99 | const d = buildDate( date, timeValue, period ); |
| 100 | return d ? formatDateFns( d, 'MMM d, yyyy' ) + ' ' + __( 'at', 'presto-player' ) + ' ' + formatDateFns( d, 'h:mm a' ) : ''; |
| 101 | } |
| 102 | |
| 103 | export function getInitialState( initialData ) { |
| 104 | if ( ! initialData ) { |
| 105 | return { ...DEFAULT_STATE, publishedDate: getLocalDateOnly( new Date() ) }; |
| 106 | } |
| 107 | const dateObj = initialData.date ? new Date( initialData.date ) : new Date(); |
| 108 | const { timeValue, period } = fromDate( dateObj ); |
| 109 | let visibilityValue = initialData.visibility || 'public'; |
| 110 | if ( initialData.status === 'private' ) visibilityValue = 'private'; |
| 111 | return { |
| 112 | email: initialData.email || '', |
| 113 | status: initialData.status === 'private' ? 'publish' : ( initialData.status || 'publish' ), |
| 114 | visibility: visibilityValue, |
| 115 | publishedDate: getLocalDateOnly( dateObj ), |
| 116 | publishedTimeValue: timeValue, |
| 117 | publishedPeriod: period, |
| 118 | }; |
| 119 | } |
| 120 |