@entropy-softworks/ui 2026.8.40 → 2026.8.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,137 +7,161 @@ are CalVer — `YYYY.M.D` of the publish date, with leading zeros dropped (npm's
7
7
  them), matching the `entropy-auth` crate. The `0.1.0` entry below predates the switch and was never
8
8
  published to npm.
9
9
 
10
+ ## 2026.8.42
11
+
12
+ ### Fixed
13
+
14
+ - Custom uploaded icons never rendered on a phone. `useUploadBlobUrl` built its URI with
15
+ `URL.createObjectURL`, which is a browser API — React Native's `URL` does not implement it in any
16
+ way that can be relied on, so the call either threw or produced something no image loader would
17
+ open, and every upload fell through to the fallback glyph on every native consumer. Web still gets
18
+ a blob URL (no copy, and revocable); native now reads the bytes and builds
19
+ `data:<type>;base64,...`, which every RN image loader accepts. Base64 inflates by a third and the
20
+ string lives as long as the row is mounted, which is affordable for icons — vault caps an upload
21
+ at 256KB — and is why the two platforms differ rather than both taking the data URI.
22
+ - The base64 encoder is hand-rolled rather than `btoa`. React Native ships no `btoa`, so whether one
23
+ exists depends on which polyfill the consuming app installed, which is not something a library can
24
+ assume.
25
+
26
+ ### Added
27
+
28
+ - `useUploadBlobUrl` takes an options object — `{ uploadsPath, resolve }` — in place of the bare
29
+ path, which still works. `resolve` produces the URI for an upload id without going through
30
+ `apiFetch` at all, for a consumer whose uploads are not behind a server: vault's device-only
31
+ vaults keep theirs in on-device SQLite. Returning `null` reports `errored`, so an id that does not
32
+ resolve draws the same fallback a failed fetch does. The resolver is held in a ref and only
33
+ `uploadId` / `uploadsPath` re-run the effect, so an arrow defined inline at the call site cannot
34
+ turn every render into a refetch.
35
+ - `ItemIcon` takes `resolveUpload`, passed straight through. Without it such a consumer had to
36
+ pre-resolve the reference into a `data:` URI and hand it over as `iconUrl` — which works only
37
+ because anything that is not `custom:` is treated as a plain URL, and hides the intent completely.
38
+
39
+ ## 2026.8.41
40
+
41
+ ### Fixed
42
+
43
+ - `AuthScreen`'s form-height lock skipped its measuring pass for any consumer that resolves
44
+ `signUpEnabled` asynchronously. `probed` was initialised from `signUpEnabled`, which is right when
45
+ the consumer knows its registration policy at mount — but one that has to ASK for it (Access's
46
+ portal fetches the tenant's policy and starts at the conservative "closed") mounted with sign-up
47
+ off, skipped the pre-measure, and turned sign-up on a moment later with nothing measured. The
48
+ first real toggle was then the one that grew the card: it resized once, then held still forever,
49
+ which is exactly the symptom the lock exists to remove. The probe now re-arms on the false -> true
50
+ edge.
51
+
10
52
  ## 2026.8.40
11
53
 
12
54
  ### Added
13
55
 
14
- - `AuthScreen` takes `lockFormHeight` (default `true`). The card reserves the
15
- height of the taller of Sign In / Sign Up so the two toggle without the
16
- layout moving; the cost is slack, left as empty space above the shorter
17
- mode's submit button. Where Sign Up carries several more fields that gap is
18
- the more visible flaw of the two most people only ever sign in, and they
19
- all see it — so a consumer can now let the card resize instead. The body is
20
- already wrapped in a layout animation, so the change eases rather than jumps.
56
+ - `AuthScreen` takes `lockFormHeight` (default `true`). The card reserves the height of the taller
57
+ of Sign In / Sign Up so the two toggle without the layout moving; the cost is slack, left as empty
58
+ space above the shorter mode's submit button. Where Sign Up carries several more fields that gap
59
+ is the more visible flaw of the two most people only ever sign in, and they all see it — so a
60
+ consumer can now let the card resize instead. The body is already wrapped in a layout animation,
61
+ so the change eases rather than jumps.
21
62
 
22
63
  ## 2026.8.39
23
64
 
24
65
  ### Added
25
66
 
26
- - `SocialConnections` takes `showEntropy` (default `true`), which drops the
27
- first-party "Continue with Entropy" button from the row. Unlike the other
28
- three, that one is not a staged-rollout placeholder waiting for its
29
- connector: on Entropy Access's own sign-in surfaces it offers to sign in to
30
- the identity provider with the identity provider, which is a button that can
31
- never mean anything. A flag rather than inferring intent from a missing
32
- `onEntropy`, because rendering an unhandled provider is deliberate here
33
- simple-finance renders the whole row with no handlers at all, and inference
34
- would silently take that button away from it.
67
+ - `SocialConnections` takes `showEntropy` (default `true`), which drops the first-party "Continue
68
+ with Entropy" button from the row. Unlike the other three, that one is not a staged-rollout
69
+ placeholder waiting for its connector: on Entropy Access's own sign-in surfaces it offers to sign
70
+ in to the identity provider with the identity provider, which is a button that can never mean
71
+ anything. A flag rather than inferring intent from a missing `onEntropy`, because rendering an
72
+ unhandled provider is deliberate here simple-finance renders the whole row with no handlers at
73
+ all, and inference would silently take that button away from it.
35
74
 
36
75
  ## 2026.8.35
37
76
 
38
77
  ### Fixed
39
78
 
40
- - `AuthService.getCurrentUser()` destroyed the session it was asked about. It
41
- returned `loadPersistedUser()` and nothing else, and on web that reads a
42
- module-level variable which is null in every freshly loaded document — so it
43
- answered "nobody" on each page load and refresh while the session cookie was
44
- perfectly valid. `useAuth.checkAuth` treats that as a session it cannot
45
- resolve and calls `logout()`, which POSTs `/auth/logout` and ends the session
46
- server-side: the user was signed out by the act of loading the page.
47
-
48
- Observed on Access, whose portal and admin console are separate SPAs on one
49
- origin so every navigation between them is a fresh document and therefore a
50
- guaranteed logout, presenting as an infinite sign-in loop. Its audit log
51
- recorded the logouts nobody had asked for.
52
-
53
- Native was affected too, if less visibly: `isAuthenticated()` there is
54
- satisfied by a token in SecureStore, so a token that outlived its cached user
55
- record produced the same spurious logout.
56
-
57
- `getCurrentUser()` now falls back to `GET /auth/me` when nothing is persisted.
58
- It deliberately does not route through `isAuthenticated()`, whose web branch
59
- falls back to calling `getCurrentUser()` going the other way would recurse
60
- until the stack gave out, on exactly the signed-out path this makes cheap.
61
- The request is shared with `refreshUserData()` through a private `fetchMe`,
62
- parameterised on whether a 401 should log out: a refresh of a session
63
- believed live that 401s means the session died, while a lookup that 401s is
64
- just an anonymous visitor and must not tear anything down.
79
+ - `AuthService.getCurrentUser()` destroyed the session it was asked about. It returned
80
+ `loadPersistedUser()` and nothing else, and on web that reads a module-level variable which is
81
+ null in every freshly loaded document — so it answered "nobody" on each page load and refresh
82
+ while the session cookie was perfectly valid. `useAuth.checkAuth` treats that as a session it
83
+ cannot resolve and calls `logout()`, which POSTs `/auth/logout` and ends the session server-side:
84
+ the user was signed out by the act of loading the page.
85
+
86
+ Observed on Access, whose portal and admin console are separate SPAs on one origin — so every
87
+ navigation between them is a fresh document and therefore a guaranteed logout, presenting as an
88
+ infinite sign-in loop. Its audit log recorded the logouts nobody had asked for.
89
+
90
+ Native was affected too, if less visibly: `isAuthenticated()` there is satisfied by a token in
91
+ SecureStore, so a token that outlived its cached user record produced the same spurious logout.
92
+
93
+ `getCurrentUser()` now falls back to `GET /auth/me` when nothing is persisted. It deliberately
94
+ does not route through `isAuthenticated()`, whose web branch falls back to calling
95
+ `getCurrentUser()` — going the other way would recurse until the stack gave out, on exactly the
96
+ signed-out path this makes cheap. The request is shared with `refreshUserData()` through a private
97
+ `fetchMe`, parameterised on whether a 401 should log out: a refresh of a session believed live
98
+ that 401s means the session died, while a lookup that 401s is just an anonymous visitor and must
99
+ not tear anything down.
65
100
 
66
101
  ## 2026.8.17
67
102
 
68
103
  ### Fixed
69
104
 
70
- - The systemic bare-CSS-var-no-`dark:`-fallback pattern flagged as a known
71
- follow-up in 2026.8.15 — completed across the rest of the package: `Card`,
72
- `ContextMenu`, `DataTable`, `DropdownMenu`, `Popover`, `SettingsRow`,
73
- `PinPad`, `PasswordGenerator`, `EntropyByline`, `ErrorBoundary` (severe:
74
- its retry button's label was invisible white-on-white in dark mode),
75
- `Pagination`, `LockOverlay`, `ProtectedRoute`, and every screen under
76
- `src/screens/auth/*` (root backgrounds, plus scattered destructive-text and
77
- `ServerConfigScreen`'s from-scratch raw-`Text` markup).
78
- - `CardFooter`'s base `items-center` was never overridden by the `flex-col`
79
- className every consuming screen passes it (`AuthScreen`, `LoginScreen`,
80
- `RegisterScreen`, and ~10 others) `flex-row`→`flex-col` and `border-t`→
81
- `border-t-0` correctly override via tailwind-merge's same-property
82
- matching, but `items-center` has no `items-*` counterpart in that
83
- className to conflict with, so it silently survived. Left the submit
84
- button's wrapping view with ambiguous (content-fit) width instead of
85
- stretching to match its own `w-full` Pressable child — surfaced as the
86
- "Sign In" button rendering with a runaway width specifically once its
87
- content swapped from label text to the loading spinner (the one thing
88
- that actually changes Yoga's layout resolution for that ambiguous case).
89
- Added `items-stretch` alongside `flex-col` at every call site.
105
+ - The systemic bare-CSS-var-no-`dark:`-fallback pattern flagged as a known follow-up in 2026.8.15 —
106
+ completed across the rest of the package: `Card`, `ContextMenu`, `DataTable`, `DropdownMenu`,
107
+ `Popover`, `SettingsRow`, `PinPad`, `PasswordGenerator`, `EntropyByline`, `ErrorBoundary` (severe:
108
+ its retry button's label was invisible white-on-white in dark mode), `Pagination`, `LockOverlay`,
109
+ `ProtectedRoute`, and every screen under `src/screens/auth/*` (root backgrounds, plus scattered
110
+ destructive-text and `ServerConfigScreen`'s from-scratch raw-`Text` markup).
111
+ - `CardFooter`'s base `items-center` was never overridden by the `flex-col` className every
112
+ consuming screen passes it (`AuthScreen`, `LoginScreen`, `RegisterScreen`, and ~10 others)
113
+ `flex-row`→`flex-col` and `border-t`→ `border-t-0` correctly override via tailwind-merge's
114
+ same-property matching, but `items-center` has no `items-*` counterpart in that className to
115
+ conflict with, so it silently survived. Left the submit button's wrapping view with ambiguous
116
+ (content-fit) width instead of stretching to match its own `w-full` Pressable child surfaced as
117
+ the "Sign In" button rendering with a runaway width specifically once its content swapped from
118
+ label text to the loading spinner (the one thing that actually changes Yoga's layout resolution
119
+ for that ambiguous case). Added `items-stretch` alongside `flex-col` at every call site.
90
120
 
91
121
  ## 2026.8.15
92
122
 
93
123
  ### Fixed
94
124
 
95
- - Several components colored themselves with a bare CSS-variable utility
96
- (`bg-primary`, `bg-background`, `border-border`, `bg-destructive`, …) and no
97
- `dark:` fallback on native Android that value stays stuck on its light
98
- setting regardless of actual system theme; only components carrying an
99
- explicit `dark:`-prefixed class (the existing pattern in `Heading`/`Text`/
100
- `MutedText`) reacted correctly. Added matching `dark:` variants one per
101
- CSS variable's own `.dark` value in `styles/globals.css` to `Button`,
102
- `Input`, `Dialog`'s destructive variant, `legal-dialog`'s body text, and
103
- `AuthScreen`'s root background. Surfaced as vault's "Get Started" screen
104
- going render-broken in dark mode: invisible button, invisible heading/legal
105
- text, and a background that stayed white.
106
- - `AuthScreen`'s tagline and "← Back" link used the plain `Text` component
107
- with a `text-muted-foreground` override that fought `Text`'s own base
108
- `dark:text-white` the override lost to the base class's `dark:` variant,
109
- so both rendered bright white instead of muted in dark mode. Switched both
110
- to `MutedText`, which carries the correct `dark:text-neutral-400`.
111
- - Known follow-up, not fixed here: the same bare-CSS-var-no-dark:-fallback
112
- pattern is present in roughly 15 more files in this package
113
- (`password-generator.tsx`, `pin-pad.tsx`, `tag-chip.tsx`, several other
114
- still-active auth screens, …) — scoped out of this release to unblock the
115
- reported bug; needs its own pass.
125
+ - Several components colored themselves with a bare CSS-variable utility (`bg-primary`,
126
+ `bg-background`, `border-border`, `bg-destructive`, …) and no `dark:` fallback — on native Android
127
+ that value stays stuck on its light setting regardless of actual system theme; only components
128
+ carrying an explicit `dark:`-prefixed class (the existing pattern in `Heading`/`Text`/
129
+ `MutedText`) reacted correctly. Added matching `dark:` variants one per CSS variable's own
130
+ `.dark` value in `styles/globals.css` to `Button`, `Input`, `Dialog`'s destructive variant,
131
+ `legal-dialog`'s body text, and `AuthScreen`'s root background. Surfaced as vault's "Get Started"
132
+ screen going render-broken in dark mode: invisible button, invisible heading/legal text, and a
133
+ background that stayed white.
134
+ - `AuthScreen`'s tagline and "← Back" link used the plain `Text` component with a
135
+ `text-muted-foreground` override that fought `Text`'s own base `dark:text-white` — the override
136
+ lost to the base class's `dark:` variant, so both rendered bright white instead of muted in dark
137
+ mode. Switched both to `MutedText`, which carries the correct `dark:text-neutral-400`.
138
+ - Known follow-up, not fixed here: the same bare-CSS-var-no-dark:-fallback pattern is present in
139
+ roughly 15 more files in this package (`password-generator.tsx`, `pin-pad.tsx`, `tag-chip.tsx`,
140
+ several other still-active auth screens, …) — scoped out of this release to unblock the reported
141
+ bug; needs its own pass.
116
142
 
117
143
  ## 2026.8.2
118
144
 
119
145
  ### Fixed
120
146
 
121
- - `registerTelemetry` and the rest of the telemetry registry were exported from
122
- the root barrel twice — once through `export * from "./services"` and once by
123
- name. Metro's CommonJS interop resolved the duplicate ambiguously and
124
- consumers got `undefined` at runtime despite the types checking. The root
125
- barrel now re-exports them only through `./services`.
147
+ - `registerTelemetry` and the rest of the telemetry registry were exported from the root barrel
148
+ twice — once through `export * from "./services"` and once by name. Metro's CommonJS interop
149
+ resolved the duplicate ambiguously and consumers got `undefined` at runtime despite the types
150
+ checking. The root barrel now re-exports them only through `./services`.
126
151
 
127
152
  ## 2026.8.1
128
153
 
129
154
  ### Added
130
155
 
131
- - Telemetry registry (`registerTelemetry`, `trackClick`, `withClickTracking`). The
132
- package reports interactions to a sink the host app supplies; it knows nothing
133
- about batching, authentication or transport. No sink registered means every
134
- entry point is a no-op, so consumers that do not opt in are unaffected.
135
- - `telemetryId` prop on `Button` and `IconButton`. Reports a stable,
136
- author-assigned id when the control is activated. Wired through `onPress`, so
137
- a disabled or loading button records nothing.
138
- - All 69 interactive controls across the 18 auth screens carry a `telemetryId`,
139
- making sign-in, sign-up, MFA and consent measurable by consuming apps without
140
- each of them re-tagging the packaged screens.
156
+ - Telemetry registry (`registerTelemetry`, `trackClick`, `withClickTracking`). The package reports
157
+ interactions to a sink the host app supplies; it knows nothing about batching, authentication or
158
+ transport. No sink registered means every entry point is a no-op, so consumers that do not opt in
159
+ are unaffected.
160
+ - `telemetryId` prop on `Button` and `IconButton`. Reports a stable, author-assigned id when the
161
+ control is activated. Wired through `onPress`, so a disabled or loading button records nothing.
162
+ - All 69 interactive controls across the 18 auth screens carry a `telemetryId`, making sign-in,
163
+ sign-up, MFA and consent measurable by consuming apps without each of them re-tagging the packaged
164
+ screens.
141
165
 
142
166
  ## [Unreleased]
143
167
 
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { type UploadResolver } from "../hooks/useUploadBlobUrl";
2
3
  export interface ItemIconProps {
3
4
  /**
4
5
  * Stored icon reference. Either a literal URL, a `custom:<uploadId>`
@@ -22,6 +23,21 @@ export interface ItemIconProps {
22
23
  faviconPath?: string;
23
24
  /** Upload-fetch path prefix. Defaults to `/uploads`. */
24
25
  uploadsPath?: string;
26
+ /**
27
+ * Resolve a `custom:<uploadId>` reference yourself instead of letting this
28
+ * component fetch it.
29
+ *
30
+ * For a consumer whose uploads are not behind `apiFetch` at all — vault's
31
+ * device-only vaults keep them in on-device SQLite, with no server to ask.
32
+ * Without this, such a consumer has to either fork the component or
33
+ * pre-resolve the reference into a `data:` URI and pass it as `iconUrl`,
34
+ * which works by accident (anything that is not `custom:` is treated as a
35
+ * plain URL) and hides the intent.
36
+ *
37
+ * Return `null` for an id that does not resolve; the fallback glyph is drawn,
38
+ * exactly as for a fetch that fails.
39
+ */
40
+ resolveUpload?: UploadResolver;
25
41
  }
26
42
  /**
27
43
  * Per-item icon: shows the custom upload, falls back to the favicon
@@ -1 +1 @@
1
- {"version":3,"file":"item-icon.d.ts","sourceRoot":"","sources":["../../src/components/item-icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAOnD,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,2CAmEnB,CAAC"}
1
+ {"version":3,"file":"item-icon.d.ts","sourceRoot":"","sources":["../../src/components/item-icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,OAAO,EAAE,KAAK,cAAc,EAAoB,MAAM,2BAA2B,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAQD;;;;GAIG;AACH,eAAO,MAAM,QAAQ,2CAoEnB,CAAC"}
@@ -13,12 +13,12 @@ const WEB_LAZY_PROPS = Platform.OS === "web" ? { loading: "lazy", decoding: "asy
13
13
  * proxy, then to a generic globe glyph on a tile-toned square. Shared
14
14
  * between RN clients (mobile) and RN-Web (extension popup).
15
15
  */
16
- export const ItemIcon = React.memo(function ItemIcon({ iconUrl, siteUrl, size = 40, imageSize, borderRadius, background, fallbackColor, faviconPath = "/favicon", uploadsPath = "/uploads", }) {
16
+ export const ItemIcon = React.memo(function ItemIcon({ iconUrl, siteUrl, size = 40, imageSize, borderRadius, background, fallbackColor, faviconPath = "/favicon", uploadsPath = "/uploads", resolveUpload, }) {
17
17
  const { colorScheme } = useColorScheme();
18
18
  const isDark = colorScheme === "dark";
19
19
  const resolved = resolveIcon({ iconUrl, siteUrl, faviconPath });
20
20
  const uploadId = resolved.kind === "upload" ? resolved.uploadId : null;
21
- const upload = useUploadBlobUrl(uploadId, uploadsPath);
21
+ const upload = useUploadBlobUrl(uploadId, { uploadsPath, resolve: resolveUpload });
22
22
  const directUri = resolved.kind === "url" ? resolved.uri : null;
23
23
  const [urlErrored, setUrlErrored] = useState(false);
24
24
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"item-icon.jsx","sourceRoot":"","sources":["../../src/components/item-icon.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"item-icon.jsx","sourceRoot":"","sources":["../../src/components/item-icon.tsx"],"names":[],"mappings":"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"}
@@ -2,6 +2,6 @@ export { AuthProvider, useAuth, useRole, useRequireAuth, useSession, type AuthPr
2
2
  export { useShakeAnimation } from "./useShakeAnimation";
3
3
  export { useDebouncedValue } from "./useDebouncedValue";
4
4
  export { useAutoLock, type UseAutoLockOptions } from "./useAutoLock";
5
- export { useUploadBlobUrl, type UploadBlobUrlResult } from "./useUploadBlobUrl";
5
+ export { type UploadBlobUrlOptions, type UploadBlobUrlResult, type UploadResolver, useUploadBlobUrl, } from "./useUploadBlobUrl";
6
6
  export { usePasswordGenerator, getPasswordStrength, type UsePasswordGeneratorResult, type PasswordStrength, type BreachState, } from "./usePasswordGenerator";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC"}
@@ -2,6 +2,6 @@ export { AuthProvider, useAuth, useRole, useRequireAuth, useSession, } from "./u
2
2
  export { useShakeAnimation } from "./useShakeAnimation";
3
3
  export { useDebouncedValue } from "./useDebouncedValue";
4
4
  export { useAutoLock } from "./useAutoLock";
5
- export { useUploadBlobUrl } from "./useUploadBlobUrl";
5
+ export { useUploadBlobUrl, } from "./useUploadBlobUrl";
6
6
  export { usePasswordGenerator, getPasswordStrength, } from "./usePasswordGenerator";
7
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAIpB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAIL,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAIpB,MAAM,wBAAwB,CAAC"}
@@ -4,10 +4,54 @@ export interface UploadBlobUrlResult {
4
4
  setErrored: (v: boolean) => void;
5
5
  }
6
6
  /**
7
- * Fetch an authenticated upload via `apiFetch`, expose a blob URL the
8
- * caller can hand to an `<Image>` / `<img>`. Cancels in-flight on input
9
- * change and revokes the URL on unmount so list scrolling doesn't leak
10
- * memory.
7
+ * Produce a renderable URI for an upload id without going through
8
+ * `apiFetch`.
9
+ *
10
+ * For a consumer that keeps uploads somewhere this package cannot reach — an
11
+ * on-device database, a cache it populated itself — the resolver is the whole
12
+ * seam. Return `null` for an id that does not resolve; the hook reports that
13
+ * as `errored`, which is what a renderer draws its fallback on.
14
+ *
15
+ * The `signal` aborts with the effect, so a resolver doing real work can stop
16
+ * when the row scrolls away.
11
17
  */
12
- export declare function useUploadBlobUrl(uploadId: string | null | undefined, uploadsPath?: string): UploadBlobUrlResult;
18
+ export type UploadResolver = (uploadId: string, signal: AbortSignal) => Promise<string | null>;
19
+ export interface UploadBlobUrlOptions {
20
+ /** Upload-fetch path prefix. Defaults to `/uploads`. */
21
+ uploadsPath?: string;
22
+ /**
23
+ * Resolve the URI yourself instead of fetching it.
24
+ *
25
+ * Identity does not have to be stable: the resolver is held in a ref and
26
+ * only `uploadId` / `uploadsPath` re-run the effect. An inline arrow would
27
+ * otherwise refetch on every render, and what identifies the resource is the
28
+ * id, not the function that happens to fetch it.
29
+ */
30
+ resolve?: UploadResolver;
31
+ }
32
+ /**
33
+ * A URI for an upload, fetched through `apiFetch` unless the consumer resolves
34
+ * it itself.
35
+ *
36
+ * # Web gets a blob URL; native gets a data URI
37
+ *
38
+ * `URL.createObjectURL` is a browser API. React Native's `URL` does not
39
+ * implement it in any way that can be relied on across both platforms — so on
40
+ * native this hook used to hand `<Image>` a URI built by a function that
41
+ * either threw or produced something the image loader could not open, and the
42
+ * fetch fell into the `catch` below. The visible symptom was that custom
43
+ * uploaded icons never appeared on a phone: they resolved to the fallback
44
+ * glyph every time, on every consumer, and looked like a missing-image problem
45
+ * rather than a URL one.
46
+ *
47
+ * Native therefore reads the bytes and builds `data:<type>;base64,…`, which
48
+ * every RN image loader accepts. The cost is that base64 inflates by a third
49
+ * and the string sits in the JS heap for as long as the row is mounted, which
50
+ * is affordable for the icons this exists to draw (the vault caps an upload at
51
+ * 256KB) and is why web keeps the blob URL — no copy, and revocable.
52
+ *
53
+ * The second argument accepts a bare path for backwards compatibility with
54
+ * `useUploadBlobUrl(id, "/uploads")`; new call sites should pass options.
55
+ */
56
+ export declare function useUploadBlobUrl(uploadId: string | null | undefined, options?: string | UploadBlobUrlOptions): UploadBlobUrlResult;
13
57
  //# sourceMappingURL=useUploadBlobUrl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useUploadBlobUrl.d.ts","sourceRoot":"","sources":["../../src/hooks/useUploadBlobUrl.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,WAAW,SAAa,GACvB,mBAAmB,CAsDrB"}
1
+ {"version":3,"file":"useUploadBlobUrl.d.ts","sourceRoot":"","sources":["../../src/hooks/useUploadBlobUrl.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,OAAO,GAAE,MAAM,GAAG,oBAAyB,GAC1C,mBAAmB,CAgGrB"}
@@ -1,15 +1,39 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { Platform } from "react-native";
2
3
  import { apiFetch } from "../utils/api-fetch";
3
4
  const FETCH_TIMEOUT_MS = 15_000;
4
5
  /**
5
- * Fetch an authenticated upload via `apiFetch`, expose a blob URL the
6
- * caller can hand to an `<Image>` / `<img>`. Cancels in-flight on input
7
- * change and revokes the URL on unmount so list scrolling doesn't leak
8
- * memory.
6
+ * A URI for an upload, fetched through `apiFetch` unless the consumer resolves
7
+ * it itself.
8
+ *
9
+ * # Web gets a blob URL; native gets a data URI
10
+ *
11
+ * `URL.createObjectURL` is a browser API. React Native's `URL` does not
12
+ * implement it in any way that can be relied on across both platforms — so on
13
+ * native this hook used to hand `<Image>` a URI built by a function that
14
+ * either threw or produced something the image loader could not open, and the
15
+ * fetch fell into the `catch` below. The visible symptom was that custom
16
+ * uploaded icons never appeared on a phone: they resolved to the fallback
17
+ * glyph every time, on every consumer, and looked like a missing-image problem
18
+ * rather than a URL one.
19
+ *
20
+ * Native therefore reads the bytes and builds `data:<type>;base64,…`, which
21
+ * every RN image loader accepts. The cost is that base64 inflates by a third
22
+ * and the string sits in the JS heap for as long as the row is mounted, which
23
+ * is affordable for the icons this exists to draw (the vault caps an upload at
24
+ * 256KB) and is why web keeps the blob URL — no copy, and revocable.
25
+ *
26
+ * The second argument accepts a bare path for backwards compatibility with
27
+ * `useUploadBlobUrl(id, "/uploads")`; new call sites should pass options.
9
28
  */
10
- export function useUploadBlobUrl(uploadId, uploadsPath = "/uploads") {
29
+ export function useUploadBlobUrl(uploadId, options = {}) {
30
+ const { uploadsPath = "/uploads", resolve } = typeof options === "string" ? { uploadsPath: options, resolve: undefined } : options;
11
31
  const [uri, setUri] = useState(null);
12
32
  const [errored, setErrored] = useState(false);
33
+ // See `UploadBlobUrlOptions.resolve`: kept in a ref so a resolver defined
34
+ // inline at the call site cannot turn every render into a refetch.
35
+ const resolveRef = useRef(resolve);
36
+ resolveRef.current = resolve;
13
37
  useEffect(() => {
14
38
  setErrored(false);
15
39
  if (!uploadId) {
@@ -19,21 +43,51 @@ export function useUploadBlobUrl(uploadId, uploadsPath = "/uploads") {
19
43
  const controller = new AbortController();
20
44
  const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
21
45
  let createdUrl = null;
22
- apiFetch(`${uploadsPath}/${encodeURIComponent(uploadId)}`, { signal: controller.signal })
23
- .then(async (response) => {
46
+ const load = async () => {
47
+ const resolver = resolveRef.current;
48
+ if (resolver) {
49
+ return resolver(uploadId, controller.signal);
50
+ }
51
+ const response = await apiFetch(`${uploadsPath}/${encodeURIComponent(uploadId)}`, {
52
+ signal: controller.signal,
53
+ });
24
54
  if (controller.signal.aborted) {
25
- return;
55
+ return null;
26
56
  }
27
57
  if (!response.ok) {
28
58
  throw new Error(`Failed to load upload (${response.status})`);
29
59
  }
30
- const blob = await response.blob();
60
+ if (Platform.OS === "web") {
61
+ const blob = await response.blob();
62
+ if (controller.signal.aborted) {
63
+ return null;
64
+ }
65
+ createdUrl = URL.createObjectURL(blob);
66
+ return createdUrl;
67
+ }
68
+ const bytes = new Uint8Array(await response.arrayBuffer());
69
+ if (controller.signal.aborted) {
70
+ return null;
71
+ }
72
+ // The server sends the stored content type; anything else is a server
73
+ // that has lost track of what it is holding, and `<Image>` will refuse
74
+ // the URI rather than guess — which surfaces as the fallback glyph, the
75
+ // same outcome a failed fetch gives.
76
+ const contentType = response.headers.get("content-type") ?? "application/octet-stream";
77
+ return `data:${contentType};base64,${toBase64(bytes)}`;
78
+ };
79
+ load()
80
+ .then((next) => {
31
81
  if (controller.signal.aborted) {
32
82
  return;
33
83
  }
34
- const url = URL.createObjectURL(blob);
35
- createdUrl = url;
36
- setUri(url);
84
+ if (next === null) {
85
+ // A resolver that has nothing for this id, or an aborted read. Either
86
+ // way there is no image, which is what `errored` means to a renderer.
87
+ setErrored(true);
88
+ return;
89
+ }
90
+ setUri(next);
37
91
  })
38
92
  .catch((e) => {
39
93
  if (controller.signal.aborted) {
@@ -57,4 +111,29 @@ export function useUploadBlobUrl(uploadId, uploadsPath = "/uploads") {
57
111
  }, [uploadId, uploadsPath]);
58
112
  return { uri, errored, setErrored };
59
113
  }
114
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
115
+ /**
116
+ * Base64, without depending on the host having `btoa`.
117
+ *
118
+ * React Native ships no `btoa`, and whether one exists depends entirely on
119
+ * which polyfill the consuming app happened to install — a library cannot
120
+ * assume it. Encoding by hand is a dozen lines and removes the question.
121
+ *
122
+ * The `?? 0` on the tail bytes is the padding rule rather than a guard: base64
123
+ * completes a short final group with zero bits and then marks the missing
124
+ * bytes with `=`. Reading past the end is therefore correct here, and doing it
125
+ * this way keeps the loop free of the non-null assertions
126
+ * `noUncheckedIndexedAccess` would otherwise demand at every lookup.
127
+ */
128
+ function toBase64(bytes) {
129
+ const chars = [];
130
+ for (let index = 0; index < bytes.length; index += 3) {
131
+ const b0 = bytes[index] ?? 0;
132
+ const b1 = bytes[index + 1] ?? 0;
133
+ const b2 = bytes[index + 2] ?? 0;
134
+ const remaining = bytes.length - index;
135
+ chars.push(BASE64_ALPHABET.charAt(b0 >> 2), BASE64_ALPHABET.charAt(((b0 & 3) << 4) | (b1 >> 4)), remaining > 1 ? BASE64_ALPHABET.charAt(((b1 & 15) << 2) | (b2 >> 6)) : "=", remaining > 2 ? BASE64_ALPHABET.charAt(b2 & 63) : "=");
136
+ }
137
+ return chars.join("");
138
+ }
60
139
  //# sourceMappingURL=useUploadBlobUrl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useUploadBlobUrl.js","sourceRoot":"","sources":["../../src/hooks/useUploadBlobUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAQhC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAmC,EACnC,WAAW,GAAG,UAAU;IAExB,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACvE,IAAI,UAAU,GAAkB,IAAI,CAAC;QAErC,QAAQ,CAAC,GAAG,WAAW,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC;aACtF,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YACvB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAChE,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACtC,UAAU,GAAG,GAAG,CAAC;YACjB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,YAAY,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACzD,OAAO;YACT,CAAC;YACD,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YAClC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IAE5B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC"}
1
+ {"version":3,"file":"useUploadBlobUrl.js","sourceRoot":"","sources":["../../src/hooks/useUploadBlobUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAoChC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAmC,EACnC,UAAyC,EAAE;IAE3C,MAAM,EAAE,WAAW,GAAG,UAAU,EAAE,OAAO,EAAE,GACzC,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAEvF,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,0EAA0E;IAC1E,mEAAmE;IACnE,MAAM,UAAU,GAAG,MAAM,CAA6B,OAAO,CAAC,CAAC;IAC/D,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACvE,IAAI,UAAU,GAAkB,IAAI,CAAC;QAErC,MAAM,IAAI,GAAG,KAAK,IAA4B,EAAE;YAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC;YACpC,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,GAAG,WAAW,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE;gBAChF,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YACH,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAChE,CAAC;YAED,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBAC9B,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,UAAU,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;gBACvC,OAAO,UAAU,CAAC;YACpB,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;YAC3D,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,sEAAsE;YACtE,uEAAuE;YACvE,wEAAwE;YACxE,qCAAqC;YACrC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,0BAA0B,CAAC;YACvF,OAAO,QAAQ,WAAW,WAAW,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,CAAC,CAAC;QAEF,IAAI,EAAE;aACH,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YACb,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,sEAAsE;gBACtE,sEAAsE;gBACtE,UAAU,CAAC,IAAI,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,YAAY,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACzD,OAAO;YACT,CAAC;YACD,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YAClC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IAE5B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,eAAe,GAAG,kEAAkE,CAAC;AAE3F;;;;;;;;;;;;GAYG;AACH,SAAS,QAAQ,CAAC,KAAiB;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QAEvC,KAAK,CAAC,IAAI,CACR,eAAe,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,EAC/B,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EACnD,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAC1E,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CACtD,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"AuthScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AuthScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAWjF,OAAO,QAON,MAAM,yBAAyB,CAAC;AA4DjC,KAAK,iBAAiB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;CACxC;AA+BD,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kEAAkE;IAClE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IACjF;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,yDAAyD;IACzD,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,aAA0B,EAC1B,mBAA2C,EAC3C,UAAU,EACV,cAAsB,EACtB,wBAAuD,EACvD,kBAAwC,EACxC,wBAAsJ,EACtJ,QAAQ,EACR,eAAe,EACf,UAAU,EACV,UAAU,EACV,MAAM,EACN,KAAK,EACL,eAAsB,EACtB,cAAqB,EACrB,QAAQ,EACR,YAAY,EACZ,iBAAsB,EACtB,eAA+B,EAC/B,kBAAkB,EAClB,aAAoB,EACpB,iBAAyB,EACzB,QAAQ,EACR,oBAA+F,EAC/F,WAAkB,GACnB,GAAE,eAAoB,qBAmsCtB;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"AuthScreen.d.ts","sourceRoot":"","sources":["../../../src/screens/auth/AuthScreen.tsx"],"names":[],"mappings":"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"}