@oxyhq/services 22.4.2 → 22.6.0

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.
Files changed (174) hide show
  1. package/LICENSE +661 -21
  2. package/README.md +1 -1
  3. package/lib/commonjs/index.js +20 -1
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/client.js +1 -1
  6. package/lib/commonjs/ui/components/FontLoader.js +18 -145
  7. package/lib/commonjs/ui/components/FontLoader.js.map +1 -1
  8. package/lib/commonjs/ui/components/FontLoader.native.js +88 -0
  9. package/lib/commonjs/ui/components/FontLoader.native.js.map +1 -0
  10. package/lib/commonjs/ui/components/OxyProvider.js +1 -1
  11. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js +16 -27
  12. package/lib/commonjs/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  13. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
  14. package/lib/commonjs/ui/components/fileManagement/styles.js +1 -225
  15. package/lib/commonjs/ui/components/fileManagement/styles.js.map +1 -1
  16. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +5 -2
  17. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  18. package/lib/commonjs/ui/context/useOxyAccountGraph.js +7 -0
  19. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +14 -5
  21. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  22. package/lib/commonjs/ui/hooks/queries/userCache.js +286 -0
  23. package/lib/commonjs/ui/hooks/queries/userCache.js.map +1 -0
  24. package/lib/commonjs/ui/hooks/useAvatarPicker.js +23 -8
  25. package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
  26. package/lib/commonjs/ui/hooks/useFileDownloadUrl.js +7 -21
  27. package/lib/commonjs/ui/hooks/useFileDownloadUrl.js.map +1 -1
  28. package/lib/commonjs/ui/hooks/useResolvedFileUrls.js +163 -0
  29. package/lib/commonjs/ui/hooks/useResolvedFileUrls.js.map +1 -0
  30. package/lib/commonjs/ui/index.js +1 -1
  31. package/lib/commonjs/ui/screens/FileManagementScreen.js +51 -22
  32. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +216 -203
  34. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  35. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +26 -9
  36. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  37. package/lib/commonjs/ui/screens/fileManagement/photoGridLayout.js +44 -0
  38. package/lib/commonjs/ui/screens/fileManagement/photoGridLayout.js.map +1 -0
  39. package/lib/commonjs/ui/session/tokenTransport.js +21 -10
  40. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
  41. package/lib/commonjs/ui/utils/fileManagement.js +0 -40
  42. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  43. package/lib/module/index.js +7 -1
  44. package/lib/module/index.js.map +1 -1
  45. package/lib/module/ui/client.js +1 -1
  46. package/lib/module/ui/client.js.map +1 -1
  47. package/lib/module/ui/components/FontLoader.js +18 -146
  48. package/lib/module/ui/components/FontLoader.js.map +1 -1
  49. package/lib/module/ui/components/FontLoader.native.js +83 -0
  50. package/lib/module/ui/components/FontLoader.native.js.map +1 -0
  51. package/lib/module/ui/components/OxyProvider.js +1 -1
  52. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  53. package/lib/module/ui/components/fileManagement/FileDetailsModal.js +16 -27
  54. package/lib/module/ui/components/fileManagement/FileDetailsModal.js.map +1 -1
  55. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
  56. package/lib/module/ui/components/fileManagement/styles.js +0 -224
  57. package/lib/module/ui/components/fileManagement/styles.js.map +1 -1
  58. package/lib/module/ui/context/hooks/useAuthOperations.js +5 -2
  59. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  60. package/lib/module/ui/context/useOxyAccountGraph.js +7 -0
  61. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  62. package/lib/module/ui/hooks/queries/useAccountQueries.js +14 -5
  63. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  64. package/lib/module/ui/hooks/queries/userCache.js +282 -0
  65. package/lib/module/ui/hooks/queries/userCache.js.map +1 -0
  66. package/lib/module/ui/hooks/useAvatarPicker.js +23 -8
  67. package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
  68. package/lib/module/ui/hooks/useFileDownloadUrl.js +7 -21
  69. package/lib/module/ui/hooks/useFileDownloadUrl.js.map +1 -1
  70. package/lib/module/ui/hooks/useResolvedFileUrls.js +157 -0
  71. package/lib/module/ui/hooks/useResolvedFileUrls.js.map +1 -0
  72. package/lib/module/ui/index.js +1 -1
  73. package/lib/module/ui/index.js.map +1 -1
  74. package/lib/module/ui/screens/FileManagementScreen.js +52 -23
  75. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  76. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +218 -205
  77. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  78. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +26 -9
  79. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  80. package/lib/module/ui/screens/fileManagement/photoGridLayout.js +39 -0
  81. package/lib/module/ui/screens/fileManagement/photoGridLayout.js.map +1 -0
  82. package/lib/module/ui/session/tokenTransport.js +21 -10
  83. package/lib/module/ui/session/tokenTransport.js.map +1 -1
  84. package/lib/module/ui/utils/fileManagement.js +0 -39
  85. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  86. package/lib/typescript/commonjs/index.d.ts +2 -0
  87. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/ui/components/FontLoader.d.ts +14 -5
  89. package/lib/typescript/commonjs/ui/components/FontLoader.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts +13 -0
  91. package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts.map +1 -0
  92. package/lib/typescript/commonjs/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts +0 -218
  94. package/lib/typescript/commonjs/ui/components/fileManagement/styles.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +9 -0
  98. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts +96 -0
  100. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts +3 -2
  103. package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/hooks/useResolvedFileUrls.d.ts +48 -0
  105. package/lib/typescript/commonjs/ui/hooks/useResolvedFileUrls.d.ts.map +1 -0
  106. package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts +1 -1
  108. package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/screens/fileManagement/photoGridLayout.d.ts +37 -0
  111. package/lib/typescript/commonjs/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -0
  112. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +15 -8
  113. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +4 -11
  115. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  116. package/lib/typescript/module/index.d.ts +3 -1
  117. package/lib/typescript/module/index.d.ts.map +1 -1
  118. package/lib/typescript/module/ui/client.d.ts +1 -1
  119. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  120. package/lib/typescript/module/ui/components/FontLoader.d.ts +14 -5
  121. package/lib/typescript/module/ui/components/FontLoader.d.ts.map +1 -1
  122. package/lib/typescript/module/ui/components/FontLoader.native.d.ts +13 -0
  123. package/lib/typescript/module/ui/components/FontLoader.native.d.ts.map +1 -0
  124. package/lib/typescript/module/ui/components/fileManagement/FileDetailsModal.d.ts.map +1 -1
  125. package/lib/typescript/module/ui/components/fileManagement/styles.d.ts +0 -218
  126. package/lib/typescript/module/ui/components/fileManagement/styles.d.ts.map +1 -1
  127. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  129. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +9 -0
  130. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts +96 -0
  132. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts.map +1 -0
  133. package/lib/typescript/module/ui/hooks/useAvatarPicker.d.ts.map +1 -1
  134. package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts +3 -2
  135. package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
  136. package/lib/typescript/module/ui/hooks/useResolvedFileUrls.d.ts +48 -0
  137. package/lib/typescript/module/ui/hooks/useResolvedFileUrls.d.ts.map +1 -0
  138. package/lib/typescript/module/ui/index.d.ts +1 -1
  139. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  140. package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
  141. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts +1 -1
  142. package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
  143. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  144. package/lib/typescript/module/ui/screens/fileManagement/photoGridLayout.d.ts +37 -0
  145. package/lib/typescript/module/ui/screens/fileManagement/photoGridLayout.d.ts.map +1 -0
  146. package/lib/typescript/module/ui/session/tokenTransport.d.ts +15 -8
  147. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/utils/fileManagement.d.ts +4 -11
  149. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  150. package/package.json +6 -11
  151. package/src/index.ts +8 -0
  152. package/src/ui/components/FontLoader.native.tsx +88 -0
  153. package/src/ui/components/FontLoader.tsx +16 -151
  154. package/src/ui/components/fileManagement/FileDetailsModal.tsx +17 -18
  155. package/src/ui/components/fileManagement/UploadPreview.tsx +1 -1
  156. package/src/ui/components/fileManagement/styles.ts +0 -225
  157. package/src/ui/context/hooks/useAuthOperations.ts +5 -2
  158. package/src/ui/context/useOxyAccountGraph.ts +5 -0
  159. package/src/ui/hooks/queries/__tests__/userCache.test.ts +361 -0
  160. package/src/ui/hooks/queries/useAccountQueries.ts +14 -5
  161. package/src/ui/hooks/queries/userCache.ts +308 -0
  162. package/src/ui/hooks/useAvatarPicker.ts +27 -7
  163. package/src/ui/hooks/useFileDownloadUrl.ts +7 -23
  164. package/src/ui/hooks/useResolvedFileUrls.ts +170 -0
  165. package/src/ui/screens/FileManagementScreen.tsx +55 -27
  166. package/src/ui/screens/__tests__/photoGridLayout.test.ts +57 -0
  167. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +215 -213
  168. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +27 -3
  169. package/src/ui/screens/fileManagement/photoGridLayout.ts +45 -0
  170. package/src/ui/session/__tests__/tokenTransport.test.ts +25 -10
  171. package/src/ui/session/tokenTransport.ts +21 -10
  172. package/src/ui/utils/fileManagement.ts +3 -46
  173. package/lib/typescript/types/expo-vector-icons.d.ts +0 -22
  174. package/src/types/expo-vector-icons.d.ts +0 -22
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.upsertCachedUser = upsertCachedUser;
7
+ exports.upsertCachedUsers = upsertCachedUsers;
8
+ var _queryKeys = require("./queryKeys.js");
9
+ var _authStore = require("../../stores/authStore.js");
10
+ /**
11
+ * Canonical user-cache UPSERT for the Oxy React Query cache.
12
+ *
13
+ * The problem this solves: many places across an app write a user object into
14
+ * the React Query cache and REPLACE the existing entry — profile fetch, feed /
15
+ * post hydration, search, notifications, lists. Each of those sources carries a
16
+ * DIFFERENT (often sparse) slice of the user: a feed author has no viewer
17
+ * `relationship`, a search hit has no `createdAt`, a notification actor has no
18
+ * `_count`. A plain `setQueryData(key, sparseUser)` therefore STRIPS whatever
19
+ * fields the authoritative single-profile fetch had already stored — the
20
+ * "Follows you tag vanishes when the feed loads" / "counts flash to zero" class
21
+ * of bug.
22
+ *
23
+ * The fix (owned here, in the SDK): ONE canonical upsert that MERGES. It writes
24
+ * under BOTH keys the SDK's user hooks read from:
25
+ * - by-id: `queryKeys.users.detail(id)` (read by `useUserById`)
26
+ * - by-username: `queryKeys.users.byUsername(username, viewerId)` (viewer-scoped;
27
+ * read by `useUserByUsername`, carries the viewer `relationship`)
28
+ *
29
+ * Merge semantics (per key):
30
+ * - No existing entry -> seed the (normalized) incoming object and mark it
31
+ * STALE (`updatedAt: 0`) so react-query refetches the full authoritative
32
+ * profile (viewer-relative `relationship`, counts, `createdAt`, …). Instant
33
+ * first paint, then the real fetch.
34
+ * - Existing entry -> `{ ...existing, ...pickMeaningful(incoming) }`: only
35
+ * the DEFINED, non-empty fields of `incoming` win; every other field is kept
36
+ * from `existing`. A sparse source can never NULL-out or STRIP a field the
37
+ * authoritative fetch set. The entry's freshness is left untouched (never
38
+ * marked stale — it is already managed).
39
+ * - Nested objects (`name`, `_count`, `relationship`) merge field-by-field, so
40
+ * a partial `name`/`_count`/`relationship` never replaces a fuller one.
41
+ * - `relationship` is written ONLY under the viewer-scoped by-username key —
42
+ * the viewer-independent by-id key never stores it (prevents one viewer's
43
+ * follow state from leaking into every other viewer's identity cache).
44
+ * - Anti-degradation: a good `username` / `name.displayName` / `avatar` is
45
+ * never overwritten by a degraded/empty one (empty username, the
46
+ * `'Unknown user'` ghost-author sentinel, `null` avatar).
47
+ *
48
+ * It is a cache write only — zero network, one `setQueryData` per key.
49
+ */
50
+
51
+ /**
52
+ * A user-shaped object that can be upserted into the cache. Intentionally
53
+ * permissive: it covers the SDK `User` PLUS the looser actor objects embedded on
54
+ * posts / notifications / lists, where `name` may be a plain string and the id
55
+ * may arrive as Mongo `_id`. Every field is optional — a sparse feed author is a
56
+ * valid `CacheableUser`. The index signature lets any additional `User` field
57
+ * pass through untouched (so the upsert never has to know the full DTO shape).
58
+ */
59
+
60
+ /** The degraded display-name sentinel (ghost-author rule). */
61
+ const DEGRADED_DISPLAY_NAME = 'Unknown user';
62
+
63
+ /** A cache entry always carries a resolved string `id`. */
64
+
65
+ /**
66
+ * Whether a value is "meaningful" — i.e. it should override an existing field.
67
+ * Drops `undefined` / `null` / empty-or-whitespace strings so a sparse source
68
+ * can never strip a field. `false`, `0` and other falsy-but-defined values ARE
69
+ * meaningful (a real `verified: false` or `_count.followers: 0`).
70
+ */
71
+ function isMeaningful(value) {
72
+ if (value === undefined || value === null) return false;
73
+ if (typeof value === 'string') return value.trim() !== '';
74
+ return true;
75
+ }
76
+
77
+ /** A display name is meaningful only when non-empty AND not the degraded sentinel. */
78
+ function isMeaningfulDisplayName(value) {
79
+ return typeof value === 'string' && value.trim() !== '' && value !== DEGRADED_DISPLAY_NAME;
80
+ }
81
+ function isPlainObject(value) {
82
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
83
+ }
84
+
85
+ /** Normalize the polymorphic `name` (string | object | nullish) to the canonical object shape. */
86
+ function normalizeName(name) {
87
+ if (name === undefined || name === null) return undefined;
88
+ if (typeof name === 'string') {
89
+ const trimmed = name.trim();
90
+ return trimmed ? {
91
+ displayName: trimmed
92
+ } : undefined;
93
+ }
94
+ return name;
95
+ }
96
+
97
+ /**
98
+ * Coerce a user-shaped object to a cache entry: resolve the id from
99
+ * `id ?? _id ?? fallbackId` and normalize `name` to the canonical object shape
100
+ * (the polymorphic `string` name is dropped from the spread and re-set as an
101
+ * object so the cache never holds a bare-string name).
102
+ */
103
+ function toCachedUser(user, fallbackId) {
104
+ const {
105
+ name: rawName,
106
+ ...rest
107
+ } = user;
108
+ const id = String(user.id ?? user._id ?? fallbackId);
109
+ const name = normalizeName(rawName);
110
+ const normalized = {
111
+ ...rest,
112
+ id
113
+ };
114
+ if (name !== undefined) normalized.name = name;
115
+ return normalized;
116
+ }
117
+
118
+ /**
119
+ * Normalize an incoming (possibly partial) user to a cache entry. Returns `null`
120
+ * when no id can be resolved (nothing to key on).
121
+ */
122
+ function normalizeIncoming(user) {
123
+ const cached = toCachedUser(user, '');
124
+ return cached.id ? cached : null;
125
+ }
126
+
127
+ /** Merge two `name` objects field-by-field, with anti-degradation on `displayName`. */
128
+ function mergeName(existing, incoming) {
129
+ if (incoming === undefined) return existing;
130
+ if (existing === undefined) return incoming;
131
+ const merged = {
132
+ ...existing
133
+ };
134
+ for (const [key, value] of Object.entries(incoming)) {
135
+ if (key === 'displayName') continue;
136
+ if (isMeaningful(value)) merged[key] = value;
137
+ }
138
+ // Never let an empty / `'Unknown user'` displayName overwrite a real one.
139
+ if (isMeaningfulDisplayName(incoming.displayName)) {
140
+ merged.displayName = incoming.displayName;
141
+ }
142
+ return merged;
143
+ }
144
+
145
+ /** Merge `_count` field-by-field so a partial count never replaces a fuller one. */
146
+ function mergeCount(existing, incoming) {
147
+ if (!isPlainObject(incoming)) return existing;
148
+ const merged = {
149
+ ...(isPlainObject(existing) ? existing : {})
150
+ };
151
+ if (typeof incoming.followers === 'number') merged.followers = incoming.followers;
152
+ if (typeof incoming.following === 'number') merged.following = incoming.following;
153
+ return merged;
154
+ }
155
+
156
+ /**
157
+ * Merge `relationship`. A source without a relationship (feed/list/notification,
158
+ * or an anon/self/bulk `null`) must NEVER strip an existing viewer relationship.
159
+ */
160
+ function mergeRelationship(existing, incoming) {
161
+ if (!isPlainObject(incoming)) return existing;
162
+ const merged = {
163
+ ...(isPlainObject(existing) ? existing : {})
164
+ };
165
+ if (typeof incoming.isFollowing === 'boolean') merged.isFollowing = incoming.isFollowing;
166
+ if (typeof incoming.followsYou === 'boolean') merged.followsYou = incoming.followsYou;
167
+ return merged;
168
+ }
169
+
170
+ /**
171
+ * Merge a (normalized) incoming user over an existing cache entry: keep every
172
+ * existing field, override only with the meaningful fields of `incoming`.
173
+ *
174
+ * When `includeRelationship` is false (the viewer-independent by-id key), the
175
+ * viewer-relative `relationship` field is never read, written, or preserved —
176
+ * only the by-username key carries it (`useUserByUsername`).
177
+ */
178
+ function mergeUsers(existing, incoming, options) {
179
+ const includeRelationship = options?.includeRelationship ?? true;
180
+ const merged = {
181
+ ...existing
182
+ };
183
+ for (const [key, value] of Object.entries(incoming)) {
184
+ if (key === 'name' || key === '_count' || key === 'relationship') continue;
185
+ if (key === 'id') {
186
+ merged.id = incoming.id;
187
+ continue;
188
+ }
189
+ if (isMeaningful(value)) merged[key] = value;
190
+ }
191
+ const name = mergeName(existing.name, incoming.name);
192
+ if (name !== undefined) merged.name = name;
193
+ const count = mergeCount(existing._count, incoming._count);
194
+ if (count !== undefined) merged._count = count;
195
+ if (includeRelationship) {
196
+ const relationship = mergeRelationship(existing.relationship, incoming.relationship);
197
+ if (relationship !== undefined) merged.relationship = relationship;
198
+ } else {
199
+ delete merged.relationship;
200
+ }
201
+ return merged;
202
+ }
203
+
204
+ /** Merge-upsert a normalized user into one cache key (see module docs for semantics). */
205
+ function upsertOneKey(queryClient, key, incoming, options) {
206
+ const mergeOpts = {
207
+ includeRelationship: options.includeRelationship
208
+ };
209
+ const existing = queryClient.getQueryData(key);
210
+ if (existing === undefined) {
211
+ // Cold slot: seed the full incoming object, STALE, so react-query refetches
212
+ // the full authoritative profile (relationship, counts, createdAt, …).
213
+ const seeded = mergeUsers({
214
+ id: incoming.id
215
+ }, incoming, mergeOpts);
216
+ queryClient.setQueryData(key, seeded, {
217
+ updatedAt: 0
218
+ });
219
+ return;
220
+ }
221
+ // Existing entry: merge and leave its freshness lifecycle untouched. The
222
+ // existing entry is keyed by `incoming.id`, so use it as the fallback id.
223
+ const merged = mergeUsers(toCachedUser(existing, incoming.id), incoming, mergeOpts);
224
+ const dataUpdatedAt = queryClient.getQueryState(key)?.dataUpdatedAt ?? 0;
225
+ queryClient.setQueryData(key, merged, {
226
+ updatedAt: dataUpdatedAt
227
+ });
228
+ }
229
+
230
+ /**
231
+ * Resolve the active viewer id. The by-username cache key is viewer-scoped; the
232
+ * seed must land on the exact key `useUserByUsername` reads. When a caller does
233
+ * not pass `viewerId`, read it from the auth store — the same store behind the
234
+ * hook's `useOxy().user?.id`, so seed and read stay in lockstep. An explicit
235
+ * empty string is honoured (anonymous scope).
236
+ */
237
+ function resolveViewerId(viewerId) {
238
+ return viewerId ?? _authStore.useAuthStore.getState().user?.id ?? '';
239
+ }
240
+
241
+ /**
242
+ * Merge-upsert a (possibly partial) user into the SDK's user query cache under
243
+ * both the by-id key and, when a username is present, the viewer-scoped
244
+ * by-username key.
245
+ *
246
+ * @param queryClient The app's React Query client.
247
+ * @param user A `User`-shaped object (may be sparse).
248
+ * @param viewerId The active viewer id for the by-username key. Defaults to
249
+ * the current auth-store user id.
250
+ */
251
+ function upsertCachedUser(queryClient, user, viewerId) {
252
+ const incoming = normalizeIncoming(user);
253
+ if (!incoming) return;
254
+
255
+ // By-id identity entry (read by `useUserById`). Not viewer-scoped — never store
256
+ // the viewer-relative `relationship` here or one viewer's follow state leaks
257
+ // into every other viewer's by-id cache entry.
258
+ upsertOneKey(queryClient, _queryKeys.queryKeys.users.detail(incoming.id), incoming, {
259
+ includeRelationship: false
260
+ });
261
+ const username = incoming.username;
262
+ if (typeof username === 'string' && username.trim() !== '') {
263
+ // By-username entry (read by `useUserByUsername`). Viewer-scoped because the
264
+ // authenticated single-profile fetch embeds the viewer `relationship`. Build
265
+ // the key through the SAME helper the hook uses so username normalization
266
+ // (`trim().toLowerCase()`) matches byte-for-byte.
267
+ const key = _queryKeys.queryKeys.users.byUsername(username, resolveViewerId(viewerId));
268
+ upsertOneKey(queryClient, key, incoming, {
269
+ includeRelationship: true
270
+ });
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Batch merge-upsert many users at once (for a feed / list / search response).
276
+ * Resolves the viewer id once and upserts each user cumulatively — a user that
277
+ * appears twice merges both slices into the single cache entry.
278
+ */
279
+ function upsertCachedUsers(queryClient, users, viewerId) {
280
+ if (!Array.isArray(users) || users.length === 0) return;
281
+ const resolvedViewerId = resolveViewerId(viewerId);
282
+ for (const user of users) {
283
+ if (user) upsertCachedUser(queryClient, user, resolvedViewerId);
284
+ }
285
+ }
286
+ //# sourceMappingURL=userCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_queryKeys","require","_authStore","DEGRADED_DISPLAY_NAME","isMeaningful","value","undefined","trim","isMeaningfulDisplayName","isPlainObject","Array","isArray","normalizeName","name","trimmed","displayName","toCachedUser","user","fallbackId","rawName","rest","id","String","_id","normalized","normalizeIncoming","cached","mergeName","existing","incoming","merged","key","Object","entries","mergeCount","followers","following","mergeRelationship","isFollowing","followsYou","mergeUsers","options","includeRelationship","count","_count","relationship","upsertOneKey","queryClient","mergeOpts","getQueryData","seeded","setQueryData","updatedAt","dataUpdatedAt","getQueryState","resolveViewerId","viewerId","useAuthStore","getState","upsertCachedUser","queryKeys","users","detail","username","byUsername","upsertCachedUsers","length","resolvedViewerId"],"sourceRoot":"../../../../../src","sources":["ui/hooks/queries/userCache.ts"],"mappings":"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","ignoreList":[]}
@@ -68,19 +68,34 @@ function useAvatarPicker({
68
68
  }, [activeSessionId, currentLanguage, oxyServices, queryClient]);
69
69
 
70
70
  /**
71
- * After the user picks a file in the FileManagement sheet, route to the
72
- * crop screen with the file's download URL as the source image.
71
+ * After the user picks a file in the FileManagement sheet, resolve a working
72
+ * source URL and route to the crop screen.
73
+ *
74
+ * The picked file is usually PRIVATE, so the synchronous
75
+ * `getFileDownloadUrl` (public CDN origin) would 404 and the crop screen's
76
+ * `Image.getSize` would fail silently. We resolve the authenticated URL via
77
+ * `assetGetUrl` (which throws on failure — unlike `getFileDownloadUrlAsync`,
78
+ * which swallows errors and falls back to the broken public URL) so a
79
+ * failure surfaces a real user-facing error instead of a blank crop canvas.
80
+ * No variant is requested — cropping needs the original.
73
81
  */
74
- const handleFilePicked = (0, _react.useCallback)(file => {
82
+ const handleFilePicked = (0, _react.useCallback)(async file => {
75
83
  if (!file.contentType?.startsWith('image/')) {
76
84
  _bloom.toast.error((0, _core.translate)(currentLanguage ?? undefined, 'editProfile.toasts.selectImage') || 'Please select an image file');
77
85
  return;
78
86
  }
79
-
80
- // Use the public download URL of the picked file as the crop source.
81
- // The OxyServices file URL endpoints accept image variants, but for
82
- // cropping we want the original — passing no variant returns it.
83
- const sourceUri = oxyServices.getFileDownloadUrl(file.id);
87
+ let sourceUri;
88
+ try {
89
+ const resolved = await oxyServices.assetGetUrl(file.id);
90
+ if (!resolved?.url) {
91
+ throw new Error('No download URL returned for the selected image');
92
+ }
93
+ sourceUri = resolved.url;
94
+ } catch (e) {
95
+ const message = e instanceof Error ? e.message : undefined;
96
+ _bloom.toast.error(message || (0, _core.translate)(currentLanguage ?? undefined, 'editProfile.toasts.cropMeasureFailed') || 'Could not load the selected image');
97
+ return;
98
+ }
84
99
  showBottomSheet({
85
100
  screen: 'AvatarCrop',
86
101
  props: {
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_core","_bloom","_avatarUtils","extractUploadedFileId","result","undefined","id","file","first","files","useAvatarPicker","oxyServices","currentLanguage","activeSessionId","queryClient","showBottomSheet","finalizeCroppedAvatar","useCallback","cropped","uploadResult","assetUpload","uri","type","mime","name","Date","now","newFileId","Error","updateAvatarVisibility","updateProfileWithAvatar","avatar","toast","success","translate","e","message","error","handleFilePicked","contentType","startsWith","sourceUri","getFileDownloadUrl","screen","props","imageUri","onConfirm","openAvatarPicker","selectMode","multiSelect","disabledMimeTypes","afterSelect","onSelect"],"sourceRoot":"../../../../src","sources":["ui/hooks/useAvatarPicker.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_core","_bloom","_avatarUtils","extractUploadedFileId","result","undefined","id","file","first","files","useAvatarPicker","oxyServices","currentLanguage","activeSessionId","queryClient","showBottomSheet","finalizeCroppedAvatar","useCallback","cropped","uploadResult","assetUpload","uri","type","mime","name","Date","now","newFileId","Error","updateAvatarVisibility","updateProfileWithAvatar","avatar","toast","success","translate","e","message","error","handleFilePicked","contentType","startsWith","sourceUri","resolved","assetGetUrl","url","screen","props","imageUri","onConfirm","openAvatarPicker","selectMode","multiSelect","disabledMimeTypes","afterSelect","onSelect"],"sourceRoot":"../../../../src","sources":["ui/hooks/useAvatarPicker.ts"],"mappings":"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","ignoreList":[]}
@@ -8,8 +8,9 @@ var _react = require("react");
8
8
  /**
9
9
  * Hook to resolve a file's download URL asynchronously.
10
10
  *
11
- * Uses `getFileDownloadUrlAsync` first, falling back to the synchronous
12
- * `getFileDownloadUrl` if the async call fails.
11
+ * Uses `getFileDownloadUrlAsync`, which returns a scoped media-token stream URL
12
+ * for private assets. There is no sync CDN fallback — the synchronous
13
+ * `getFileDownloadUrl` is public-CDN-only and 404s for private uploads.
13
14
  */
14
15
  const useFileDownloadUrl = (oxyServices, fileId, options) => {
15
16
  const [url, setUrl] = (0, _react.useState)(null);
@@ -37,31 +38,16 @@ const useFileDownloadUrl = (oxyServices, fileId, options) => {
37
38
  setLoading(true);
38
39
  setError(null);
39
40
  try {
40
- let resolvedUrl = null;
41
- if (typeof instance.getFileDownloadUrlAsync === 'function') {
42
- resolvedUrl = await instance.getFileDownloadUrlAsync(targetFileId, variant, expiresIn);
43
- }
44
- if (!resolvedUrl && typeof instance.getFileDownloadUrl === 'function') {
45
- resolvedUrl = instance.getFileDownloadUrl(targetFileId, variant, expiresIn);
41
+ if (typeof instance.getFileDownloadUrlAsync !== 'function') {
42
+ throw new Error('getFileDownloadUrlAsync is not available on this OxyServices instance');
46
43
  }
44
+ const resolvedUrl = await instance.getFileDownloadUrlAsync(targetFileId, variant, expiresIn);
47
45
  if (!cancelled) {
48
46
  setUrl(resolvedUrl || null);
49
47
  }
50
48
  } catch (err) {
51
- // Fallback to sync URL on error where possible
52
- try {
53
- if (typeof instance.getFileDownloadUrl === 'function') {
54
- const fallbackUrl = instance.getFileDownloadUrl(targetFileId, variant, expiresIn);
55
- if (!cancelled) {
56
- setUrl(fallbackUrl || null);
57
- setError(err instanceof Error ? err : new Error(String(err)));
58
- }
59
- return;
60
- }
61
- } catch {
62
- // Secondary failure: surface the original error below.
63
- }
64
49
  if (!cancelled) {
50
+ setUrl(null);
65
51
  setError(err instanceof Error ? err : new Error(String(err)));
66
52
  }
67
53
  } finally {
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","useFileDownloadUrl","oxyServices","fileId","options","url","setUrl","useState","loading","setLoading","error","setError","variant","expiresIn","useEffect","Error","cancelled","instance","targetFileId","load","resolvedUrl","getFileDownloadUrlAsync","getFileDownloadUrl","err","fallbackUrl","String","exports"],"sourceRoot":"../../../../src","sources":["ui/hooks/useFileDownloadUrl.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","useFileDownloadUrl","oxyServices","fileId","options","url","setUrl","useState","loading","setLoading","error","setError","variant","expiresIn","useEffect","Error","cancelled","instance","targetFileId","load","getFileDownloadUrlAsync","resolvedUrl","err","String","exports"],"sourceRoot":"../../../../src","sources":["ui/hooks/useFileDownloadUrl.ts"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,kBAAkB,GAAGA,CAChCC,WAA2C,EAC3CC,MAAiC,EACjCC,OAAmC,KACN;EAC7B,MAAM,CAACC,GAAG,EAAEC,MAAM,CAAC,GAAG,IAAAC,eAAQ,EAAgB,IAAI,CAAC;EACnD,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAG,IAAAF,eAAQ,EAAC,KAAK,CAAC;EAC7C,MAAM,CAACG,KAAK,EAAEC,QAAQ,CAAC,GAAG,IAAAJ,eAAQ,EAAe,IAAI,CAAC;EAEtD,MAAMK,OAAO,GAAGR,OAAO,EAAEQ,OAAO;EAChC,MAAMC,SAAS,GAAGT,OAAO,EAAES,SAAS;EAEpC,IAAAC,gBAAS,EAAC,MAAM;IACd,IAAI,CAACX,MAAM,EAAE;MACXG,MAAM,CAAC,IAAI,CAAC;MACZG,UAAU,CAAC,KAAK,CAAC;MACjBE,QAAQ,CAAC,IAAI,CAAC;MACd;IACF;IAEA,IAAI,CAACT,WAAW,EAAE;MAChBI,MAAM,CAAC,IAAI,CAAC;MACZG,UAAU,CAAC,KAAK,CAAC;MACjBE,QAAQ,CAAC,IAAII,KAAK,CAAC,4DAA4D,CAAC,CAAC;MACjF;IACF;IAEA,IAAIC,SAAS,GAAG,KAAK;IACrB,MAAMC,QAAQ,GAAGf,WAAW;IAC5B,MAAMgB,YAAY,GAAGf,MAAM;IAE3B,MAAMgB,IAAI,GAAG,MAAAA,CAAA,KAAY;MACvBV,UAAU,CAAC,IAAI,CAAC;MAChBE,QAAQ,CAAC,IAAI,CAAC;MAEd,IAAI;QACF,IAAI,OAAOM,QAAQ,CAACG,uBAAuB,KAAK,UAAU,EAAE;UAC1D,MAAM,IAAIL,KAAK,CAAC,uEAAuE,CAAC;QAC1F;QAEA,MAAMM,WAAW,GAAG,MAAMJ,QAAQ,CAACG,uBAAuB,CAACF,YAAY,EAAEN,OAAO,EAAEC,SAAS,CAAC;QAE5F,IAAI,CAACG,SAAS,EAAE;UACdV,MAAM,CAACe,WAAW,IAAI,IAAI,CAAC;QAC7B;MACF,CAAC,CAAC,OAAOC,GAAY,EAAE;QACrB,IAAI,CAACN,SAAS,EAAE;UACdV,MAAM,CAAC,IAAI,CAAC;UACZK,QAAQ,CAACW,GAAG,YAAYP,KAAK,GAAGO,GAAG,GAAG,IAAIP,KAAK,CAACQ,MAAM,CAACD,GAAG,CAAC,CAAC,CAAC;QAC/D;MACF,CAAC,SAAS;QACR,IAAI,CAACN,SAAS,EAAE;UACdP,UAAU,CAAC,KAAK,CAAC;QACnB;MACF;IACF,CAAC;IAEDU,IAAI,CAAC,CAAC;IAEN,OAAO,MAAM;MACXH,SAAS,GAAG,IAAI;IAClB,CAAC;EACH,CAAC,EAAE,CAACb,MAAM,EAAED,WAAW,EAAEU,OAAO,EAAEC,SAAS,CAAC,CAAC;EAE7C,OAAO;IAAER,GAAG;IAAEG,OAAO;IAAEE;EAAM,CAAC;AAChC,CAAC;AAACc,OAAA,CAAAvB,kBAAA,GAAAA,kBAAA","ignoreList":[]}
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ASSET_DOWNLOAD_URLS_QUERY_KEY = void 0;
7
+ exports.fileThumbSource = fileThumbSource;
8
+ exports.isOptimisticFile = isOptimisticFile;
9
+ exports.useResolvedFileUrls = useResolvedFileUrls;
10
+ var _react = require("react");
11
+ var _reactQuery = require("@tanstack/react-query");
12
+ /**
13
+ * The variant-aware batch resolver contract (`@oxyhq/core`).
14
+ *
15
+ * `getFileDownloadUrls` takes a per-file `{ fileId, variant }` list and returns
16
+ * a map keyed by `fileId`; ids that are denied or missing are OMITTED (never an
17
+ * empty string), and PRIVATE assets resolve to a working scoped-media-token
18
+ * stream URL. Typed here rather than `Pick<OxyServices, …>` so this file states
19
+ * the exact contract it depends on.
20
+ */
21
+
22
+ /**
23
+ * Private-safe grid/thumbnail URL resolution.
24
+ *
25
+ * The synchronous `oxyServices.getFileDownloadUrl(id, variant)` always yields
26
+ * the public CDN origin (`cloud.oxy.so/<id>`), which 404s for PRIVATE assets —
27
+ * and uploads default to private. Thumbnails and previews therefore have to be
28
+ * resolved through the authenticated batch endpoint, which returns a working
29
+ * `/assets/:id/stream?…&mt=<scoped media token>` URL for private files.
30
+ *
31
+ * This hook resolves an ENTIRE page of files in ONE (chunked) batch request
32
+ * rather than N per-tile calls, and caches the result in React Query keyed by
33
+ * the (fileId, variant) set. `staleTime` is deliberately shorter than the
34
+ * server media-token TTL so a tile never renders a URL whose token has already
35
+ * expired.
36
+ *
37
+ * Optimistic (`temp-…` / `uploading`) entries are NEVER resolved here — an
38
+ * asset URL must never be built from an id the server does not yet know. Their
39
+ * preview comes from the locally-picked uri (see {@link fileThumbSource}).
40
+ */
41
+
42
+ /** Server media-token lifetime we request for each resolved URL (seconds). */
43
+ const ASSET_URL_TTL_SECONDS = 600; // 10 minutes
44
+ /**
45
+ * Refresh window. Kept below {@link ASSET_URL_TTL_SECONDS} so React Query
46
+ * re-resolves in the background before a rendered URL's token can die.
47
+ */
48
+ const ASSET_URL_STALE_MS = 5 * 60 * 1000; // 5 minutes
49
+ const ASSET_URL_GC_MS = 30 * 60 * 1000;
50
+ /** Server-side batch cap for `POST /assets/batch-access` — chunk beyond this. */
51
+ const BATCH_CAP = 100;
52
+
53
+ /** React Query prefix — scoped media URLs must never leak across accounts. */
54
+ const ASSET_DOWNLOAD_URLS_QUERY_KEY = exports.ASSET_DOWNLOAD_URLS_QUERY_KEY = 'assetDownloadUrls';
55
+
56
+ /**
57
+ * True when a file is an optimistic placeholder that has not been persisted by
58
+ * the server yet — either its id is still the client-minted `temp-…` id, or it
59
+ * carries the `uploading` flag. Mirrors the `temp-` id guard precedent in
60
+ * `@oxyhq/core`'s `avatarUtils.updateAvatarVisibility`.
61
+ */
62
+ function isOptimisticFile(file) {
63
+ if (typeof file.id === 'string' && file.id.startsWith('temp-')) return true;
64
+ return file.metadata?.uploading === true;
65
+ }
66
+
67
+ /** Grid thumbnail variant for a file, or `undefined` when it needs no URL. */
68
+ function thumbVariantFor(file) {
69
+ if (file.contentType.startsWith('video/')) return 'poster';
70
+ if (file.contentType.startsWith('image/')) return 'thumb';
71
+ return undefined;
72
+ }
73
+
74
+ /**
75
+ * Locally-picked preview uri stashed on an optimistic entry's metadata by the
76
+ * upload flow, if present.
77
+ */
78
+ function localPreviewUri(file) {
79
+ const uri = file.metadata?.localPreviewUri;
80
+ return typeof uri === 'string' && uri.length > 0 ? uri : undefined;
81
+ }
82
+
83
+ /**
84
+ * Resolve the image source for a grid tile.
85
+ *
86
+ * - Optimistic entry → its locally-picked preview uri (never an asset URL).
87
+ * - Persisted entry → the private-safe URL from {@link useResolvedFileUrls},
88
+ * or `undefined` while it is still resolving / was denied (tile shows a
89
+ * placeholder instead of a guaranteed-404 CDN URL).
90
+ */
91
+ function fileThumbSource(file, resolved) {
92
+ if (isOptimisticFile(file)) {
93
+ return localPreviewUri(file);
94
+ }
95
+ return resolved.get(file.id);
96
+ }
97
+
98
+ /**
99
+ * Batch-resolve private-safe thumbnail URLs for the given files.
100
+ *
101
+ * Only image/video, non-optimistic files are resolved (documents render an
102
+ * icon, optimistic entries render their local preview). Returns a stable
103
+ * `Map<fileId, url>`; missing entries mean "not resolved yet or access denied".
104
+ */
105
+ function useResolvedFileUrls(oxyServices, files, /** Active viewer account id — media tokens are minted per user; must be in the query key. */
106
+ viewerId) {
107
+ const viewerKey = viewerId ?? '';
108
+ const requests = (0, _react.useMemo)(() => {
109
+ const list = [];
110
+ for (const file of files) {
111
+ if (isOptimisticFile(file)) continue;
112
+ const variant = thumbVariantFor(file);
113
+ if (!variant) continue;
114
+ list.push({
115
+ fileId: file.id,
116
+ variant
117
+ });
118
+ }
119
+ return list;
120
+ }, [files]);
121
+
122
+ // A content signature so the query key is stable across renders while the
123
+ // resolvable set is unchanged (the `files` array is a fresh reference every
124
+ // render). Sorted so ordering churn does not force a refetch.
125
+ const signature = (0, _react.useMemo)(() => requests.map(r => `${r.fileId}:${r.variant ?? ''}`).sort().join('|'), [requests]);
126
+ const query = (0, _reactQuery.useQuery)({
127
+ queryKey: [ASSET_DOWNLOAD_URLS_QUERY_KEY, viewerKey, signature],
128
+ enabled: requests.length > 0 && viewerKey.length > 0,
129
+ staleTime: ASSET_URL_STALE_MS,
130
+ gcTime: ASSET_URL_GC_MS,
131
+ // Keep previously-resolved URLs on screen while the SAME viewer's file set
132
+ // grows (pagination), but never carry another account's scoped media tokens
133
+ // across a switch — `keepPreviousData` alone would do that.
134
+ placeholderData: (previousData, previousQuery) => {
135
+ if (!previousData || !previousQuery) return undefined;
136
+ const prevViewer = previousQuery.queryKey[1];
137
+ if (prevViewer !== viewerKey) return undefined;
138
+ return previousData;
139
+ },
140
+ queryFn: async () => {
141
+ const merged = {};
142
+ for (let i = 0; i < requests.length; i += BATCH_CAP) {
143
+ const chunk = requests.slice(i, i + BATCH_CAP);
144
+ const urls = await oxyServices.getFileDownloadUrls(chunk, {
145
+ expiresIn: ASSET_URL_TTL_SECONDS
146
+ });
147
+ Object.assign(merged, urls);
148
+ }
149
+ return merged;
150
+ }
151
+ });
152
+ return (0, _react.useMemo)(() => {
153
+ const map = new Map();
154
+ const data = query.data;
155
+ if (data) {
156
+ for (const [id, url] of Object.entries(data)) {
157
+ if (typeof url === 'string' && url.length > 0) map.set(id, url);
158
+ }
159
+ }
160
+ return map;
161
+ }, [query.data]);
162
+ }
163
+ //# sourceMappingURL=useResolvedFileUrls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_reactQuery","ASSET_URL_TTL_SECONDS","ASSET_URL_STALE_MS","ASSET_URL_GC_MS","BATCH_CAP","ASSET_DOWNLOAD_URLS_QUERY_KEY","exports","isOptimisticFile","file","id","startsWith","metadata","uploading","thumbVariantFor","contentType","undefined","localPreviewUri","uri","length","fileThumbSource","resolved","get","useResolvedFileUrls","oxyServices","files","viewerId","viewerKey","requests","useMemo","list","variant","push","fileId","signature","map","r","sort","join","query","useQuery","queryKey","enabled","staleTime","gcTime","placeholderData","previousData","previousQuery","prevViewer","queryFn","merged","i","chunk","slice","urls","getFileDownloadUrls","expiresIn","Object","assign","Map","data","url","entries","set"],"sourceRoot":"../../../../src","sources":["ui/hooks/useResolvedFileUrls.ts"],"mappings":"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","ignoreList":[]}
@@ -173,7 +173,7 @@ var _LogoText = require("./components/logo/LogoText.js");
173
173
  var _RequireOxyAuth = require("./components/RequireOxyAuth.js");
174
174
  var _FollowButton = _interopRequireDefault(require("./components/FollowButton.js"));
175
175
  var _OxyPayButton = _interopRequireDefault(require("./components/OxyPayButton.js"));
176
- var _FontLoader = require("./components/FontLoader.js");
176
+ var _FontLoader = require("./components/FontLoader");
177
177
  var _ProfileButton = _interopRequireDefault(require("./components/ProfileButton.js"));
178
178
  var _OxyContext = require("./context/OxyContext.js");
179
179
  var _useAuth = require("./hooks/useAuth.js");