@oxy.so/services 13.1.0 → 13.3.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 (81) hide show
  1. package/lib/commonjs/ui/client.js +13 -0
  2. package/lib/commonjs/ui/client.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +17 -15
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/hooks/accountQueryPersistence.js +50 -15
  6. package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/persistedQueryCache.js +10 -4
  8. package/lib/commonjs/ui/hooks/persistedQueryCache.js.map +1 -1
  9. package/lib/commonjs/ui/hooks/queryClient.js +9 -15
  10. package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
  11. package/lib/commonjs/ui/hooks/useAccountTheme.js +182 -0
  12. package/lib/commonjs/ui/hooks/useAccountTheme.js.map +1 -0
  13. package/lib/commonjs/ui/hooks/useFileDownloadUrl.js +64 -44
  14. package/lib/commonjs/ui/hooks/useFileDownloadUrl.js.map +1 -1
  15. package/lib/module/ui/client.js +1 -0
  16. package/lib/module/ui/client.js.map +1 -1
  17. package/lib/module/ui/context/OxyContext.js +17 -14
  18. package/lib/module/ui/context/OxyContext.js.map +1 -1
  19. package/lib/module/ui/hooks/accountQueryPersistence.js +50 -15
  20. package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -1
  21. package/lib/module/ui/hooks/persistedQueryCache.js +10 -4
  22. package/lib/module/ui/hooks/persistedQueryCache.js.map +1 -1
  23. package/lib/module/ui/hooks/queryClient.js +9 -15
  24. package/lib/module/ui/hooks/queryClient.js.map +1 -1
  25. package/lib/module/ui/hooks/useAccountTheme.js +178 -0
  26. package/lib/module/ui/hooks/useAccountTheme.js.map +1 -0
  27. package/lib/module/ui/hooks/useFileDownloadUrl.js +64 -44
  28. package/lib/module/ui/hooks/useFileDownloadUrl.js.map +1 -1
  29. package/lib/typescript/commonjs/index.d.ts +1 -1
  30. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/ui/client.d.ts +2 -0
  32. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts +22 -4
  35. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts +5 -3
  37. package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts +5 -4
  39. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/ui/hooks/useAccountTheme.d.ts +47 -0
  41. package/lib/typescript/commonjs/ui/hooks/useAccountTheme.d.ts.map +1 -0
  42. package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts +10 -4
  43. package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/ui/icons/Ionicons.d.ts +1 -1
  45. package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  46. package/lib/typescript/commonjs/ui/index.d.ts +1 -1
  47. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/ui/types/navigation.d.ts +2 -0
  49. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  50. package/lib/typescript/module/index.d.ts +1 -1
  51. package/lib/typescript/module/index.d.ts.map +1 -1
  52. package/lib/typescript/module/ui/client.d.ts +2 -0
  53. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  54. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  55. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts +22 -4
  56. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  57. package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts +5 -3
  58. package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts.map +1 -1
  59. package/lib/typescript/module/ui/hooks/queryClient.d.ts +5 -4
  60. package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
  61. package/lib/typescript/module/ui/hooks/useAccountTheme.d.ts +47 -0
  62. package/lib/typescript/module/ui/hooks/useAccountTheme.d.ts.map +1 -0
  63. package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts +10 -4
  64. package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
  65. package/lib/typescript/module/ui/icons/Ionicons.d.ts +1 -1
  66. package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  67. package/lib/typescript/module/ui/index.d.ts +1 -1
  68. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  69. package/lib/typescript/module/ui/types/navigation.d.ts +2 -0
  70. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  71. package/package.json +4 -4
  72. package/src/index.ts +1 -1
  73. package/src/ui/client.ts +2 -0
  74. package/src/ui/context/OxyContext.tsx +27 -19
  75. package/src/ui/hooks/accountQueryPersistence.ts +61 -16
  76. package/src/ui/hooks/persistedQueryCache.ts +13 -5
  77. package/src/ui/hooks/queryClient.ts +12 -18
  78. package/src/ui/hooks/useAccountTheme.ts +177 -0
  79. package/src/ui/hooks/useFileDownloadUrl.ts +59 -49
  80. package/src/ui/index.ts +1 -1
  81. package/src/ui/types/navigation.ts +2 -0
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import type { OxyServices } from '@oxy.so/core';
3
+ import { useOptionalOxy } from '../context/oxyRuntimeContext';
3
4
 
