@scalemule/nextjs 0.1.23 → 0.1.26
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 +94 -19
- package/dist/index.d.ts +94 -19
- package/dist/index.js +395 -484
- package/dist/index.mjs +394 -484
- 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 +1 -1
package/dist/index.js
CHANGED
|
@@ -580,6 +580,7 @@ var ScaleMuleClient = class {
|
|
|
580
580
|
this.anonymousId = null;
|
|
581
581
|
this.sessionPool = /* @__PURE__ */ new Map();
|
|
582
582
|
this.knownAccounts = /* @__PURE__ */ new Map();
|
|
583
|
+
this.refreshPromise = null;
|
|
583
584
|
this.apiKey = config.apiKey;
|
|
584
585
|
this.applicationId = config.applicationId || null;
|
|
585
586
|
this.baseUrl = config.baseUrl || GATEWAY_URLS[config.environment || "prod"];
|
|
@@ -598,6 +599,9 @@ var ScaleMuleClient = class {
|
|
|
598
599
|
this.multiSessionEnabled = config.enableMultiSession || false;
|
|
599
600
|
this.accountSwitcherEnabled = config.enableAccountSwitcher || false;
|
|
600
601
|
this.accountSwitcherPrivacy = config.accountSwitcherPrivacy || "full";
|
|
602
|
+
this.onRefreshStart = config.onRefreshStart;
|
|
603
|
+
this.onRefreshEnd = config.onRefreshEnd;
|
|
604
|
+
this.onAutoRefreshFailed = config.onAutoRefreshFailed;
|
|
601
605
|
}
|
|
602
606
|
// --------------------------------------------------------------------------
|
|
603
607
|
// Session Management
|
|
@@ -883,34 +887,28 @@ var ScaleMuleClient = class {
|
|
|
883
887
|
const method = (init.method || "GET").toUpperCase();
|
|
884
888
|
const timeout = init.timeout || this.defaultTimeout;
|
|
885
889
|
const maxRetries = init.skipRetry ? 0 : init.retries ?? this.maxRetries;
|
|
886
|
-
const
|
|
887
|
-
"x-api-key": this.apiKey,
|
|
888
|
-
"User-Agent": `ScaleMule-SDK-TypeScript/${SDK_VERSION}`,
|
|
889
|
-
...init.headers
|
|
890
|
-
};
|
|
891
|
-
if (!init.skipAuth && this.sessionToken) {
|
|
892
|
-
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
893
|
-
}
|
|
894
|
-
if (this.workspaceId) {
|
|
895
|
-
headers["x-sm-workspace-id"] = this.workspaceId;
|
|
896
|
-
}
|
|
897
|
-
if (!this.sessionToken && this.anonymousId) {
|
|
898
|
-
headers["x-anonymous-id"] = this.anonymousId;
|
|
899
|
-
}
|
|
900
|
-
let bodyStr;
|
|
901
|
-
if (init.body !== void 0 && init.body !== null) {
|
|
902
|
-
bodyStr = typeof init.body === "string" ? init.body : JSON.stringify(init.body);
|
|
903
|
-
if (!headers["Content-Type"]) {
|
|
904
|
-
headers["Content-Type"] = "application/json";
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
if (this.debug) {
|
|
908
|
-
console.log(`[ScaleMule] ${method} ${path}`);
|
|
909
|
-
}
|
|
910
|
-
const idempotencyKey = method === "POST" ? generateIdempotencyKey() : void 0;
|
|
890
|
+
const bodyStr = init.body ? JSON.stringify(init.body) : void 0;
|
|
911
891
|
let lastError = null;
|
|
892
|
+
let idempotencyKey = null;
|
|
912
893
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
913
|
-
|
|
894
|
+
const headers = {
|
|
895
|
+
"x-api-key": this.apiKey,
|
|
896
|
+
"User-Agent": `ScaleMule-SDK-TypeScript/${SDK_VERSION}`,
|
|
897
|
+
...init.headers
|
|
898
|
+
};
|
|
899
|
+
if (!init.skipAuth && this.sessionToken) {
|
|
900
|
+
headers["Authorization"] = `Bearer ${this.sessionToken}`;
|
|
901
|
+
}
|
|
902
|
+
if (this.workspaceId) {
|
|
903
|
+
headers["x-sm-workspace-id"] = this.workspaceId;
|
|
904
|
+
}
|
|
905
|
+
if (!this.sessionToken && this.anonymousId) {
|
|
906
|
+
headers["x-anonymous-id"] = this.anonymousId;
|
|
907
|
+
}
|
|
908
|
+
if (attempt > 0 && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
909
|
+
if (!idempotencyKey) {
|
|
910
|
+
idempotencyKey = generateIdempotencyKey();
|
|
911
|
+
}
|
|
914
912
|
headers["x-idempotency-key"] = idempotencyKey;
|
|
915
913
|
}
|
|
916
914
|
const controller = new AbortController();
|
|
@@ -930,6 +928,10 @@ var ScaleMuleClient = class {
|
|
|
930
928
|
signal: controller.signal
|
|
931
929
|
});
|
|
932
930
|
clearTimeout(timeoutId);
|
|
931
|
+
const rotatedToken = response.headers.get("x-rotated-session-token");
|
|
932
|
+
if (rotatedToken && this.sessionToken && this.userId) {
|
|
933
|
+
this.setSession(rotatedToken, this.userId);
|
|
934
|
+
}
|
|
933
935
|
if (this.rateLimitQueue) {
|
|
934
936
|
this.rateLimitQueue.updateFromHeaders(response.headers);
|
|
935
937
|
}
|
|
@@ -952,6 +954,45 @@ var ScaleMuleClient = class {
|
|
|
952
954
|
status: response.status,
|
|
953
955
|
details: responseData?.error?.details || responseData?.details
|
|
954
956
|
};
|
|
957
|
+
if (response.status === 401 && this.sessionToken && !init.isAutoRefresh) {
|
|
958
|
+
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
959
|
+
let isPrimaryRefresh = false;
|
|
960
|
+
if (!this.refreshPromise) {
|
|
961
|
+
isPrimaryRefresh = true;
|
|
962
|
+
this.onRefreshStart?.();
|
|
963
|
+
this.refreshPromise = this.auth?.refreshSession ? this.auth.refreshSession() : this.post("/auth/refresh", {}, { isAutoRefresh: true });
|
|
964
|
+
}
|
|
965
|
+
const currentRefreshPromise = this.refreshPromise;
|
|
966
|
+
try {
|
|
967
|
+
const refreshResult = await currentRefreshPromise;
|
|
968
|
+
if (!refreshResult || refreshResult.error) {
|
|
969
|
+
if (this.debug) console.log("[ScaleMule] Auto-refresh failed:", refreshResult?.error);
|
|
970
|
+
const apiError = refreshResult?.error || {
|
|
971
|
+
code: "refresh_failed",
|
|
972
|
+
message: "Auto-refresh failed",
|
|
973
|
+
status: 400
|
|
974
|
+
};
|
|
975
|
+
init.onAutoRefreshFailed?.(apiError);
|
|
976
|
+
this.onAutoRefreshFailed?.(apiError);
|
|
977
|
+
return { data: null, error };
|
|
978
|
+
}
|
|
979
|
+
if (this.debug) console.log("[ScaleMule] Auto-refresh succeeded, retrying original request...");
|
|
980
|
+
const newToken = refreshResult.data?.session_token || refreshResult.data?.access_token;
|
|
981
|
+
if (newToken) {
|
|
982
|
+
this.sessionToken = newToken;
|
|
983
|
+
if (this.userId) {
|
|
984
|
+
await this.storage.setItem(SESSION_STORAGE_KEY, newToken);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
attempt--;
|
|
988
|
+
continue;
|
|
989
|
+
} finally {
|
|
990
|
+
if (isPrimaryRefresh && this.refreshPromise === currentRefreshPromise) {
|
|
991
|
+
this.refreshPromise = null;
|
|
992
|
+
this.onRefreshEnd?.();
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
955
996
|
if (response.status === 429) {
|
|
956
997
|
const retryAfter = response.headers.get("Retry-After");
|
|
957
998
|
if (retryAfter) {
|
|
@@ -5735,7 +5776,7 @@ var AudioService = class extends ServiceModule {
|
|
|
5735
5776
|
constructor(client, storage) {
|
|
5736
5777
|
super(client);
|
|
5737
5778
|
this.storage = storage;
|
|
5738
|
-
this.basePath = "/v1/
|
|
5779
|
+
this.basePath = "/v1/audio";
|
|
5739
5780
|
}
|
|
5740
5781
|
/**
|
|
5741
5782
|
* Register a storage-uploaded audio asset with the audio service.
|
|
@@ -5792,6 +5833,32 @@ var AudioService = class extends ServiceModule {
|
|
|
5792
5833
|
};
|
|
5793
5834
|
}
|
|
5794
5835
|
};
|
|
5836
|
+
var TtsService = class extends ServiceModule {
|
|
5837
|
+
constructor() {
|
|
5838
|
+
super(...arguments);
|
|
5839
|
+
this.basePath = "/v1/tts";
|
|
5840
|
+
}
|
|
5841
|
+
async synthesize(params, options) {
|
|
5842
|
+
return this.post(
|
|
5843
|
+
"/synthesize",
|
|
5844
|
+
{
|
|
5845
|
+
text: params.text,
|
|
5846
|
+
voice: params.voice,
|
|
5847
|
+
model: params.model,
|
|
5848
|
+
provider: params.provider,
|
|
5849
|
+
async: params.async,
|
|
5850
|
+
access_mode: params.accessMode
|
|
5851
|
+
},
|
|
5852
|
+
options
|
|
5853
|
+
);
|
|
5854
|
+
}
|
|
5855
|
+
async getJob(id, options) {
|
|
5856
|
+
return this._get(`/jobs/${id}`, options);
|
|
5857
|
+
}
|
|
5858
|
+
async listVoices(params, options) {
|
|
5859
|
+
return this._get(this.withQuery("/voices", { provider: params?.provider }), options);
|
|
5860
|
+
}
|
|
5861
|
+
};
|
|
5795
5862
|
var DeadLetterApi = class extends ServiceModule {
|
|
5796
5863
|
constructor() {
|
|
5797
5864
|
super(...arguments);
|
|
@@ -6612,6 +6679,7 @@ var ScaleMule = class {
|
|
|
6612
6679
|
this.functions = new FunctionsService(this._client);
|
|
6613
6680
|
this.photo = new PhotoService(this._client, this.storage);
|
|
6614
6681
|
this.audio = new AudioService(this._client, this.storage);
|
|
6682
|
+
this.tts = new TtsService(this._client);
|
|
6615
6683
|
this.flagContent = new FlagContentService(this._client);
|
|
6616
6684
|
this.creatorMaker = new CreatorMakerService(this._client);
|
|
6617
6685
|
this.compliance = new ComplianceService(this._client);
|
|
@@ -7178,6 +7246,24 @@ var ScaleMuleClient2 = class {
|
|
|
7178
7246
|
console.log("[ScaleMule] Session set for user:", userId);
|
|
7179
7247
|
}
|
|
7180
7248
|
}
|
|
7249
|
+
/**
|
|
7250
|
+
* Token-only setter for member-auth surfaces.
|
|
7251
|
+
*
|
|
7252
|
+
* Unlike `setSession(token, userId)`, this does NOT persist a userId or
|
|
7253
|
+
* write to local storage — useful when the host platform owns identity
|
|
7254
|
+
* (e.g. ScaleMule's member dashboards use `MemberAuthProvider` and the
|
|
7255
|
+
* token comes from a `${env}_member_access_token` cookie). The token is
|
|
7256
|
+
* applied in-memory and used as a `Bearer` Authorization header on
|
|
7257
|
+
* subsequent requests.
|
|
7258
|
+
*
|
|
7259
|
+
* Pass `null` to clear without touching userId/storage.
|
|
7260
|
+
*/
|
|
7261
|
+
setSessionToken(token) {
|
|
7262
|
+
this.sessionToken = token;
|
|
7263
|
+
if (this.debug) {
|
|
7264
|
+
console.log("[ScaleMule] Session token", token ? "set (token-only)" : "cleared (token-only)");
|
|
7265
|
+
}
|
|
7266
|
+
}
|
|
7181
7267
|
/**
|
|
7182
7268
|
* Clear session on logout
|
|
7183
7269
|
*/
|
|
@@ -7242,6 +7328,8 @@ var ScaleMuleClient2 = class {
|
|
|
7242
7328
|
console.log(`[ScaleMule] ${options.method || "GET"} ${path}`);
|
|
7243
7329
|
}
|
|
7244
7330
|
let lastError = null;
|
|
7331
|
+
const MAX_REFRESH_ATTEMPTS = 1;
|
|
7332
|
+
let refreshAttempts = 0;
|
|
7245
7333
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
7246
7334
|
const controller = new AbortController();
|
|
7247
7335
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
@@ -7265,7 +7353,8 @@ var ScaleMuleClient2 = class {
|
|
|
7265
7353
|
if (!response.ok) {
|
|
7266
7354
|
const rawError = responseData?.error;
|
|
7267
7355
|
const error = rawError && typeof rawError === "object" ? rawError : { code: `HTTP_${response.status}`, message: typeof rawError === "string" ? rawError : responseData?.message || text || response.statusText };
|
|
7268
|
-
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh) {
|
|
7356
|
+
if (response.status === 401 && this.sessionToken && !options.isAutoRefresh && refreshAttempts < MAX_REFRESH_ATTEMPTS) {
|
|
7357
|
+
refreshAttempts++;
|
|
7269
7358
|
if (this.debug) console.log("[ScaleMule] 401 received, attempting auto-refresh...");
|
|
7270
7359
|
if (!this.refreshPromise) {
|
|
7271
7360
|
this.onRefreshStart?.();
|
|
@@ -7634,8 +7723,12 @@ function ScaleMuleProvider({
|
|
|
7634
7723
|
onLogout,
|
|
7635
7724
|
onAuthError,
|
|
7636
7725
|
bootstrapFlags,
|
|
7637
|
-
mediaPolicy
|
|
7726
|
+
mediaPolicy,
|
|
7727
|
+
getToken,
|
|
7728
|
+
userResolver,
|
|
7729
|
+
memberTokenPollMs
|
|
7638
7730
|
}) {
|
|
7731
|
+
const memberMode = typeof getToken === "function";
|
|
7639
7732
|
const [user, setUser] = React.useState(null);
|
|
7640
7733
|
const [initializing, setInitializing] = React.useState(true);
|
|
7641
7734
|
const [error, setError] = React.useState(null);
|
|
@@ -7652,9 +7745,13 @@ function ScaleMuleProvider({
|
|
|
7652
7745
|
gatewayUrl: resolvedGatewayUrl,
|
|
7653
7746
|
debug,
|
|
7654
7747
|
storage,
|
|
7655
|
-
|
|
7748
|
+
// Make outbound API calls wait for the first token to land in any
|
|
7749
|
+
// mode where the provider is responsible for populating the
|
|
7750
|
+
// session asynchronously: auth-proxy fetch, or member-mode
|
|
7751
|
+
// getToken() callback. Resolved in the init effect below.
|
|
7752
|
+
pendingSessionInit: !!authProxyUrl || memberMode
|
|
7656
7753
|
}),
|
|
7657
|
-
[apiKey, applicationId, environment, resolvedGatewayUrl, debug, storage, authProxyUrl]
|
|
7754
|
+
[apiKey, applicationId, environment, resolvedGatewayUrl, debug, storage, authProxyUrl, memberMode]
|
|
7658
7755
|
);
|
|
7659
7756
|
const money$1 = React.useMemo(
|
|
7660
7757
|
() => money.createMoneyClient({
|
|
@@ -7676,8 +7773,10 @@ function ScaleMuleProvider({
|
|
|
7676
7773
|
});
|
|
7677
7774
|
}, [apiKey, applicationId, environment, resolvedGatewayUrl, debug]);
|
|
7678
7775
|
const [fetchedPolicy, setFetchedPolicy] = React.useState(void 0);
|
|
7776
|
+
const [tokenVersion, setTokenVersion] = React.useState(0);
|
|
7679
7777
|
React.useEffect(() => {
|
|
7680
7778
|
if (mediaPolicy) return;
|
|
7779
|
+
if (memberMode && tokenVersion === 0) return;
|
|
7681
7780
|
let mounted = true;
|
|
7682
7781
|
void (async () => {
|
|
7683
7782
|
try {
|
|
@@ -7695,7 +7794,7 @@ function ScaleMuleProvider({
|
|
|
7695
7794
|
return () => {
|
|
7696
7795
|
mounted = false;
|
|
7697
7796
|
};
|
|
7698
|
-
}, [baseClient, mediaPolicy]);
|
|
7797
|
+
}, [baseClient, mediaPolicy, memberMode, tokenVersion]);
|
|
7699
7798
|
const effectiveMediaPolicy = mediaPolicy ?? fetchedPolicy;
|
|
7700
7799
|
React.useEffect(() => {
|
|
7701
7800
|
const token = client.getSessionToken();
|
|
@@ -7713,6 +7812,43 @@ function ScaleMuleProvider({
|
|
|
7713
7812
|
try {
|
|
7714
7813
|
await client.initialize();
|
|
7715
7814
|
const cachedUser = getCachedUser();
|
|
7815
|
+
if (memberMode) {
|
|
7816
|
+
try {
|
|
7817
|
+
const token = await Promise.resolve(getToken()) ?? null;
|
|
7818
|
+
if (mounted) {
|
|
7819
|
+
if (token) {
|
|
7820
|
+
client.setSessionToken(token);
|
|
7821
|
+
baseClient.setAccessToken(token);
|
|
7822
|
+
money$1.setAccessToken(token);
|
|
7823
|
+
} else {
|
|
7824
|
+
client.setSessionToken(null);
|
|
7825
|
+
baseClient.clearAccessToken();
|
|
7826
|
+
money$1.setAccessToken(void 0);
|
|
7827
|
+
}
|
|
7828
|
+
setTokenVersion((v) => v + 1);
|
|
7829
|
+
}
|
|
7830
|
+
if (userResolver) {
|
|
7831
|
+
try {
|
|
7832
|
+
const resolvedUser = await userResolver();
|
|
7833
|
+
if (mounted) {
|
|
7834
|
+
setUser(resolvedUser);
|
|
7835
|
+
setCachedUser(resolvedUser);
|
|
7836
|
+
}
|
|
7837
|
+
} catch (resolveErr) {
|
|
7838
|
+
if (mounted && debug) {
|
|
7839
|
+
console.debug("[ScaleMule] userResolver() failed:", resolveErr);
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
} catch (memberErr) {
|
|
7844
|
+
if (mounted && debug) {
|
|
7845
|
+
console.debug("[ScaleMule] getToken() failed:", memberErr);
|
|
7846
|
+
}
|
|
7847
|
+
} finally {
|
|
7848
|
+
client.resolveSessionPending();
|
|
7849
|
+
}
|
|
7850
|
+
return;
|
|
7851
|
+
}
|
|
7716
7852
|
if (authProxyUrl) {
|
|
7717
7853
|
if (cachedUser && mounted) {
|
|
7718
7854
|
setUser(cachedUser);
|
|
@@ -7784,7 +7920,43 @@ function ScaleMuleProvider({
|
|
|
7784
7920
|
return () => {
|
|
7785
7921
|
mounted = false;
|
|
7786
7922
|
};
|
|
7787
|
-
}, [client, debug, onAuthError, authProxyUrl]);
|
|
7923
|
+
}, [client, baseClient, money$1, debug, onAuthError, authProxyUrl, memberMode, getToken, userResolver]);
|
|
7924
|
+
React.useEffect(() => {
|
|
7925
|
+
if (!memberMode) return;
|
|
7926
|
+
if (memberTokenPollMs === null) return;
|
|
7927
|
+
const interval = memberTokenPollMs ?? 6e4;
|
|
7928
|
+
if (interval <= 0) return;
|
|
7929
|
+
let cancelled = false;
|
|
7930
|
+
const id = setInterval(async () => {
|
|
7931
|
+
try {
|
|
7932
|
+
const token = await Promise.resolve(getToken()) ?? null;
|
|
7933
|
+
if (cancelled) return;
|
|
7934
|
+
const current = client.getSessionToken();
|
|
7935
|
+
if (token === current) return;
|
|
7936
|
+
if (token) {
|
|
7937
|
+
client.setSessionToken(token);
|
|
7938
|
+
baseClient.setAccessToken(token);
|
|
7939
|
+
money$1.setAccessToken(token);
|
|
7940
|
+
} else {
|
|
7941
|
+
client.setSessionToken(null);
|
|
7942
|
+
baseClient.clearAccessToken();
|
|
7943
|
+
money$1.setAccessToken(void 0);
|
|
7944
|
+
if (debug) {
|
|
7945
|
+
console.debug("[ScaleMule] Member token cleared on refresh");
|
|
7946
|
+
}
|
|
7947
|
+
}
|
|
7948
|
+
setTokenVersion((v) => v + 1);
|
|
7949
|
+
} catch (err) {
|
|
7950
|
+
if (debug) {
|
|
7951
|
+
console.debug("[ScaleMule] Member token refresh failed:", err);
|
|
7952
|
+
}
|
|
7953
|
+
}
|
|
7954
|
+
}, interval);
|
|
7955
|
+
return () => {
|
|
7956
|
+
cancelled = true;
|
|
7957
|
+
clearInterval(id);
|
|
7958
|
+
};
|
|
7959
|
+
}, [memberMode, memberTokenPollMs, getToken, client, baseClient, money$1, debug]);
|
|
7788
7960
|
const handleSetUser = React.useCallback(
|
|
7789
7961
|
(newUser) => {
|
|
7790
7962
|
setUser(newUser);
|
|
@@ -8266,8 +8438,8 @@ function useAuth() {
|
|
|
8266
8438
|
const getLinkedAccounts = React.useCallback(async () => {
|
|
8267
8439
|
setError(null);
|
|
8268
8440
|
try {
|
|
8269
|
-
const data = await client.get("/v1/auth/oauth/
|
|
8270
|
-
return data.
|
|
8441
|
+
const data = await client.get("/v1/auth/oauth/providers");
|
|
8442
|
+
return data.providers;
|
|
8271
8443
|
} catch (err) {
|
|
8272
8444
|
if (err instanceof ScaleMuleApiError) {
|
|
8273
8445
|
setError(err);
|
|
@@ -8276,41 +8448,30 @@ function useAuth() {
|
|
|
8276
8448
|
}
|
|
8277
8449
|
}, [client, setError]);
|
|
8278
8450
|
const linkAccount = React.useCallback(
|
|
8279
|
-
async (
|
|
8451
|
+
async (_config) => {
|
|
8280
8452
|
setError(null);
|
|
8281
8453
|
if (!user) {
|
|
8282
|
-
const
|
|
8454
|
+
const err2 = {
|
|
8283
8455
|
code: "NOT_AUTHENTICATED",
|
|
8284
8456
|
message: "Must be logged in to link accounts"
|
|
8285
8457
|
};
|
|
8286
|
-
setError(
|
|
8287
|
-
throw
|
|
8288
|
-
}
|
|
8289
|
-
let linkData;
|
|
8290
|
-
try {
|
|
8291
|
-
linkData = await client.post("/v1/auth/oauth/link", {
|
|
8292
|
-
provider: config.provider,
|
|
8293
|
-
redirect_url: config.redirectUrl,
|
|
8294
|
-
scopes: config.scopes
|
|
8295
|
-
});
|
|
8296
|
-
} catch (err) {
|
|
8297
|
-
if (err instanceof ScaleMuleApiError) {
|
|
8298
|
-
setError(err);
|
|
8299
|
-
}
|
|
8300
|
-
throw err;
|
|
8301
|
-
}
|
|
8302
|
-
if (typeof sessionStorage !== "undefined") {
|
|
8303
|
-
sessionStorage.setItem("scalemule_oauth_state", linkData.state);
|
|
8458
|
+
setError(err2);
|
|
8459
|
+
throw err2;
|
|
8304
8460
|
}
|
|
8305
|
-
|
|
8461
|
+
const err = {
|
|
8462
|
+
code: "NOT_IMPLEMENTED",
|
|
8463
|
+
message: "OAuth account linking from a logged-in session is not yet supported. Use the standard OAuth sign-in flow instead."
|
|
8464
|
+
};
|
|
8465
|
+
setError(err);
|
|
8466
|
+
throw err;
|
|
8306
8467
|
},
|
|
8307
|
-
[
|
|
8468
|
+
[user, setError]
|
|
8308
8469
|
);
|
|
8309
8470
|
const unlinkAccount = React.useCallback(
|
|
8310
8471
|
async (provider) => {
|
|
8311
8472
|
setError(null);
|
|
8312
8473
|
try {
|
|
8313
|
-
await client.delete(`/v1/auth/oauth/
|
|
8474
|
+
await client.delete(`/v1/auth/oauth/providers/${encodeURIComponent(provider)}`);
|
|
8314
8475
|
} catch (err) {
|
|
8315
8476
|
if (err instanceof ScaleMuleApiError) {
|
|
8316
8477
|
setError(err);
|
|
@@ -10920,6 +11081,75 @@ function useFeedback(options = {}) {
|
|
|
10920
11081
|
);
|
|
10921
11082
|
return { items, loading, error, submit, refresh };
|
|
10922
11083
|
}
|
|
11084
|
+
var ZERO = { value: 0, up_count: 0, down_count: 0, score: 0 };
|
|
11085
|
+
function useVote({
|
|
11086
|
+
targetType,
|
|
11087
|
+
targetId,
|
|
11088
|
+
initialState,
|
|
11089
|
+
refetchOnMount = false,
|
|
11090
|
+
enabled = true
|
|
11091
|
+
}) {
|
|
11092
|
+
const { client } = useScaleMule();
|
|
11093
|
+
const [state, setState] = React.useState(initialState ?? ZERO);
|
|
11094
|
+
const [isLoading, setIsLoading] = React.useState(!initialState && enabled);
|
|
11095
|
+
const [error, setError] = React.useState(null);
|
|
11096
|
+
const inFlight = React.useRef(null);
|
|
11097
|
+
const path = `/v1/social/${encodeURIComponent(targetType)}/${encodeURIComponent(targetId)}/vote`;
|
|
11098
|
+
const refetch = React.useCallback(async () => {
|
|
11099
|
+
if (!enabled) return;
|
|
11100
|
+
setIsLoading(true);
|
|
11101
|
+
setError(null);
|
|
11102
|
+
try {
|
|
11103
|
+
const data = await client.get(path);
|
|
11104
|
+
setState(data);
|
|
11105
|
+
} catch (e) {
|
|
11106
|
+
setError(e);
|
|
11107
|
+
} finally {
|
|
11108
|
+
setIsLoading(false);
|
|
11109
|
+
}
|
|
11110
|
+
}, [client, path, enabled]);
|
|
11111
|
+
React.useEffect(() => {
|
|
11112
|
+
if (!enabled) return;
|
|
11113
|
+
if (initialState && !refetchOnMount) return;
|
|
11114
|
+
void refetch();
|
|
11115
|
+
}, [enabled, refetch, initialState, refetchOnMount]);
|
|
11116
|
+
const cast = React.useCallback(
|
|
11117
|
+
async (value) => {
|
|
11118
|
+
const prev = state;
|
|
11119
|
+
const optimistic = applyVote(prev, value);
|
|
11120
|
+
setState(optimistic);
|
|
11121
|
+
setError(null);
|
|
11122
|
+
const send = async () => {
|
|
11123
|
+
try {
|
|
11124
|
+
const data = await client.put(path, { value });
|
|
11125
|
+
setState(data);
|
|
11126
|
+
} catch (e) {
|
|
11127
|
+
setState(prev);
|
|
11128
|
+
setError(e);
|
|
11129
|
+
}
|
|
11130
|
+
};
|
|
11131
|
+
const p = inFlight.current ? inFlight.current.then(send) : send();
|
|
11132
|
+
inFlight.current = p;
|
|
11133
|
+
await p;
|
|
11134
|
+
if (inFlight.current === p) inFlight.current = null;
|
|
11135
|
+
},
|
|
11136
|
+
[client, path, state]
|
|
11137
|
+
);
|
|
11138
|
+
return { state, isLoading, error, cast, refetch };
|
|
11139
|
+
}
|
|
11140
|
+
function applyVote(prev, next) {
|
|
11141
|
+
let { up_count, down_count } = prev;
|
|
11142
|
+
if (prev.value === 1) up_count = Math.max(0, up_count - 1);
|
|
11143
|
+
if (prev.value === -1) down_count = Math.max(0, down_count - 1);
|
|
11144
|
+
if (next === 1) up_count += 1;
|
|
11145
|
+
if (next === -1) down_count += 1;
|
|
11146
|
+
return {
|
|
11147
|
+
value: next,
|
|
11148
|
+
up_count,
|
|
11149
|
+
down_count,
|
|
11150
|
+
score: up_count - down_count
|
|
11151
|
+
};
|
|
11152
|
+
}
|
|
10923
11153
|
var TYPE_LABELS = {
|
|
10924
11154
|
bug_report: "Bug",
|
|
10925
11155
|
feature_request: "Feature request",
|
|
@@ -11288,448 +11518,128 @@ function FeedbackWidget(props) {
|
|
|
11288
11518
|
) })
|
|
11289
11519
|
] });
|
|
11290
11520
|
}
|
|
11291
|
-
var
|
|
11292
|
-
var
|
|
11293
|
-
var
|
|
11521
|
+
var ARROW_UP = "\u25B2";
|
|
11522
|
+
var ARROW_DOWN = "\u25BC";
|
|
11523
|
+
var STYLES2 = {
|
|
11294
11524
|
root: {
|
|
11295
|
-
|
|
11296
|
-
border: "1px solid rgba(226, 232, 240, 0.8)",
|
|
11297
|
-
background: "linear-gradient(180deg, #f8fbff 0%, #edf4ff 100%)",
|
|
11298
|
-
padding: "1rem",
|
|
11299
|
-
color: "#0f172b",
|
|
11300
|
-
boxShadow: "0 22px 70px rgba(4, 12, 24, 0.28)"
|
|
11301
|
-
},
|
|
11302
|
-
layout: {
|
|
11303
|
-
display: "flex",
|
|
11304
|
-
flexWrap: "wrap",
|
|
11305
|
-
alignItems: "center",
|
|
11306
|
-
gap: "1rem"
|
|
11307
|
-
},
|
|
11308
|
-
playButton: {
|
|
11309
|
-
display: "flex",
|
|
11310
|
-
height: "4.5rem",
|
|
11311
|
-
width: "4.5rem",
|
|
11312
|
-
flexShrink: 0,
|
|
11525
|
+
display: "inline-flex",
|
|
11313
11526
|
alignItems: "center",
|
|
11314
|
-
|
|
11315
|
-
borderRadius: "9999px",
|
|
11316
|
-
border: "none",
|
|
11317
|
-
backgroundColor: "#020618",
|
|
11318
|
-
color: "#ffffff",
|
|
11319
|
-
boxShadow: "0 10px 28px rgba(15, 23, 42, 0.25)",
|
|
11320
|
-
cursor: "pointer"
|
|
11321
|
-
},
|
|
11322
|
-
playIcon: {
|
|
11323
|
-
display: "block",
|
|
11324
|
-
width: "1.75rem",
|
|
11325
|
-
height: "1.75rem",
|
|
11326
|
-
fill: "currentColor"
|
|
11327
|
-
},
|
|
11328
|
-
playIconOffset: {
|
|
11329
|
-
marginLeft: "0.25rem"
|
|
11330
|
-
},
|
|
11331
|
-
content: {
|
|
11332
|
-
minWidth: "16rem",
|
|
11333
|
-
flex: "1 1 18rem"
|
|
11527
|
+
gap: 6
|
|
11334
11528
|
},
|
|
11335
|
-
|
|
11336
|
-
display: "flex",
|
|
11337
|
-
|
|
11338
|
-
justifyContent: "space-between",
|
|
11339
|
-
gap: "0.75rem",
|
|
11340
|
-
fontSize: "0.8rem",
|
|
11341
|
-
fontWeight: 500,
|
|
11342
|
-
color: "#62748e"
|
|
11343
|
-
},
|
|
11344
|
-
metaRight: {
|
|
11345
|
-
display: "flex",
|
|
11529
|
+
rootStacked: {
|
|
11530
|
+
display: "inline-flex",
|
|
11531
|
+
flexDirection: "column",
|
|
11346
11532
|
alignItems: "center",
|
|
11347
|
-
gap:
|
|
11533
|
+
gap: 2
|
|
11348
11534
|
},
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
fontSize: "0.65rem",
|
|
11354
|
-
fontWeight: 600,
|
|
11355
|
-
letterSpacing: "0.24em",
|
|
11356
|
-
textTransform: "uppercase",
|
|
11357
|
-
color: "#1447e6"
|
|
11358
|
-
},
|
|
11359
|
-
scrubber: {
|
|
11360
|
-
position: "relative",
|
|
11361
|
-
marginTop: "1rem",
|
|
11362
|
-
height: "3rem",
|
|
11535
|
+
button: {
|
|
11536
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
11537
|
+
background: "rgba(255,255,255,0.04)",
|
|
11538
|
+
color: "inherit",
|
|
11363
11539
|
cursor: "pointer",
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
},
|
|
11369
|
-
waveform: {
|
|
11370
|
-
pointerEvents: "none",
|
|
11371
|
-
position: "absolute",
|
|
11372
|
-
inset: 0,
|
|
11373
|
-
height: "100%",
|
|
11374
|
-
width: "100%"
|
|
11375
|
-
},
|
|
11376
|
-
fallbackTrack: {
|
|
11377
|
-
position: "absolute",
|
|
11378
|
-
inset: 0,
|
|
11379
|
-
backgroundColor: "rgba(226, 232, 240, 0.9)"
|
|
11380
|
-
},
|
|
11381
|
-
fallbackProgress: {
|
|
11382
|
-
position: "absolute",
|
|
11383
|
-
insetBlock: 0,
|
|
11384
|
-
left: 0,
|
|
11385
|
-
backgroundColor: "#155dfc"
|
|
11540
|
+
borderRadius: 9999,
|
|
11541
|
+
padding: "6px 10px",
|
|
11542
|
+
lineHeight: 1,
|
|
11543
|
+
fontSize: 14
|
|
11386
11544
|
},
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
flexWrap: "wrap",
|
|
11391
|
-
alignItems: "center",
|
|
11392
|
-
gap: "0.5rem",
|
|
11393
|
-
fontSize: "0.72rem",
|
|
11394
|
-
fontWeight: 500,
|
|
11395
|
-
color: "#62748e"
|
|
11396
|
-
},
|
|
11397
|
-
chipStrong: {
|
|
11398
|
-
borderRadius: "9999px",
|
|
11399
|
-
border: "1px solid #e2e8f0",
|
|
11400
|
-
backgroundColor: "rgba(255, 255, 255, 0.85)",
|
|
11401
|
-
padding: "0.25rem 0.75rem",
|
|
11402
|
-
color: "#334155"
|
|
11403
|
-
},
|
|
11404
|
-
chipMuted: {
|
|
11405
|
-
borderRadius: "9999px",
|
|
11406
|
-
border: "1px solid #e2e8f0",
|
|
11407
|
-
backgroundColor: "rgba(255, 255, 255, 0.65)",
|
|
11408
|
-
padding: "0.25rem 0.75rem"
|
|
11409
|
-
},
|
|
11410
|
-
controls: {
|
|
11411
|
-
display: "flex",
|
|
11412
|
-
flexShrink: 0,
|
|
11413
|
-
alignItems: "center",
|
|
11414
|
-
gap: "0.5rem",
|
|
11415
|
-
alignSelf: "flex-start"
|
|
11545
|
+
buttonCompact: {
|
|
11546
|
+
padding: "3px 7px",
|
|
11547
|
+
fontSize: 12
|
|
11416
11548
|
},
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
padding: "0.5rem 1rem",
|
|
11422
|
-
fontSize: "1rem",
|
|
11423
|
-
fontWeight: 600,
|
|
11424
|
-
color: "#334155",
|
|
11425
|
-
boxShadow: "0 1px 3px rgba(15, 23, 42, 0.08)",
|
|
11426
|
-
cursor: "pointer"
|
|
11549
|
+
buttonActiveUp: {
|
|
11550
|
+
background: "rgba(34,197,94,0.18)",
|
|
11551
|
+
borderColor: "rgba(34,197,94,0.45)",
|
|
11552
|
+
color: "#86efac"
|
|
11427
11553
|
},
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
padding: "0.5rem 1rem",
|
|
11433
|
-
fontSize: "0.875rem",
|
|
11434
|
-
fontWeight: 600,
|
|
11435
|
-
color: "#334155",
|
|
11436
|
-
cursor: "pointer"
|
|
11554
|
+
buttonActiveDown: {
|
|
11555
|
+
background: "rgba(239,68,68,0.18)",
|
|
11556
|
+
borderColor: "rgba(239,68,68,0.45)",
|
|
11557
|
+
color: "#fca5a5"
|
|
11437
11558
|
},
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11559
|
+
score: {
|
|
11560
|
+
minWidth: 24,
|
|
11561
|
+
textAlign: "center",
|
|
11562
|
+
fontVariantNumeric: "tabular-nums",
|
|
11563
|
+
fontWeight: 600
|
|
11441
11564
|
}
|
|
11442
11565
|
};
|
|
11443
|
-
function
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
if (currentIndex === -1) {
|
|
11455
|
-
return SPEEDS[0];
|
|
11456
|
-
}
|
|
11457
|
-
return SPEEDS[(currentIndex + 1) % SPEEDS.length];
|
|
11458
|
-
}
|
|
11459
|
-
function NarrationPlayer({
|
|
11460
|
-
audio,
|
|
11461
|
-
className,
|
|
11462
|
-
providerLabel = "ScaleMule",
|
|
11463
|
-
narrationLabel = "AI-Narrated",
|
|
11464
|
-
refreshing = false,
|
|
11465
|
-
onRefresh,
|
|
11466
|
-
onPlaybackError
|
|
11566
|
+
function VoteButton({
|
|
11567
|
+
targetType,
|
|
11568
|
+
targetId,
|
|
11569
|
+
initialState,
|
|
11570
|
+
layout = "horizontal",
|
|
11571
|
+
size = "regular",
|
|
11572
|
+
onSignInRequired,
|
|
11573
|
+
onError,
|
|
11574
|
+
classNames,
|
|
11575
|
+
upLabel = "Upvote",
|
|
11576
|
+
downLabel = "Downvote"
|
|
11467
11577
|
}) {
|
|
11468
|
-
const
|
|
11469
|
-
const
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
const
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
return [];
|
|
11480
|
-
}
|
|
11481
|
-
const sampled = audio.waveform_peaks.filter((_, index) => index % 3 === 0).slice(0, 220);
|
|
11482
|
-
return sampled.length > 0 ? sampled : audio.waveform_peaks.slice(0, 220);
|
|
11483
|
-
}, [audio.waveform_peaks]);
|
|
11484
|
-
React.useEffect(() => {
|
|
11485
|
-
const element = audioRef.current;
|
|
11486
|
-
if (!element) return;
|
|
11487
|
-
recoveredUrlRef.current = null;
|
|
11488
|
-
const resumeTime = pendingResumeTimeRef.current;
|
|
11489
|
-
setCurrentTime(resumeTime ?? 0);
|
|
11490
|
-
setPlaying(false);
|
|
11491
|
-
element.pause();
|
|
11492
|
-
try {
|
|
11493
|
-
element.currentTime = resumeTime ?? 0;
|
|
11494
|
-
} catch {
|
|
11495
|
-
element.currentTime = 0;
|
|
11496
|
-
}
|
|
11497
|
-
if (pendingResumeRef.current) {
|
|
11498
|
-
pendingResumeRef.current = false;
|
|
11499
|
-
pendingResumeTimeRef.current = null;
|
|
11500
|
-
element.play().catch(() => {
|
|
11501
|
-
setPlaying(false);
|
|
11502
|
-
onPlaybackError?.();
|
|
11503
|
-
});
|
|
11504
|
-
return;
|
|
11505
|
-
}
|
|
11506
|
-
pendingResumeTimeRef.current = null;
|
|
11507
|
-
}, [audio.url]);
|
|
11508
|
-
React.useEffect(() => {
|
|
11509
|
-
const element = audioRef.current;
|
|
11510
|
-
if (!element) return;
|
|
11511
|
-
element.playbackRate = speed;
|
|
11512
|
-
element.preservesPitch = true;
|
|
11513
|
-
}, [speed]);
|
|
11514
|
-
function isUrlExpiringSoon() {
|
|
11515
|
-
if (!audio.expires_at) {
|
|
11516
|
-
return false;
|
|
11517
|
-
}
|
|
11518
|
-
const expiresAtMs = Date.parse(audio.expires_at);
|
|
11519
|
-
if (!Number.isFinite(expiresAtMs)) {
|
|
11520
|
-
return false;
|
|
11521
|
-
}
|
|
11522
|
-
return expiresAtMs - Date.now() <= EXPIRY_REFRESH_WINDOW_MS;
|
|
11523
|
-
}
|
|
11524
|
-
async function refreshAndResume() {
|
|
11525
|
-
if (!onRefresh || refreshing) {
|
|
11526
|
-
return false;
|
|
11527
|
-
}
|
|
11528
|
-
if (recoveredUrlRef.current === (audio.url ?? null)) {
|
|
11529
|
-
return false;
|
|
11530
|
-
}
|
|
11531
|
-
recoveredUrlRef.current = audio.url ?? null;
|
|
11532
|
-
pendingResumeRef.current = true;
|
|
11533
|
-
pendingResumeTimeRef.current = currentTime;
|
|
11534
|
-
try {
|
|
11535
|
-
await Promise.resolve(onRefresh());
|
|
11536
|
-
return true;
|
|
11537
|
-
} catch {
|
|
11538
|
-
pendingResumeRef.current = false;
|
|
11539
|
-
pendingResumeTimeRef.current = null;
|
|
11540
|
-
onPlaybackError?.();
|
|
11541
|
-
return false;
|
|
11542
|
-
}
|
|
11543
|
-
}
|
|
11544
|
-
function togglePlayback() {
|
|
11545
|
-
const element = audioRef.current;
|
|
11546
|
-
if (!element) return;
|
|
11547
|
-
if (element.paused) {
|
|
11548
|
-
if (refreshing) {
|
|
11549
|
-
pendingResumeRef.current = true;
|
|
11550
|
-
pendingResumeTimeRef.current = currentTime;
|
|
11578
|
+
const { isAuthenticated } = useAuth();
|
|
11579
|
+
const { state, cast, error } = useVote({
|
|
11580
|
+
targetType,
|
|
11581
|
+
targetId,
|
|
11582
|
+
initialState: initialState ?? null,
|
|
11583
|
+
enabled: isAuthenticated
|
|
11584
|
+
});
|
|
11585
|
+
const handleVote = React.useCallback(
|
|
11586
|
+
async (next) => {
|
|
11587
|
+
if (!isAuthenticated) {
|
|
11588
|
+
onSignInRequired?.();
|
|
11551
11589
|
return;
|
|
11552
11590
|
}
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11591
|
+
const desired = state.value === next ? 0 : next;
|
|
11592
|
+
try {
|
|
11593
|
+
await cast(desired);
|
|
11594
|
+
} catch (e) {
|
|
11595
|
+
onError?.(e);
|
|
11556
11596
|
}
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11597
|
+
},
|
|
11598
|
+
[cast, isAuthenticated, onSignInRequired, onError, state.value]
|
|
11599
|
+
);
|
|
11600
|
+
React.useEffect(() => {
|
|
11601
|
+
if (error) onError?.(error);
|
|
11602
|
+
}, [error, onError]);
|
|
11603
|
+
const compactStyle = size === "compact" ? STYLES2.buttonCompact : {};
|
|
11604
|
+
const upActive = state.value === 1;
|
|
11605
|
+
const downActive = state.value === -1;
|
|
11606
|
+
const upBtn = /* @__PURE__ */ jsxRuntime.jsx(
|
|
11607
|
+
"button",
|
|
11608
|
+
{
|
|
11609
|
+
type: "button",
|
|
11610
|
+
"aria-label": upLabel,
|
|
11611
|
+
"aria-pressed": upActive,
|
|
11612
|
+
title: isAuthenticated ? upLabel : "Sign in to vote",
|
|
11613
|
+
className: [classNames?.upButton, upActive ? classNames?.upButtonActive : void 0].filter(Boolean).join(" ") || void 0,
|
|
11614
|
+
onClick: () => void handleVote(1),
|
|
11615
|
+
style: classNames?.upButton ? void 0 : { ...STYLES2.button, ...compactStyle, ...upActive ? STYLES2.buttonActiveUp : {} },
|
|
11616
|
+
children: ARROW_UP
|
|
11562
11617
|
}
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
}
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
src: audio.url ?? void 0,
|
|
11589
|
-
preload: "metadata",
|
|
11590
|
-
onLoadedMetadata: (event) => {
|
|
11591
|
-
setDuration(event.currentTarget.duration || 0);
|
|
11592
|
-
},
|
|
11593
|
-
onTimeUpdate: (event) => {
|
|
11594
|
-
setCurrentTime(event.currentTarget.currentTime);
|
|
11595
|
-
},
|
|
11596
|
-
onPlay: () => setPlaying(true),
|
|
11597
|
-
onPause: () => setPlaying(false),
|
|
11598
|
-
onEnded: () => setPlaying(false),
|
|
11599
|
-
onError: () => {
|
|
11600
|
-
if (onRefresh) {
|
|
11601
|
-
void refreshAndResume().then((didRefresh) => {
|
|
11602
|
-
if (!didRefresh) {
|
|
11603
|
-
onPlaybackError?.();
|
|
11604
|
-
}
|
|
11605
|
-
});
|
|
11606
|
-
return;
|
|
11607
|
-
}
|
|
11608
|
-
onPlaybackError?.();
|
|
11609
|
-
}
|
|
11610
|
-
}
|
|
11611
|
-
),
|
|
11612
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.layout, children: [
|
|
11613
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11614
|
-
"button",
|
|
11615
|
-
{
|
|
11616
|
-
type: "button",
|
|
11617
|
-
onClick: togglePlayback,
|
|
11618
|
-
"aria-label": playing ? "Pause narration" : "Play narration",
|
|
11619
|
-
style: styles.playButton,
|
|
11620
|
-
children: playing ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", style: styles.playIcon, "aria-hidden": "true", children: [
|
|
11621
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "5", width: "4", height: "14", rx: "1.2" }),
|
|
11622
|
-
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "14", y: "5", width: "4", height: "14", rx: "1.2" })
|
|
11623
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
11624
|
-
"svg",
|
|
11625
|
-
{
|
|
11626
|
-
viewBox: "0 0 24 24",
|
|
11627
|
-
style: { ...styles.playIcon, ...styles.playIconOffset },
|
|
11628
|
-
"aria-hidden": "true",
|
|
11629
|
-
children: /* @__PURE__ */ jsxRuntime.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" })
|
|
11630
|
-
}
|
|
11631
|
-
)
|
|
11632
|
-
}
|
|
11633
|
-
),
|
|
11634
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.content, children: [
|
|
11635
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.metaRow, children: [
|
|
11636
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(currentTime / speed) }),
|
|
11637
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.metaRight, children: [
|
|
11638
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: styles.badge, children: narrationLabel }),
|
|
11639
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatTime(duration / speed) })
|
|
11640
|
-
] })
|
|
11641
|
-
] }),
|
|
11642
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11643
|
-
"div",
|
|
11644
|
-
{
|
|
11645
|
-
ref: scrubRef,
|
|
11646
|
-
role: "slider",
|
|
11647
|
-
"aria-label": "Narration progress",
|
|
11648
|
-
"aria-valuemin": 0,
|
|
11649
|
-
"aria-valuemax": Math.max(1, Math.floor(duration)),
|
|
11650
|
-
"aria-valuenow": Math.floor(currentTime),
|
|
11651
|
-
tabIndex: 0,
|
|
11652
|
-
onClick: (event) => {
|
|
11653
|
-
seekTo(event.clientX);
|
|
11654
|
-
},
|
|
11655
|
-
style: styles.scrubber,
|
|
11656
|
-
children: peaks.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11657
|
-
"svg",
|
|
11658
|
-
{
|
|
11659
|
-
viewBox: `0 0 ${peaks.length} 40`,
|
|
11660
|
-
preserveAspectRatio: "none",
|
|
11661
|
-
style: styles.waveform,
|
|
11662
|
-
children: peaks.map((peak, index) => {
|
|
11663
|
-
const height = Math.max(2, Math.round(Math.abs(peak) * 34));
|
|
11664
|
-
const y = (40 - height) / 2;
|
|
11665
|
-
const filled = index / peaks.length < progress;
|
|
11666
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11667
|
-
"rect",
|
|
11668
|
-
{
|
|
11669
|
-
x: index,
|
|
11670
|
-
y,
|
|
11671
|
-
width: 0.82,
|
|
11672
|
-
height,
|
|
11673
|
-
rx: 0.4,
|
|
11674
|
-
fill: filled ? "#155dfc" : "#90c5ff"
|
|
11675
|
-
},
|
|
11676
|
-
`${audio.id}-${index}`
|
|
11677
|
-
);
|
|
11678
|
-
})
|
|
11679
|
-
}
|
|
11680
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11681
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.fallbackTrack }),
|
|
11682
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11683
|
-
"div",
|
|
11684
|
-
{
|
|
11685
|
-
style: { ...styles.fallbackProgress, width: `${progress * 100}%` }
|
|
11686
|
-
}
|
|
11687
|
-
)
|
|
11688
|
-
] })
|
|
11689
|
-
}
|
|
11690
|
-
),
|
|
11691
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.chipsRow, children: [
|
|
11692
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles.chipStrong, children: [
|
|
11693
|
-
"Provider: ",
|
|
11694
|
-
providerLabel
|
|
11695
|
-
] }),
|
|
11696
|
-
duration > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles.chipMuted, children: [
|
|
11697
|
-
"Length ",
|
|
11698
|
-
formatTime(duration / speed)
|
|
11699
|
-
] }),
|
|
11700
|
-
expiresLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles.chipMuted, children: [
|
|
11701
|
-
"Expires ",
|
|
11702
|
-
expiresLabel
|
|
11703
|
-
] })
|
|
11704
|
-
] })
|
|
11705
|
-
] }),
|
|
11706
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.controls, children: [
|
|
11707
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11708
|
-
"button",
|
|
11709
|
-
{
|
|
11710
|
-
type: "button",
|
|
11711
|
-
onClick: () => {
|
|
11712
|
-
setSpeed((currentSpeed) => getNextSpeed(currentSpeed));
|
|
11713
|
-
},
|
|
11714
|
-
style: styles.secondaryButton,
|
|
11715
|
-
children: [
|
|
11716
|
-
speed,
|
|
11717
|
-
"\xD7"
|
|
11718
|
-
]
|
|
11719
|
-
}
|
|
11720
|
-
),
|
|
11721
|
-
onRefresh && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11722
|
-
"button",
|
|
11723
|
-
{
|
|
11724
|
-
type: "button",
|
|
11725
|
-
onClick: onRefresh,
|
|
11726
|
-
disabled: refreshing,
|
|
11727
|
-
style: refreshing ? { ...styles.refreshButton, ...styles.disabled } : styles.refreshButton,
|
|
11728
|
-
children: refreshing ? "Refreshing\u2026" : "Refresh"
|
|
11729
|
-
}
|
|
11730
|
-
)
|
|
11731
|
-
] })
|
|
11732
|
-
] })
|
|
11618
|
+
);
|
|
11619
|
+
const downBtn = /* @__PURE__ */ jsxRuntime.jsx(
|
|
11620
|
+
"button",
|
|
11621
|
+
{
|
|
11622
|
+
type: "button",
|
|
11623
|
+
"aria-label": downLabel,
|
|
11624
|
+
"aria-pressed": downActive,
|
|
11625
|
+
title: isAuthenticated ? downLabel : "Sign in to vote",
|
|
11626
|
+
className: [classNames?.downButton, downActive ? classNames?.downButtonActive : void 0].filter(Boolean).join(" ") || void 0,
|
|
11627
|
+
onClick: () => void handleVote(-1),
|
|
11628
|
+
style: classNames?.downButton ? void 0 : { ...STYLES2.button, ...compactStyle, ...downActive ? STYLES2.buttonActiveDown : {} },
|
|
11629
|
+
children: ARROW_DOWN
|
|
11630
|
+
}
|
|
11631
|
+
);
|
|
11632
|
+
const scoreClass = [
|
|
11633
|
+
classNames?.score,
|
|
11634
|
+
state.score > 0 ? classNames?.scorePositive : void 0,
|
|
11635
|
+
state.score < 0 ? classNames?.scoreNegative : void 0
|
|
11636
|
+
].filter(Boolean).join(" ") || void 0;
|
|
11637
|
+
const scoreEl = /* @__PURE__ */ jsxRuntime.jsx("span", { className: scoreClass, style: classNames?.score ? void 0 : STYLES2.score, children: state.score });
|
|
11638
|
+
const rootStyle = layout === "stacked" ? STYLES2.rootStacked : STYLES2.root;
|
|
11639
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classNames?.root, style: classNames?.root ? void 0 : rootStyle, children: [
|
|
11640
|
+
upBtn,
|
|
11641
|
+
scoreEl,
|
|
11642
|
+
downBtn
|
|
11733
11643
|
] });
|
|
11734
11644
|
}
|
|
11735
11645
|
|
|
@@ -12033,11 +11943,11 @@ Object.defineProperty(exports, "createMoneyClient", {
|
|
|
12033
11943
|
get: function () { return money.createMoneyClient; }
|
|
12034
11944
|
});
|
|
12035
11945
|
exports.FeedbackWidget = FeedbackWidget;
|
|
12036
|
-
exports.NarrationPlayer = NarrationPlayer;
|
|
12037
11946
|
exports.ScaleMuleApiError = ScaleMuleApiError;
|
|
12038
11947
|
exports.ScaleMuleClient = ScaleMuleClient2;
|
|
12039
11948
|
exports.ScaleMuleMedia = ScaleMuleMedia;
|
|
12040
11949
|
exports.ScaleMuleProvider = ScaleMuleProvider;
|
|
11950
|
+
exports.VoteButton = VoteButton;
|
|
12041
11951
|
exports.composePhone = composePhone;
|
|
12042
11952
|
exports.createClient = createClient;
|
|
12043
11953
|
exports.createSafeLogger = createSafeLogger;
|
|
@@ -12062,5 +11972,6 @@ exports.useScaleMuleClient = useScaleMuleClient;
|
|
|
12062
11972
|
exports.useShare = useShare;
|
|
12063
11973
|
exports.useTtsJob = useTtsJob;
|
|
12064
11974
|
exports.useUser = useUser;
|
|
11975
|
+
exports.useVote = useVote;
|
|
12065
11976
|
exports.validateForm = validateForm;
|
|
12066
11977
|
exports.validators = validators;
|