@wowlabtech/mini-app-adapter 0.2.2 → 0.2.3
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.cjs +144 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -35
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -421,7 +421,7 @@ async function startHtml5Qrcode() {
|
|
|
421
421
|
if (typeof document === "undefined") {
|
|
422
422
|
throw new Error("QR scanning requires a browser environment.");
|
|
423
423
|
}
|
|
424
|
-
const [{ Html5Qrcode
|
|
424
|
+
const [{ Html5Qrcode }] = await Promise.all([import("html5-qrcode")]);
|
|
425
425
|
return new Promise((resolve, reject) => {
|
|
426
426
|
const elementId = `native-shell-qr-${Date.now()}`;
|
|
427
427
|
const overlay = document.createElement("div");
|
|
@@ -458,7 +458,7 @@ async function startHtml5Qrcode() {
|
|
|
458
458
|
const previousOverflow = document.body.style.overflow;
|
|
459
459
|
document.body.style.overflow = "hidden";
|
|
460
460
|
let disposed = false;
|
|
461
|
-
const scanner = new
|
|
461
|
+
const scanner = new Html5Qrcode(elementId);
|
|
462
462
|
const cleanup = async (result, error) => {
|
|
463
463
|
if (disposed) {
|
|
464
464
|
return;
|
|
@@ -2376,7 +2376,7 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2376
2376
|
};
|
|
2377
2377
|
|
|
2378
2378
|
// src/adapters/webAdapter.ts
|
|
2379
|
-
import
|
|
2379
|
+
import jsQR from "jsqr";
|
|
2380
2380
|
var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
2381
2381
|
constructor() {
|
|
2382
2382
|
super("web", {
|
|
@@ -2446,14 +2446,31 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2446
2446
|
closeBtn.style.cursor = "pointer";
|
|
2447
2447
|
closeBtn.style.zIndex = "9999999999";
|
|
2448
2448
|
overlay.appendChild(closeBtn);
|
|
2449
|
+
const scanSize = Math.min(Math.floor(Math.min(window.innerWidth, window.innerHeight) * 0.72), 320);
|
|
2450
|
+
const scanBox = document.createElement("div");
|
|
2451
|
+
scanBox.style.width = `${scanSize}px`;
|
|
2452
|
+
scanBox.style.height = `${scanSize}px`;
|
|
2453
|
+
scanBox.style.position = "relative";
|
|
2454
|
+
scanBox.style.flex = "0 0 auto";
|
|
2455
|
+
scanBox.style.borderRadius = "18px";
|
|
2456
|
+
scanBox.style.overflow = "hidden";
|
|
2457
|
+
overlay.appendChild(scanBox);
|
|
2449
2458
|
const scanArea = document.createElement("div");
|
|
2450
|
-
scanArea.
|
|
2451
|
-
scanArea.style.
|
|
2452
|
-
scanArea.style.
|
|
2453
|
-
scanArea.style.
|
|
2454
|
-
scanArea
|
|
2455
|
-
|
|
2456
|
-
|
|
2459
|
+
scanArea.style.position = "absolute";
|
|
2460
|
+
scanArea.style.inset = "0";
|
|
2461
|
+
scanArea.style.zIndex = "1";
|
|
2462
|
+
scanArea.style.background = "#000";
|
|
2463
|
+
scanBox.appendChild(scanArea);
|
|
2464
|
+
const video = document.createElement("video");
|
|
2465
|
+
video.setAttribute("playsinline", "true");
|
|
2466
|
+
video.autoplay = true;
|
|
2467
|
+
video.muted = true;
|
|
2468
|
+
video.style.width = "100%";
|
|
2469
|
+
video.style.height = "100%";
|
|
2470
|
+
video.style.objectFit = "cover";
|
|
2471
|
+
video.style.position = "absolute";
|
|
2472
|
+
video.style.inset = "0";
|
|
2473
|
+
scanArea.appendChild(video);
|
|
2457
2474
|
const frame = document.createElement("div");
|
|
2458
2475
|
frame.style.position = "absolute";
|
|
2459
2476
|
frame.style.top = "0";
|
|
@@ -2463,8 +2480,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2463
2480
|
frame.style.border = "3px solid rgba(255,255,255,0.9)";
|
|
2464
2481
|
frame.style.borderRadius = "18px";
|
|
2465
2482
|
frame.style.pointerEvents = "none";
|
|
2466
|
-
frame.style.zIndex = "
|
|
2467
|
-
|
|
2483
|
+
frame.style.zIndex = "3";
|
|
2484
|
+
scanBox.appendChild(frame);
|
|
2468
2485
|
const line = document.createElement("div");
|
|
2469
2486
|
line.style.position = "absolute";
|
|
2470
2487
|
line.style.left = "0";
|
|
@@ -2473,8 +2490,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2473
2490
|
line.style.background = "rgba(255,255,255,0.85)";
|
|
2474
2491
|
line.style.borderRadius = "2px";
|
|
2475
2492
|
line.style.animation = "qr-line 2s infinite";
|
|
2476
|
-
line.style.zIndex = "
|
|
2477
|
-
|
|
2493
|
+
line.style.zIndex = "4";
|
|
2494
|
+
scanBox.appendChild(line);
|
|
2478
2495
|
const styleTag = document.createElement("style");
|
|
2479
2496
|
styleTag.innerHTML = `
|
|
2480
2497
|
@keyframes qr-line {
|
|
@@ -2491,17 +2508,26 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2491
2508
|
hint.style.fontSize = "17px";
|
|
2492
2509
|
hint.style.opacity = "0.9";
|
|
2493
2510
|
overlay.appendChild(hint);
|
|
2494
|
-
const
|
|
2511
|
+
const canvas = document.createElement("canvas");
|
|
2512
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
2513
|
+
let stream = null;
|
|
2514
|
+
let rafId = null;
|
|
2515
|
+
let lastScanAt = 0;
|
|
2495
2516
|
let closed = false;
|
|
2496
2517
|
const finalize = async (result) => {
|
|
2497
2518
|
if (closed) {
|
|
2498
2519
|
return;
|
|
2499
2520
|
}
|
|
2500
2521
|
closed = true;
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2522
|
+
if (rafId !== null) {
|
|
2523
|
+
cancelAnimationFrame(rafId);
|
|
2524
|
+
rafId = null;
|
|
2504
2525
|
}
|
|
2526
|
+
if (stream) {
|
|
2527
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
2528
|
+
stream = null;
|
|
2529
|
+
}
|
|
2530
|
+
video.srcObject = null;
|
|
2505
2531
|
overlay.remove();
|
|
2506
2532
|
styleTag.remove();
|
|
2507
2533
|
document.body.style.overflow = prevOverflow;
|
|
@@ -2520,19 +2546,50 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2520
2546
|
};
|
|
2521
2547
|
closeBtn.onclick = () => closeScanner(null);
|
|
2522
2548
|
try {
|
|
2523
|
-
|
|
2524
|
-
{ facingMode: "environment" },
|
|
2525
|
-
{
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
()
|
|
2549
|
+
const constraints = [
|
|
2550
|
+
{ video: { facingMode: { ideal: "environment" } }, audio: false },
|
|
2551
|
+
{ video: { facingMode: "environment" }, audio: false },
|
|
2552
|
+
{ video: true, audio: false }
|
|
2553
|
+
];
|
|
2554
|
+
let lastError = null;
|
|
2555
|
+
for (const constraint of constraints) {
|
|
2556
|
+
try {
|
|
2557
|
+
stream = await navigator.mediaDevices.getUserMedia(constraint);
|
|
2558
|
+
break;
|
|
2559
|
+
} catch (error) {
|
|
2560
|
+
lastError = error;
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
if (!stream) {
|
|
2564
|
+
throw lastError instanceof Error ? lastError : new Error("Unable to access camera");
|
|
2565
|
+
}
|
|
2566
|
+
video.srcObject = stream;
|
|
2567
|
+
await video.play();
|
|
2568
|
+
const scanFrame = (now) => {
|
|
2569
|
+
if (closed) {
|
|
2570
|
+
return;
|
|
2534
2571
|
}
|
|
2535
|
-
|
|
2572
|
+
if (now - lastScanAt >= 100 && context && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
|
|
2573
|
+
const width = video.videoWidth;
|
|
2574
|
+
const height = video.videoHeight;
|
|
2575
|
+
if (width > 0 && height > 0) {
|
|
2576
|
+
canvas.width = width;
|
|
2577
|
+
canvas.height = height;
|
|
2578
|
+
context.drawImage(video, 0, 0, width, height);
|
|
2579
|
+
const imageData = context.getImageData(0, 0, width, height);
|
|
2580
|
+
const result = jsQR(imageData.data, width, height, {
|
|
2581
|
+
inversionAttempts: "attemptBoth"
|
|
2582
|
+
});
|
|
2583
|
+
if (result?.data) {
|
|
2584
|
+
closeScanner(result.data);
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
lastScanAt = now;
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
rafId = requestAnimationFrame(scanFrame);
|
|
2591
|
+
};
|
|
2592
|
+
rafId = requestAnimationFrame(scanFrame);
|
|
2536
2593
|
} catch (error) {
|
|
2537
2594
|
console.error("QR Start error", error);
|
|
2538
2595
|
closeScanner(null);
|
|
@@ -2585,6 +2642,8 @@ ${url}` : url;
|
|
|
2585
2642
|
};
|
|
2586
2643
|
|
|
2587
2644
|
// src/adapters/index.ts
|
|
2645
|
+
var CONFIRMED_PLATFORM_STORAGE_KEY = "mini-app-adapter:confirmed-platform";
|
|
2646
|
+
var CONFIRMED_PLATFORM_TTL_MS = 30 * 60 * 1e3;
|
|
2588
2647
|
function detectPlatform() {
|
|
2589
2648
|
if (typeof window === "undefined") {
|
|
2590
2649
|
return "web";
|
|
@@ -2596,30 +2655,76 @@ function detectPlatform() {
|
|
|
2596
2655
|
})();
|
|
2597
2656
|
const getParam = (name) => searchParams.get(name) ?? hashParams.get(name);
|
|
2598
2657
|
const hasParam = (...names) => names.some((name) => getParam(name));
|
|
2658
|
+
const readConfirmedPlatform = () => {
|
|
2659
|
+
try {
|
|
2660
|
+
const raw = window.sessionStorage.getItem(CONFIRMED_PLATFORM_STORAGE_KEY);
|
|
2661
|
+
if (!raw) {
|
|
2662
|
+
return null;
|
|
2663
|
+
}
|
|
2664
|
+
const parsed = JSON.parse(raw);
|
|
2665
|
+
if (!parsed?.platform || typeof parsed.ts !== "number") {
|
|
2666
|
+
return null;
|
|
2667
|
+
}
|
|
2668
|
+
if (Date.now() - parsed.ts > CONFIRMED_PLATFORM_TTL_MS) {
|
|
2669
|
+
return null;
|
|
2670
|
+
}
|
|
2671
|
+
return parsed.platform;
|
|
2672
|
+
} catch {
|
|
2673
|
+
return null;
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
const persistConfirmedPlatform = (platform) => {
|
|
2677
|
+
if (platform === "web") {
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
try {
|
|
2681
|
+
window.sessionStorage.setItem(
|
|
2682
|
+
CONFIRMED_PLATFORM_STORAGE_KEY,
|
|
2683
|
+
JSON.stringify({ platform, ts: Date.now() })
|
|
2684
|
+
);
|
|
2685
|
+
} catch {
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2599
2688
|
const shellPlatform = readShellPlatform();
|
|
2600
2689
|
if (shellPlatform) {
|
|
2690
|
+
persistConfirmedPlatform(shellPlatform);
|
|
2601
2691
|
return shellPlatform;
|
|
2602
2692
|
}
|
|
2603
2693
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
2604
2694
|
const hasNativeBridge = typeof window.NativeBridge?.postMessage === "function";
|
|
2605
2695
|
if (hasNativeBridge) {
|
|
2606
2696
|
if (userAgent.includes("android")) {
|
|
2697
|
+
persistConfirmedPlatform("shell_android");
|
|
2607
2698
|
return "shell_android";
|
|
2608
2699
|
}
|
|
2700
|
+
persistConfirmedPlatform("shell_ios");
|
|
2609
2701
|
return "shell_ios";
|
|
2610
2702
|
}
|
|
2611
|
-
|
|
2703
|
+
const telegramGlobals = window;
|
|
2704
|
+
const hasTelegramGlobal = Boolean(window.Telegram?.WebApp) || typeof telegramGlobals.TelegramWebviewProxy !== "undefined" || typeof telegramGlobals.TelegramGameProxy !== "undefined";
|
|
2705
|
+
const hasTelegramParams = hasParam("tgWebAppPlatform", "tgWebAppVersion", "tgWebAppData", "tgWebAppLanguage");
|
|
2706
|
+
if (hasTelegramGlobal || hasTelegramParams || userAgent.includes("telegram")) {
|
|
2707
|
+
persistConfirmedPlatform("telegram");
|
|
2612
2708
|
return "telegram";
|
|
2613
2709
|
}
|
|
2614
2710
|
if (window.WebApp) {
|
|
2711
|
+
persistConfirmedPlatform("max");
|
|
2615
2712
|
return "max";
|
|
2616
2713
|
}
|
|
2617
2714
|
if (window.MaxMiniApp) {
|
|
2715
|
+
persistConfirmedPlatform("max");
|
|
2618
2716
|
return "max";
|
|
2619
2717
|
}
|
|
2620
|
-
|
|
2718
|
+
const hasVkParams = hasParam("vk_app_id", "vk_platform", "vk_user_id", "vk_language", "sign");
|
|
2719
|
+
const hasVkUserAgentSignal = userAgent.includes("vkclient") || userAgent.includes("vk-android") || userAgent.includes("vkontakte");
|
|
2720
|
+
if (hasVkParams || hasVkUserAgentSignal) {
|
|
2721
|
+
persistConfirmedPlatform("vk");
|
|
2621
2722
|
return "vk";
|
|
2622
2723
|
}
|
|
2724
|
+
const confirmedPlatform = readConfirmedPlatform();
|
|
2725
|
+
if (confirmedPlatform && confirmedPlatform !== "web") {
|
|
2726
|
+
return confirmedPlatform;
|
|
2727
|
+
}
|
|
2623
2728
|
return "web";
|
|
2624
2729
|
}
|
|
2625
2730
|
function createAdapter(input) {
|
|
@@ -2820,14 +2925,18 @@ var cachedPlatform = null;
|
|
|
2820
2925
|
function getPlatform() {
|
|
2821
2926
|
const adapter = getActiveAdapter();
|
|
2822
2927
|
if (adapter) {
|
|
2823
|
-
|
|
2928
|
+
if (adapter.platform !== "web") {
|
|
2929
|
+
cachedPlatform = adapter.platform;
|
|
2930
|
+
}
|
|
2824
2931
|
return adapter.platform;
|
|
2825
2932
|
}
|
|
2826
|
-
if (cachedPlatform) {
|
|
2933
|
+
if (cachedPlatform && cachedPlatform !== "web") {
|
|
2827
2934
|
return cachedPlatform;
|
|
2828
2935
|
}
|
|
2829
2936
|
const detectedPlatform = detectPlatform();
|
|
2830
|
-
|
|
2937
|
+
if (detectedPlatform !== "web") {
|
|
2938
|
+
cachedPlatform = detectedPlatform;
|
|
2939
|
+
}
|
|
2831
2940
|
return detectedPlatform;
|
|
2832
2941
|
}
|
|
2833
2942
|
|