Skip to content

Commit 1b3c09d

Browse files
author
Jonathan D.A. Jewell
committed
Implement popup fill->download flow for active PDF tab
1 parent 577b16c commit 1b3c09d

1 file changed

Lines changed: 105 additions & 22 deletions

File tree

src/popup.res

Lines changed: 105 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
type runtime
44
type tabsApi
55
type tab = Js.Json.t
6+
type urlLookup = result<string, string>
7+
type fillResult = result<unit, string>
68

79
@val @scope("browser")
810
external runtime: runtime = "runtime"
@@ -16,6 +18,19 @@ external sendMessage: (runtime, Js.Json.t) => Js.Promise.t<Js.Json.t> = "sendMes
1618
@send
1719
external 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+
1934
let unsafeJson = (value: 'a): Js.Json.t => Obj.magic(value)
2035

2136
let 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+
4887
type detectResult = {
4988
ok: bool,
5089
blocks: array<PdfTool.block>,
@@ -71,23 +110,14 @@ let decodeDetectResult = (payload: Js.Json.t): detectResult => {
71110
}
72111

73112
let 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+
100168
module 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

Comments
 (0)