@oxy.so/services 13.2.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.
- package/lib/commonjs/ui/client.js +13 -0
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAccountTheme.js +182 -0
- package/lib/commonjs/ui/hooks/useAccountTheme.js.map +1 -0
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js +64 -44
- package/lib/commonjs/ui/hooks/useFileDownloadUrl.js.map +1 -1
- package/lib/module/ui/client.js +1 -0
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/hooks/useAccountTheme.js +178 -0
- package/lib/module/ui/hooks/useAccountTheme.js.map +1 -0
- package/lib/module/ui/hooks/useFileDownloadUrl.js +64 -44
- package/lib/module/ui/hooks/useFileDownloadUrl.js.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +2 -0
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAccountTheme.d.ts +47 -0
- package/lib/typescript/commonjs/ui/hooks/useAccountTheme.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts +10 -4
- package/lib/typescript/commonjs/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +2 -0
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAccountTheme.d.ts +47 -0
- package/lib/typescript/module/ui/hooks/useAccountTheme.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts +10 -4
- package/lib/typescript/module/ui/hooks/useFileDownloadUrl.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ui/client.ts +2 -0
- package/src/ui/hooks/useAccountTheme.ts +177 -0
- package/src/ui/hooks/useFileDownloadUrl.ts +59 -49
|
@@ -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
|
-
*
|
|
27
|
+
* Resolve a file's download URL for the current caller.
|
|
17
28
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
68
|
+
setSettled({
|
|
69
|
+
request,
|
|
70
|
+
url: null,
|
|
71
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
72
|
+
});
|
|
62
73
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
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
|
};
|