PluginProbe ʕ •ᴥ•ʔ
Presto Player / trunk
Presto Player vtrunk
4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / src / admin / dashboard / hooks / useEmail.ts
presto-player / src / admin / dashboard / hooks Last commit date
test 3 weeks ago useCompleteOnboarding.js 3 weeks ago useDateRangePicker.js 1 month ago useEmail.ts 1 month ago useEngagementChartData.js 1 month ago useLicenseSettings.js 1 month ago useLink.js 1 month ago useMediaDetail.js 1 month ago useMediaLibrary.js 1 month ago useMediaList.ts 1 month ago usePerformanceSettings.js 1 month ago useRegisterActivePage.js 1 month ago useSettingOption.js 1 month ago useSimpleSettingsPage.js 1 month ago useTopPerforming.js 1 month ago useTopVideosPaginated.js 1 month ago useUpgradeCTA.js 1 month ago useUserDetail.js 1 month ago
useEmail.ts
148 lines
1 // eslint-disable-next-line import/no-extraneous-dependencies
2 import { useState, useMemo, useEffect } from 'react';
3 import apiFetch from '@wordpress/api-fetch';
4
5 // ============================================================================
6 // TYPES – same shape as backend list response
7 // ============================================================================
8
9 export interface EmailItem {
10 id: number;
11 email: string;
12 video_title: string;
13 preset_name: string;
14 date: string;
15 status?: string;
16 visibility?: string;
17 }
18
19 export interface UseEmailReturn {
20 emails: EmailItem[];
21 rawEmails: EmailItem[];
22 setRawEmails: React.Dispatch< React.SetStateAction< EmailItem[] > >;
23 loading: boolean;
24 sortField: string;
25 sortOrder: 'asc' | 'desc';
26 handleSort: ( field: string ) => void;
27 fetchEmails: () => Promise< void >;
28 }
29
30 export const EMAIL_SUBMISSIONS_PATH = '/presto-player/v1/email-submissions';
31
32 // Must match RestEmailSubmissionsController::MAX_PER_PAGE.
33 const EMAIL_PAGE_SIZE = 100;
34 // Safety ceiling so a misconfigured server can't loop forever. 50 * 100 = 5,000 records
35 // fetched into the UI at most.
36 const EMAIL_MAX_PAGES = 50;
37
38 // ============================================================================
39 // SORTING UTILITIES – same pattern as useMedia
40 // ============================================================================
41
42 const createDateComparator = (
43 sortOrder: 'asc' | 'desc'
44 ): ( ( a: EmailItem, b: EmailItem ) => number ) => {
45 return ( a: EmailItem, b: EmailItem ) => {
46 const dateA = new Date( a.date || 0 ).getTime();
47 const dateB = new Date( b.date || 0 ).getTime();
48 if ( sortOrder === 'asc' ) {
49 return dateA - dateB;
50 }
51 return dateB - dateA;
52 };
53 };
54
55 const sortEmailItems = (
56 items: EmailItem[],
57 sortField: string,
58 sortOrder: 'asc' | 'desc'
59 ): EmailItem[] => {
60 if ( ! items || items.length === 0 ) {
61 return [];
62 }
63 const comparator = createDateComparator( sortOrder );
64 return [ ...items ].sort( comparator );
65 };
66
67 // ============================================================================
68 // MAIN HOOK
69 // ============================================================================
70
71 const useEmail = (): UseEmailReturn => {
72 const [ emails, setEmails ] = useState< EmailItem[] >( [] );
73 const [ loading, setLoading ] = useState< boolean >( true );
74 const [ sortField, setSortField ] = useState< string >( 'date' );
75 const [ sortOrder, setSortOrder ] = useState< 'asc' | 'desc' >( 'desc' );
76
77 /**
78 * Fetch email submissions from backend.
79 *
80 * The server caps per_page at EMAIL_PAGE_SIZE so a single request stays bounded; we walk
81 * pages using X-WP-TotalPages and stop at EMAIL_MAX_PAGES as a safety ceiling.
82 */
83 const fetchEmails = async (): Promise< void > => {
84 try {
85 setLoading( true );
86 const collected: EmailItem[] = [];
87 let page = 1;
88 let totalPages = 1;
89 do {
90 const response = ( await apiFetch( {
91 path: `${ EMAIL_SUBMISSIONS_PATH }?per_page=${ EMAIL_PAGE_SIZE }&page=${ page }`,
92 method: 'GET',
93 parse: false,
94 } ) ) as Response;
95 const headerTotalPages = parseInt(
96 response.headers.get( 'X-WP-TotalPages' ) || '1',
97 10
98 );
99 totalPages = Number.isFinite( headerTotalPages ) && headerTotalPages > 0
100 ? headerTotalPages
101 : 1;
102 const items = ( await response.json() ) as EmailItem[];
103 if ( Array.isArray( items ) ) {
104 collected.push( ...items );
105 }
106 page += 1;
107 } while ( page <= totalPages && page <= EMAIL_MAX_PAGES );
108 setEmails( collected );
109 } catch ( error ) {
110 // eslint-disable-next-line no-console
111 console.error( 'Error fetching emails:', error );
112 setEmails( [] );
113 } finally {
114 setLoading( false );
115 }
116 };
117
118 useEffect( () => {
119 fetchEmails();
120 }, [] );
121
122 const handleSort = ( field: string ): void => {
123 if ( sortField === field ) {
124 setSortOrder( ( prev ) => ( prev === 'asc' ? 'desc' : 'asc' ) );
125 } else {
126 setSortField( field );
127 setSortOrder( field === 'date' ? 'desc' : 'asc' );
128 }
129 };
130
131 const sortedEmails = useMemo( () => {
132 return sortEmailItems( emails, sortField, sortOrder );
133 }, [ emails, sortField, sortOrder ] );
134
135 return {
136 emails: sortedEmails,
137 rawEmails: emails,
138 setRawEmails: setEmails,
139 loading,
140 sortField,
141 sortOrder,
142 handleSort,
143 fetchEmails,
144 };
145 };
146
147 export default useEmail;
148