@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/dist/index.d.cts
CHANGED
|
@@ -2928,6 +2928,10 @@ declare function useShopOptional(): ShopContextValue | undefined;
|
|
|
2928
2928
|
interface MockupResult {
|
|
2929
2929
|
mockupId: string;
|
|
2930
2930
|
imageUrl: string;
|
|
2931
|
+
/** The canvas version this frame was rendered from (see getCanvasVersion). */
|
|
2932
|
+
requestVersion?: number;
|
|
2933
|
+
/** Per-placement versions folded into this frame (servers with per-placement stamps; see getLatestSentVersions). */
|
|
2934
|
+
versions?: Record<string, number>;
|
|
2931
2935
|
}
|
|
2932
2936
|
type RealtimeState = RealtimeContextValue;
|
|
2933
2937
|
interface RealtimeContextValue {
|
|
@@ -3043,6 +3047,19 @@ interface RealtimeContextValue {
|
|
|
3043
3047
|
* subsequent blobs to the same placement.
|
|
3044
3048
|
*/
|
|
3045
3049
|
subscribeBlobSent: (callback: (placement: string) => void) => () => void;
|
|
3050
|
+
/**
|
|
3051
|
+
* The canvas version the session last sent (0 before the first edit
|
|
3052
|
+
* reaches the server). Every rendered result carries the `requestVersion`
|
|
3053
|
+
* it was rendered from: a picture whose version is below this one shows an
|
|
3054
|
+
* older design. Read it after a `subscribeBlobSent` notification.
|
|
3055
|
+
*/
|
|
3056
|
+
getCanvasVersion: () => number;
|
|
3057
|
+
/**
|
|
3058
|
+
* The latest canvas version sent per placement. With a server that stamps
|
|
3059
|
+
* `versions` on every frame, a picture is current exactly when, for every
|
|
3060
|
+
* placement here, its frame's `versions[placement]` is at least this.
|
|
3061
|
+
*/
|
|
3062
|
+
getLatestSentVersions: () => Record<string, number>;
|
|
3046
3063
|
flushPendingBlobs: () => void;
|
|
3047
3064
|
hasPendingBlobs: () => boolean;
|
|
3048
3065
|
subscribePipelineSettled: (callback: () => void) => () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -2928,6 +2928,10 @@ declare function useShopOptional(): ShopContextValue | undefined;
|
|
|
2928
2928
|
interface MockupResult {
|
|
2929
2929
|
mockupId: string;
|
|
2930
2930
|
imageUrl: string;
|
|
2931
|
+
/** The canvas version this frame was rendered from (see getCanvasVersion). */
|
|
2932
|
+
requestVersion?: number;
|
|
2933
|
+
/** Per-placement versions folded into this frame (servers with per-placement stamps; see getLatestSentVersions). */
|
|
2934
|
+
versions?: Record<string, number>;
|
|
2931
2935
|
}
|
|
2932
2936
|
type RealtimeState = RealtimeContextValue;
|
|
2933
2937
|
interface RealtimeContextValue {
|
|
@@ -3043,6 +3047,19 @@ interface RealtimeContextValue {
|
|
|
3043
3047
|
* subsequent blobs to the same placement.
|
|
3044
3048
|
*/
|
|
3045
3049
|
subscribeBlobSent: (callback: (placement: string) => void) => () => void;
|
|
3050
|
+
/**
|
|
3051
|
+
* The canvas version the session last sent (0 before the first edit
|
|
3052
|
+
* reaches the server). Every rendered result carries the `requestVersion`
|
|
3053
|
+
* it was rendered from: a picture whose version is below this one shows an
|
|
3054
|
+
* older design. Read it after a `subscribeBlobSent` notification.
|
|
3055
|
+
*/
|
|
3056
|
+
getCanvasVersion: () => number;
|
|
3057
|
+
/**
|
|
3058
|
+
* The latest canvas version sent per placement. With a server that stamps
|
|
3059
|
+
* `versions` on every frame, a picture is current exactly when, for every
|
|
3060
|
+
* placement here, its frame's `versions[placement]` is at least this.
|
|
3061
|
+
*/
|
|
3062
|
+
getLatestSentVersions: () => Record<string, number>;
|
|
3046
3063
|
flushPendingBlobs: () => void;
|
|
3047
3064
|
hasPendingBlobs: () => boolean;
|
|
3048
3065
|
subscribePipelineSettled: (callback: () => void) => () => void;
|
package/dist/index.js
CHANGED
|
@@ -5703,6 +5703,11 @@ async function compositeCroppedFrame(args) {
|
|
|
5703
5703
|
import { jsx as jsx52 } from "react/jsx-runtime";
|
|
5704
5704
|
var DEFAULT_ARTBOARD_SIZE = 2e3;
|
|
5705
5705
|
var MAX_CONCURRENT_BLOBS = 5;
|
|
5706
|
+
function mergeMockupIds(current, extra) {
|
|
5707
|
+
const out = [...current];
|
|
5708
|
+
for (const id of extra) if (!out.includes(id)) out.push(id);
|
|
5709
|
+
return out;
|
|
5710
|
+
}
|
|
5706
5711
|
var RealtimeContext = createContext8(
|
|
5707
5712
|
void 0
|
|
5708
5713
|
);
|
|
@@ -5781,6 +5786,12 @@ function RealtimeProvider({
|
|
|
5781
5786
|
},
|
|
5782
5787
|
onError: (error, detail) => {
|
|
5783
5788
|
console.error(`[RealtimeProvider] Render error [${detail.code}]:`, error);
|
|
5789
|
+
if (detail.code.startsWith("E_RERENDER_") && pendingRerenderRef.current.length > 0) {
|
|
5790
|
+
const ids = pendingRerenderRef.current;
|
|
5791
|
+
pendingRerenderRef.current = [];
|
|
5792
|
+
forceRerenderByConfigRef.current?.(ids);
|
|
5793
|
+
return;
|
|
5794
|
+
}
|
|
5784
5795
|
if (detail.mockupId) {
|
|
5785
5796
|
const id = detail.mockupId;
|
|
5786
5797
|
setUnavailableMockups((prev) => prev.get(id)?.code === detail.code ? prev : new Map(prev).set(id, detail));
|
|
@@ -5806,7 +5817,9 @@ function RealtimeProvider({
|
|
|
5806
5817
|
disconnect,
|
|
5807
5818
|
flushPendingBlobs,
|
|
5808
5819
|
hasPendingBlobs,
|
|
5809
|
-
getLastSentVersion
|
|
5820
|
+
getLastSentVersion,
|
|
5821
|
+
getLatestSentVersions,
|
|
5822
|
+
rerender: rerenderRaw
|
|
5810
5823
|
} = realtimeMockup;
|
|
5811
5824
|
const mockupResultsRef = useRef12([]);
|
|
5812
5825
|
const mockupSubscribersRef = useRef12(
|
|
@@ -5843,6 +5856,18 @@ function RealtimeProvider({
|
|
|
5843
5856
|
const compositeQueueRef = useRef12(/* @__PURE__ */ new Map());
|
|
5844
5857
|
const compositeBusyRef = useRef12(/* @__PURE__ */ new Set());
|
|
5845
5858
|
const compositeCanvasRef = useRef12(null);
|
|
5859
|
+
const requestedMockupIdsRef = useRef12([]);
|
|
5860
|
+
const serverStampsVersionsRef = useRef12(false);
|
|
5861
|
+
const pendingRerenderRef = useRef12([]);
|
|
5862
|
+
const forceRerenderByConfigRef = useRef12(null);
|
|
5863
|
+
const fullFrameRerenderIdsRef = useRef12(/* @__PURE__ */ new Set());
|
|
5864
|
+
const fullFrameRerenderTimerRef = useRef12(null);
|
|
5865
|
+
useEffect17(
|
|
5866
|
+
() => () => {
|
|
5867
|
+
if (fullFrameRerenderTimerRef.current) clearTimeout(fullFrameRerenderTimerRef.current);
|
|
5868
|
+
},
|
|
5869
|
+
[]
|
|
5870
|
+
);
|
|
5846
5871
|
const registerBlobUrl = useCallback10((blobOrUrl, placementName) => {
|
|
5847
5872
|
let url;
|
|
5848
5873
|
if (blobOrUrl instanceof Blob) {
|
|
@@ -5874,6 +5899,7 @@ function RealtimeProvider({
|
|
|
5874
5899
|
const base = lastConfigBaseRef.current;
|
|
5875
5900
|
if (!base) return;
|
|
5876
5901
|
croppedActiveRef.current = withCrop;
|
|
5902
|
+
requestedMockupIdsRef.current = base.mockupIds ?? [];
|
|
5877
5903
|
sendConfigRaw({
|
|
5878
5904
|
...base,
|
|
5879
5905
|
placementSettings: { ...placementSettingsOverridesRef.current },
|
|
@@ -5886,6 +5912,65 @@ function RealtimeProvider({
|
|
|
5886
5912
|
if (!croppedActiveRef.current) return;
|
|
5887
5913
|
sendCropConfig(false);
|
|
5888
5914
|
}, [sendCropConfig]);
|
|
5915
|
+
const sendFullFrameConfig = useCallback10(
|
|
5916
|
+
(mockupIds) => {
|
|
5917
|
+
const base = lastConfigBaseRef.current;
|
|
5918
|
+
if (!base) return;
|
|
5919
|
+
sendConfigRaw({
|
|
5920
|
+
...base,
|
|
5921
|
+
mockupIds,
|
|
5922
|
+
placementSettings: { ...placementSettingsOverridesRef.current }
|
|
5923
|
+
});
|
|
5924
|
+
},
|
|
5925
|
+
[sendConfigRaw]
|
|
5926
|
+
);
|
|
5927
|
+
const forceRerenderByConfig = useCallback10(
|
|
5928
|
+
(failed) => {
|
|
5929
|
+
const inForce = requestedMockupIdsRef.current;
|
|
5930
|
+
const fp = (list) => [...list].sort().join(",");
|
|
5931
|
+
const step = fp(failed) !== fp(inForce) ? failed : inForce.slice(0, -1);
|
|
5932
|
+
if (step.length === 0) {
|
|
5933
|
+
console.error(
|
|
5934
|
+
`[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.`
|
|
5935
|
+
);
|
|
5936
|
+
sendFullFrameConfig(inForce);
|
|
5937
|
+
return;
|
|
5938
|
+
}
|
|
5939
|
+
sendFullFrameConfig(step);
|
|
5940
|
+
sendFullFrameConfig(inForce);
|
|
5941
|
+
},
|
|
5942
|
+
[sendFullFrameConfig]
|
|
5943
|
+
);
|
|
5944
|
+
forceRerenderByConfigRef.current = forceRerenderByConfig;
|
|
5945
|
+
const rerenderFullFrame = useCallback10(
|
|
5946
|
+
(mockupId) => {
|
|
5947
|
+
fullFrameRerenderIdsRef.current.add(mockupId);
|
|
5948
|
+
if (fullFrameRerenderTimerRef.current) return;
|
|
5949
|
+
fullFrameRerenderTimerRef.current = setTimeout(() => {
|
|
5950
|
+
fullFrameRerenderTimerRef.current = null;
|
|
5951
|
+
const failed = [...fullFrameRerenderIdsRef.current];
|
|
5952
|
+
fullFrameRerenderIdsRef.current.clear();
|
|
5953
|
+
if (!lastConfigBaseRef.current || failed.length === 0) return;
|
|
5954
|
+
const previous = requestedMockupIdsRef.current;
|
|
5955
|
+
const ids = mergeMockupIds(previous, failed);
|
|
5956
|
+
croppedActiveRef.current = false;
|
|
5957
|
+
requestedMockupIdsRef.current = ids;
|
|
5958
|
+
const fp = (list) => [...list].sort().join(",");
|
|
5959
|
+
if (fp(ids) !== fp(previous)) {
|
|
5960
|
+
sendFullFrameConfig(ids);
|
|
5961
|
+
return;
|
|
5962
|
+
}
|
|
5963
|
+
if (serverStampsVersionsRef.current) {
|
|
5964
|
+
sendFullFrameConfig(ids);
|
|
5965
|
+
pendingRerenderRef.current = failed;
|
|
5966
|
+
if (rerenderRaw(failed)) return;
|
|
5967
|
+
pendingRerenderRef.current = [];
|
|
5968
|
+
}
|
|
5969
|
+
forceRerenderByConfig(failed);
|
|
5970
|
+
}, 0);
|
|
5971
|
+
},
|
|
5972
|
+
[sendFullFrameConfig, forceRerenderByConfig, rerenderRaw]
|
|
5973
|
+
);
|
|
5889
5974
|
const maybeFlipCrop = useCallback10(() => {
|
|
5890
5975
|
if (!croppedDeliveryEnabled || !cropCapableRef.current) return;
|
|
5891
5976
|
if (croppedActiveRef.current) return;
|
|
@@ -5919,6 +6004,10 @@ function RealtimeProvider({
|
|
|
5919
6004
|
else merged.push(result);
|
|
5920
6005
|
});
|
|
5921
6006
|
mockupResultsRef.current = merged;
|
|
6007
|
+
if (pendingRerenderRef.current.length > 0) {
|
|
6008
|
+
const delivered = new Set(results.map((r) => r.mockupId));
|
|
6009
|
+
pendingRerenderRef.current = pendingRerenderRef.current.filter((id) => !delivered.has(id));
|
|
6010
|
+
}
|
|
5922
6011
|
const clearedPlacements = /* @__PURE__ */ new Set();
|
|
5923
6012
|
results.forEach((result) => {
|
|
5924
6013
|
const wasPending = pendingMockupIdsRef.current.has(result.mockupId);
|
|
@@ -5985,8 +6074,7 @@ function RealtimeProvider({
|
|
|
5985
6074
|
console.error(
|
|
5986
6075
|
`[RealtimeProvider] E_OVERLAY_NO_BACKGROUND: cropped frame for ${result.mockupId} has no frozen background \u2014 falling back to full-frame and re-rendering it.`
|
|
5987
6076
|
);
|
|
5988
|
-
|
|
5989
|
-
updateMockupIdsRaw([result.mockupId]);
|
|
6077
|
+
rerenderFullFrame(result.mockupId);
|
|
5990
6078
|
return;
|
|
5991
6079
|
}
|
|
5992
6080
|
try {
|
|
@@ -6004,16 +6092,18 @@ function RealtimeProvider({
|
|
|
6004
6092
|
if (gen !== sceneGenRef.current) return;
|
|
6005
6093
|
deliverResults([{ ...result, imageUrl: registerBlobUrl(blob) }]);
|
|
6006
6094
|
} catch (err) {
|
|
6095
|
+
if (gen !== sceneGenRef.current || compositeQueueRef.current.has(result.mockupId) || processedFrameRef.current.get(result.mockupId) !== result.imageUrl) {
|
|
6096
|
+
return;
|
|
6097
|
+
}
|
|
6007
6098
|
console.error(
|
|
6008
6099
|
"[RealtimeProvider] E_OVERLAY_COMPOSITE_FAILED: cropped-frame composite failed \u2014 full-frame delivery for the rest of this session.",
|
|
6009
6100
|
err
|
|
6010
6101
|
);
|
|
6011
6102
|
cropCapableRef.current = false;
|
|
6012
|
-
|
|
6013
|
-
updateMockupIdsRaw([result.mockupId]);
|
|
6103
|
+
rerenderFullFrame(result.mockupId);
|
|
6014
6104
|
}
|
|
6015
6105
|
},
|
|
6016
|
-
[deliverResults, registerBlobUrl,
|
|
6106
|
+
[deliverResults, registerBlobUrl, rerenderFullFrame]
|
|
6017
6107
|
);
|
|
6018
6108
|
const pumpCompositeQueue = useCallback10(
|
|
6019
6109
|
(mockupId) => {
|
|
@@ -6039,6 +6129,9 @@ function RealtimeProvider({
|
|
|
6039
6129
|
}
|
|
6040
6130
|
console.log(`[RTP] rawMockupResults changed: ${rawMockupResults.length} result(s)`, rawMockupResults.map((r) => `${r.mockupId}${r.imageUrl ? " \u2713url" : " \u2717NO-URL"}${r.cropRect ? " [crop]" : ""}`));
|
|
6041
6131
|
const displayable = [];
|
|
6132
|
+
if (!serverStampsVersionsRef.current && rawMockupResults.some((r) => r.versions && typeof r.versions === "object")) {
|
|
6133
|
+
serverStampsVersionsRef.current = true;
|
|
6134
|
+
}
|
|
6042
6135
|
rawMockupResults.forEach((result) => {
|
|
6043
6136
|
if (!result.imageUrl) {
|
|
6044
6137
|
console.log(`[RTP] Result for ${result.mockupId} has NO imageUrl \u2014 not caching, pending NOT cleared`);
|
|
@@ -6201,6 +6294,7 @@ function RealtimeProvider({
|
|
|
6201
6294
|
});
|
|
6202
6295
|
}
|
|
6203
6296
|
lastConfigBaseRef.current = configPayload;
|
|
6297
|
+
requestedMockupIdsRef.current = mockupIds;
|
|
6204
6298
|
sendConfigRaw(
|
|
6205
6299
|
croppedActiveRef.current ? { ...configPayload, overlayCrop: "auto" } : configPayload
|
|
6206
6300
|
);
|
|
@@ -6260,6 +6354,7 @@ function RealtimeProvider({
|
|
|
6260
6354
|
if (croppedActiveRef.current && mockupIds.some((id) => !backgroundsRef.current.has(id))) {
|
|
6261
6355
|
unflipCrop();
|
|
6262
6356
|
}
|
|
6357
|
+
requestedMockupIdsRef.current = [...mockupIds];
|
|
6263
6358
|
return updateMockupIdsRaw(mockupIds);
|
|
6264
6359
|
},
|
|
6265
6360
|
[updateMockupIdsRaw, unflipCrop]
|
|
@@ -6516,6 +6611,12 @@ function RealtimeProvider({
|
|
|
6516
6611
|
},
|
|
6517
6612
|
[]
|
|
6518
6613
|
);
|
|
6614
|
+
const getLastSentVersionRef = useRef12(getLastSentVersion);
|
|
6615
|
+
getLastSentVersionRef.current = getLastSentVersion;
|
|
6616
|
+
const getCanvasVersion = useCallback10(() => getLastSentVersionRef.current?.() ?? 0, []);
|
|
6617
|
+
const getLatestSentVersionsRef = useRef12(getLatestSentVersions);
|
|
6618
|
+
getLatestSentVersionsRef.current = getLatestSentVersions;
|
|
6619
|
+
const getLatestSentVersionsStable = useCallback10(() => getLatestSentVersionsRef.current?.() ?? {}, []);
|
|
6519
6620
|
const stableSubscribePipelineSettled = useCallback10(
|
|
6520
6621
|
(callback) => {
|
|
6521
6622
|
return subscribePipelineSettledRef.current(callback);
|
|
@@ -6564,6 +6665,8 @@ function RealtimeProvider({
|
|
|
6564
6665
|
// Centralized URL management
|
|
6565
6666
|
getMockupUrl: getMockupUrl3,
|
|
6566
6667
|
getMockupUrls,
|
|
6668
|
+
getCanvasVersion,
|
|
6669
|
+
getLatestSentVersions: getLatestSentVersionsStable,
|
|
6567
6670
|
isMockupLoading,
|
|
6568
6671
|
registerBlobUrl,
|
|
6569
6672
|
// WebRTC streaming
|
|
@@ -6595,6 +6698,8 @@ function RealtimeProvider({
|
|
|
6595
6698
|
hasPendingBlobs,
|
|
6596
6699
|
getMockupUrl3,
|
|
6597
6700
|
getMockupUrls,
|
|
6701
|
+
getCanvasVersion,
|
|
6702
|
+
getLatestSentVersionsStable,
|
|
6598
6703
|
isMockupLoading,
|
|
6599
6704
|
registerBlobUrl
|
|
6600
6705
|
]
|
|
@@ -7471,17 +7576,33 @@ function MockupPriorityProvider({ children }) {
|
|
|
7471
7576
|
if (!realtimeContext?.updateMockupIds) {
|
|
7472
7577
|
return;
|
|
7473
7578
|
}
|
|
7579
|
+
const mobile = mobileCarouselRef.current;
|
|
7580
|
+
const universe = mobile && mobile.mockupIds.length > 0 ? mobile.mockupIds : registrationOrderRef.current;
|
|
7581
|
+
const inView = [];
|
|
7582
|
+
if (level1MockupRef.current) {
|
|
7583
|
+
inView.push(level1MockupRef.current);
|
|
7584
|
+
if (!mobile || mobile.mockupIds.length === 0) {
|
|
7585
|
+
visibilityMapRef.current.forEach((info, id) => {
|
|
7586
|
+
if (info.visibleArea > 0 && !inView.includes(id)) inView.push(id);
|
|
7587
|
+
});
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7474
7590
|
let mockupIds;
|
|
7475
7591
|
switch (level) {
|
|
7476
7592
|
case 1:
|
|
7477
|
-
mockupIds =
|
|
7593
|
+
mockupIds = inView;
|
|
7478
7594
|
break;
|
|
7479
7595
|
case 2:
|
|
7480
|
-
mockupIds =
|
|
7596
|
+
mockupIds = level2MockupsRef.current.filter((id) => !inView.includes(id));
|
|
7481
7597
|
break;
|
|
7482
|
-
case 3:
|
|
7483
|
-
|
|
7598
|
+
case 3: {
|
|
7599
|
+
const earlier = /* @__PURE__ */ new Set([...inView, ...level2MockupsRef.current]);
|
|
7600
|
+
mockupIds = [
|
|
7601
|
+
...level3QueueRef.current.filter((id) => !earlier.has(id)),
|
|
7602
|
+
...universe.filter((id) => !earlier.has(id) && !level3QueueRef.current.includes(id))
|
|
7603
|
+
];
|
|
7484
7604
|
break;
|
|
7605
|
+
}
|
|
7485
7606
|
default:
|
|
7486
7607
|
mockupIds = [];
|
|
7487
7608
|
}
|
|
@@ -7506,6 +7627,16 @@ function MockupPriorityProvider({ children }) {
|
|
|
7506
7627
|
const isStillDragging = timeSinceLastPendingBlob < DRAG_STOP_DEBOUNCE_MS;
|
|
7507
7628
|
if (currentLevel === 1 && isStillDragging) {
|
|
7508
7629
|
currentRenderingLevelRef.current = null;
|
|
7630
|
+
if (levelAdvanceTimeoutRef.current) {
|
|
7631
|
+
clearTimeout(levelAdvanceTimeoutRef.current);
|
|
7632
|
+
}
|
|
7633
|
+
levelAdvanceTimeoutRef.current = setTimeout(() => {
|
|
7634
|
+
levelAdvanceTimeoutRef.current = null;
|
|
7635
|
+
const quiet = Date.now() - lastPendingBlobTimeRef.current >= DRAG_STOP_DEBOUNCE_MS;
|
|
7636
|
+
if (quiet && currentRenderingLevelRef.current === null && pendingMockupsRef.current.size === 0) {
|
|
7637
|
+
requestMockupsForLevel(2);
|
|
7638
|
+
}
|
|
7639
|
+
}, DRAG_STOP_DEBOUNCE_MS - timeSinceLastPendingBlob + 10);
|
|
7509
7640
|
return;
|
|
7510
7641
|
}
|
|
7511
7642
|
if (isStillDragging) {
|
|
@@ -10363,6 +10494,57 @@ var HeroProductImage = memo3(function HeroProductImage2({
|
|
|
10363
10494
|
unsubscribe?.();
|
|
10364
10495
|
};
|
|
10365
10496
|
}, [mode, realtimeContext?.subscribePendingBlob, placement]);
|
|
10497
|
+
const renderVersionRef = useRef23(0);
|
|
10498
|
+
const renderVersionsRef = useRef23(null);
|
|
10499
|
+
const getLatestSentVersions = realtimeContext?.getLatestSentVersions;
|
|
10500
|
+
const refusalCodeRef = useRef23(null);
|
|
10501
|
+
refusalCodeRef.current = unavailable?.code ?? null;
|
|
10502
|
+
const sessionErrorCodeRef = useRef23(null);
|
|
10503
|
+
sessionErrorCodeRef.current = realtimeContext?.renderError?.code ?? null;
|
|
10504
|
+
const getCanvasVersion = realtimeContext?.getCanvasVersion;
|
|
10505
|
+
const applyRenderStamp = useCallback18(() => {
|
|
10506
|
+
const node = containerRef.current;
|
|
10507
|
+
if (!node) return;
|
|
10508
|
+
const canvas = getCanvasVersion?.() ?? 0;
|
|
10509
|
+
if (canvas <= 0) {
|
|
10510
|
+
delete node.dataset.canvasVersion;
|
|
10511
|
+
delete node.dataset.renderVersion;
|
|
10512
|
+
delete node.dataset.renderState;
|
|
10513
|
+
return;
|
|
10514
|
+
}
|
|
10515
|
+
const rendered = renderVersionRef.current;
|
|
10516
|
+
const versions = renderVersionsRef.current;
|
|
10517
|
+
const sent = versions ? getLatestSentVersions?.() : void 0;
|
|
10518
|
+
const behind = versions && sent ? Object.entries(sent).some(([placement2, v]) => (versions[placement2] ?? -1) < v) : rendered < canvas;
|
|
10519
|
+
node.dataset.canvasVersion = String(canvas);
|
|
10520
|
+
node.dataset.renderVersion = String(rendered);
|
|
10521
|
+
node.dataset.renderState = refusalCodeRef.current || behind && sessionErrorCodeRef.current ? "refused" : behind ? "stale" : "current";
|
|
10522
|
+
}, [getCanvasVersion, getLatestSentVersions]);
|
|
10523
|
+
useEffect32(() => {
|
|
10524
|
+
applyRenderStamp();
|
|
10525
|
+
});
|
|
10526
|
+
useEffect32(() => {
|
|
10527
|
+
const subscribeSent = realtimeContext?.subscribeBlobSent;
|
|
10528
|
+
const subscribeResult = realtimeContext?.subscribeMockupResultById;
|
|
10529
|
+
if (!actualMockupId || !subscribeSent || !subscribeResult) return;
|
|
10530
|
+
const offSent = subscribeSent(() => applyRenderStamp());
|
|
10531
|
+
const offResult = subscribeResult(actualMockupId, (result) => {
|
|
10532
|
+
if (typeof result?.requestVersion === "number" && result.requestVersion >= renderVersionRef.current) {
|
|
10533
|
+
renderVersionRef.current = result.requestVersion;
|
|
10534
|
+
renderVersionsRef.current = result.versions ?? null;
|
|
10535
|
+
}
|
|
10536
|
+
applyRenderStamp();
|
|
10537
|
+
});
|
|
10538
|
+
return () => {
|
|
10539
|
+
offSent?.();
|
|
10540
|
+
offResult?.();
|
|
10541
|
+
};
|
|
10542
|
+
}, [
|
|
10543
|
+
actualMockupId,
|
|
10544
|
+
realtimeContext?.subscribeBlobSent,
|
|
10545
|
+
realtimeContext?.subscribeMockupResultById,
|
|
10546
|
+
applyRenderStamp
|
|
10547
|
+
]);
|
|
10366
10548
|
useEffect32(() => {
|
|
10367
10549
|
if (!priorityContext || !actualMockupId || !placement) return;
|
|
10368
10550
|
priorityContext.registerMockup(actualMockupId, placement, initialPriority);
|