PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / src / admin / hooks / useFileUploader.js
advanced-ads / src / admin / hooks Last commit date
useFetch.js 2 days ago useFileUploader.js 2 days ago useModalState.js 2 days ago useSearchCombobox.js 2 days ago
useFileUploader.js
166 lines
1 import { useState, useCallback, useRef } from 'react';
2
3 function generateId() {
4 return (
5 Math.random().toString( 36 ).slice( 2, 9 ) + Date.now().toString( 36 )
6 );
7 }
8
9 function formatSize( bytes, decimals = 2 ) {
10 if ( bytes === 0 ) {
11 return '0 Bytes';
12 }
13 const k = 1024;
14 const sizes = [ 'Bytes', 'KB', 'MB', 'GB', 'TB' ];
15 const i = Math.floor( Math.log( bytes ) / Math.log( k ) );
16 return (
17 parseFloat(
18 ( bytes / Math.pow( k, i ) ).toFixed( Math.max( 0, decimals ) )
19 ) +
20 ' ' +
21 sizes[ i ]
22 );
23 }
24
25 function isImageFile( filename ) {
26 return /\.(jpg|jpeg|png|gif|svg)$/i.test( filename );
27 }
28
29 function getExt( filename ) {
30 return filename.split( '.' ).pop().toLowerCase();
31 }
32
33 export function useFileUploader( {
34 maxFiles = 10,
35 maxSizeMB = 5,
36 allowedExt = [ 'jpg', 'jpeg', 'png', 'gif', 'svg', 'pdf', 'docx', 'txt' ],
37 } = {} ) {
38 const [ selectedFiles, setSelectedFiles ] = useState( [] );
39 const [ errors, setErrors ] = useState( [] );
40
41 // Tracks committed + in-flight count so slots never goes stale between async reads
42 const totalCountRef = useRef( 0 );
43
44 const syncFiles = ( updater ) => {
45 setSelectedFiles( ( prev ) => {
46 const next =
47 typeof updater === 'function' ? updater( prev ) : updater;
48 totalCountRef.current = next.length;
49 return next;
50 } );
51 };
52
53 const maxSizeBytes = maxSizeMB * 1024 * 1024;
54
55 const validateFile = useCallback(
56 ( file ) => {
57 const errs = [];
58 const ext = getExt( file.name );
59 if ( ! allowedExt.includes( ext ) ) {
60 errs.push( `".${ ext }" is not an allowed extension` );
61 }
62 if ( file.size > maxSizeBytes ) {
63 errs.push(
64 `exceeds max size of ${ maxSizeMB } MB (is ${ formatSize(
65 file.size
66 ) })`
67 );
68 }
69 return errs;
70 },
71 [ allowedExt, maxSizeBytes, maxSizeMB ]
72 );
73
74 const processFiles = useCallback(
75 ( fileList ) => {
76 const slots = maxFiles - totalCountRef.current;
77
78 if ( slots <= 0 ) {
79 setErrors( [
80 {
81 name: 'Selection blocked',
82 reasons: [
83 `you have already reached the limit of ${ maxFiles } files`,
84 ],
85 },
86 ] );
87 return;
88 }
89
90 const rejections = [];
91 let accepted = 0;
92
93 for ( const file of fileList ) {
94 if ( accepted >= slots ) {
95 rejections.push( {
96 name: file.name,
97 reasons: [ `max file limit of ${ maxFiles } reached` ],
98 } );
99 continue;
100 }
101
102 const errs = validateFile( file );
103 if ( errs.length ) {
104 rejections.push( { name: file.name, reasons: errs } );
105 continue;
106 }
107
108 // Increment ref immediately — before the async read completes —
109 // so the next processFiles call sees the correct count right away
110 totalCountRef.current += 1;
111
112 const reader = new FileReader();
113 reader.onloadend = () => {
114 syncFiles( ( prev ) => [
115 ...prev,
116 {
117 id: generateId(),
118 filename: file.name,
119 filetype: file.type,
120 fileimage: reader.result,
121 datetime: file.lastModified
122 ? new Date( file.lastModified ).toLocaleString(
123 'en-IN'
124 )
125 : 'Unknown',
126 filesize: formatSize( file.size ),
127 isImage: isImageFile( file.name ),
128 },
129 ] );
130 };
131 reader.readAsDataURL( file );
132 accepted++;
133 }
134
135 setErrors( rejections );
136 },
137 [ maxFiles, validateFile ]
138 );
139
140 const deleteFile = useCallback( ( id ) => {
141 if (
142 globalThis.confirm( 'Are you sure you want to delete this file?' )
143 ) {
144 syncFiles( ( prev ) => prev.filter( ( f ) => f.id !== id ) );
145 }
146 }, [] );
147
148 const reset = useCallback( () => {
149 totalCountRef.current = 0;
150 syncFiles( [] );
151 setErrors( [] );
152 }, [] );
153
154 const atLimit = selectedFiles.length >= maxFiles;
155
156 return {
157 selectedFiles,
158 errors,
159 atLimit,
160 processFiles,
161 deleteFile,
162 reset,
163 meta: { maxFiles, maxSizeMB, allowedExt },
164 };
165 }
166