@snowcone-app/ui 0.9.2 → 0.9.4
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/CHANGELOG.md +17 -0
- package/dist/index.cjs +192 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +192 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/composed/HeroProductImage.tsx +70 -0
- package/src/patterns/MockupPriorityProvider.tsx +49 -7
- package/src/patterns/RealtimeProvider.tsx +184 -5
- package/src/patterns/__tests__/MockupPriorityProvider.dragStop.test.tsx +112 -0
- package/src/patterns/__tests__/RealtimeProvider.cropDelivery.test.tsx +159 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#4344](https://github.com/snowcone-app/snowcone-monorepo/pull/4344) [`379a042`](https://github.com/snowcone-app/snowcone-monorepo/commit/379a042eb69a26f98fe0050f694c4ccf342e55b8) Thanks [@kevinsproles](https://github.com/kevinsproles)! - Per-placement render versions and an explicit re-render. SDK: `MockupResult.versions` (every placement folded into a frame, from boxes that stamp it), `getLatestSentVersions()`, and `rerender(mockupIds)` (answered by `rerender_queued`; refusals are typed `E_RERENDER_*` errors). UI: a picture's `data-render-state` is `current` only when every placement is at its latest sent version, and a picture whose cropped frame failed is re-rendered with `rerender` once the box stamps versions (falling back to forcing it with configs on an `E_RERENDER_*` refusal or an older box).
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`379a042`](https://github.com/snowcone-app/snowcone-monorepo/commit/379a042eb69a26f98fe0050f694c4ccf342e55b8)]:
|
|
10
|
+
- @snowcone-app/sdk@0.25.0
|
|
11
|
+
|
|
12
|
+
## 0.9.3
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#4338](https://github.com/snowcone-app/snowcone-monorepo/pull/4338) [`80ec516`](https://github.com/snowcone-app/snowcone-monorepo/commit/80ec5166f3b2d9e52a9791ae54c8d67e8a217cae) Thanks [@kevinsproles](https://github.com/kevinsproles)! - The full-frame fallback really re-renders: when a cropped frame can't be composited, the realtime session now sends a config that differs from the one before it and then the full set, so the render server (which ignores overlayCrop and sorts mockupIds when deciding whether a config changed anything) re-renders the failed pictures instead of leaving them on their old render until the next edit.
|
|
17
|
+
|
|
18
|
+
- [#4307](https://github.com/snowcone-app/snowcone-monorepo/pull/4307) [`1b510d0`](https://github.com/snowcone-app/snowcone-monorepo/commit/1b510d08c9e2c4399fd7731d3032b7e71abbfe9f) Thanks [@kevinsproles](https://github.com/kevinsproles)! - Live pictures update on every edit: a cropped frame that can't be shown no longer narrows the realtime session to one mockup (it re-renders full-frame with the whole set); a frame superseded mid-drag is dropped instead of killing cropped delivery; the priority provider requests every picture in view as one set and, after the edit stops, every registered picture. `HeroProductImage` exposes `data-canvas-version`, `data-render-version` and `data-render-state` (current | stale | refused); the realtime context adds `getCanvasVersion()`.
|
|
19
|
+
|
|
3
20
|
## 0.9.2
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -6062,6 +6062,11 @@ async function compositeCroppedFrame(args) {
|
|
|
6062
6062
|
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
6063
6063
|
var DEFAULT_ARTBOARD_SIZE = 2e3;
|
|
6064
6064
|
var MAX_CONCURRENT_BLOBS = 5;
|
|
6065
|
+
function mergeMockupIds(current, extra) {
|
|
6066
|
+
const out = [...current];
|
|
6067
|
+
for (const id of extra) if (!out.includes(id)) out.push(id);
|
|
6068
|
+
return out;
|
|
6069
|
+
}
|
|
6065
6070
|
var RealtimeContext = (0, import_react25.createContext)(
|
|
6066
6071
|
void 0
|
|
6067
6072
|
);
|
|
@@ -6140,6 +6145,12 @@ function RealtimeProvider({
|
|
|
6140
6145
|
},
|
|
6141
6146
|
onError: (error, detail) => {
|
|
6142
6147
|
console.error(`[RealtimeProvider] Render error [${detail.code}]:`, error);
|
|
6148
|
+
if (detail.code.startsWith("E_RERENDER_") && pendingRerenderRef.current.length > 0) {
|
|
6149
|
+
const ids = pendingRerenderRef.current;
|
|
6150
|
+
pendingRerenderRef.current = [];
|
|
6151
|
+
forceRerenderByConfigRef.current?.(ids);
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6143
6154
|
if (detail.mockupId) {
|
|
6144
6155
|
const id = detail.mockupId;
|
|
6145
6156
|
setUnavailableMockups((prev) => prev.get(id)?.code === detail.code ? prev : new Map(prev).set(id, detail));
|
|
@@ -6165,7 +6176,9 @@ function RealtimeProvider({
|
|
|
6165
6176
|
disconnect,
|
|
6166
6177
|
flushPendingBlobs,
|
|
6167
6178
|
hasPendingBlobs,
|
|
6168
|
-
getLastSentVersion
|
|
6179
|
+
getLastSentVersion,
|
|
6180
|
+
getLatestSentVersions,
|
|
6181
|
+
rerender: rerenderRaw
|
|
6169
6182
|
} = realtimeMockup;
|
|
6170
6183
|
const mockupResultsRef = (0, import_react25.useRef)([]);
|
|
6171
6184
|
const mockupSubscribersRef = (0, import_react25.useRef)(
|
|
@@ -6202,6 +6215,18 @@ function RealtimeProvider({
|
|
|
6202
6215
|
const compositeQueueRef = (0, import_react25.useRef)(/* @__PURE__ */ new Map());
|
|
6203
6216
|
const compositeBusyRef = (0, import_react25.useRef)(/* @__PURE__ */ new Set());
|
|
6204
6217
|
const compositeCanvasRef = (0, import_react25.useRef)(null);
|
|
6218
|
+
const requestedMockupIdsRef = (0, import_react25.useRef)([]);
|
|
6219
|
+
const serverStampsVersionsRef = (0, import_react25.useRef)(false);
|
|
6220
|
+
const pendingRerenderRef = (0, import_react25.useRef)([]);
|
|
6221
|
+
const forceRerenderByConfigRef = (0, import_react25.useRef)(null);
|
|
6222
|
+
const fullFrameRerenderIdsRef = (0, import_react25.useRef)(/* @__PURE__ */ new Set());
|
|
6223
|
+
const fullFrameRerenderTimerRef = (0, import_react25.useRef)(null);
|
|
6224
|
+
(0, import_react25.useEffect)(
|
|
6225
|
+
() => () => {
|
|
6226
|
+
if (fullFrameRerenderTimerRef.current) clearTimeout(fullFrameRerenderTimerRef.current);
|
|
6227
|
+
},
|
|
6228
|
+
[]
|
|
6229
|
+
);
|
|
6205
6230
|
const registerBlobUrl = (0, import_react25.useCallback)((blobOrUrl, placementName) => {
|
|
6206
6231
|
let url;
|
|
6207
6232
|
if (blobOrUrl instanceof Blob) {
|
|
@@ -6233,6 +6258,7 @@ function RealtimeProvider({
|
|
|
6233
6258
|
const base = lastConfigBaseRef.current;
|
|
6234
6259
|
if (!base) return;
|
|
6235
6260
|
croppedActiveRef.current = withCrop;
|
|
6261
|
+
requestedMockupIdsRef.current = base.mockupIds ?? [];
|
|
6236
6262
|
sendConfigRaw({
|
|
6237
6263
|
...base,
|
|
6238
6264
|
placementSettings: { ...placementSettingsOverridesRef.current },
|
|
@@ -6245,6 +6271,65 @@ function RealtimeProvider({
|
|
|
6245
6271
|
if (!croppedActiveRef.current) return;
|
|
6246
6272
|
sendCropConfig(false);
|
|
6247
6273
|
}, [sendCropConfig]);
|
|
6274
|
+
const sendFullFrameConfig = (0, import_react25.useCallback)(
|
|
6275
|
+
(mockupIds) => {
|
|
6276
|
+
const base = lastConfigBaseRef.current;
|
|
6277
|
+
if (!base) return;
|
|
6278
|
+
sendConfigRaw({
|
|
6279
|
+
...base,
|
|
6280
|
+
mockupIds,
|
|
6281
|
+
placementSettings: { ...placementSettingsOverridesRef.current }
|
|
6282
|
+
});
|
|
6283
|
+
},
|
|
6284
|
+
[sendConfigRaw]
|
|
6285
|
+
);
|
|
6286
|
+
const forceRerenderByConfig = (0, import_react25.useCallback)(
|
|
6287
|
+
(failed) => {
|
|
6288
|
+
const inForce = requestedMockupIdsRef.current;
|
|
6289
|
+
const fp = (list) => [...list].sort().join(",");
|
|
6290
|
+
const step = fp(failed) !== fp(inForce) ? failed : inForce.slice(0, -1);
|
|
6291
|
+
if (step.length === 0) {
|
|
6292
|
+
console.error(
|
|
6293
|
+
`[RealtimeProvider] E_OVERLAY_RERENDER_UNAVAILABLE: cannot force a full-frame re-render of ${failed.join(", ")} (single-mockup session on a box without \`rerender\`); it updates on the next edit.`
|
|
6294
|
+
);
|
|
6295
|
+
sendFullFrameConfig(inForce);
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
sendFullFrameConfig(step);
|
|
6299
|
+
sendFullFrameConfig(inForce);
|
|
6300
|
+
},
|
|
6301
|
+
[sendFullFrameConfig]
|
|
6302
|
+
);
|
|
6303
|
+
forceRerenderByConfigRef.current = forceRerenderByConfig;
|
|
6304
|
+
const rerenderFullFrame = (0, import_react25.useCallback)(
|
|
6305
|
+
(mockupId) => {
|
|
6306
|
+
fullFrameRerenderIdsRef.current.add(mockupId);
|
|
6307
|
+
if (fullFrameRerenderTimerRef.current) return;
|
|
6308
|
+
fullFrameRerenderTimerRef.current = setTimeout(() => {
|
|
6309
|
+
fullFrameRerenderTimerRef.current = null;
|
|
6310
|
+
const failed = [...fullFrameRerenderIdsRef.current];
|
|
6311
|
+
fullFrameRerenderIdsRef.current.clear();
|
|
6312
|
+
if (!lastConfigBaseRef.current || failed.length === 0) return;
|
|
6313
|
+
const previous = requestedMockupIdsRef.current;
|
|
6314
|
+
const ids = mergeMockupIds(previous, failed);
|
|
6315
|
+
croppedActiveRef.current = false;
|
|
6316
|
+
requestedMockupIdsRef.current = ids;
|
|
6317
|
+
const fp = (list) => [...list].sort().join(",");
|
|
6318
|
+
if (fp(ids) !== fp(previous)) {
|
|
6319
|
+
sendFullFrameConfig(ids);
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
if (serverStampsVersionsRef.current) {
|
|
6323
|
+
sendFullFrameConfig(ids);
|
|
6324
|
+
pendingRerenderRef.current = failed;
|
|
6325
|
+
if (rerenderRaw(failed)) return;
|
|
6326
|
+
pendingRerenderRef.current = [];
|
|
6327
|
+
}
|
|
6328
|
+
forceRerenderByConfig(failed);
|
|
6329
|
+
}, 0);
|
|
6330
|
+
},
|
|
6331
|
+
[sendFullFrameConfig, forceRerenderByConfig, rerenderRaw]
|
|
6332
|
+
);
|
|
6248
6333
|
const maybeFlipCrop = (0, import_react25.useCallback)(() => {
|
|
6249
6334
|
if (!croppedDeliveryEnabled || !cropCapableRef.current) return;
|
|
6250
6335
|
if (croppedActiveRef.current) return;
|
|
@@ -6278,6 +6363,10 @@ function RealtimeProvider({
|
|
|
6278
6363
|
else merged.push(result);
|
|
6279
6364
|
});
|
|
6280
6365
|
mockupResultsRef.current = merged;
|
|
6366
|
+
if (pendingRerenderRef.current.length > 0) {
|
|
6367
|
+
const delivered = new Set(results.map((r) => r.mockupId));
|
|
6368
|
+
pendingRerenderRef.current = pendingRerenderRef.current.filter((id) => !delivered.has(id));
|
|
6369
|
+
}
|
|
6281
6370
|
const clearedPlacements = /* @__PURE__ */ new Set();
|
|
6282
6371
|
results.forEach((result) => {
|
|
6283
6372
|
const wasPending = pendingMockupIdsRef.current.has(result.mockupId);
|
|
@@ -6344,8 +6433,7 @@ function RealtimeProvider({
|
|
|
6344
6433
|
console.error(
|
|
6345
6434
|
`[RealtimeProvider] E_OVERLAY_NO_BACKGROUND: cropped frame for ${result.mockupId} has no frozen background \u2014 falling back to full-frame and re-rendering it.`
|
|
6346
6435
|
);
|
|
6347
|
-
|
|
6348
|
-
updateMockupIdsRaw([result.mockupId]);
|
|
6436
|
+
rerenderFullFrame(result.mockupId);
|
|
6349
6437
|
return;
|
|
6350
6438
|
}
|
|
6351
6439
|
try {
|
|
@@ -6363,16 +6451,18 @@ function RealtimeProvider({
|
|
|
6363
6451
|
if (gen !== sceneGenRef.current) return;
|
|
6364
6452
|
deliverResults([{ ...result, imageUrl: registerBlobUrl(blob) }]);
|
|
6365
6453
|
} catch (err) {
|
|
6454
|
+
if (gen !== sceneGenRef.current || compositeQueueRef.current.has(result.mockupId) || processedFrameRef.current.get(result.mockupId) !== result.imageUrl) {
|
|
6455
|
+
return;
|
|
6456
|
+
}
|
|
6366
6457
|
console.error(
|
|
6367
6458
|
"[RealtimeProvider] E_OVERLAY_COMPOSITE_FAILED: cropped-frame composite failed \u2014 full-frame delivery for the rest of this session.",
|
|
6368
6459
|
err
|
|
6369
6460
|
);
|
|
6370
6461
|
cropCapableRef.current = false;
|
|
6371
|
-
|
|
6372
|
-
updateMockupIdsRaw([result.mockupId]);
|
|
6462
|
+
rerenderFullFrame(result.mockupId);
|
|
6373
6463
|
}
|
|
6374
6464
|
},
|
|
6375
|
-
[deliverResults, registerBlobUrl,
|
|
6465
|
+
[deliverResults, registerBlobUrl, rerenderFullFrame]
|
|
6376
6466
|
);
|
|
6377
6467
|
const pumpCompositeQueue = (0, import_react25.useCallback)(
|
|
6378
6468
|
(mockupId) => {
|
|
@@ -6398,6 +6488,9 @@ function RealtimeProvider({
|
|
|
6398
6488
|
}
|
|
6399
6489
|
console.log(`[RTP] rawMockupResults changed: ${rawMockupResults.length} result(s)`, rawMockupResults.map((r) => `${r.mockupId}${r.imageUrl ? " \u2713url" : " \u2717NO-URL"}${r.cropRect ? " [crop]" : ""}`));
|
|
6400
6490
|
const displayable = [];
|
|
6491
|
+
if (!serverStampsVersionsRef.current && rawMockupResults.some((r) => r.versions && typeof r.versions === "object")) {
|
|
6492
|
+
serverStampsVersionsRef.current = true;
|
|
6493
|
+
}
|
|
6401
6494
|
rawMockupResults.forEach((result) => {
|
|
6402
6495
|
if (!result.imageUrl) {
|
|
6403
6496
|
console.log(`[RTP] Result for ${result.mockupId} has NO imageUrl \u2014 not caching, pending NOT cleared`);
|
|
@@ -6560,6 +6653,7 @@ function RealtimeProvider({
|
|
|
6560
6653
|
});
|
|
6561
6654
|
}
|
|
6562
6655
|
lastConfigBaseRef.current = configPayload;
|
|
6656
|
+
requestedMockupIdsRef.current = mockupIds;
|
|
6563
6657
|
sendConfigRaw(
|
|
6564
6658
|
croppedActiveRef.current ? { ...configPayload, overlayCrop: "auto" } : configPayload
|
|
6565
6659
|
);
|
|
@@ -6619,6 +6713,7 @@ function RealtimeProvider({
|
|
|
6619
6713
|
if (croppedActiveRef.current && mockupIds.some((id) => !backgroundsRef.current.has(id))) {
|
|
6620
6714
|
unflipCrop();
|
|
6621
6715
|
}
|
|
6716
|
+
requestedMockupIdsRef.current = [...mockupIds];
|
|
6622
6717
|
return updateMockupIdsRaw(mockupIds);
|
|
6623
6718
|
},
|
|
6624
6719
|
[updateMockupIdsRaw, unflipCrop]
|
|
@@ -6875,6 +6970,12 @@ function RealtimeProvider({
|
|
|
6875
6970
|
},
|
|
6876
6971
|
[]
|
|
6877
6972
|
);
|
|
6973
|
+
const getLastSentVersionRef = (0, import_react25.useRef)(getLastSentVersion);
|
|
6974
|
+
getLastSentVersionRef.current = getLastSentVersion;
|
|
6975
|
+
const getCanvasVersion = (0, import_react25.useCallback)(() => getLastSentVersionRef.current?.() ?? 0, []);
|
|
6976
|
+
const getLatestSentVersionsRef = (0, import_react25.useRef)(getLatestSentVersions);
|
|
6977
|
+
getLatestSentVersionsRef.current = getLatestSentVersions;
|
|
6978
|
+
const getLatestSentVersionsStable = (0, import_react25.useCallback)(() => getLatestSentVersionsRef.current?.() ?? {}, []);
|
|
6878
6979
|
const stableSubscribePipelineSettled = (0, import_react25.useCallback)(
|
|
6879
6980
|
(callback) => {
|
|
6880
6981
|
return subscribePipelineSettledRef.current(callback);
|
|
@@ -6923,6 +7024,8 @@ function RealtimeProvider({
|
|
|
6923
7024
|
// Centralized URL management
|
|
6924
7025
|
getMockupUrl: getMockupUrl3,
|
|
6925
7026
|
getMockupUrls,
|
|
7027
|
+
getCanvasVersion,
|
|
7028
|
+
getLatestSentVersions: getLatestSentVersionsStable,
|
|
6926
7029
|
isMockupLoading,
|
|
6927
7030
|
registerBlobUrl,
|
|
6928
7031
|
// WebRTC streaming
|
|
@@ -6954,6 +7057,8 @@ function RealtimeProvider({
|
|
|
6954
7057
|
hasPendingBlobs,
|
|
6955
7058
|
getMockupUrl3,
|
|
6956
7059
|
getMockupUrls,
|
|
7060
|
+
getCanvasVersion,
|
|
7061
|
+
getLatestSentVersionsStable,
|
|
6957
7062
|
isMockupLoading,
|
|
6958
7063
|
registerBlobUrl
|
|
6959
7064
|
]
|
|
@@ -7822,17 +7927,33 @@ function MockupPriorityProvider({ children }) {
|
|
|
7822
7927
|
if (!realtimeContext?.updateMockupIds) {
|
|
7823
7928
|
return;
|
|
7824
7929
|
}
|
|
7930
|
+
const mobile = mobileCarouselRef.current;
|
|
7931
|
+
const universe = mobile && mobile.mockupIds.length > 0 ? mobile.mockupIds : registrationOrderRef.current;
|
|
7932
|
+
const inView = [];
|
|
7933
|
+
if (level1MockupRef.current) {
|
|
7934
|
+
inView.push(level1MockupRef.current);
|
|
7935
|
+
if (!mobile || mobile.mockupIds.length === 0) {
|
|
7936
|
+
visibilityMapRef.current.forEach((info, id) => {
|
|
7937
|
+
if (info.visibleArea > 0 && !inView.includes(id)) inView.push(id);
|
|
7938
|
+
});
|
|
7939
|
+
}
|
|
7940
|
+
}
|
|
7825
7941
|
let mockupIds;
|
|
7826
7942
|
switch (level) {
|
|
7827
7943
|
case 1:
|
|
7828
|
-
mockupIds =
|
|
7944
|
+
mockupIds = inView;
|
|
7829
7945
|
break;
|
|
7830
7946
|
case 2:
|
|
7831
|
-
mockupIds =
|
|
7947
|
+
mockupIds = level2MockupsRef.current.filter((id) => !inView.includes(id));
|
|
7832
7948
|
break;
|
|
7833
|
-
case 3:
|
|
7834
|
-
|
|
7949
|
+
case 3: {
|
|
7950
|
+
const earlier = /* @__PURE__ */ new Set([...inView, ...level2MockupsRef.current]);
|
|
7951
|
+
mockupIds = [
|
|
7952
|
+
...level3QueueRef.current.filter((id) => !earlier.has(id)),
|
|
7953
|
+
...universe.filter((id) => !earlier.has(id) && !level3QueueRef.current.includes(id))
|
|
7954
|
+
];
|
|
7835
7955
|
break;
|
|
7956
|
+
}
|
|
7836
7957
|
default:
|
|
7837
7958
|
mockupIds = [];
|
|
7838
7959
|
}
|
|
@@ -7857,6 +7978,16 @@ function MockupPriorityProvider({ children }) {
|
|
|
7857
7978
|
const isStillDragging = timeSinceLastPendingBlob < DRAG_STOP_DEBOUNCE_MS;
|
|
7858
7979
|
if (currentLevel === 1 && isStillDragging) {
|
|
7859
7980
|
currentRenderingLevelRef.current = null;
|
|
7981
|
+
if (levelAdvanceTimeoutRef.current) {
|
|
7982
|
+
clearTimeout(levelAdvanceTimeoutRef.current);
|
|
7983
|
+
}
|
|
7984
|
+
levelAdvanceTimeoutRef.current = setTimeout(() => {
|
|
7985
|
+
levelAdvanceTimeoutRef.current = null;
|
|
7986
|
+
const quiet = Date.now() - lastPendingBlobTimeRef.current >= DRAG_STOP_DEBOUNCE_MS;
|
|
7987
|
+
if (quiet && currentRenderingLevelRef.current === null && pendingMockupsRef.current.size === 0) {
|
|
7988
|
+
requestMockupsForLevel(2);
|
|
7989
|
+
}
|
|
7990
|
+
}, DRAG_STOP_DEBOUNCE_MS - timeSinceLastPendingBlob + 10);
|
|
7860
7991
|
return;
|
|
7861
7992
|
}
|
|
7862
7993
|
if (isStillDragging) {
|
|
@@ -10681,6 +10812,57 @@ var HeroProductImage = (0, import_react46.memo)(function HeroProductImage2({
|
|
|
10681
10812
|
unsubscribe?.();
|
|
10682
10813
|
};
|
|
10683
10814
|
}, [mode, realtimeContext?.subscribePendingBlob, placement]);
|
|
10815
|
+
const renderVersionRef = (0, import_react46.useRef)(0);
|
|
10816
|
+
const renderVersionsRef = (0, import_react46.useRef)(null);
|
|
10817
|
+
const getLatestSentVersions = realtimeContext?.getLatestSentVersions;
|
|
10818
|
+
const refusalCodeRef = (0, import_react46.useRef)(null);
|
|
10819
|
+
refusalCodeRef.current = unavailable?.code ?? null;
|
|
10820
|
+
const sessionErrorCodeRef = (0, import_react46.useRef)(null);
|
|
10821
|
+
sessionErrorCodeRef.current = realtimeContext?.renderError?.code ?? null;
|
|
10822
|
+
const getCanvasVersion = realtimeContext?.getCanvasVersion;
|
|
10823
|
+
const applyRenderStamp = (0, import_react46.useCallback)(() => {
|
|
10824
|
+
const node = containerRef.current;
|
|
10825
|
+
if (!node) return;
|
|
10826
|
+
const canvas = getCanvasVersion?.() ?? 0;
|
|
10827
|
+
if (canvas <= 0) {
|
|
10828
|
+
delete node.dataset.canvasVersion;
|
|
10829
|
+
delete node.dataset.renderVersion;
|
|
10830
|
+
delete node.dataset.renderState;
|
|
10831
|
+
return;
|
|
10832
|
+
}
|
|
10833
|
+
const rendered = renderVersionRef.current;
|
|
10834
|
+
const versions = renderVersionsRef.current;
|
|
10835
|
+
const sent = versions ? getLatestSentVersions?.() : void 0;
|
|
10836
|
+
const behind = versions && sent ? Object.entries(sent).some(([placement2, v]) => (versions[placement2] ?? -1) < v) : rendered < canvas;
|
|
10837
|
+
node.dataset.canvasVersion = String(canvas);
|
|
10838
|
+
node.dataset.renderVersion = String(rendered);
|
|
10839
|
+
node.dataset.renderState = refusalCodeRef.current || behind && sessionErrorCodeRef.current ? "refused" : behind ? "stale" : "current";
|
|
10840
|
+
}, [getCanvasVersion, getLatestSentVersions]);
|
|
10841
|
+
(0, import_react46.useEffect)(() => {
|
|
10842
|
+
applyRenderStamp();
|
|
10843
|
+
});
|
|
10844
|
+
(0, import_react46.useEffect)(() => {
|
|
10845
|
+
const subscribeSent = realtimeContext?.subscribeBlobSent;
|
|
10846
|
+
const subscribeResult = realtimeContext?.subscribeMockupResultById;
|
|
10847
|
+
if (!actualMockupId || !subscribeSent || !subscribeResult) return;
|
|
10848
|
+
const offSent = subscribeSent(() => applyRenderStamp());
|
|
10849
|
+
const offResult = subscribeResult(actualMockupId, (result) => {
|
|
10850
|
+
if (typeof result?.requestVersion === "number" && result.requestVersion >= renderVersionRef.current) {
|
|
10851
|
+
renderVersionRef.current = result.requestVersion;
|
|
10852
|
+
renderVersionsRef.current = result.versions ?? null;
|
|
10853
|
+
}
|
|
10854
|
+
applyRenderStamp();
|
|
10855
|
+
});
|
|
10856
|
+
return () => {
|
|
10857
|
+
offSent?.();
|
|
10858
|
+
offResult?.();
|
|
10859
|
+
};
|
|
10860
|
+
}, [
|
|
10861
|
+
actualMockupId,
|
|
10862
|
+
realtimeContext?.subscribeBlobSent,
|
|
10863
|
+
realtimeContext?.subscribeMockupResultById,
|
|
10864
|
+
applyRenderStamp
|
|
10865
|
+
]);
|
|
10684
10866
|
(0, import_react46.useEffect)(() => {
|
|
10685
10867
|
if (!priorityContext || !actualMockupId || !placement) return;
|
|
10686
10868
|
priorityContext.registerMockup(actualMockupId, placement, initialPriority);
|