@playruo/embed 1.3.2 → 1.4.0
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/README.md +69 -0
- package/dist/index.cjs +217 -56
- package/dist/index.d.cts +157 -63
- package/dist/index.d.ts +157 -63
- package/dist/index.js +217 -56
- package/package.json +32 -9
package/dist/index.js
CHANGED
|
@@ -8,15 +8,33 @@ var normalizeOperation = (operation) => {
|
|
|
8
8
|
if (trimmedOperation === "") {
|
|
9
9
|
throw new Error("`operation` is required to build a Playruo embed URL.");
|
|
10
10
|
}
|
|
11
|
+
const operationQuery = trimmedOperation.split("?")[1];
|
|
12
|
+
if (operationQuery != null && new URLSearchParams(operationQuery).has("op")) {
|
|
13
|
+
console.warn(
|
|
14
|
+
"[PlayruoEmbed] `?op=` inside `operation` is deprecated: pass it as the `operationKey` prop."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
11
17
|
return trimmedOperation.startsWith("/") ? trimmedOperation.slice(1) : trimmedOperation;
|
|
12
18
|
};
|
|
13
|
-
var buildEmbedUrl = ({
|
|
14
|
-
|
|
19
|
+
var buildEmbedUrl = ({
|
|
20
|
+
operation,
|
|
21
|
+
operationKey,
|
|
22
|
+
storeUrl
|
|
23
|
+
}) => {
|
|
24
|
+
return buildEmbedUrlWithOrigin(PLAYRUO_ORIGIN, {
|
|
25
|
+
operation,
|
|
26
|
+
operationKey,
|
|
27
|
+
storeUrl
|
|
28
|
+
});
|
|
15
29
|
};
|
|
16
|
-
var buildEmbedUrlWithOrigin = (origin, { operation, storeUrl }) => {
|
|
30
|
+
var buildEmbedUrlWithOrigin = (origin, { operation, operationKey, storeUrl }) => {
|
|
17
31
|
const operationPath = normalizeOperation(operation);
|
|
18
32
|
const url = new URL(`/${operationPath}`, origin);
|
|
19
33
|
url.searchParams.set("iframe", "true");
|
|
34
|
+
const trimmedOperationKey = operationKey?.trim();
|
|
35
|
+
if (trimmedOperationKey) {
|
|
36
|
+
url.searchParams.set("op", trimmedOperationKey);
|
|
37
|
+
}
|
|
20
38
|
if (storeUrl) {
|
|
21
39
|
url.searchParams.set("storeUrl", storeUrl);
|
|
22
40
|
}
|
|
@@ -28,6 +46,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
28
46
|
var PlayruoEmbed = ({
|
|
29
47
|
baseUrl,
|
|
30
48
|
operation,
|
|
49
|
+
operationKey,
|
|
31
50
|
storeUrl,
|
|
32
51
|
showIframe = false,
|
|
33
52
|
className,
|
|
@@ -37,6 +56,9 @@ var PlayruoEmbed = ({
|
|
|
37
56
|
onEvent,
|
|
38
57
|
onPurchaseRequest,
|
|
39
58
|
onError,
|
|
59
|
+
onPasswordRequired,
|
|
60
|
+
onPasswordResult,
|
|
61
|
+
enableWindowedLaunch = false,
|
|
40
62
|
forwardEventsToParent = true,
|
|
41
63
|
renderState,
|
|
42
64
|
iframeRef: externalIframeRef
|
|
@@ -53,12 +75,24 @@ var PlayruoEmbed = ({
|
|
|
53
75
|
const [isValidChromium, setIsValidChromium] = useState(true);
|
|
54
76
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
55
77
|
const [iframeState, setIframeState] = useState(null);
|
|
78
|
+
const [passwordRequired, setPasswordRequired] = useState(false);
|
|
79
|
+
const [passwordChecked, setPasswordChecked] = useState(false);
|
|
80
|
+
const [passwordUnlocked, setPasswordUnlocked] = useState(false);
|
|
81
|
+
const [passwordError, setPasswordError] = useState(
|
|
82
|
+
void 0
|
|
83
|
+
);
|
|
56
84
|
const src = useMemo(
|
|
57
|
-
() => baseUrl != null && baseUrl !== "" ? buildEmbedUrlWithOrigin(baseUrl, { operation, storeUrl }) : buildEmbedUrl({ operation, storeUrl }),
|
|
58
|
-
[baseUrl, operation, storeUrl]
|
|
85
|
+
() => baseUrl != null && baseUrl !== "" ? buildEmbedUrlWithOrigin(baseUrl, { operation, operationKey, storeUrl }) : buildEmbedUrl({ operation, operationKey, storeUrl }),
|
|
86
|
+
[baseUrl, operation, operationKey, storeUrl]
|
|
59
87
|
);
|
|
88
|
+
const embedOrigin = useMemo(() => new URL(src).origin, [src]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
setPasswordRequired(false);
|
|
91
|
+
setPasswordChecked(false);
|
|
92
|
+
setPasswordUnlocked(false);
|
|
93
|
+
setPasswordError(void 0);
|
|
94
|
+
}, [src]);
|
|
60
95
|
useEffect(() => {
|
|
61
|
-
if (typeof window === "undefined") return;
|
|
62
96
|
const detectMobile = () => {
|
|
63
97
|
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
|
64
98
|
return /android|iPad|iPhone|iPod/i.test(userAgent);
|
|
@@ -96,15 +130,14 @@ var PlayruoEmbed = ({
|
|
|
96
130
|
tryNow: {
|
|
97
131
|
...currentState.tryNow,
|
|
98
132
|
available: hasIframeConfirmed,
|
|
99
|
-
|
|
133
|
+
// A missing status means the state came from the iframe, which only loads on
|
|
134
|
+
// compatible browsers — the incompatible copy is supplied by currentState.
|
|
135
|
+
status: currentState.tryNow.status ?? (hasIframeConfirmed ? "Ready" : "Checking availability\u2026")
|
|
100
136
|
}
|
|
101
137
|
}),
|
|
102
|
-
[currentState, hasIframeConfirmed
|
|
138
|
+
[currentState, hasIframeConfirmed]
|
|
103
139
|
);
|
|
104
140
|
const getKeyboardApi = useCallback(() => {
|
|
105
|
-
if (typeof navigator === "undefined") {
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
141
|
return navigator.keyboard;
|
|
109
142
|
}, []);
|
|
110
143
|
const releaseEscapeKeyLock = useCallback(() => {
|
|
@@ -134,6 +167,23 @@ var PlayruoEmbed = ({
|
|
|
134
167
|
},
|
|
135
168
|
[onDemoCompleted, storeUrl]
|
|
136
169
|
);
|
|
170
|
+
const postPasswordAction = useCallback(
|
|
171
|
+
(message) => {
|
|
172
|
+
const contentWindow = iframeRef.current?.contentWindow;
|
|
173
|
+
if (!contentWindow) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
contentWindow.postMessage(
|
|
178
|
+
{ type: "playruo", fromEmbed: true, ...message },
|
|
179
|
+
embedOrigin
|
|
180
|
+
);
|
|
181
|
+
} catch {
|
|
182
|
+
console.error("[PlayruoEmbed] Failed to send the password action");
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
[embedOrigin, iframeRef]
|
|
186
|
+
);
|
|
137
187
|
const postExitFullscreenToIframe = useCallback(() => {
|
|
138
188
|
const iframe = iframeRef.current;
|
|
139
189
|
if (!iframe) {
|
|
@@ -146,22 +196,22 @@ var PlayruoEmbed = ({
|
|
|
146
196
|
fromEmbed: true,
|
|
147
197
|
isFullscreen: false,
|
|
148
198
|
parentWillHandleFullscreen: true
|
|
149
|
-
},
|
|
199
|
+
}, embedOrigin);
|
|
150
200
|
} catch {
|
|
151
201
|
console.error("[PlayruoEmbed] Failed to exit fullscreen");
|
|
152
202
|
}
|
|
153
|
-
}, [iframeRef]);
|
|
203
|
+
}, [embedOrigin, iframeRef]);
|
|
154
204
|
const postExitFullscreenAckToIframe = useCallback(() => {
|
|
155
205
|
const iframe = iframeRef.current;
|
|
156
206
|
if (!iframe) {
|
|
157
207
|
return;
|
|
158
208
|
}
|
|
159
209
|
try {
|
|
160
|
-
iframe.contentWindow?.postMessage({ type: "ACK_EXIT_FULLSCREEN" },
|
|
210
|
+
iframe.contentWindow?.postMessage({ type: "ACK_EXIT_FULLSCREEN" }, embedOrigin);
|
|
161
211
|
} catch {
|
|
162
212
|
console.error("[PlayruoEmbed] Failed to acknowledge fullscreen exit");
|
|
163
213
|
}
|
|
164
|
-
}, [iframeRef]);
|
|
214
|
+
}, [embedOrigin, iframeRef]);
|
|
165
215
|
const exitEmbedFullscreen = useCallback(
|
|
166
216
|
async ({
|
|
167
217
|
reason = "cleanup",
|
|
@@ -247,7 +297,7 @@ var PlayruoEmbed = ({
|
|
|
247
297
|
}, []);
|
|
248
298
|
const forwardEventToParent = useCallback(
|
|
249
299
|
(message) => {
|
|
250
|
-
if (!forwardEventsToParent ||
|
|
300
|
+
if (!forwardEventsToParent || window.parent === window) {
|
|
251
301
|
return;
|
|
252
302
|
}
|
|
253
303
|
try {
|
|
@@ -297,19 +347,26 @@ var PlayruoEmbed = ({
|
|
|
297
347
|
hasNotifiedCompletionRef.current = false;
|
|
298
348
|
setShowGameIframe(true);
|
|
299
349
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
console.
|
|
350
|
+
let isParentFullscreen = false;
|
|
351
|
+
if (enableWindowedLaunch) {
|
|
352
|
+
releaseEscapeKeyLock();
|
|
353
|
+
console.log("[PlayruoEmbed] Windowed launch enabled, skipping fullscreen request");
|
|
354
|
+
} else {
|
|
355
|
+
try {
|
|
356
|
+
await requestImmersiveMode(iframe);
|
|
357
|
+
isParentFullscreen = true;
|
|
358
|
+
} catch (fsError) {
|
|
359
|
+
console.warn("[PlayruoEmbed] Fullscreen denied, continuing without:", fsError);
|
|
360
|
+
}
|
|
304
361
|
}
|
|
305
362
|
iframe.contentWindow?.postMessage({
|
|
306
363
|
type: "playruo",
|
|
307
364
|
action: "tryNowClick",
|
|
308
365
|
fromEmbed: true,
|
|
309
|
-
isFullscreen:
|
|
310
|
-
parentWillHandleFullscreen:
|
|
366
|
+
isFullscreen: isParentFullscreen,
|
|
367
|
+
parentWillHandleFullscreen: isParentFullscreen,
|
|
311
368
|
isReplay: hasPlayedOnce
|
|
312
|
-
},
|
|
369
|
+
}, embedOrigin);
|
|
313
370
|
console.log("[PlayruoEmbed] Sent tryNowClick to game", hasPlayedOnce ? "(replay)" : "(first time)");
|
|
314
371
|
iframe.focus();
|
|
315
372
|
requestAnimationFrame(() => {
|
|
@@ -322,7 +379,15 @@ var PlayruoEmbed = ({
|
|
|
322
379
|
setErrorMessage("Failed to start game");
|
|
323
380
|
onError?.("Failed to start game");
|
|
324
381
|
}
|
|
325
|
-
}, [
|
|
382
|
+
}, [
|
|
383
|
+
enableWindowedLaunch,
|
|
384
|
+
hasPlayedOnce,
|
|
385
|
+
iframeRef,
|
|
386
|
+
isCompatible,
|
|
387
|
+
onError,
|
|
388
|
+
releaseEscapeKeyLock,
|
|
389
|
+
requestImmersiveMode
|
|
390
|
+
]);
|
|
326
391
|
const handleBuyNow = useCallback(() => {
|
|
327
392
|
if (!storeUrl) {
|
|
328
393
|
console.warn("[PlayruoEmbed] No store URL provided");
|
|
@@ -348,6 +413,9 @@ var PlayruoEmbed = ({
|
|
|
348
413
|
if (!iframe || event.source !== iframe.contentWindow) {
|
|
349
414
|
return;
|
|
350
415
|
}
|
|
416
|
+
if (event.origin !== embedOrigin) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
351
419
|
console.log("\u{1F537} [Embed] Message received from iframe:", { type, data });
|
|
352
420
|
if (type === "playruo_ready") {
|
|
353
421
|
console.log("\u{1F537} [Embed] playruo_ready received");
|
|
@@ -416,6 +484,24 @@ var PlayruoEmbed = ({
|
|
|
416
484
|
const eventPayload = message.payload;
|
|
417
485
|
onEvent?.(eventPayload);
|
|
418
486
|
forwardEventToParent({ type: "playruo_event", data: eventPayload });
|
|
487
|
+
} else if (type === "playruo_password_required") {
|
|
488
|
+
console.log("\u{1F537} [Embed] playruo_password_required received");
|
|
489
|
+
setPasswordRequired(true);
|
|
490
|
+
setPasswordChecked(true);
|
|
491
|
+
setPasswordUnlocked(false);
|
|
492
|
+
setPasswordError(void 0);
|
|
493
|
+
onPasswordRequired?.();
|
|
494
|
+
} else if (type === "playruo_password_not_required") {
|
|
495
|
+
console.log("\u{1F537} [Embed] playruo_password_not_required received");
|
|
496
|
+
setPasswordRequired(false);
|
|
497
|
+
setPasswordChecked(true);
|
|
498
|
+
} else if (type === "playruo_password_result" && data) {
|
|
499
|
+
const result = data;
|
|
500
|
+
const success = result.success === true;
|
|
501
|
+
console.log("\u{1F537} [Embed] playruo_password_result received:", result);
|
|
502
|
+
setPasswordUnlocked(success);
|
|
503
|
+
setPasswordError(success ? void 0 : result.error ?? "unknown");
|
|
504
|
+
onPasswordResult?.({ success, error: result.error });
|
|
419
505
|
}
|
|
420
506
|
if (type === "error" || type === "closeForBuy" || type === "goToStore") {
|
|
421
507
|
console.log("\u{1F537} [Embed] Legacy message received:", type);
|
|
@@ -431,6 +517,7 @@ var PlayruoEmbed = ({
|
|
|
431
517
|
return () => window.removeEventListener("message", handleGameMessage);
|
|
432
518
|
}, [
|
|
433
519
|
cleanupEmbedSession,
|
|
520
|
+
embedOrigin,
|
|
434
521
|
exitEmbedFullscreen,
|
|
435
522
|
forwardEventToParent,
|
|
436
523
|
hasPlayedOnce,
|
|
@@ -438,6 +525,8 @@ var PlayruoEmbed = ({
|
|
|
438
525
|
markStreamActive,
|
|
439
526
|
onEvent,
|
|
440
527
|
onError,
|
|
528
|
+
onPasswordRequired,
|
|
529
|
+
onPasswordResult,
|
|
441
530
|
onPurchaseRequest,
|
|
442
531
|
onReady,
|
|
443
532
|
onStateChange,
|
|
@@ -460,6 +549,9 @@ var PlayruoEmbed = ({
|
|
|
460
549
|
if (!iframe || event.source !== iframe.contentWindow) {
|
|
461
550
|
return;
|
|
462
551
|
}
|
|
552
|
+
if (event.origin !== embedOrigin) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
463
555
|
const { type, requestId } = event.data || {};
|
|
464
556
|
if (type === "playruo_uuid_request") {
|
|
465
557
|
try {
|
|
@@ -474,34 +566,27 @@ var PlayruoEmbed = ({
|
|
|
474
566
|
requestId,
|
|
475
567
|
uuid: storedUUID
|
|
476
568
|
},
|
|
477
|
-
|
|
569
|
+
embedOrigin
|
|
478
570
|
);
|
|
479
571
|
} catch (e) {
|
|
480
572
|
}
|
|
481
|
-
} else if (type === "playruo_uuid_remove") {
|
|
482
|
-
try {
|
|
483
|
-
localStorage.removeItem("playruo_uuid");
|
|
484
|
-
} catch (e) {
|
|
485
|
-
}
|
|
486
573
|
}
|
|
487
574
|
};
|
|
488
575
|
window.addEventListener("message", handleUUIDMessage);
|
|
489
576
|
return () => window.removeEventListener("message", handleUUIDMessage);
|
|
490
|
-
}, [iframeRef]);
|
|
577
|
+
}, [embedOrigin, iframeRef]);
|
|
491
578
|
useEffect(() => {
|
|
492
579
|
const handleFullscreenChange = () => {
|
|
493
580
|
const iframe = iframeRef.current;
|
|
494
581
|
const fullscreenElement = getFullscreenElement();
|
|
495
582
|
const isEmbedFullscreen = Boolean(iframe && fullscreenElement === iframe);
|
|
496
|
-
if (isEmbedFullscreen) {
|
|
583
|
+
if (isEmbedFullscreen && iframe) {
|
|
497
584
|
wasEmbedFullscreenRef.current = true;
|
|
498
585
|
void lockEscapeKey();
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}, 200);
|
|
504
|
-
}
|
|
586
|
+
setTimeout(() => {
|
|
587
|
+
iframe.focus();
|
|
588
|
+
iframe.contentWindow?.focus();
|
|
589
|
+
}, 200);
|
|
505
590
|
return;
|
|
506
591
|
}
|
|
507
592
|
if (fullscreenElement) {
|
|
@@ -546,17 +631,16 @@ var PlayruoEmbed = ({
|
|
|
546
631
|
const logCrossOriginStatus = () => {
|
|
547
632
|
const isIsolated = self.crossOriginIsolated;
|
|
548
633
|
const hasSharedArrayBuffer = typeof SharedArrayBuffer !== "undefined";
|
|
549
|
-
const hasHighResTime = typeof performance !== "undefined" && typeof performance.now === "function";
|
|
550
634
|
console.group("[PlayruoEmbed] \u{1F512} Cross-Origin Isolation Status");
|
|
551
635
|
if (isIsolated) {
|
|
552
636
|
console.log("\u2705 Cross-origin isolation: ENABLED");
|
|
553
637
|
console.log("\u2705 SharedArrayBuffer support:", hasSharedArrayBuffer ? "Available" : "Not available");
|
|
554
|
-
console.log("\u2705 High-resolution timers:
|
|
638
|
+
console.log("\u2705 High-resolution timers: Full precision");
|
|
555
639
|
console.log("\u{1F3AE} Game performance: Optimal");
|
|
556
640
|
} else {
|
|
557
641
|
console.warn("\u26A0\uFE0F Cross-origin isolation: DISABLED");
|
|
558
642
|
console.warn("\u26A0\uFE0F SharedArrayBuffer support:", hasSharedArrayBuffer ? "Available" : "Not available");
|
|
559
|
-
console.warn("\u26A0\uFE0F High-resolution timers:
|
|
643
|
+
console.warn("\u26A0\uFE0F High-resolution timers: Reduced precision");
|
|
560
644
|
console.warn("\u{1F3AE} Game performance: May be degraded");
|
|
561
645
|
console.group("\u{1F4CB} Setup Instructions:");
|
|
562
646
|
console.log("Add these headers to your server:");
|
|
@@ -600,7 +684,7 @@ var PlayruoEmbed = ({
|
|
|
600
684
|
console.log("User agent:", navigator.userAgent);
|
|
601
685
|
console.log("Available APIs:", {
|
|
602
686
|
SharedArrayBuffer: hasSharedArrayBuffer,
|
|
603
|
-
Performance:
|
|
687
|
+
Performance: true,
|
|
604
688
|
WebGL: !!document.createElement("canvas").getContext("webgl"),
|
|
605
689
|
WebGL2: !!document.createElement("canvas").getContext("webgl2"),
|
|
606
690
|
WebAssembly: typeof WebAssembly !== "undefined"
|
|
@@ -632,22 +716,44 @@ var PlayruoEmbed = ({
|
|
|
632
716
|
tryNow: handleTryNow,
|
|
633
717
|
buyNow: handleBuyNow,
|
|
634
718
|
canTryNow,
|
|
635
|
-
canBuyNow: Boolean(isReady)
|
|
719
|
+
canBuyNow: Boolean(isReady),
|
|
720
|
+
submitPassword: (password2) => {
|
|
721
|
+
if (!password2) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
postPasswordAction({ action: "submitPassword", password: password2 });
|
|
725
|
+
},
|
|
726
|
+
requestPassword: () => postPasswordAction({ action: "requestPassword" })
|
|
727
|
+
};
|
|
728
|
+
const password = {
|
|
729
|
+
required: passwordRequired,
|
|
730
|
+
checked: passwordChecked,
|
|
731
|
+
unlocked: passwordUnlocked,
|
|
732
|
+
error: passwordError
|
|
636
733
|
};
|
|
637
734
|
if (errorMessage) {
|
|
638
|
-
return renderState({ status: "error", message: errorMessage, actions });
|
|
735
|
+
return renderState({ status: "error", message: errorMessage, actions, password });
|
|
736
|
+
}
|
|
737
|
+
if (password.required && !password.unlocked) {
|
|
738
|
+
return renderState({
|
|
739
|
+
status: "loading",
|
|
740
|
+
message: "Password required",
|
|
741
|
+
actions,
|
|
742
|
+
password
|
|
743
|
+
});
|
|
639
744
|
}
|
|
640
745
|
if (!isReady) {
|
|
641
|
-
return renderState({ status: "loading", message: "Loading\u2026", actions });
|
|
746
|
+
return renderState({ status: "loading", message: "Loading\u2026", actions, password });
|
|
642
747
|
}
|
|
643
748
|
if (isCompatible && !iframeState) {
|
|
644
|
-
return renderState({ status: "loading", message: "Checking availability\u2026", actions });
|
|
749
|
+
return renderState({ status: "loading", message: "Checking availability\u2026", actions, password });
|
|
645
750
|
}
|
|
646
751
|
const message = !isAvailable ? currentState.tryNow.status ?? exposedState.tryNow.status : void 0;
|
|
647
752
|
return renderState({
|
|
648
753
|
status: isAvailable ? "available" : "unavailable",
|
|
649
754
|
message,
|
|
650
|
-
actions
|
|
755
|
+
actions,
|
|
756
|
+
password
|
|
651
757
|
});
|
|
652
758
|
};
|
|
653
759
|
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
@@ -742,10 +848,16 @@ var EmbedTester = (props = {}) => {
|
|
|
742
848
|
const [storeUrlDraft, setStoreUrlDraft] = useState2(
|
|
743
849
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
744
850
|
);
|
|
851
|
+
const [enableWindowedLaunchDraft, setEnableWindowedLaunchDraft] = useState2(
|
|
852
|
+
props.enableWindowedLaunch ?? false
|
|
853
|
+
);
|
|
745
854
|
const [appliedOperation, setAppliedOperation] = useState2(props.operation ?? "iframe");
|
|
746
855
|
const [appliedStoreUrl, setAppliedStoreUrl] = useState2(
|
|
747
856
|
props.storeUrl ?? "https://store.steampowered.com/app/123456"
|
|
748
857
|
);
|
|
858
|
+
const [appliedEnableWindowedLaunch, setAppliedEnableWindowedLaunch] = useState2(
|
|
859
|
+
props.enableWindowedLaunch ?? false
|
|
860
|
+
);
|
|
749
861
|
const [key, setKey] = useState2(0);
|
|
750
862
|
const [hasUserClicked, setHasUserClicked] = useState2(false);
|
|
751
863
|
const [showIframe, setShowIframe] = useState2(false);
|
|
@@ -760,8 +872,9 @@ var EmbedTester = (props = {}) => {
|
|
|
760
872
|
const iframeRef = useRef2(null);
|
|
761
873
|
const isOperationPending = operationDraft !== appliedOperation;
|
|
762
874
|
const isStoreUrlPending = storeUrlDraft !== appliedStoreUrl;
|
|
763
|
-
const
|
|
764
|
-
const
|
|
875
|
+
const isWindowedLaunchPending = enableWindowedLaunchDraft !== appliedEnableWindowedLaunch;
|
|
876
|
+
const hasPendingConfigChanges = isOperationPending || isStoreUrlPending || isWindowedLaunchPending;
|
|
877
|
+
const pendingChangeCount = Number(isOperationPending) + Number(isStoreUrlPending) + Number(isWindowedLaunchPending);
|
|
765
878
|
const addEvent = useCallback2(
|
|
766
879
|
(message) => {
|
|
767
880
|
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
@@ -867,21 +980,20 @@ var EmbedTester = (props = {}) => {
|
|
|
867
980
|
addEvent(message);
|
|
868
981
|
}, [addEvent]);
|
|
869
982
|
const handleApply = useCallback2(() => {
|
|
870
|
-
if (!hasPendingConfigChanges) {
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
983
|
setAppliedOperation(operationDraft);
|
|
874
984
|
setAppliedStoreUrl(storeUrlDraft);
|
|
985
|
+
setAppliedEnableWindowedLaunch(enableWindowedLaunchDraft);
|
|
875
986
|
resetEmbedSession("Applied updated embed settings");
|
|
876
|
-
}, [
|
|
987
|
+
}, [enableWindowedLaunchDraft, operationDraft, resetEmbedSession, storeUrlDraft]);
|
|
877
988
|
const handleRestartSession = useCallback2(() => {
|
|
878
989
|
resetEmbedSession("Embed restarted with applied settings");
|
|
879
990
|
}, [resetEmbedSession]);
|
|
880
991
|
const handleResetDrafts = useCallback2(() => {
|
|
881
992
|
setOperationDraft(appliedOperation);
|
|
882
993
|
setStoreUrlDraft(appliedStoreUrl);
|
|
994
|
+
setEnableWindowedLaunchDraft(appliedEnableWindowedLaunch);
|
|
883
995
|
addEvent("Reverted draft changes to the applied configuration");
|
|
884
|
-
}, [addEvent, appliedOperation, appliedStoreUrl]);
|
|
996
|
+
}, [addEvent, appliedEnableWindowedLaunch, appliedOperation, appliedStoreUrl]);
|
|
885
997
|
const panelStyle = {
|
|
886
998
|
position: "relative",
|
|
887
999
|
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
@@ -1449,6 +1561,50 @@ var EmbedTester = (props = {}) => {
|
|
|
1449
1561
|
"Applied value: ",
|
|
1450
1562
|
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedStoreUrl })
|
|
1451
1563
|
] })
|
|
1564
|
+
] }),
|
|
1565
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs2("div", { style: labelStyle, children: [
|
|
1567
|
+
/* @__PURE__ */ jsx2("span", { children: "Launch mode" }),
|
|
1568
|
+
/* @__PURE__ */ jsxs2("span", { style: buildIndicatorStyle(isWindowedLaunchPending), children: [
|
|
1569
|
+
/* @__PURE__ */ jsx2("span", { style: buildIndicatorDotStyle(isWindowedLaunchPending) }),
|
|
1570
|
+
isWindowedLaunchPending ? "Pending" : "Live"
|
|
1571
|
+
] })
|
|
1572
|
+
] }),
|
|
1573
|
+
/* @__PURE__ */ jsxs2(
|
|
1574
|
+
"label",
|
|
1575
|
+
{
|
|
1576
|
+
htmlFor: "enableWindowedLaunch",
|
|
1577
|
+
style: {
|
|
1578
|
+
...buildInputShellStyle(isWindowedLaunchPending),
|
|
1579
|
+
justifyContent: "space-between",
|
|
1580
|
+
minHeight: "48px",
|
|
1581
|
+
cursor: "pointer"
|
|
1582
|
+
},
|
|
1583
|
+
children: [
|
|
1584
|
+
/* @__PURE__ */ jsx2("span", { style: { color: "#f8fafc", fontSize: "15px", fontWeight: 600 }, children: "Windowed launch" }),
|
|
1585
|
+
/* @__PURE__ */ jsx2(
|
|
1586
|
+
"input",
|
|
1587
|
+
{
|
|
1588
|
+
id: "enableWindowedLaunch",
|
|
1589
|
+
type: "checkbox",
|
|
1590
|
+
checked: enableWindowedLaunchDraft,
|
|
1591
|
+
onChange: (event) => setEnableWindowedLaunchDraft(event.target.checked),
|
|
1592
|
+
style: {
|
|
1593
|
+
width: "18px",
|
|
1594
|
+
height: "18px",
|
|
1595
|
+
accentColor: "#38bdf8",
|
|
1596
|
+
cursor: "pointer"
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
)
|
|
1600
|
+
]
|
|
1601
|
+
}
|
|
1602
|
+
),
|
|
1603
|
+
/* @__PURE__ */ jsxs2("p", { style: helperTextStyle, children: [
|
|
1604
|
+
"Applied value:",
|
|
1605
|
+
" ",
|
|
1606
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: appliedEnableWindowedLaunch ? "windowed" : "fullscreen request" })
|
|
1607
|
+
] })
|
|
1452
1608
|
] })
|
|
1453
1609
|
] }),
|
|
1454
1610
|
/* @__PURE__ */ jsxs2(
|
|
@@ -1728,6 +1884,10 @@ var EmbedTester = (props = {}) => {
|
|
|
1728
1884
|
/* @__PURE__ */ jsxs2("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
|
|
1729
1885
|
"Operation: ",
|
|
1730
1886
|
appliedOperation
|
|
1887
|
+
] }),
|
|
1888
|
+
/* @__PURE__ */ jsxs2("span", { style: { ...codeStyle, padding: "6px 10px" }, children: [
|
|
1889
|
+
"Launch: ",
|
|
1890
|
+
appliedEnableWindowedLaunch ? "windowed" : "fullscreen request"
|
|
1731
1891
|
] })
|
|
1732
1892
|
] })
|
|
1733
1893
|
]
|
|
@@ -1803,6 +1963,7 @@ var EmbedTester = (props = {}) => {
|
|
|
1803
1963
|
onPurchaseRequest: handlePurchaseRequest,
|
|
1804
1964
|
onReady: handleReady,
|
|
1805
1965
|
onStateChange: handleStateChange,
|
|
1966
|
+
enableWindowedLaunch: appliedEnableWindowedLaunch,
|
|
1806
1967
|
operation: appliedOperation,
|
|
1807
1968
|
renderState: ({ actions, message, status }) => /* @__PURE__ */ jsx2(Fragment, { children: !hasUserClicked && /* @__PURE__ */ jsx2(
|
|
1808
1969
|
"div",
|
|
@@ -1927,12 +2088,12 @@ var isPlayruoEmbedMessage = (data) => {
|
|
|
1927
2088
|
if (!data || typeof data !== "object") return false;
|
|
1928
2089
|
const msg = data;
|
|
1929
2090
|
if (typeof msg.type !== "string") return false;
|
|
1930
|
-
return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error" || msg.type === "playruo_session_over" || msg.type === "playruo_stream_start" || msg.type === "playruo_stream_stop" || msg.type === "REQUEST_EXIT_FULLSCREEN" || msg.type === "playruo_event";
|
|
2091
|
+
return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error" || msg.type === "playruo_session_over" || msg.type === "playruo_stream_start" || msg.type === "playruo_stream_stop" || msg.type === "REQUEST_EXIT_FULLSCREEN" || msg.type === "playruo_event" || msg.type === "playruo_password_required" || msg.type === "playruo_password_not_required" || msg.type === "playruo_password_result";
|
|
1931
2092
|
};
|
|
1932
2093
|
var isPlayruoInternalMessage = (data) => {
|
|
1933
2094
|
if (!data || typeof data !== "object") return false;
|
|
1934
2095
|
const msg = data;
|
|
1935
|
-
return msg.type === "playruo" && typeof msg.action === "string" && (msg.action === "tryNowClick" || msg.action === "exitFullscreen" || msg.action === "mute" || msg.action === "unmute") && msg.fromEmbed === true;
|
|
2096
|
+
return msg.type === "playruo" && typeof msg.action === "string" && (msg.action === "tryNowClick" || msg.action === "exitFullscreen" || msg.action === "mute" || msg.action === "unmute" || msg.action === "submitPassword" || msg.action === "requestPassword") && msg.fromEmbed === true;
|
|
1936
2097
|
};
|
|
1937
2098
|
export {
|
|
1938
2099
|
EmbedTester,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playruo/embed",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "React SDK to embed Playruo with postMessage API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,27 +9,50 @@
|
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
},
|
|
18
|
-
"files": [
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
19
25
|
"sideEffects": false,
|
|
20
26
|
"peerDependencies": {
|
|
21
27
|
"react": ">=18",
|
|
22
28
|
"react-dom": ">=18"
|
|
23
29
|
},
|
|
24
30
|
"devDependencies": {
|
|
31
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
32
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
33
|
+
"@testing-library/react": "^16.3.0",
|
|
34
|
+
"@testing-library/user-event": "^14.6.1",
|
|
35
|
+
"@types/node": "^26.2.0",
|
|
36
|
+
"@types/react": "^19",
|
|
37
|
+
"@types/react-dom": "^19",
|
|
38
|
+
"@vitest/browser": "^3.2.7",
|
|
39
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
40
|
+
"jsdom": "^26.1.0",
|
|
41
|
+
"playwright": "^1.62.1",
|
|
42
|
+
"react": "^19.1.0",
|
|
43
|
+
"react-dom": "^19.1.0",
|
|
25
44
|
"tsup": "^8.0.0",
|
|
26
45
|
"typescript": "^5.4.0",
|
|
27
|
-
"
|
|
28
|
-
"@types/react-dom": "^19"
|
|
46
|
+
"vitest": "^3.2.4"
|
|
29
47
|
},
|
|
30
48
|
"scripts": {
|
|
31
49
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
32
50
|
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
33
|
-
"prepare": "npm run build"
|
|
51
|
+
"prepare": "npm run build",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
53
|
+
"check:exports": "attw --pack .",
|
|
54
|
+
"test": "npm run build && vitest run --coverage",
|
|
55
|
+
"test:browser": "vitest run --config vitest.browser.config.ts",
|
|
56
|
+
"test:watch": "npm run build && vitest"
|
|
34
57
|
}
|
|
35
58
|
}
|