33type runtime
44type tabsApi
55type tab = Js .Json .t
6+ type urlLookup = result <string , string >
7+ type fillResult = result <unit , string >
68
79@val @scope ("browser" )
810external runtime : runtime = "runtime"
@@ -16,6 +18,19 @@ external sendMessage: (runtime, Js.Json.t) => Js.Promise.t<Js.Json.t> = "sendMes
1618@send
1719external queryTabs : (tabsApi , Js .Json .t ) => Js .Promise .t <array <tab >> = "query"
1820
21+ let triggerDownload : (string , string ) => unit = %raw (` (url , filename ) => {
22+ const anchor = document .createElement (" a" );
23+ anchor .href = url;
24+ anchor .download = filename;
25+ anchor .rel = " noopener" ;
26+ anchor .style .display = " none" ;
27+ document .body .appendChild (anchor);
28+ anchor .click ();
29+ anchor .remove ();
30+ }` )
31+
32+ let makeFilledFilename : unit => string = %raw (` () => " blocky-writer-filled-" + Date .now () + " .pdf" ` )
33+
1934let unsafeJson = (value : 'a ): Js .Json .t => Obj .magic (value )
2035
2136let makeTabQuery = (): Js .Json .t => {
@@ -45,6 +60,30 @@ let tabUrl = (entry: tab): option<string> =>
4560 | None => None
4661 }
4762
63+ let getActivePdfUrl = (): Js .Promise .t <urlLookup > => {
64+ let lookupPromise =
65+ Js .Promise2 .then (queryTabs (tabsApi , makeTabQuery ()), entries => {
66+ if Belt .Array .length (entries ) == 0 {
67+ Js .Promise .resolve (Error ("no active tab found" ))
68+ } else {
69+ switch tabUrl (entries [0 ]) {
70+ | None => Js .Promise .resolve (Error ("active tab URL unavailable" ))
71+ | Some (url ) =>
72+ if looksLikePdfUrl (url ) {
73+ Js .Promise .resolve (Ok (url ))
74+ } else {
75+ Js .Promise .resolve (Error ("active tab is not a PDF URL" ))
76+ }
77+ }
78+ }
79+ })
80+
81+ Js .Promise2 .catch (lookupPromise , err => {
82+ Js .log2 ("active tab lookup failed" , err )
83+ Js .Promise .resolve (Error ("failed to query active tab" ))
84+ })
85+ }
86+
4887type detectResult = {
4988 ok : bool ,
5089 blocks : array <PdfTool .block >,
@@ -71,23 +110,14 @@ let decodeDetectResult = (payload: Js.Json.t): detectResult => {
71110}
72111
73112let detectBlocksForActiveTab = (): Js .Promise .t <detectResult > => {
74- let tabsPromise = queryTabs (tabsApi , makeTabQuery ())
75113 let detectPromise =
76- Js .Promise2 .then (tabsPromise , entries => {
77- if Belt .Array .length (entries ) == 0 {
78- Js .Promise .resolve ({ok : false , blocks : [], error : Some ("no active tab found" )})
79- } else {
80- switch tabUrl (entries [0 ]) {
81- | None => Js .Promise .resolve ({ok : false , blocks : [], error : Some ("active tab URL unavailable" )})
82- | Some (url ) =>
83- if ! looksLikePdfUrl (url ) {
84- Js .Promise .resolve ({ok : false , blocks : [], error : Some ("active tab is not a PDF URL" )})
85- } else {
86- Js .Promise2 .then (sendMessage (runtime , makeDetectMessage (url )), response =>
87- Js .Promise .resolve (decodeDetectResult (response ))
88- )
89- }
90- }
114+ Js .Promise2 .then (getActivePdfUrl (), lookup => {
115+ switch lookup {
116+ | Error (error ) => Js .Promise .resolve ({ok : false , blocks : [], error : Some (error )})
117+ | Ok (url ) =>
118+ Js .Promise2 .then (sendMessage (runtime , makeDetectMessage (url )), response =>
119+ Js .Promise .resolve (decodeDetectResult (response ))
120+ )
91121 }
92122 })
93123
@@ -97,6 +127,44 @@ let detectBlocksForActiveTab = (): Js.Promise.t<detectResult> => {
97127 })
98128}
99129
130+ let fillPdfAndDownload = (
131+ ~blocks : array <PdfTool .block >,
132+ ~fields : Js .Dict .t <string >,
133+ ): Js .Promise .t <fillResult > => {
134+ let fillPromise =
135+ Js .Promise2 .then (getActivePdfUrl (), lookup => {
136+ switch lookup {
137+ | Error (error ) => Js .Promise .resolve (Error (error ))
138+ | Ok (url ) =>
139+ Js .Promise2 .then (Webapi .Fetch .fetch (url ), response =>
140+ Js .Promise2 .then (response -> Webapi .Fetch .Response .arrayBuffer , pdfBuffer =>
141+ Js .Promise2 .then (PdfTool .fillBlocks (pdfBuffer , blocks , fields ), filledBuffer => {
142+ let nativeBuffer : Js .Typed_array .ArrayBuffer .t = Obj .magic (filledBuffer )
143+ if Js .Typed_array .ArrayBuffer .byteLength (nativeBuffer ) == 0 {
144+ Js .Promise .resolve (Error ("fill_blocks returned an empty PDF payload" ))
145+ } else {
146+ let blobPart = Webapi .Blob .arrayBufferToBlobPart (nativeBuffer )
147+ let blob = Webapi .Blob .makeWithOptions (
148+ [blobPart ],
149+ Webapi .Blob .makeBlobPropertyBag (~_type = "application/pdf" , ()),
150+ )
151+ let objectUrl = Webapi .Url .createObjectURLFromBlob (blob )
152+ triggerDownload (objectUrl , makeFilledFilename ())
153+ let _timeoutId = Js .Global .setTimeout (() => Webapi .Url .revokeObjectURL (objectUrl ), 60000 )
154+ Js .Promise .resolve (Ok (()))
155+ }
156+ })
157+ )
158+ )
159+ }
160+ })
161+
162+ Js .Promise2 .catch (fillPromise , err => {
163+ Js .log2 ("fill and download failed" , err )
164+ Js .Promise .resolve (Error ("fill and download request failed" ))
165+ })
166+ }
167+
100168module App = {
101169 @react.component
102170 let make = () => {
@@ -135,6 +203,26 @@ module App = {
135203 ()
136204 }
137205
206+ let onFill = (fields : Js .Dict .t <string >): unit => {
207+ setIsLoading (_ => true )
208+ setStatus (_ => "Filling PDF and preparing download..." )
209+ let fillPromise =
210+ Js .Promise2 .then (fillPdfAndDownload (~blocks , ~fields ), result => {
211+ setIsLoading (_ => false )
212+ switch result {
213+ | Ok (()) => setStatus (_ => "Filled PDF downloaded" )
214+ | Error (message ) => setStatus (_ => "Fill failed: " ++ message )
215+ }
216+ Js .Promise .resolve (())
217+ })
218+ let _ = Js .Promise2 .catch (fillPromise , _ => {
219+ setIsLoading (_ => false )
220+ setStatus (_ => "Fill failed: unexpected error" )
221+ Js .Promise .resolve (())
222+ })
223+ ()
224+ }
225+
138226 React .useEffect0 (() => {
139227 refresh ()
140228 None
@@ -172,12 +260,7 @@ module App = {
172260 }>
173261 {React .string ("Refresh Detection" )}
174262 </button >
175- <FormFiller
176- blocks = {blocks }
177- onFill = {fields => {
178- Js .log2 ("Fill requested" , fields )
179- }}
180- />
263+ <FormFiller blocks = {blocks } onFill />
181264 </div >
182265 }
183266}
0 commit comments