| A web version, from the same core 23846db nandi 9h ago | 1 | /// The same two things, in a browser, where there is neither. |
| 2 | /// |
| 3 | /// A page has no environment: `FRQ_AUTOCONNECT` is a thing you set before |
| 4 | /// starting a process, and nothing here was started that way. And it has no |
| 5 | /// filesystem the renderer could open — a picture a reader attaches arrives |
| 6 | /// as a blob URL or not at all, and the path branch is never reached. |
| 7 | library; |
| 8 | |
| 9 | import 'package:flutter/widgets.dart'; |
| 10 | |
| 11 | String envOr(String name, String fallback) => fallback; |
| 12 | |
| 13 | ImageProvider? localImage(String path) => null; |
| 14 | |
| 15 | /// A picture from the network, drawn by the browser rather than decoded by |
| 16 | /// Flutter. |
| 17 | /// |
| 18 | /// This is the one place the web build cannot simply use the same widget. |
| 19 | /// Flutter fetches image bytes with an XMLHttpRequest so it can decode them |
| 20 | /// into a texture, and an XHR is subject to CORS — so every avatar on |
| 21 | /// `cdn.bsky.app` and every picture on freeq's media host failed with "No |
| 22 | /// 'Access-Control-Allow-Origin' header", because neither sends one to a |
| 23 | /// third-party page and neither has any reason to. |
| 24 | /// |
| 25 | /// An `<img>` element has never needed permission to display a picture, and |
| 26 | /// `WebHtmlElementStrategy.prefer` is Flutter asking for exactly that: the |
| 27 | /// browser loads and draws it, and Flutter positions the element. The cost is |
| 28 | /// that such an image is outside the canvas — it cannot be blended or |
| 29 | /// transformed like a texture — which for a face and a photograph in a |
| 30 | /// conversation is no cost at all. |
| 31 | Widget networkImage(String url, |
| 32 | {BoxFit fit = BoxFit.contain, |
| 33 | Widget Function()? onError}) => |
| 34 | Image.network(url, |
| 35 | fit: fit, |
| 36 | webHtmlElementStrategy: WebHtmlElementStrategy.prefer, |
| 37 | errorBuilder: (_, _, _) => |
| 38 | onError == null ? const SizedBox.shrink() : onError()); |
| 39 | |
| 40 | /// No family is named on the web, and that is the fix rather than the gap. |
| 41 | /// |
| 42 | /// A browser is not painting with the machine's fonts: CanvasKit carries its |
| 43 | /// own, and downloads a Noto face on demand for any glyph it cannot draw — |
| 44 | /// emoji included. Naming a family it does not have defeats that, because a |
| 45 | /// named family that is missing is a notdef box rather than a search. Every |
| 46 | /// reaction chip drew ▯ until this list was empty. |
| 47 | const List<String> emojiFonts = <String>[]; |