@scalemule/nextjs 0.1.24 → 0.1.27
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/dist/client.d.mts +14 -1
- package/dist/client.d.ts +14 -1
- package/dist/client.js +22 -1
- package/dist/client.mjs +22 -1
- package/dist/{index-DTWyUcyd.d.mts → index-zQloSkpW.d.mts} +5 -1
- package/dist/{index-DTWyUcyd.d.ts → index-zQloSkpW.d.ts} +5 -1
- package/dist/index.d.mts +150 -23
- package/dist/index.d.ts +150 -23
- package/dist/index.js +443 -493
- package/dist/index.mjs +442 -493
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +7 -9
- package/dist/server/index.mjs +7 -9
- package/dist/server/ledvery.js +1 -5
- package/dist/server/ledvery.mjs +1 -5
- package/dist/server/webhook-handler.d.mts +2 -2
- package/dist/server/webhook-handler.d.ts +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{webhook-handler-DsxJS_He.d.mts → webhook-handler-C_bKDevj.d.mts} +1 -1
- package/dist/{webhook-handler-CTx4Uh8u.d.ts → webhook-handler-D4uulN2T.d.ts} +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -560,6 +560,7 @@ var ScaleMuleClient = class {
|
|
|
560
560
|
this.anonymousId = null;
|
|
561
561
|
this.sessionPool = /* @__PURE__ */ new Map();
|
|
562
562
|
this.knownAccounts = /* @__PURE__ */ new Map();
|
|
563
|
+
this.refreshPromise = null;
|
|
563
564
|
this.apiKey = config.apiKey;
|
|
564
565
|
this.applicationId = config.applicationId || null;
|
|
565
566
|
this.baseUrl = config.baseUrl || GATEWAY_URLS[config.environment || "prod"];
|
|
@@ -578,6 +579,9 @@ var ScaleMuleClient = class {
|
|
|
578
579
|
this.multiSessionEnabled = config.enableMultiSession || false;
|
|
579
580
|
this.accountSwitcherEnabled = config.enableAccountSwitcher || false;
|
|
580
581
|
this.accountSwitcherPrivacy = config.accountSwitcherPrivacy || "full";
|
|
582
|
+
this.onRefreshStart = config.onRefreshStart;
|
|
583
|
+
this.onRefreshEnd = config.onRefreshEnd;
|
|
584
|
+
this.onAutoRefreshFailed = config.onAutoRefreshFailed;
|
|
581
585
|
}
|
|
582
586
|
// --------------------------------------------------------------------------
|
|
583
587
|
// Session Management
|
|
@@ -863,34 +867,28 @@ var ScaleMuleClient = class {
|
|
|
863
867
|
const method = (init.method || "GET").toUpperCase();
|
|
864
868
|
const timeout = init.timeout || this.defaultTimeout;
|
|
865
869
|
const maxRetries = init.skipRetry ? 0 : init.retries ?? this.maxRetries;
|
|
866
|
-
const
|
|
867
|
-
"x-api-key": this.apiKey,
|
|
868
|
-
"User-Agent": `ScaleMule-SDK-TypeScript/${SDK_VERSION}`,
|
|
869
|
-
...init.headers
|
|
870
|
-
};
|
|
871
|
-
if (!init.skipAuth && this.sessionToken) {
|
|
872
|
-
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
873
|
-
}
|
|
874
|
-
if (this.workspaceId) {
|
|
875
|
-
headers["x-sm-workspace-id"] = this.workspaceId;
|
|
876
|
-
}
|
|
877
|
-
if (!this.sessionToken && this.anonymousId) {
|
|
878
|
-
headers["x-anonymous-id"] = this.anonymousId;
|
|
879
|
-
}
|
|
880
|
-
let bodyStr;
|
|
881
|
-
if (init.body !== void 0 && init.body !== null) {
|
|
882
|
-
bodyStr = typeof init.body === "string" ? init.body : JSON.stringify(init.body);
|
|
883
|
-
if (!headers["Content-Type"]) {
|
|
884
|
-
headers["Content-Type"] = "application/json";
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
if (this.debug) {
|
|
888
|
-
console.log(`[ScaleMule] ${method} ${path}`);
|
|
889
|
-
}
|
|
890
|
-
const idempotencyKey = method === "POST" ? generateIdempotencyKey() : void 0;
|
|
870
|
+
const bodyStr = init.body ? JSON.stringify(init.body) : void 0;
|
|
891
871
|
let lastError = null;
|
|
872
|
+
let idempotencyKey = null;
|
|
892
873
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
893
|
-
|
|
874
|
+
const headers = {
|
|
875
|
+
"x-api-key": this.apiKey,
|
|
876
|
+
"User-Agent": `ScaleMule-SDK-TypeScript/${SDK_VERSION}`,
|
|
877
|
+
...init.headers
|
|
878
|
+
};
|
|
879
|
+
if (!init.skipAuth && this.sessionToken) {
|
|
880
|
+
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
881
|
+
}
|
|
882
|
+
if (this.workspaceId) {
|
|
883
|
+
headers["x-sm-workspace-id"] = this.workspaceId;
|
|
884
|
+
}
|
|
885
|
+
if (!this.sessionToken && this.anonymousId) {
|
|
886
|
+
headers["x-anonymous-id"] = this.anonymousId;
|
|
887
|
+
}
|
|
888
|
+
if (attempt > 0 && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
889
|
+
if (!idempotencyKey) {
|
|
890
|
+
idempotencyKey = generateIdempotencyKey();
|
|
891
|
+
}
|
|
894
892
|
headers["x-idempotency-key"] = idempotencyKey;
|
|
895
893
|
}
|
|
896
894
|
const controller = new AbortController();
|
|
@@ -910,6 +908,10 @@ var ScaleMuleClient = class {
|
|
|
910
908
|
signal: controller.signal
|
|
911
909
|
});
|
|
912
910
|
clearTimeout(timeoutId);
|
|
911
|
+
const rotatedToken = response.headers.get("x-rotated-session-token");
|
|
912
|
+
if (rotatedToken && this.sessionToken && this.userId) {
|
|
913
|
+
this.setSession(rotatedToken, this.userId);
|
|
914
|
+
}
|
|
913
915
|
if (this.rateLimitQueue) {
|
|
914
916
|
this.rateLimitQueue.updateFromHeaders(response.headers);
|
|
915
917
|
}
|
|
@@ -932,6 +934,45 @@ var ScaleMuleClient = class {
|
|
|
932
934
|
status: response.status,
|
|
933
935
|
details: responseData?.error?.details || responseData?.details
|
|
934
936
|
};
|
|
937
|
+
if (response.status === 401 && this.sessionToken && !init.isAutoRefresh) {
|
|
938
|
+
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
939
|
+
let isPrimaryRefresh = false;
|
|
940
|
+
if (!this.refreshPromise) {
|
|
941
|
+
isPrimaryRefresh = true;
|
|
942
|
+
this.onRefreshStart?.();
|
|
943
|
+
this.refreshPromise = this.auth?.refreshSession ? this.auth.refreshSession() : this.post("/auth/refresh", {}, { isAutoRefresh: true });
|
|
944
|
+
}
|
|
945
|
+
const currentRefreshPromise = this.refreshPromise;
|
|
946
|
+
try {
|
|
947
|
+
const refreshResult = await currentRefreshPromise;
|
|
948
|
+
if (!refreshResult || refreshResult.error) {
|
|
949
|
+
if (this.debug) console.log("[ScaleMule] Auto-refresh failed:", refreshResult?.error);
|
|
950
|
+
const apiError = refreshResult?.error || {
|
|
951
|
+
code: "refresh_failed",
|
|
952
|
+
message: "Auto-refresh failed",
|
|
953
|
+
status: 400
|
|
954
|
+
};
|
|
955
|
+
init.onAutoRefreshFailed?.(apiError);
|
|
956
|
+
this.onAutoRefreshFailed?.(apiError);
|
|
957
|
+
return { data: null, error };
|
|
958
|
+
}
|
|
959
|
+
if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
|
|
960
|
+
const newToken = refreshResult.data?.session_token || refreshResult.data?.access_token;
|
|
961
|
+
if (newToken) {
|
|
962
|
+
this.sessionToken = newToken;
|
|
963
|
+
if (this.userId) {
|
|
964
|
+
await this.storage.setItem(SESSION_STORAGE_KEY, newToken);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
attempt--;
|
|
968
|
+
continue;
|
|
969
|
+
} finally {
|
|
970
|
+
if (isPrimaryRefresh && this.refreshPromise === currentRefreshPromise) {
|
|
971
|
+
this.refreshPromise = null;
|
|
972
|
+
this.onRefreshEnd?.();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
935
976
|
if (response.status === 429) {
|
|
936
977
|
const retryAfter = response.headers.get("Retry-After");
|
|
937
978
|
if (retryAfter) {
|
|
@@ -5715,7 +5756,7 @@ var AudioService = class extends ServiceModule {
|
|
|
5715
5756
|
constructor(client, storage) {
|
|
5716
5757
|
super(client);
|
|
5717
5758
|
this.storage = storage;
|
|
5718
|
-
this.basePath = "/v1/
|
|
5759
|
+
this.basePath = "/v1/audio";
|
|
5719
5760
|
}
|
|
5720
5761
|
/**
|
|
5721
5762
|
* Register a storage-uploaded audio asset with the audio service.
|
|
@@ -5772,6 +5813,32 @@ var AudioService = class extends ServiceModule {
|
|
|
5772
5813
|
};
|
|
5773
5814
|
}
|
|
5774
5815
|
};
|
|
5816
|
+
var TtsService = class extends ServiceModule {
|
|
5817
|
+
constructor() {
|
|
5818
|
+
super(...arguments);
|
|
5819
|
+
this.basePath = "/v1/tts";
|
|
5820
|
+
}
|
|
5821
|
+
async synthesize(params, options) {
|
|
5822
|
+
return this.post(
|
|
5823
|
+
"/synthesize",
|
|
5824
|
+
{
|
|
5825
|
+
text: params.text,
|
|
5826
|
+
voice: params.voice,
|
|
5827
|
+
model: params.model,
|
|
5828
|
+
provider: params.provider,
|
|
5829
|
+
async: params.async,
|
|
5830
|
+
access_mode: params.accessMode
|
|
5831
|
+
},
|
|
5832
|
+
options
|
|
5833
|
+
);
|
|
5834
|
+
}
|
|
5835
|
+
async getJob(id, options) {
|
|
5836
|
+
return this._get(`/jobs/${id}`, options);
|
|
5837
|
+
}
|
|
5838
|
+
async listVoices(params, options) {
|
|
5839
|
+
return this._get(this.withQuery("/voices", { provider: params?.provider }), options);
|
|
5840
|
+
}
|
|
5841
|
+
};
|
|
5775
5842
|
var DeadLetterApi = class extends ServiceModule {
|
|
5776
5843
|
constructor() {
|
|
5777
5844
|
super(...arguments);
|
|
@@ -6592,6 +6659,7 @@ var ScaleMule = class {
|
|
|
6592
6659
|
this.functions = new FunctionsService(this._client);
|
|
6593
6660
|
this.photo = new PhotoService(this._client, this.storage);
|
|
6594
6661
|
this.audio = new AudioService(this._client, this.storage);
|
|
6662
|
+
this.tts = new TtsService(this._client);
|
|
6595
6663
|
this.flagContent = new FlagContentService(this._client);
|
|
6596
6664
|
this.creatorMaker = new CreatorMakerService(this._client);
|
|
6597
6665
|
this.compliance = new ComplianceService(this._client);
|
|
@@ -7158,6 +7226,24 @@ var ScaleMuleClient2 = class {
|
|
|
7158
7226
|
console.log("[ScaleMule] Session set for user:", userId);
|
|
7159
7227
|
}
|
|
7160
7228
|
}
|
|
7229
|
+
/**
|
|
7230
|
+
* Token-only setter for member-auth surfaces.
|
|
7231
|
+
*
|
|
7232
|
+
* Unlike `setSession(token, userId)`, this does NOT persist a userId or
|
|
7233
|
+
* write to local storage — useful when the host platform owns identity
|
|
7234
|
+
* (e.g. ScaleMule's member dashboards use `MemberAuthProvider` and the
|
|
7235
|
+
* token comes from a `${env}_member_access_token` cookie). The token is
|
|
7236
|
+
* applied in-memory and used as a `Bearer` Authorization header on
|
|
7237
|
+
* subsequent requests.
|
|
7238
|
+
*
|
|
7239
|
+
* Pass `null` to clear without touching userId/storage.
|
|
7240
|
+
*/
|
|
7241
|
+
setSessionToken(token) {
|
|
7242
|
+
this.sessionToken = token;
|
|
7243
|
+
if (this.debug) {
|
|
7244
|
+
console.log("[ScaleMule] Session token", token ? "set (token-only)" : "cleared (token-only)");
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7161
7247
|
/**
|
|
7162
7248
|
* Clear session on logout
|
|
7163
7249
|
*/
|
|
@@ -7222,6 +7308,8 @@ var ScaleMuleClient2 = class {
|
|
|
7222
7308
|
console.log(`[ScaleMule] ${options.method || "GET"} ${path}`);
|
|
7223
7309
|
}
|
|
7224
7310
|
let lastError = null;
|
|
7311
|
+
const MAX_REFRESH_ATTEMPTS = 1;
|
|
7312
|
+
let refreshAttempts = 0;
|
|
7225
7313
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
7226
7314
|
const controller = new AbortController();
|
|
7227
7315
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
@@ -7245,7 +7333,8 @@ var ScaleMuleClient2 = class {
|
|
|
7245
7333
|
if (!response.ok) {
|
|
7246
7334
|
const rawError = responseData?.error;
|
|
7247
7335
|
const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
|
|
7248
|
-
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
|
|
7336
|
+
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh && refreshAttempts < MAX_REFRESH_ATTEMPTS) {
|
|
7337
|
+
refreshAttempts++;
|
|
7249
7338
|
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
7250
7339
|
if (!this.refreshPromise) {
|
|
7251
7340
|
this.onRefreshStart?.();
|
|
@@ -7614,8 +7703,12 @@ function ScaleMuleProvider({
|
|
|
7614
7703
|
onLogout,
|
|
7615
7704
|
onAuthError,
|
|
7616
7705
|
bootstrapFlags,
|
|
7617
|
-
mediaPolicy
|
|
7706
|
+
mediaPolicy,
|
|
7707
|
+
getToken,
|
|
7708
|
+
userResolver,
|
|
7709
|
+
memberTokenPollMs
|
|
7618
7710
|
}) {
|
|
7711
|
+
const memberMode = typeof getToken === "function";
|
|
7619
7712
|
const [user, setUser] = useState(null);
|
|
7620
7713
|
const [initializing, setInitializing] = useState(true);
|
|
7621
7714
|
const [error, setError] = useState(null);
|
|
@@ -7632,9 +7725,13 @@ function ScaleMuleProvider({
|
|
|
7632
7725
|
gatewayUrl: resolvedGatewayUrl,
|
|
7633
7726
|
debug,
|
|
7634
7727
|
storage,
|
|
7635
|
-
|
|
7728
|
+
// Make outbound API calls wait for the first token to land in any
|
|
7729
|
+
// mode where the provider is responsible for populating the
|
|
7730
|
+
// session asynchronously: auth-proxy fetch, or member-mode
|
|
7731
|
+
// getToken() callback. Resolved in the init effect below.
|
|
7732
|
+
pendingSessionInit: !!authProxyUrl || memberMode
|
|
7636
7733
|
}),
|
|
7637
|
-
[apiKey, applicationId, environment, resolvedGatewayUrl, debug, storage, authProxyUrl]
|
|
7734
|
+
[apiKey, applicationId, environment, resolvedGatewayUrl, debug, storage, authProxyUrl, memberMode]
|
|
7638
7735
|
);
|
|
7639
7736
|
const money = useMemo(
|
|
7640
7737
|
() => createMoneyClient({
|
|
@@ -7656,8 +7753,10 @@ function ScaleMuleProvider({
|
|
|
7656
7753
|
});
|
|
7657
7754
|
}, [apiKey, applicationId, environment, resolvedGatewayUrl, debug]);
|
|
7658
7755
|
const [fetchedPolicy, setFetchedPolicy] = useState(void 0);
|
|
7756
|
+
const [tokenVersion, setTokenVersion] = useState(0);
|
|
7659
7757
|
useEffect(() => {
|
|
7660
7758
|
if (mediaPolicy) return;
|
|
7759
|
+
if (memberMode && tokenVersion === 0) return;
|
|
7661
7760
|
let mounted = true;
|
|
7662
7761
|
void (async () => {
|
|
7663
7762
|
try {
|
|
@@ -7675,7 +7774,7 @@ function ScaleMuleProvider({
|
|
|
7675
7774
|
return () => {
|
|
7676
7775
|
mounted = false;
|
|
7677
7776
|
};
|
|
7678
|
-
}, [baseClient, mediaPolicy]);
|
|
7777
|
+
}, [baseClient, mediaPolicy, memberMode, tokenVersion]);
|
|
7679
7778
|
const effectiveMediaPolicy = mediaPolicy ?? fetchedPolicy;
|
|
7680
7779
|
useEffect(() => {
|
|
7681
7780
|
const token = client.getSessionToken();
|
|
@@ -7693,6 +7792,43 @@ function ScaleMuleProvider({
|
|
|
7693
7792
|
try {
|
|
7694
7793
|
await client.initialize();
|
|
7695
7794
|
const cachedUser = getCachedUser();
|
|
7795
|
+
if (memberMode) {
|
|
7796
|
+
try {
|
|
7797
|
+
const token = await Promise.resolve(getToken()) ?? null;
|
|
7798
|
+
if (mounted) {
|
|
7799
|
+
if (token) {
|
|
7800
|
+
client.setSessionToken(token);
|
|
7801
|
+
baseClient.setAccessToken(token);
|
|
7802
|
+
money.setAccessToken(token);
|
|
7803
|
+
} else {
|
|
7804
|
+
client.setSessionToken(null);
|
|
7805
|
+
baseClient.clearAccessToken();
|
|
7806
|
+
money.setAccessToken(void 0);
|
|
7807
|
+
}
|
|
7808
|
+
setTokenVersion((v) => v + 1);
|
|
7809
|
+
}
|
|
7810
|
+
if (userResolver) {
|
|
7811
|
+
try {
|
|
7812
|
+
const resolvedUser = await userResolver();
|
|
7813
|
+
if (mounted) {
|
|
7814
|
+
setUser(resolvedUser);
|
|
7815
|
+
setCachedUser(resolvedUser);
|
|
7816
|
+
}
|
|
7817
|
+
} catch (resolveErr) {
|
|
7818
|
+
if (mounted && debug) {
|
|
7819
|
+
console.debug("[ScaleMule] userResolver() failed:", resolveErr);
|
|
7820
|
+
}
|
|
7821
|
+
}
|
|
7822
|
+
}
|
|
7823
|
+
} catch (memberErr) {
|
|
7824
|
+
if (mounted && debug) {
|
|
7825
|
+
console.debug("[ScaleMule] getToken() failed:", memberErr);
|
|
7826
|
+
}
|
|
7827
|
+
} finally {
|
|
7828
|
+
client.resolveSessionPending();
|
|
7829
|
+
}
|
|
7830
|
+
return;
|
|
7831
|
+
}
|
|
7696
7832
|
if (authProxyUrl) {
|
|
7697
7833
|
if (cachedUser && mounted) {
|
|
7698
7834
|
setUser(cachedUser);
|
|
@@ -7764,7 +7900,43 @@ function ScaleMuleProvider({
|
|
|
7764
7900
|
return () => {
|
|
7765
7901
|
mounted = false;
|
|
7766
7902
|
};
|
|
7767
|
-
}, [client, debug, onAuthError, authProxyUrl]);
|
|
7903
|
+
}, [client, baseClient, money, debug, onAuthError, authProxyUrl, memberMode, getToken, userResolver]);
|
|
7904
|
+
useEffect(() => {
|
|
7905
|
+
if (!memberMode) return;
|
|
7906
|
+
if (memberTokenPollMs === null) return;
|
|
7907
|
+
const interval = memberTokenPollMs ?? 6e4;
|
|
7908
|
+
if (interval <= 0) return;
|
|
7909
|
+
let cancelled = false;
|
|
7910
|
+
const id = setInterval(async () => {
|
|
7911
|
+
try {
|
|
7912
|
+
const token = await Promise.resolve(getToken()) ?? null;
|
|
7913
|
+
if (cancelled) return;
|
|
7914
|
+
const current = client.getSessionToken();
|
|
7915
|
+
if (token === current) return;
|
|
7916
|
+
if (token) {
|
|
7917
|
+
client.setSessionToken(token);
|
|
7918
|
+
baseClient.setAccessToken(token);
|
|
7919
|
+
money.setAccessToken(token);
|
|
7920
|
+
} else {
|
|
7921
|
+
client.setSessionToken(null);
|
|
7922
|
+
baseClient.clearAccessToken();
|
|
7923
|
+
money.setAccessToken(void 0);
|
|
7924
|
+
if (debug) {
|
|
7925
|
+
console.debug("[ScaleMule] Member token cleared on refresh");
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
setTokenVersion((v) => v + 1);
|
|
7929
|
+
} catch (err) {
|
|
7930
|
+
if (debug) {
|
|
7931
|
+
console.debug("[ScaleMule] Member token refresh failed:", err);
|
|
7932
|
+
}
|
|
7933
|
+
}
|
|
7934
|
+
}, interval);
|
|
7935
|
+
return () => {
|
|
7936
|
+
cancelled = true;
|
|
7937
|
+
clearInterval(id);
|
|
7938
|
+
};
|
|
7939
|
+
}, [memberMode, memberTokenPollMs, getToken, client, baseClient, money, debug]);
|
|
7768
7940
|
const handleSetUser = useCallback(
|
|
7769
7941
|
(newUser) => {
|
|
7770
7942
|
setUser(newUser);
|
|
@@ -8246,8 +8418,8 @@ function useAuth() {
|
|
|
8246
8418
|
const getLinkedAccounts = useCallback(async () => {
|
|
8247
8419
|
setError(null);
|
|
8248
8420
|
try {
|
|
8249
|
-
const data = await client.get("/v1/auth/oauth/
|
|
8250
|
-
return data.
|
|
8421
|
+
const data = await client.get("/v1/auth/oauth/providers");
|
|
8422
|
+
return data.providers;
|
|
8251
8423
|
} catch (err) {
|
|
8252
8424
|
if (err instanceof ScaleMuleApiError) {
|
|
8253
8425
|
setError(err);
|
|
@@ -8256,41 +8428,30 @@ function useAuth() {
|
|
|
8256
8428
|
}
|
|
8257
8429
|
}, [client, setError]);
|
|
8258
8430
|
const linkAccount = useCallback(
|
|
8259
|
-
async (
|
|
8431
|
+
async (_config) => {
|
|
8260
8432
|
setError(null);
|
|
8261
8433
|
if (!user) {
|
|
8262
|
-
const
|
|
8434
|
+
const err2 = {
|
|
8263
8435
|
code: "NOT_AUTHENTICATED",
|
|
8264
8436
|
message: "Must be logged in to link accounts"
|
|
8265
8437
|
};
|
|
8266
|
-
setError(
|
|
8267
|
-
throw
|
|
8438
|
+
setError(err2);
|
|
8439
|
+
throw err2;
|
|
8268
8440
|
}
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
});
|
|
8276
|
-
} catch (err) {
|
|
8277
|
-
if (err instanceof ScaleMuleApiError) {
|
|
8278
|
-
setError(err);
|
|
8279
|
-
}
|
|
8280
|
-
throw err;
|
|
8281
|
-
}
|
|
8282
|
-
if (typeof sessionStorage !== "undefined") {
|
|
8283
|
-
sessionStorage.setItem("scalemule_oauth_state", linkData.state);
|
|
8284
|
-
}
|
|
8285
|
-
return linkData;
|
|
8441
|
+
const err = {
|
|
8442
|
+
code: "NOT_IMPLEMENTED",
|
|
8443
|
+
message: "OAuth account linking from a logged-in session is not yet supported. Use the standard OAuth sign-in flow instead."
|
|
8444
|
+
};
|
|
8445
|
+
setError(err);
|
|
8446
|
+
throw err;
|
|
8286
8447
|
},
|
|
8287
|
-
[
|
|
8448
|
+
[user, setError]
|
|
8288
8449
|
);
|
|
8289
8450
|
const unlinkAccount = useCallback(
|
|
8290
8451
|
async (provider) => {
|
|
8291
8452
|
setError(null);
|
|
8292
8453
|
try {
|
|
8293
|
-
await client.delete(`/v1/auth/oauth/
|
|
8454
|
+
await client.delete(`/v1/auth/oauth/providers/${encodeURIComponent(provider)}`);
|
|
8294
8455
|
} catch (err) {
|
|
8295
8456
|
if (err instanceof ScaleMuleApiError) {
|
|
8296
8457
|
setError(err);
|
|
@@ -9363,7 +9524,9 @@ function useMedia() {
|
|
|
9363
9524
|
async (file, options) => {
|
|
9364
9525
|
setUploading(true);
|
|
9365
9526
|
setError(null);
|
|
9366
|
-
const
|
|
9527
|
+
const visibility = options?.visibility ?? (options?.is_public === true ? "app_public" : options?.is_public === false ? "private" : "private");
|
|
9528
|
+
const isPublic = visibility !== "private";
|
|
9529
|
+
const isAnonymous = visibility === "anonymous_visible";
|
|
9367
9530
|
const mimeType = file.type || "application/octet-stream";
|
|
9368
9531
|
const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
|
|
9369
9532
|
const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
|
|
@@ -9373,7 +9536,31 @@ function useMedia() {
|
|
|
9373
9536
|
onProgress: options?.onProgress,
|
|
9374
9537
|
signal: options?.signal
|
|
9375
9538
|
};
|
|
9539
|
+
const storageAnon = storage;
|
|
9376
9540
|
try {
|
|
9541
|
+
if (isAnonymous) {
|
|
9542
|
+
const r2 = await storageAnon.uploadAnonymous(file, sharedOpts);
|
|
9543
|
+
if (r2.error || !r2.data) {
|
|
9544
|
+
throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
|
|
9545
|
+
}
|
|
9546
|
+
const f2 = r2.data;
|
|
9547
|
+
return {
|
|
9548
|
+
file_id: f2.id,
|
|
9549
|
+
photo_id: null,
|
|
9550
|
+
// For anon files, original_view_url is the same unsigned
|
|
9551
|
+
// CDN URL — no signed alternative exists. Callers should
|
|
9552
|
+
// prefer `cdn_url` (more specific) but `original_view_url`
|
|
9553
|
+
// works too for back-compat with code that already reads
|
|
9554
|
+
// that field.
|
|
9555
|
+
original_view_url: f2.cdn_url ?? null,
|
|
9556
|
+
optimized_url_promise: Promise.resolve(null),
|
|
9557
|
+
hls_url_promise: Promise.resolve(null),
|
|
9558
|
+
mime_type: mimeType,
|
|
9559
|
+
is_public: true,
|
|
9560
|
+
visibility: "anonymous_visible",
|
|
9561
|
+
cdn_url: f2.cdn_url ?? null
|
|
9562
|
+
};
|
|
9563
|
+
}
|
|
9377
9564
|
if (mimeType.startsWith("image/") && !options?.skipPhotoRegister && !isPublic) {
|
|
9378
9565
|
const r2 = await photo.uploadViaStorage(file, sharedOpts);
|
|
9379
9566
|
if (r2.error || !r2.data) {
|
|
@@ -9389,7 +9576,9 @@ function useMedia() {
|
|
|
9389
9576
|
optimized_url_promise: r2.data.optimized_url_promise,
|
|
9390
9577
|
hls_url_promise: Promise.resolve(null),
|
|
9391
9578
|
mime_type: mimeType,
|
|
9392
|
-
is_public: false
|
|
9579
|
+
is_public: false,
|
|
9580
|
+
visibility: "private",
|
|
9581
|
+
cdn_url: null
|
|
9393
9582
|
};
|
|
9394
9583
|
}
|
|
9395
9584
|
if (mimeType.startsWith("video/") && !isPublic) {
|
|
@@ -9407,7 +9596,9 @@ function useMedia() {
|
|
|
9407
9596
|
optimized_url_promise: Promise.resolve(null),
|
|
9408
9597
|
hls_url_promise: r2.data.hls_url_promise,
|
|
9409
9598
|
mime_type: mimeType,
|
|
9410
|
-
is_public: false
|
|
9599
|
+
is_public: false,
|
|
9600
|
+
visibility: "private",
|
|
9601
|
+
cdn_url: null
|
|
9411
9602
|
};
|
|
9412
9603
|
}
|
|
9413
9604
|
if (mimeType.startsWith("audio/") && !isPublic) {
|
|
@@ -9422,13 +9613,15 @@ function useMedia() {
|
|
|
9422
9613
|
optimized_url_promise: Promise.resolve(null),
|
|
9423
9614
|
hls_url_promise: Promise.resolve(null),
|
|
9424
9615
|
mime_type: mimeType,
|
|
9425
|
-
is_public: false
|
|
9616
|
+
is_public: false,
|
|
9617
|
+
visibility: "private",
|
|
9618
|
+
cdn_url: null
|
|
9426
9619
|
};
|
|
9427
9620
|
}
|
|
9428
9621
|
if (mimeType.startsWith("image/") && isPublic) {
|
|
9429
9622
|
const r2 = await storage.upload(file, {
|
|
9430
9623
|
...sharedOpts,
|
|
9431
|
-
|
|
9624
|
+
visibility: "app_public",
|
|
9432
9625
|
skipCompression: true
|
|
9433
9626
|
});
|
|
9434
9627
|
if (r2.error || !r2.data) {
|
|
@@ -9442,10 +9635,16 @@ function useMedia() {
|
|
|
9442
9635
|
optimized_url_promise: Promise.resolve(null),
|
|
9443
9636
|
hls_url_promise: Promise.resolve(null),
|
|
9444
9637
|
mime_type: mimeType,
|
|
9445
|
-
is_public: f2.is_public ?? true
|
|
9638
|
+
is_public: f2.is_public ?? true,
|
|
9639
|
+
visibility: f2.visibility ?? "app_public",
|
|
9640
|
+
cdn_url: f2.cdn_url ?? null
|
|
9446
9641
|
};
|
|
9447
9642
|
}
|
|
9448
|
-
const r = isPublic ? await storage.upload(file, {
|
|
9643
|
+
const r = isPublic ? await storage.upload(file, {
|
|
9644
|
+
...sharedOpts,
|
|
9645
|
+
visibility: "app_public",
|
|
9646
|
+
skipCompression: true
|
|
9647
|
+
}) : await storage.uploadPrivate(file, sharedOpts);
|
|
9449
9648
|
if (r.error || !r.data) {
|
|
9450
9649
|
throw r.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
|
|
9451
9650
|
}
|
|
@@ -9457,7 +9656,9 @@ function useMedia() {
|
|
|
9457
9656
|
optimized_url_promise: Promise.resolve(null),
|
|
9458
9657
|
hls_url_promise: Promise.resolve(null),
|
|
9459
9658
|
mime_type: mimeType,
|
|
9460
|
-
is_public: f.is_public ?? isPublic
|
|
9659
|
+
is_public: f.is_public ?? isPublic,
|
|
9660
|
+
visibility: f.visibility ?? (isPublic ? "app_public" : "private"),
|
|
9661
|
+
cdn_url: f.cdn_url ?? null
|
|
9461
9662
|
};
|
|
9462
9663
|
} catch (err) {
|
|
9463
9664
|
const e = err;
|
|
@@ -10900,6 +11101,75 @@ function useFeedback(options = {}) {
|
|
|
10900
11101
|
);
|
|
10901
11102
|
return { items, loading, error, submit, refresh };
|
|
10902
11103
|
}
|
|
11104
|
+
var ZERO = { value: 0, up_count: 0, down_count: 0, score: 0 };
|
|
11105
|
+
function useVote({
|
|
11106
|
+
targetType,
|
|
11107
|
+
targetId,
|
|
11108
|
+
initialState,
|
|
11109
|
+
refetchOnMount = false,
|
|
11110
|
+
enabled = true
|
|
11111
|
+
}) {
|
|
11112
|
+
const { client } = useScaleMule();
|
|
11113
|
+
const [state, setState] = useState(initialState ?? ZERO);
|
|
11114
|
+
const [isLoading, setIsLoading] = useState(!initialState && enabled);
|
|
11115
|
+
const [error, setError] = useState(null);
|
|
11116
|
+
const inFlight = useRef(null);
|
|
11117
|
+
const path = `/v1/social/${encodeURIComponent(targetType)}/${encodeURIComponent(targetId)}/vote`;
|
|
11118
|
+
const refetch = useCallback(async () => {
|
|
11119
|
+
if (!enabled) return;
|
|
11120
|
+
setIsLoading(true);
|
|
11121
|
+
setError(null);
|
|
11122
|
+
try {
|
|
11123
|
+
const data = await client.get(path);
|
|
11124
|
+
setState(data);
|
|
11125
|
+
} catch (e) {
|
|
11126
|
+
setError(e);
|
|
11127
|
+
} finally {
|
|
11128
|
+
setIsLoading(false);
|
|
11129
|
+
}
|
|
11130
|
+
}, [client, path, enabled]);
|
|
11131
|
+
useEffect(() => {
|
|
11132
|
+
if (!enabled) return;
|
|
11133
|
+
if (initialState && !refetchOnMount) return;
|
|
11134
|
+
void refetch();
|
|
11135
|
+
}, [enabled, refetch, initialState, refetchOnMount]);
|
|
11136
|
+
const cast = useCallback(
|
|
11137
|
+
async (value) => {
|
|
11138
|
+
const prev = state;
|
|
11139
|
+
const optimistic = applyVote(prev, value);
|
|
11140
|
+
setState(optimistic);
|
|
11141
|
+
setError(null);
|
|
11142
|
+
const send = async () => {
|
|
11143
|
+
try {
|
|
11144
|
+
const data = await client.put(path, { value });
|
|
11145
|
+
setState(data);
|
|
11146
|
+
} catch (e) {
|
|
11147
|
+
setState(prev);
|
|
11148
|
+
setError(e);
|
|
11149
|
+
}
|
|
11150
|
+
};
|
|
11151
|
+
const p = inFlight.current ? inFlight.current.then(send) : send();
|
|
11152
|
+
inFlight.current = p;
|
|
11153
|
+
await p;
|
|
11154
|
+
if (inFlight.current === p) inFlight.current = null;
|
|
11155
|
+
},
|
|
11156
|
+
[client, path, state]
|
|
11157
|
+
);
|
|
11158
|
+
return { state, isLoading, error, cast, refetch };
|
|
11159
|
+
}
|
|
11160
|
+
function applyVote(prev, next) {
|
|
11161
|
+
let { up_count, down_count } = prev;
|
|
11162
|
+
if (prev.value === 1) up_count = Math.max(0, up_count - 1);
|
|
11163
|
+
if (prev.value === -1) down_count = Math.max(0, down_count - 1);
|
|
11164
|
+
if (next === 1) up_count += 1;
|
|
11165
|
+
if (next === -1) down_count += 1;
|
|
11166
|
+
return {
|
|
11167
|
+
value: next,
|
|
11168
|
+
up_count,
|
|
11169
|
+
down_count,
|
|
11170
|
+
score: up_count - down_count
|
|
11171
|
+
};
|
|
11172
|
+
}
|
|
10903
11173
|
var TYPE_LABELS = {
|
|
10904
11174
|
bug_report: "Bug",
|
|
10905
11175
|
feature_request: "Feature request",
|
|
@@ -11268,449 +11538,128 @@ function FeedbackWidget(props) {
|
|
|
11268
11538
|
) })
|
|
11269
11539
|
] });
|
|
11270
11540
|
}
|
|
11271
|
-
var
|
|
11272
|
-
var
|
|
11273
|
-
var
|
|
11541
|
+
var ARROW_UP = "\u25B2";
|
|
11542
|
+
var ARROW_DOWN = "\u25BC";
|
|
11543
|
+
var STYLES2 = {
|
|
11274
11544
|
root: {
|
|
11275
|
-
|
|
11276
|
-
border: "1px solid rgba(226, 232, 240, 0.8)",
|
|
11277
|
-
background: "linear-gradient(180deg, #f8fbff 0%, #edf4ff 100%)",
|
|
11278
|
-
padding: "1rem",
|
|
11279
|
-
color: "#0f172b",
|
|
11280
|
-
boxShadow: "0 22px 70px rgba(4, 12, 24, 0.28)"
|
|
11281
|
-
},
|
|
11282
|
-
layout: {
|
|
11283
|
-
display: "flex",
|
|
11284
|
-
flexWrap: "wrap",
|
|
11545
|
+
display: "inline-flex",
|
|
11285
11546
|
alignItems: "center",
|
|
11286
|
-
gap:
|
|
11547
|
+
gap: 6
|
|
11287
11548
|
},
|
|
11288
|
-
|
|
11289
|
-
display: "flex",
|
|
11290
|
-
|
|
11291
|
-
width: "4.5rem",
|
|
11292
|
-
flexShrink: 0,
|
|
11293
|
-
alignItems: "center",
|
|
11294
|
-
justifyContent: "center",
|
|
11295
|
-
borderRadius: "9999px",
|
|
11296
|
-
border: "none",
|
|
11297
|
-
backgroundColor: "#020618",
|
|
11298
|
-
color: "#ffffff",
|
|
11299
|
-
boxShadow: "0 10px 28px rgba(15, 23, 42, 0.25)",
|
|
11300
|
-
cursor: "pointer"
|
|
11301
|
-
},
|
|
11302
|
-
playIcon: {
|
|
11303
|
-
display: "block",
|
|
11304
|
-
width: "1.75rem",
|
|
11305
|
-
height: "1.75rem",
|
|
11306
|
-
fill: "currentColor"
|
|
11307
|
-
},
|
|
11308
|
-
playIconOffset: {
|
|
11309
|
-
marginLeft: "0.25rem"
|
|
11310
|
-
},
|
|
11311
|
-
content: {
|
|
11312
|
-
minWidth: "16rem",
|
|
11313
|
-
flex: "1 1 18rem"
|
|
11314
|
-
},
|
|
11315
|
-
metaRow: {
|
|
11316
|
-
display: "flex",
|
|
11317
|
-
alignItems: "center",
|
|
11318
|
-
justifyContent: "space-between",
|
|
11319
|
-
gap: "0.75rem",
|
|
11320
|
-
fontSize: "0.8rem",
|
|
11321
|
-
fontWeight: 500,
|
|
11322
|
-
color: "#62748e"
|
|
11323
|
-
},
|
|
11324
|
-
metaRight: {
|
|
11325
|
-
display: "flex",
|
|
11549
|
+
rootStacked: {
|
|
11550
|
+
display: "inline-flex",
|
|
11551
|
+
flexDirection: "column",
|
|
11326
11552
|
alignItems: "center",
|
|
11327
|
-
gap:
|
|
11328
|
-
},
|
|
11329
|
-
badge: {
|
|
11330
|
-
borderRadius: "9999px",
|
|
11331
|
-
backgroundColor: "#dbeafe",
|
|
11332
|
-
padding: "0.25rem 0.75rem",
|
|
11333
|
-
fontSize: "0.65rem",
|
|
11334
|
-
fontWeight: 600,
|
|
11335
|
-
letterSpacing: "0.24em",
|
|
11336
|
-
textTransform: "uppercase",
|
|
11337
|
-
color: "#1447e6"
|
|
11553
|
+
gap: 2
|
|
11338
11554
|
},
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11555
|
+
button: {
|
|
11556
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
11557
|
+
background: "rgba(255,255,255,0.04)",
|
|
11558
|
+
color: "inherit",
|
|
11343
11559
|
cursor: "pointer",
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11560
|
+
borderRadius: 9999,
|
|
11561
|
+
padding: "6px 10px",
|
|
11562
|
+
lineHeight: 1,
|
|
11563
|
+
fontSize: 14
|
|
11348
11564
|
},
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
inset: 0,
|
|
11353
|
-
height: "100%",
|
|
11354
|
-
width: "100%"
|
|
11565
|
+
buttonCompact: {
|
|
11566
|
+
padding: "3px 7px",
|
|
11567
|
+
fontSize: 12
|
|
11355
11568
|
},
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
},
|
|
11361
|
-
fallbackProgress: {
|
|
11362
|
-
position: "absolute",
|
|
11363
|
-
insetBlock: 0,
|
|
11364
|
-
left: 0,
|
|
11365
|
-
backgroundColor: "#155dfc"
|
|
11366
|
-
},
|
|
11367
|
-
chipsRow: {
|
|
11368
|
-
marginTop: "1rem",
|
|
11369
|
-
display: "flex",
|
|
11370
|
-
flexWrap: "wrap",
|
|
11371
|
-
alignItems: "center",
|
|
11372
|
-
gap: "0.5rem",
|
|
11373
|
-
fontSize: "0.72rem",
|
|
11374
|
-
fontWeight: 500,
|
|
11375
|
-
color: "#62748e"
|
|
11376
|
-
},
|
|
11377
|
-
chipStrong: {
|
|
11378
|
-
borderRadius: "9999px",
|
|
11379
|
-
border: "1px solid #e2e8f0",
|
|
11380
|
-
backgroundColor: "rgba(255, 255, 255, 0.85)",
|
|
11381
|
-
padding: "0.25rem 0.75rem",
|
|
11382
|
-
color: "#334155"
|
|
11383
|
-
},
|
|
11384
|
-
chipMuted: {
|
|
11385
|
-
borderRadius: "9999px",
|
|
11386
|
-
border: "1px solid #e2e8f0",
|
|
11387
|
-
backgroundColor: "rgba(255, 255, 255, 0.65)",
|
|
11388
|
-
padding: "0.25rem 0.75rem"
|
|
11389
|
-
},
|
|
11390
|
-
controls: {
|
|
11391
|
-
display: "flex",
|
|
11392
|
-
flexShrink: 0,
|
|
11393
|
-
alignItems: "center",
|
|
11394
|
-
gap: "0.5rem",
|
|
11395
|
-
alignSelf: "flex-start"
|
|
11396
|
-
},
|
|
11397
|
-
secondaryButton: {
|
|
11398
|
-
borderRadius: "9999px",
|
|
11399
|
-
border: "1px solid #cad5e2",
|
|
11400
|
-
backgroundColor: "rgba(255, 255, 255, 0.9)",
|
|
11401
|
-
padding: "0.5rem 1rem",
|
|
11402
|
-
fontSize: "1rem",
|
|
11403
|
-
fontWeight: 600,
|
|
11404
|
-
color: "#334155",
|
|
11405
|
-
boxShadow: "0 1px 3px rgba(15, 23, 42, 0.08)",
|
|
11406
|
-
cursor: "pointer"
|
|
11569
|
+
buttonActiveUp: {
|
|
11570
|
+
background: "rgba(34,197,94,0.18)",
|
|
11571
|
+
borderColor: "rgba(34,197,94,0.45)",
|
|
11572
|
+
color: "#86efac"
|
|
11407
11573
|
},
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
padding: "0.5rem 1rem",
|
|
11413
|
-
fontSize: "0.875rem",
|
|
11414
|
-
fontWeight: 600,
|
|
11415
|
-
color: "#334155",
|
|
11416
|
-
cursor: "pointer"
|
|
11574
|
+
buttonActiveDown: {
|
|
11575
|
+
background: "rgba(239,68,68,0.18)",
|
|
11576
|
+
borderColor: "rgba(239,68,68,0.45)",
|
|
11577
|
+
color: "#fca5a5"
|
|
11417
11578
|
},
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11579
|
+
score: {
|
|
11580
|
+
minWidth: 24,
|
|
11581
|
+
textAlign: "center",
|
|
11582
|
+
fontVariantNumeric: "tabular-nums",
|
|
11583
|
+
fontWeight: 600
|
|
11421
11584
|
}
|
|
11422
11585
|
};
|
|
11423
|
-
function
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
if (currentIndex === -1) {
|
|
11435
|
-
return SPEEDS[0];
|
|
11436
|
-
}
|
|
11437
|
-
return SPEEDS[(currentIndex + 1) % SPEEDS.length];
|
|
11438
|
-
}
|
|
11439
|
-
function NarrationPlayer({
|
|
11440
|
-
audio,
|
|
11441
|
-
className,
|
|
11442
|
-
providerLabel = "ScaleMule",
|
|
11443
|
-
narrationLabel = "AI-Narrated",
|
|
11444
|
-
refreshing = false,
|
|
11445
|
-
onRefresh,
|
|
11446
|
-
showRefreshButton = false,
|
|
11447
|
-
onPlaybackError
|
|
11586
|
+
function VoteButton({
|
|
11587
|
+
targetType,
|
|
11588
|
+
targetId,
|
|
11589
|
+
initialState,
|
|
11590
|
+
layout = "horizontal",
|
|
11591
|
+
size = "regular",
|
|
11592
|
+
onSignInRequired,
|
|
11593
|
+
onError,
|
|
11594
|
+
classNames,
|
|
11595
|
+
upLabel = "Upvote",
|
|
11596
|
+
downLabel = "Downvote"
|
|
11448
11597
|
}) {
|
|
11449
|
-
const
|
|
11450
|
-
const
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
const
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
return [];
|
|
11461
|
-
}
|
|
11462
|
-
const sampled = audio.waveform_peaks.filter((_, index) => index % 3 === 0).slice(0, 220);
|
|
11463
|
-
return sampled.length > 0 ? sampled : audio.waveform_peaks.slice(0, 220);
|
|
11464
|
-
}, [audio.waveform_peaks]);
|
|
11465
|
-
useEffect(() => {
|
|
11466
|
-
const element = audioRef.current;
|
|
11467
|
-
if (!element) return;
|
|
11468
|
-
recoveredUrlRef.current = null;
|
|
11469
|
-
const resumeTime = pendingResumeTimeRef.current;
|
|
11470
|
-
setCurrentTime(resumeTime ?? 0);
|
|
11471
|
-
setPlaying(false);
|
|
11472
|
-
element.pause();
|
|
11473
|
-
try {
|
|
11474
|
-
element.currentTime = resumeTime ?? 0;
|
|
11475
|
-
} catch {
|
|
11476
|
-
element.currentTime = 0;
|
|
11477
|
-
}
|
|
11478
|
-
if (pendingResumeRef.current) {
|
|
11479
|
-
pendingResumeRef.current = false;
|
|
11480
|
-
pendingResumeTimeRef.current = null;
|
|
11481
|
-
element.play().catch(() => {
|
|
11482
|
-
setPlaying(false);
|
|
11483
|
-
onPlaybackError?.();
|
|
11484
|
-
});
|
|
11485
|
-
return;
|
|
11486
|
-
}
|
|
11487
|
-
pendingResumeTimeRef.current = null;
|
|
11488
|
-
}, [audio.url]);
|
|
11489
|
-
useEffect(() => {
|
|
11490
|
-
const element = audioRef.current;
|
|
11491
|
-
if (!element) return;
|
|
11492
|
-
element.playbackRate = speed;
|
|
11493
|
-
element.preservesPitch = true;
|
|
11494
|
-
}, [speed]);
|
|
11495
|
-
function isUrlExpiringSoon() {
|
|
11496
|
-
if (!audio.expires_at) {
|
|
11497
|
-
return false;
|
|
11498
|
-
}
|
|
11499
|
-
const expiresAtMs = Date.parse(audio.expires_at);
|
|
11500
|
-
if (!Number.isFinite(expiresAtMs)) {
|
|
11501
|
-
return false;
|
|
11502
|
-
}
|
|
11503
|
-
return expiresAtMs - Date.now() <= EXPIRY_REFRESH_WINDOW_MS;
|
|
11504
|
-
}
|
|
11505
|
-
async function refreshAndResume() {
|
|
11506
|
-
if (!onRefresh || refreshing) {
|
|
11507
|
-
return false;
|
|
11508
|
-
}
|
|
11509
|
-
if (recoveredUrlRef.current === (audio.url ?? null)) {
|
|
11510
|
-
return false;
|
|
11511
|
-
}
|
|
11512
|
-
recoveredUrlRef.current = audio.url ?? null;
|
|
11513
|
-
pendingResumeRef.current = true;
|
|
11514
|
-
pendingResumeTimeRef.current = currentTime;
|
|
11515
|
-
try {
|
|
11516
|
-
await Promise.resolve(onRefresh());
|
|
11517
|
-
return true;
|
|
11518
|
-
} catch {
|
|
11519
|
-
pendingResumeRef.current = false;
|
|
11520
|
-
pendingResumeTimeRef.current = null;
|
|
11521
|
-
onPlaybackError?.();
|
|
11522
|
-
return false;
|
|
11523
|
-
}
|
|
11524
|
-
}
|
|
11525
|
-
function togglePlayback() {
|
|
11526
|
-
const element = audioRef.current;
|
|
11527
|
-
if (!element) return;
|
|
11528
|
-
if (element.paused) {
|
|
11529
|
-
if (refreshing) {
|
|
11530
|
-
pendingResumeRef.current = true;
|
|
11531
|
-
pendingResumeTimeRef.current = currentTime;
|
|
11598
|
+
const { isAuthenticated } = useAuth();
|
|
11599
|
+
const { state, cast, error } = useVote({
|
|
11600
|
+
targetType,
|
|
11601
|
+
targetId,
|
|
11602
|
+
initialState: initialState ?? null,
|
|
11603
|
+
enabled: isAuthenticated
|
|
11604
|
+
});
|
|
11605
|
+
const handleVote = useCallback(
|
|
11606
|
+
async (next) => {
|
|
11607
|
+
if (!isAuthenticated) {
|
|
11608
|
+
onSignInRequired?.();
|
|
11532
11609
|
return;
|
|
11533
11610
|
}
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11611
|
+
const desired = state.value === next ? 0 : next;
|
|
11612
|
+
try {
|
|
11613
|
+
await cast(desired);
|
|
11614
|
+
} catch (e) {
|
|
11615
|
+
onError?.(e);
|
|
11537
11616
|
}
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11617
|
+
},
|
|
11618
|
+
[cast, isAuthenticated, onSignInRequired, onError, state.value]
|
|
11619
|
+
);
|
|
11620
|
+
useEffect(() => {
|
|
11621
|
+
if (error) onError?.(error);
|
|
11622
|
+
}, [error, onError]);
|
|
11623
|
+
const compactStyle = size === "compact" ? STYLES2.buttonCompact : {};
|
|
11624
|
+
const upActive = state.value === 1;
|
|
11625
|
+
const downActive = state.value === -1;
|
|
11626
|
+
const upBtn = /* @__PURE__ */ jsx(
|
|
11627
|
+
"button",
|
|
11628
|
+
{
|
|
11629
|
+
type: "button",
|
|
11630
|
+
"aria-label": upLabel,
|
|
11631
|
+
"aria-pressed": upActive,
|
|
11632
|
+
title: isAuthenticated ? upLabel : "Sign in to vote",
|
|
11633
|
+
className: [classNames?.upButton, upActive ? classNames?.upButtonActive : void 0].filter(Boolean).join(" ") || void 0,
|
|
11634
|
+
onClick: () => void handleVote(1),
|
|
11635
|
+
style: classNames?.upButton ? void 0 : { ...STYLES2.button, ...compactStyle, ...upActive ? STYLES2.buttonActiveUp : {} },
|
|
11636
|
+
children: ARROW_UP
|
|
11543
11637
|
}
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
}
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
src: audio.url ?? void 0,
|
|
11570
|
-
preload: "metadata",
|
|
11571
|
-
onLoadedMetadata: (event) => {
|
|
11572
|
-
setDuration(event.currentTarget.duration || 0);
|
|
11573
|
-
},
|
|
11574
|
-
onTimeUpdate: (event) => {
|
|
11575
|
-
setCurrentTime(event.currentTarget.currentTime);
|
|
11576
|
-
},
|
|
11577
|
-
onPlay: () => setPlaying(true),
|
|
11578
|
-
onPause: () => setPlaying(false),
|
|
11579
|
-
onEnded: () => setPlaying(false),
|
|
11580
|
-
onError: () => {
|
|
11581
|
-
if (onRefresh) {
|
|
11582
|
-
void refreshAndResume().then((didRefresh) => {
|
|
11583
|
-
if (!didRefresh) {
|
|
11584
|
-
onPlaybackError?.();
|
|
11585
|
-
}
|
|
11586
|
-
});
|
|
11587
|
-
return;
|
|
11588
|
-
}
|
|
11589
|
-
onPlaybackError?.();
|
|
11590
|
-
}
|
|
11591
|
-
}
|
|
11592
|
-
),
|
|
11593
|
-
/* @__PURE__ */ jsxs("div", { style: styles.layout, children: [
|
|
11594
|
-
/* @__PURE__ */ jsx(
|
|
11595
|
-
"button",
|
|
11596
|
-
{
|
|
11597
|
-
type: "button",
|
|
11598
|
-
onClick: togglePlayback,
|
|
11599
|
-
"aria-label": playing ? "Pause narration" : "Play narration",
|
|
11600
|
-
style: styles.playButton,
|
|
11601
|
-
children: playing ? /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", style: styles.playIcon, "aria-hidden": "true", children: [
|
|
11602
|
-
/* @__PURE__ */ jsx("rect", { x: "6", y: "5", width: "4", height: "14", rx: "1.2" }),
|
|
11603
|
-
/* @__PURE__ */ jsx("rect", { x: "14", y: "5", width: "4", height: "14", rx: "1.2" })
|
|
11604
|
-
] }) : /* @__PURE__ */ jsx(
|
|
11605
|
-
"svg",
|
|
11606
|
-
{
|
|
11607
|
-
viewBox: "0 0 24 24",
|
|
11608
|
-
style: { ...styles.playIcon, ...styles.playIconOffset },
|
|
11609
|
-
"aria-hidden": "true",
|
|
11610
|
-
children: /* @__PURE__ */ jsx("path", { d: "M7 5.2c0-1.02 1.12-1.65 2-.98l10.2 7.78a1.24 1.24 0 0 1 0 1.98L9 21.76c-.88.67-2-.04-2-1.08V5.2Z" })
|
|
11611
|
-
}
|
|
11612
|
-
)
|
|
11613
|
-
}
|
|
11614
|
-
),
|
|
11615
|
-
/* @__PURE__ */ jsxs("div", { style: styles.content, children: [
|
|
11616
|
-
/* @__PURE__ */ jsxs("div", { style: styles.metaRow, children: [
|
|
11617
|
-
/* @__PURE__ */ jsx("span", { children: formatTime(currentTime / speed) }),
|
|
11618
|
-
/* @__PURE__ */ jsxs("div", { style: styles.metaRight, children: [
|
|
11619
|
-
/* @__PURE__ */ jsx("span", { style: styles.badge, children: narrationLabel }),
|
|
11620
|
-
/* @__PURE__ */ jsx("span", { children: formatTime(duration / speed) })
|
|
11621
|
-
] })
|
|
11622
|
-
] }),
|
|
11623
|
-
/* @__PURE__ */ jsx(
|
|
11624
|
-
"div",
|
|
11625
|
-
{
|
|
11626
|
-
ref: scrubRef,
|
|
11627
|
-
role: "slider",
|
|
11628
|
-
"aria-label": "Narration progress",
|
|
11629
|
-
"aria-valuemin": 0,
|
|
11630
|
-
"aria-valuemax": Math.max(1, Math.floor(duration)),
|
|
11631
|
-
"aria-valuenow": Math.floor(currentTime),
|
|
11632
|
-
tabIndex: 0,
|
|
11633
|
-
onClick: (event) => {
|
|
11634
|
-
seekTo(event.clientX);
|
|
11635
|
-
},
|
|
11636
|
-
style: styles.scrubber,
|
|
11637
|
-
children: peaks.length > 0 ? /* @__PURE__ */ jsx(
|
|
11638
|
-
"svg",
|
|
11639
|
-
{
|
|
11640
|
-
viewBox: `0 0 ${peaks.length} 40`,
|
|
11641
|
-
preserveAspectRatio: "none",
|
|
11642
|
-
style: styles.waveform,
|
|
11643
|
-
children: peaks.map((peak, index) => {
|
|
11644
|
-
const height = Math.max(2, Math.round(Math.abs(peak) * 34));
|
|
11645
|
-
const y = (40 - height) / 2;
|
|
11646
|
-
const filled = index / peaks.length < progress;
|
|
11647
|
-
return /* @__PURE__ */ jsx(
|
|
11648
|
-
"rect",
|
|
11649
|
-
{
|
|
11650
|
-
x: index,
|
|
11651
|
-
y,
|
|
11652
|
-
width: 0.82,
|
|
11653
|
-
height,
|
|
11654
|
-
rx: 0.4,
|
|
11655
|
-
fill: filled ? "#155dfc" : "#90c5ff"
|
|
11656
|
-
},
|
|
11657
|
-
`${audio.id}-${index}`
|
|
11658
|
-
);
|
|
11659
|
-
})
|
|
11660
|
-
}
|
|
11661
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11662
|
-
/* @__PURE__ */ jsx("div", { style: styles.fallbackTrack }),
|
|
11663
|
-
/* @__PURE__ */ jsx(
|
|
11664
|
-
"div",
|
|
11665
|
-
{
|
|
11666
|
-
style: { ...styles.fallbackProgress, width: `${progress * 100}%` }
|
|
11667
|
-
}
|
|
11668
|
-
)
|
|
11669
|
-
] })
|
|
11670
|
-
}
|
|
11671
|
-
),
|
|
11672
|
-
/* @__PURE__ */ jsxs("div", { style: styles.chipsRow, children: [
|
|
11673
|
-
/* @__PURE__ */ jsxs("span", { style: styles.chipStrong, children: [
|
|
11674
|
-
"Provider: ",
|
|
11675
|
-
providerLabel
|
|
11676
|
-
] }),
|
|
11677
|
-
duration > 0 && /* @__PURE__ */ jsxs("span", { style: styles.chipMuted, children: [
|
|
11678
|
-
"Length ",
|
|
11679
|
-
formatTime(duration / speed)
|
|
11680
|
-
] }),
|
|
11681
|
-
expiresLabel && /* @__PURE__ */ jsxs("span", { style: styles.chipMuted, children: [
|
|
11682
|
-
"Expires ",
|
|
11683
|
-
expiresLabel
|
|
11684
|
-
] })
|
|
11685
|
-
] })
|
|
11686
|
-
] }),
|
|
11687
|
-
/* @__PURE__ */ jsxs("div", { style: styles.controls, children: [
|
|
11688
|
-
/* @__PURE__ */ jsxs(
|
|
11689
|
-
"button",
|
|
11690
|
-
{
|
|
11691
|
-
type: "button",
|
|
11692
|
-
onClick: () => {
|
|
11693
|
-
setSpeed((currentSpeed) => getNextSpeed(currentSpeed));
|
|
11694
|
-
},
|
|
11695
|
-
style: styles.secondaryButton,
|
|
11696
|
-
children: [
|
|
11697
|
-
speed,
|
|
11698
|
-
"\xD7"
|
|
11699
|
-
]
|
|
11700
|
-
}
|
|
11701
|
-
),
|
|
11702
|
-
onRefresh && showRefreshButton && /* @__PURE__ */ jsx(
|
|
11703
|
-
"button",
|
|
11704
|
-
{
|
|
11705
|
-
type: "button",
|
|
11706
|
-
onClick: onRefresh,
|
|
11707
|
-
disabled: refreshing,
|
|
11708
|
-
style: refreshing ? { ...styles.refreshButton, ...styles.disabled } : styles.refreshButton,
|
|
11709
|
-
children: refreshing ? "Refreshing\u2026" : "Refresh"
|
|
11710
|
-
}
|
|
11711
|
-
)
|
|
11712
|
-
] })
|
|
11713
|
-
] })
|
|
11638
|
+
);
|
|
11639
|
+
const downBtn = /* @__PURE__ */ jsx(
|
|
11640
|
+
"button",
|
|
11641
|
+
{
|
|
11642
|
+
type: "button",
|
|
11643
|
+
"aria-label": downLabel,
|
|
11644
|
+
"aria-pressed": downActive,
|
|
11645
|
+
title: isAuthenticated ? downLabel : "Sign in to vote",
|
|
11646
|
+
className: [classNames?.downButton, downActive ? classNames?.downButtonActive : void 0].filter(Boolean).join(" ") || void 0,
|
|
11647
|
+
onClick: () => void handleVote(-1),
|
|
11648
|
+
style: classNames?.downButton ? void 0 : { ...STYLES2.button, ...compactStyle, ...downActive ? STYLES2.buttonActiveDown : {} },
|
|
11649
|
+
children: ARROW_DOWN
|
|
11650
|
+
}
|
|
11651
|
+
);
|
|
11652
|
+
const scoreClass = [
|
|
11653
|
+
classNames?.score,
|
|
11654
|
+
state.score > 0 ? classNames?.scorePositive : void 0,
|
|
11655
|
+
state.score < 0 ? classNames?.scoreNegative : void 0
|
|
11656
|
+
].filter(Boolean).join(" ") || void 0;
|
|
11657
|
+
const scoreEl = /* @__PURE__ */ jsx("span", { className: scoreClass, style: classNames?.score ? void 0 : STYLES2.score, children: state.score });
|
|
11658
|
+
const rootStyle = layout === "stacked" ? STYLES2.rootStacked : STYLES2.root;
|
|
11659
|
+
return /* @__PURE__ */ jsxs("div", { className: classNames?.root, style: classNames?.root ? void 0 : rootStyle, children: [
|
|
11660
|
+
upBtn,
|
|
11661
|
+
scoreEl,
|
|
11662
|
+
downBtn
|
|
11714
11663
|
] });
|
|
11715
11664
|
}
|
|
11716
11665
|
|
|
@@ -12005,4 +11954,4 @@ function createSafeLogger(prefix) {
|
|
|
12005
11954
|
};
|
|
12006
11955
|
}
|
|
12007
11956
|
|
|
12008
|
-
export { FeedbackWidget,
|
|
11957
|
+
export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleMedia, ScaleMuleProvider, VoteButton, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAudio, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useTtsJob, useUser, useVote, validateForm, validators };
|