4
5
  export interface UseFileDownloadUrlOptions {
5
6
  variant?: string;
@@ -12,72 +13,81 @@ export interface UseFileDownloadUrlResult {
12
13
  error: Error | null;
13
14
  }
14
15
 
16
+ interface Settled {
17
+ request: string;
18
+ url: string | null;
19
+ error: Error | null;
20
+ }
21
+
22
+ function isAbsoluteUrl(ref: string): boolean {
23
+ return ref.startsWith('http://') || ref.startsWith('https://');
24
+ }
25
+
15
26
  /**
16
- * Hook to resolve a file's download URL asynchronously.
27
+ * Resolve a file's download URL for the current caller.
17
28
  *
18
- * Uses `getFileDownloadUrlAsync`, which returns a scoped media-token stream URL
19
- * for private assets. There is no sync CDN fallback — the synchronous
20
- * `getFileDownloadUrl` is public-CDN-only and 404s for private uploads.
29
+ * A URL `assets.url` already resolved is answered in the FIRST render from
30
+ * core's identity-scoped cache (`assets.cachedUrl`): no request, no loading
31
+ * frame. Only a miss resolves asynchronously, through `assets.url`, which
32
+ * returns a scoped media-token stream URL for a private asset. There is no
33
+ * public-CDN fallback: `assets.publicUrl` 404s for private uploads. A
34
+ * reference that is already an absolute `http(s)` URL is returned unchanged.
35
+ *
36
+ * For a synchronous, render-only answer (an image resolver, a list row) use
37
+ * `oxyServices.assets.renderUrl`, which reads the same cache.
21
38
  */
22
39
  export const useFileDownloadUrl = (
23
40
  oxyServices: OxyServices | null | undefined,
24
41
  fileId: string | null | undefined,
25
42
  options?: UseFileDownloadUrlOptions,
26
43
  ): UseFileDownloadUrlResult => {
27
- const [url, setUrl] = useState<string | null>(null);
28
- const [loading, setLoading] = useState(false);
29
- const [error, setError] = useState<Error | null>(null);
30
-
31
44
  const variant = options?.variant;
32
45
  const expiresIn = options?.expiresIn;
46
+ // A resolved URL can carry the viewer's scoped media token, so the viewer is
47
+ // part of what was asked: an account switch never shows the previous one's.
48
+ const viewerId = useOptionalOxy()?.user?.id ?? '';
49
+ const request = fileId ? `${viewerId}|${fileId}|${variant ?? ''}|${expiresIn ?? ''}` : null;
50
+ const immediate = !fileId
51
+ ? null
52
+ : isAbsoluteUrl(fileId)
53
+ ? fileId
54
+ : (oxyServices?.assets.cachedUrl(fileId, variant, expiresIn) ?? null);
55
+ const needsResolve = !!fileId && !!oxyServices && immediate === null;
33
56
 
34
- useEffect(() => {
35
- if (!fileId) {
36
- setUrl(null);
37
- setLoading(false);
38
- setError(null);
39
- return;
40
- }
41
-
42
- if (!oxyServices) {
43
- setUrl(null);
44
- setLoading(false);
45
- setError(new Error('OxyServices instance not configured for useFileDownloadUrl'));
46
- return;
47
- }
57
+ const [settled, setSettled] = useState<Settled | null>(null);
48
58
 
59
+ useEffect(() => {
60
+ if (!needsResolve || !fileId || !oxyServices || !request) return;
49
61
  let cancelled = false;
50
- const instance = oxyServices;
51
- const targetFileId = fileId;
52
-
53
- const load = async () => {
54
- setLoading(true);
55
- setError(null);
56
-
57
- try {
58
- const resolvedUrl = await instance.assets.url(targetFileId, variant, expiresIn);
59
-
62
+ oxyServices.assets.url(fileId, variant, expiresIn).then(
63
+ (url) => {
64
+ if (!cancelled) setSettled({ request, url: url || null, error: null });
65
+ },
66
+ (err: unknown) => {
60
67
  if (!cancelled) {
61
- setUrl(resolvedUrl || null);
68
+ setSettled({
69
+ request,
70
+ url: null,
71
+ error: err instanceof Error ? err : new Error(String(err)),
72
+ });
62
73
  }
63
- } catch (err: unknown) {
64
- if (!cancelled) {
65
- setUrl(null);
66
- setError(err instanceof Error ? err : new Error(String(err)));
67
- }
68
- } finally {
69
- if (!cancelled) {
70
- setLoading(false);
71
- }
72
- }
73
- };
74
-
75
- load();
76
-
74
+ },
75
+ );
77
76
  return () => {
78
77
  cancelled = true;
79
78
  };
80
- }, [fileId, oxyServices, variant, expiresIn]);
79
+ }, [needsResolve, fileId, oxyServices, variant, expiresIn, request]);
81
80
 
82
- return { url, loading, error };
81
+ if (!fileId) return { url: null, loading: false, error: null };
82
+ if (immediate !== null) return { url: immediate, loading: false, error: null };
83
+ if (!oxyServices) {
84
+ return {
85
+ url: null,
86
+ loading: false,
87
+ error: new Error('OxyServices instance not configured for useFileDownloadUrl'),
88
+ };
89
+ }
90
+ // A result settled for another file, variant or expiry is never shown.
91
+ if (settled?.request !== request) return { url: null, loading: true, error: null };
92
+ return { url: settled.url, loading: false, error: settled.error };
83
93
  };
package/src/ui/index.ts CHANGED
@@ -34,7 +34,7 @@ export { LogoText } from './components/logo/LogoText';
34
34
  export { RequireOxyAuth } from './components/RequireOxyAuth';
35
35
  export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './components/RequireOxyAuth';
36
36
  // `OxyProvider`'s `accountQueries`: the app's per-account offline cache.
37
- export type { AccountQueriesConfig } from './hooks/accountQueryPersistence';
37
+ export type { AccountQueriesConfig, AccountQueryMeta } from './hooks/accountQueryPersistence';
38
38
  export { default as FollowButton } from './components/FollowButton';
39
39
  export { default as PeableButton } from './components/PeableButton';
40
40
  export type { PeableButtonProps } from './components/PeableButton';
@@ -150,6 +150,8 @@ export interface OxyProviderProps {
150
150
  * account switch and deleted on sign-out. `RequireOxyAuth` waits for the
151
151
  * signed-in account's cache to restore, so another account's rows never
152
152
  * render. Works with the SDK's client or one passed as `queryClient`.
153
+ * A single query opts in with `meta: { persist: 'account' }` and needs no
154
+ * declaration here (`AccountQueryMeta`).
153
155
  */
154
156
  accountQueries?: AccountQueriesConfig;
155
157
  /**