1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
(ns frq.pictures
"Choosing a picture and sending it, named once for both Flutter targets.
The fifth seam, and it exists because a browser has no file. dart:io picks
one with the system chooser, copies it into the app's own storage and
uploads it by path; a browser is handed BYTES — from the chooser, or from
the clipboard — and there is no path to copy to. `File.copy` on the web
throws `Unsupported operation: _Namespace`, which is dart:io's filesystem
saying it is not there, and what the reader saw was `Could not read that
picture: _Namespace`.
So a picture here is a map, and either half of it may be missing: `:path`
where there is a filesystem, `:bytes` where there is not, `:filename`
always. `frq.upload.core` already builds the multipart body out of bytes, so
only the transport and the chooser differ."
(:refer-clojure :exclude [name]))
(defonce ^:private impl (atom {}))
(defn install! [m] (swap! impl merge m) nil)
(defn ^:async pick!
"Ask the reader for a picture. Nil when they chose none.
`dir` is where a copy belongs on the targets that have one — it is the
caller's, because it comes from `frq.io/config-dir`, and the web backend
ignores it."
[dir]
(if-let [f (:pick! @impl)]
(await (f dir))
(throw (ex-info "frq.pictures: no backend installed" {:op :pick!}))))
(defn ^:async upload!
"Send `picture` and answer the URL freeq serves it back at."
[host did channel picture]
(if-let [f (:upload! @impl)]
(await (f host did channel picture))
(throw (ex-info "frq.pictures: no backend installed" {:op :upload!}))))
(defn preview-url
"What the renderer should draw for the picture at `url`, or nil while there
is nothing to draw yet.
Two different answers, and the browser's is the simpler one. dart:io
downloads the picture into a cache and hands back a path, because a phone
wants the bytes on disk and `frq.media` is the thing that puts them there.
A browser already has an image loader with a cache in front of it, and no
disk to put anything on — so the answer is the URL, which
`frq.hiccup/src-url` then sends through the same proxy the avatars use.
Synchronous, because the screens call it while they build a row and read nil
as \"not here yet\" — see `frq.media/path-when-ready`, which is the answer on
the other two targets."
[url]
(if-let [f (:preview-url @impl)]
(f url)
nil))
|