@thefittingroom/shop-ui 5.0.19 → 5.0.20
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/index.js +89 -143
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41030,18 +41030,6 @@ class FirestoreManager {
|
|
|
41030
41030
|
});
|
|
41031
41031
|
return unsubscribe;
|
|
41032
41032
|
}
|
|
41033
|
-
// listenToSubDoc subscribes to <parent>/<parentID>/<sub>/<subID>. Used for
|
|
41034
|
-
// VTO compositions which live at users/{uid}/vto_compositions/{token}.
|
|
41035
|
-
listenToSubDoc(parent, parentID, sub, subID, callback) {
|
|
41036
|
-
const docRef = doc(this.firestore, parent, parentID, sub, subID);
|
|
41037
|
-
return onSnapshot(docRef, (snap) => {
|
|
41038
|
-
if (snap.exists()) {
|
|
41039
|
-
callback(snap.data());
|
|
41040
|
-
} else {
|
|
41041
|
-
callback(null);
|
|
41042
|
-
}
|
|
41043
|
-
});
|
|
41044
|
-
}
|
|
41045
41033
|
async queryDocs(collectionName, constraints) {
|
|
41046
41034
|
const q2 = query(this.collection(collectionName), ...constraints);
|
|
41047
41035
|
return getDocs(q2);
|
|
@@ -41239,7 +41227,8 @@ async function execApiRequest(params) {
|
|
|
41239
41227
|
useToken,
|
|
41240
41228
|
method,
|
|
41241
41229
|
endpoint,
|
|
41242
|
-
body
|
|
41230
|
+
body,
|
|
41231
|
+
timeoutMs
|
|
41243
41232
|
} = params;
|
|
41244
41233
|
const url = `${baseUrl}${endpoint}`;
|
|
41245
41234
|
if (useCache && responseCache[url]) {
|
|
@@ -41261,9 +41250,28 @@ async function execApiRequest(params) {
|
|
|
41261
41250
|
if (body) {
|
|
41262
41251
|
options.body = JSON.stringify(body);
|
|
41263
41252
|
}
|
|
41264
|
-
|
|
41253
|
+
let timeoutHandle;
|
|
41254
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
41255
|
+
const controller = new AbortController();
|
|
41256
|
+
options.signal = controller.signal;
|
|
41257
|
+
timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
41258
|
+
}
|
|
41259
|
+
let response;
|
|
41260
|
+
try {
|
|
41261
|
+
response = await fetch(url, options);
|
|
41262
|
+
} finally {
|
|
41263
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
41264
|
+
}
|
|
41265
41265
|
if (!response.ok) {
|
|
41266
|
-
|
|
41266
|
+
let detail = "";
|
|
41267
|
+
try {
|
|
41268
|
+
const errBody = await response.json();
|
|
41269
|
+
if (errBody && typeof errBody.error === "string") {
|
|
41270
|
+
detail = errBody.error;
|
|
41271
|
+
}
|
|
41272
|
+
} catch {
|
|
41273
|
+
}
|
|
41274
|
+
throw new Error(detail || `API request failed with status ${response.status}`);
|
|
41267
41275
|
}
|
|
41268
41276
|
let data;
|
|
41269
41277
|
if (response.status === 204) {
|
|
@@ -41302,15 +41310,33 @@ async function getStyleCategoryGroups() {
|
|
|
41302
41310
|
endpoint: "/v1/style-category-groups"
|
|
41303
41311
|
});
|
|
41304
41312
|
}
|
|
41305
|
-
|
|
41306
|
-
|
|
41313
|
+
const inFlightVtoRequests = /* @__PURE__ */ new Map();
|
|
41314
|
+
function canonicalVtoKey(items) {
|
|
41315
|
+
const normalized = items.map((i) => ({
|
|
41316
|
+
csa: i.colorway_size_asset_id,
|
|
41317
|
+
untucked: !!i.untucked
|
|
41318
|
+
})).sort((a, b) => a.csa - b.csa || Number(a.untucked) - Number(b.untucked));
|
|
41319
|
+
return JSON.stringify(normalized);
|
|
41320
|
+
}
|
|
41321
|
+
function requestVto(items) {
|
|
41322
|
+
const key = canonicalVtoKey(items);
|
|
41323
|
+
const existing = inFlightVtoRequests.get(key);
|
|
41324
|
+
if (existing) {
|
|
41325
|
+
return existing;
|
|
41326
|
+
}
|
|
41327
|
+
const promise = execApiRequest({
|
|
41307
41328
|
useToken: true,
|
|
41308
41329
|
method: "POST",
|
|
41309
41330
|
endpoint: "/v1/vto-compositions",
|
|
41310
41331
|
body: {
|
|
41311
41332
|
items
|
|
41312
|
-
}
|
|
41333
|
+
},
|
|
41334
|
+
timeoutMs: getStaticData().config.api.vtoTimeoutMs
|
|
41335
|
+
}).finally(() => {
|
|
41336
|
+
inFlightVtoRequests.delete(key);
|
|
41313
41337
|
});
|
|
41338
|
+
inFlightVtoRequests.set(key, promise);
|
|
41339
|
+
return promise;
|
|
41314
41340
|
}
|
|
41315
41341
|
const recordCache = {};
|
|
41316
41342
|
async function getStyleByExternalId(brandId, externalId) {
|
|
@@ -42969,67 +42995,44 @@ function TryOnView({
|
|
|
42969
42995
|
] }) })
|
|
42970
42996
|
] });
|
|
42971
42997
|
}
|
|
42972
|
-
const logger$a = getLogger("overlays/fitting-room/use-vto-
|
|
42998
|
+
const logger$a = getLogger("overlays/fitting-room/use-vto-requests");
|
|
42973
42999
|
const NON_PRIORITY_REQUEST_DELAY_MS = 500;
|
|
42974
43000
|
function outfitKey(items) {
|
|
42975
43001
|
return items.map((i) => `${i.colorway_size_asset_id}:${i.untucked ? 1 : 0}`).sort().join("|");
|
|
42976
43002
|
}
|
|
42977
|
-
function
|
|
42978
|
-
const
|
|
42979
|
-
const
|
|
42980
|
-
const [vtoDocsByToken, setVtoDocsByToken] = reactExports.useState({});
|
|
43003
|
+
function useVtoRequests() {
|
|
43004
|
+
const [framesByKey, setFramesByKey] = reactExports.useState({});
|
|
43005
|
+
const requestedKeysRef = reactExports.useRef(/* @__PURE__ */ new Set());
|
|
42981
43006
|
const lastPriorityTimeRef = reactExports.useRef(null);
|
|
42982
43007
|
const [lastError, setLastError] = reactExports.useState(null);
|
|
42983
43008
|
const clearError = reactExports.useCallback(() => setLastError(null), []);
|
|
42984
|
-
const subscribe = reactExports.useCallback((token2, key) => {
|
|
42985
|
-
if (subscriptionsRef.current.has(token2)) return;
|
|
42986
|
-
const uid = getAuthManager().getAuthUser()?.uid;
|
|
42987
|
-
if (!uid) {
|
|
42988
|
-
logger$a.logWarn("subscribe skipped: no uid", {
|
|
42989
|
-
token: token2,
|
|
42990
|
-
key
|
|
42991
|
-
});
|
|
42992
|
-
return;
|
|
42993
|
-
}
|
|
42994
|
-
const unsub = getFirestoreManager().listenToSubDoc("users", uid, "vto_compositions", token2, (data) => {
|
|
42995
|
-
if (data?.frames?.length) {
|
|
42996
|
-
logger$a.logDebug("VTO frames ready", {
|
|
42997
|
-
key,
|
|
42998
|
-
token: token2,
|
|
42999
|
-
count: data.frames.length
|
|
43000
|
-
});
|
|
43001
|
-
}
|
|
43002
|
-
if (data?.error) {
|
|
43003
|
-
setLastError(new Error(data.error));
|
|
43004
|
-
}
|
|
43005
|
-
setVtoDocsByToken((prev2) => ({
|
|
43006
|
-
...prev2,
|
|
43007
|
-
[token2]: data ?? {}
|
|
43008
|
-
}));
|
|
43009
|
-
});
|
|
43010
|
-
subscriptionsRef.current.set(token2, unsub);
|
|
43011
|
-
}, []);
|
|
43012
43009
|
const request = reactExports.useCallback((items, priority) => {
|
|
43013
43010
|
if (items.length === 0) return;
|
|
43014
43011
|
const key = outfitKey(items);
|
|
43015
|
-
if (
|
|
43012
|
+
if (requestedKeysRef.current.has(key)) return;
|
|
43016
43013
|
const exec = () => {
|
|
43017
|
-
|
|
43014
|
+
requestedKeysRef.current.add(key);
|
|
43018
43015
|
logger$a.logDebug("Requesting VTO composition", {
|
|
43019
43016
|
key,
|
|
43020
43017
|
items,
|
|
43021
43018
|
priority
|
|
43022
43019
|
});
|
|
43023
43020
|
requestVto(items).then((resp) => {
|
|
43024
|
-
|
|
43025
|
-
|
|
43021
|
+
logger$a.logDebug("VTO frames ready", {
|
|
43022
|
+
key,
|
|
43023
|
+
count: resp.frames.length
|
|
43024
|
+
});
|
|
43025
|
+
setFramesByKey((prev2) => ({
|
|
43026
|
+
...prev2,
|
|
43027
|
+
[key]: resp.frames
|
|
43028
|
+
}));
|
|
43026
43029
|
}).catch((error) => {
|
|
43027
43030
|
logger$a.logError("VTO request failed", {
|
|
43028
43031
|
error,
|
|
43029
43032
|
items,
|
|
43030
43033
|
key
|
|
43031
43034
|
});
|
|
43032
|
-
|
|
43035
|
+
requestedKeysRef.current.delete(key);
|
|
43033
43036
|
setLastError(error instanceof Error ? error : new Error(String(error)));
|
|
43034
43037
|
});
|
|
43035
43038
|
};
|
|
@@ -43050,31 +43053,15 @@ function useVtoSubscriptions() {
|
|
|
43050
43053
|
} else {
|
|
43051
43054
|
exec();
|
|
43052
43055
|
}
|
|
43053
|
-
}, [subscribe]);
|
|
43054
|
-
reactExports.useEffect(() => {
|
|
43055
|
-
return () => {
|
|
43056
|
-
subscriptionsRef.current.forEach((unsub) => {
|
|
43057
|
-
try {
|
|
43058
|
-
unsub();
|
|
43059
|
-
} catch (e) {
|
|
43060
|
-
logger$a.logError("Error unsubscribing", {
|
|
43061
|
-
error: e
|
|
43062
|
-
});
|
|
43063
|
-
}
|
|
43064
|
-
});
|
|
43065
|
-
subscriptionsRef.current.clear();
|
|
43066
|
-
};
|
|
43067
43056
|
}, []);
|
|
43068
43057
|
const framesForOutfit = reactExports.useCallback((items) => {
|
|
43069
43058
|
if (items.length === 0) return null;
|
|
43070
43059
|
const key = outfitKey(items);
|
|
43071
|
-
const
|
|
43072
|
-
if (!
|
|
43073
|
-
const doc2 = vtoDocsByToken[token2];
|
|
43074
|
-
if (!doc2?.frames?.length) return null;
|
|
43060
|
+
const frames = framesByKey[key];
|
|
43061
|
+
if (!frames || frames.length === 0) return null;
|
|
43075
43062
|
const baseUrl2 = getStaticData().config.frames.baseUrl;
|
|
43076
|
-
return
|
|
43077
|
-
}, [
|
|
43063
|
+
return frames.map((u) => applyFrameBaseUrl(u, baseUrl2));
|
|
43064
|
+
}, [framesByKey]);
|
|
43078
43065
|
return {
|
|
43079
43066
|
request,
|
|
43080
43067
|
framesForOutfit,
|
|
@@ -43131,7 +43118,7 @@ function FittingRoomOverlay() {
|
|
|
43131
43118
|
framesForOutfit,
|
|
43132
43119
|
lastError: vtoError,
|
|
43133
43120
|
clearError: clearVtoError
|
|
43134
|
-
} =
|
|
43121
|
+
} = useVtoRequests();
|
|
43135
43122
|
reactExports.useEffect(() => {
|
|
43136
43123
|
const savedScrollY = window.scrollY;
|
|
43137
43124
|
if (savedScrollY > 0) window.scrollTo(0, 0);
|
|
@@ -44155,9 +44142,8 @@ function VtoSingleOverlay() {
|
|
|
44155
44142
|
const [selectedSizeLabel, setSelectedSizeLabel] = reactExports.useState(null);
|
|
44156
44143
|
const [selectedColorLabel, setSelectedColorLabel] = reactExports.useState(null);
|
|
44157
44144
|
const [modalStyle, setModalStyle] = reactExports.useState({});
|
|
44158
|
-
const
|
|
44159
|
-
const
|
|
44160
|
-
const subscriptionsRef = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
44145
|
+
const [framesByKey, setFramesByKey] = reactExports.useState({});
|
|
44146
|
+
const requestedKeysRef = reactExports.useRef(/* @__PURE__ */ new Set());
|
|
44161
44147
|
const lastPriorityVtoRequestTimeRef = reactExports.useRef(null);
|
|
44162
44148
|
reactExports.useEffect(() => {
|
|
44163
44149
|
if (!userIsLoggedIn) {
|
|
@@ -44332,7 +44318,7 @@ function VtoSingleOverlay() {
|
|
|
44332
44318
|
const requestVto$1 = reactExports.useCallback((sizeColorRecord, priority) => {
|
|
44333
44319
|
const csaId = sizeColorRecord.colorwaySizeAssetId;
|
|
44334
44320
|
const key = compositionKey(csaId);
|
|
44335
|
-
if (
|
|
44321
|
+
if (requestedKeysRef.current.has(key)) {
|
|
44336
44322
|
return;
|
|
44337
44323
|
}
|
|
44338
44324
|
function executeRequest() {
|
|
@@ -44341,19 +44327,23 @@ function VtoSingleOverlay() {
|
|
|
44341
44327
|
priority,
|
|
44342
44328
|
sizeColorRecord
|
|
44343
44329
|
});
|
|
44344
|
-
|
|
44330
|
+
requestedKeysRef.current.add(key);
|
|
44345
44331
|
requestVto([{
|
|
44346
44332
|
colorway_size_asset_id: csaId,
|
|
44347
44333
|
untucked: false
|
|
44348
44334
|
}]).then((resp) => {
|
|
44349
|
-
|
|
44350
|
-
|
|
44335
|
+
logger$6.timerEnd(`requestVto_${sizeColorRecord.sku}`);
|
|
44336
|
+
logger$6.logTimer(`requestVto_${sizeColorRecord.sku}`, `{{ts}} - VTO data is loaded for sku: ${sizeColorRecord.sku}`);
|
|
44337
|
+
setFramesByKey((prev2) => ({
|
|
44338
|
+
...prev2,
|
|
44339
|
+
[key]: resp.frames
|
|
44340
|
+
}));
|
|
44351
44341
|
}).catch((error) => {
|
|
44352
44342
|
logger$6.logError(`Error requesting VTO for sku: ${sizeColorRecord.sku}`, {
|
|
44353
44343
|
error,
|
|
44354
44344
|
sizeColorRecord
|
|
44355
44345
|
});
|
|
44356
|
-
|
|
44346
|
+
requestedKeysRef.current.delete(key);
|
|
44357
44347
|
});
|
|
44358
44348
|
}
|
|
44359
44349
|
{
|
|
@@ -44377,45 +44367,6 @@ function VtoSingleOverlay() {
|
|
|
44377
44367
|
}
|
|
44378
44368
|
executeRequest();
|
|
44379
44369
|
}, []);
|
|
44380
|
-
const subscribeToCompositionToken = reactExports.useCallback((token2, sku) => {
|
|
44381
|
-
if (subscriptionsRef.current.has(token2)) {
|
|
44382
|
-
return;
|
|
44383
|
-
}
|
|
44384
|
-
const authManager2 = getAuthManager();
|
|
44385
|
-
const uid = authManager2.getAuthUser()?.uid;
|
|
44386
|
-
if (!uid) {
|
|
44387
|
-
logger$6.logWarn(`subscribe to vto composition skipped: no uid`, {
|
|
44388
|
-
token: token2,
|
|
44389
|
-
sku
|
|
44390
|
-
});
|
|
44391
|
-
return;
|
|
44392
|
-
}
|
|
44393
|
-
const unsub = getFirestoreManager().listenToSubDoc("users", uid, "vto_compositions", token2, (data) => {
|
|
44394
|
-
if (data && data.frames && data.frames.length > 0) {
|
|
44395
|
-
logger$6.timerEnd(`requestVto_${sku}`);
|
|
44396
|
-
logger$6.logTimer(`requestVto_${sku}`, `{{ts}} - VTO data is loaded for sku: ${sku}`);
|
|
44397
|
-
}
|
|
44398
|
-
setVtoDocsByToken((prev2) => ({
|
|
44399
|
-
...prev2,
|
|
44400
|
-
[token2]: data ?? {}
|
|
44401
|
-
}));
|
|
44402
|
-
});
|
|
44403
|
-
subscriptionsRef.current.set(token2, unsub);
|
|
44404
|
-
}, []);
|
|
44405
|
-
reactExports.useEffect(() => {
|
|
44406
|
-
return () => {
|
|
44407
|
-
subscriptionsRef.current.forEach((unsub) => {
|
|
44408
|
-
try {
|
|
44409
|
-
unsub();
|
|
44410
|
-
} catch (e) {
|
|
44411
|
-
logger$6.logError("Error unsubscribing from vto composition", {
|
|
44412
|
-
error: e
|
|
44413
|
-
});
|
|
44414
|
-
}
|
|
44415
|
-
});
|
|
44416
|
-
subscriptionsRef.current.clear();
|
|
44417
|
-
};
|
|
44418
|
-
}, []);
|
|
44419
44370
|
reactExports.useEffect(() => {
|
|
44420
44371
|
if (selectedColorSizeRecord) {
|
|
44421
44372
|
requestVto$1(selectedColorSizeRecord, true);
|
|
@@ -44435,32 +44386,24 @@ function VtoSingleOverlay() {
|
|
|
44435
44386
|
}
|
|
44436
44387
|
}
|
|
44437
44388
|
}, [requestVto$1, vtoProductData, selectedColorLabel]);
|
|
44438
|
-
const
|
|
44389
|
+
const frameUrls = reactExports.useMemo(() => {
|
|
44439
44390
|
if (!selectedColorSizeRecord) {
|
|
44440
44391
|
return null;
|
|
44441
44392
|
}
|
|
44442
44393
|
const key = compositionKey(selectedColorSizeRecord.colorwaySizeAssetId);
|
|
44443
|
-
const
|
|
44444
|
-
if (!
|
|
44445
|
-
return null;
|
|
44446
|
-
}
|
|
44447
|
-
const doc2 = vtoDocsByToken[token2];
|
|
44448
|
-
if (!doc2 || !doc2.frames || doc2.frames.length === 0) {
|
|
44394
|
+
const frames = framesByKey[key];
|
|
44395
|
+
if (!frames || frames.length === 0) {
|
|
44449
44396
|
return null;
|
|
44450
44397
|
}
|
|
44451
44398
|
logger$6.logDebug(`{{ts}} - Displaying VTO for sku: ${selectedColorSizeRecord.sku}`);
|
|
44452
|
-
return doc2;
|
|
44453
|
-
}, [selectedColorSizeRecord, vtoDocsByToken]);
|
|
44454
|
-
const frameUrls = reactExports.useMemo(() => {
|
|
44455
|
-
if (!vtoData?.frames) return null;
|
|
44456
44399
|
const baseUrl2 = getStaticData().config.frames.baseUrl;
|
|
44457
|
-
const rewritten =
|
|
44400
|
+
const rewritten = frames.map((u) => applyFrameBaseUrl(u, baseUrl2));
|
|
44458
44401
|
rewritten.forEach((url) => {
|
|
44459
44402
|
const img = new Image();
|
|
44460
44403
|
img.src = url;
|
|
44461
44404
|
});
|
|
44462
44405
|
return rewritten;
|
|
44463
|
-
}, [
|
|
44406
|
+
}, [selectedColorSizeRecord, framesByKey]);
|
|
44464
44407
|
const handleSignOutClick = reactExports.useCallback(() => {
|
|
44465
44408
|
closeOverlay();
|
|
44466
44409
|
const authManager2 = getAuthManager();
|
|
@@ -45802,9 +45745,9 @@ const SHARED_CONFIG = {
|
|
|
45802
45745
|
appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
|
|
45803
45746
|
},
|
|
45804
45747
|
build: {
|
|
45805
|
-
version: `${"5.0.
|
|
45806
|
-
commitHash: `${"
|
|
45807
|
-
date: `${"2026-05-
|
|
45748
|
+
version: `${"5.0.20"}`,
|
|
45749
|
+
commitHash: `${"ec50778"}`,
|
|
45750
|
+
date: `${"2026-05-16T14:14:47.264Z"}`
|
|
45808
45751
|
}
|
|
45809
45752
|
};
|
|
45810
45753
|
const CONFIGS = {
|
|
@@ -45822,7 +45765,8 @@ const CONFIGS = {
|
|
|
45822
45765
|
measurementId: "G-B7GDQ1Y9LL"
|
|
45823
45766
|
},
|
|
45824
45767
|
api: {
|
|
45825
|
-
baseUrl: "https://tfr.dev.thefittingroom.xyz"
|
|
45768
|
+
baseUrl: "https://tfr.dev.thefittingroom.xyz",
|
|
45769
|
+
vtoTimeoutMs: 12e4
|
|
45826
45770
|
},
|
|
45827
45771
|
asset: {
|
|
45828
45772
|
baseUrl: "https://assets.dev.thefittingroom.xyz/shop-sdk/assets/v5"
|
|
@@ -45849,7 +45793,8 @@ const CONFIGS = {
|
|
|
45849
45793
|
measurementId: "G-XH9VV5N6EW"
|
|
45850
45794
|
},
|
|
45851
45795
|
api: {
|
|
45852
|
-
baseUrl: "https://tfr.p.thefittingroom.xyz"
|
|
45796
|
+
baseUrl: "https://tfr.p.thefittingroom.xyz",
|
|
45797
|
+
vtoTimeoutMs: 12e4
|
|
45853
45798
|
},
|
|
45854
45799
|
asset: {
|
|
45855
45800
|
baseUrl: "https://assets.p.thefittingroom.xyz/shop-sdk/assets/v5"
|
|
@@ -45876,7 +45821,8 @@ const CONFIGS = {
|
|
|
45876
45821
|
measurementId: "G-B7GDQ1Y9LL"
|
|
45877
45822
|
},
|
|
45878
45823
|
api: {
|
|
45879
|
-
baseUrl: "https://minecraftbadapple.com/api"
|
|
45824
|
+
baseUrl: "https://minecraftbadapple.com/api",
|
|
45825
|
+
vtoTimeoutMs: 12e4
|
|
45880
45826
|
},
|
|
45881
45827
|
asset: {
|
|
45882
45828
|
baseUrl: "http://minecraftbadapple.com/s3/tfr-assets-dev/shop-sdk/assets/v5"
|