@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 CHANGED
@@ -479,7 +479,7 @@ async function startHtml5Qrcode() {
479
479
  if (typeof document === "undefined") {
480
480
  throw new Error("QR scanning requires a browser environment.");
481
481
  }
482
- const [{ Html5Qrcode: Html5Qrcode2 }] = await Promise.all([import("html5-qrcode")]);
482
+ const [{ Html5Qrcode }] = await Promise.all([import("html5-qrcode")]);
483
483
  return new Promise((resolve, reject) => {
484
484
  const elementId = `native-shell-qr-${Date.now()}`;
485
485
  const overlay = document.createElement("div");
@@ -516,7 +516,7 @@ async function startHtml5Qrcode() {
516
516
  const previousOverflow = document.body.style.overflow;
517
517
  document.body.style.overflow = "hidden";
518
518
  let disposed = false;
519
- const scanner = new Html5Qrcode2(elementId);
519
+ const scanner = new Html5Qrcode(elementId);
520
520
  const cleanup = async (result, error) => {
521
521
  if (disposed) {
522
522
  return;
@@ -2401,7 +2401,7 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
2401
2401
  };
2402
2402
 
2403
2403
  // src/adapters/webAdapter.ts
2404
- var import_html5_qrcode = require("html5-qrcode");
2404
+ var import_jsqr = __toESM(require("jsqr"), 1);
2405
2405
  var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2406
2406
  constructor() {
2407
2407
  super("web", {
@@ -2471,14 +2471,31 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2471
2471
  closeBtn.style.cursor = "pointer";
2472
2472
  closeBtn.style.zIndex = "9999999999";
2473
2473
  overlay.appendChild(closeBtn);
2474
+ const scanSize = Math.min(Math.floor(Math.min(window.innerWidth, window.innerHeight) * 0.72), 320);
2475
+ const scanBox = document.createElement("div");
2476
+ scanBox.style.width = `${scanSize}px`;
2477
+ scanBox.style.height = `${scanSize}px`;
2478
+ scanBox.style.position = "relative";
2479
+ scanBox.style.flex = "0 0 auto";
2480
+ scanBox.style.borderRadius = "18px";
2481
+ scanBox.style.overflow = "hidden";
2482
+ overlay.appendChild(scanBox);
2474
2483
  const scanArea = document.createElement("div");
2475
- scanArea.id = "qr-reader";
2476
- scanArea.style.width = "300px";
2477
- scanArea.style.height = "300px";
2478
- scanArea.style.borderRadius = "18px";
2479
- scanArea.style.overflow = "hidden";
2480
- scanArea.style.position = "relative";
2481
- overlay.appendChild(scanArea);
2484
+ scanArea.style.position = "absolute";
2485
+ scanArea.style.inset = "0";
2486
+ scanArea.style.zIndex = "1";
2487
+ scanArea.style.background = "#000";
2488
+ scanBox.appendChild(scanArea);
2489
+ const video = document.createElement("video");
2490
+ video.setAttribute("playsinline", "true");
2491
+ video.autoplay = true;
2492
+ video.muted = true;
2493
+ video.style.width = "100%";
2494
+ video.style.height = "100%";
2495
+ video.style.objectFit = "cover";
2496
+ video.style.position = "absolute";
2497
+ video.style.inset = "0";
2498
+ scanArea.appendChild(video);
2482
2499
  const frame = document.createElement("div");
2483
2500
  frame.style.position = "absolute";
2484
2501
  frame.style.top = "0";
@@ -2488,8 +2505,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2488
2505
  frame.style.border = "3px solid rgba(255,255,255,0.9)";
2489
2506
  frame.style.borderRadius = "18px";
2490
2507
  frame.style.pointerEvents = "none";
2491
- frame.style.zIndex = "10";
2492
- scanArea.appendChild(frame);
2508
+ frame.style.zIndex = "3";
2509
+ scanBox.appendChild(frame);
2493
2510
  const line = document.createElement("div");
2494
2511
  line.style.position = "absolute";
2495
2512
  line.style.left = "0";
@@ -2498,8 +2515,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2498
2515
  line.style.background = "rgba(255,255,255,0.85)";
2499
2516
  line.style.borderRadius = "2px";
2500
2517
  line.style.animation = "qr-line 2s infinite";
2501
- line.style.zIndex = "11";
2502
- scanArea.appendChild(line);
2518
+ line.style.zIndex = "4";
2519
+ scanBox.appendChild(line);
2503
2520
  const styleTag = document.createElement("style");
2504
2521
  styleTag.innerHTML = `
2505
2522
  @keyframes qr-line {
@@ -2516,17 +2533,26 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2516
2533
  hint.style.fontSize = "17px";
2517
2534
  hint.style.opacity = "0.9";
2518
2535
  overlay.appendChild(hint);
2519
- const scanner = new import_html5_qrcode.Html5Qrcode("qr-reader");
2536
+ const canvas = document.createElement("canvas");
2537
+ const context = canvas.getContext("2d", { willReadFrequently: true });
2538
+ let stream = null;
2539
+ let rafId = null;
2540
+ let lastScanAt = 0;
2520
2541
  let closed = false;
2521
2542
  const finalize = async (result) => {
2522
2543
  if (closed) {
2523
2544
  return;
2524
2545
  }
2525
2546
  closed = true;
2526
- try {
2527
- await scanner.stop();
2528
- } catch {
2547
+ if (rafId !== null) {
2548
+ cancelAnimationFrame(rafId);
2549
+ rafId = null;
2529
2550
  }
2551
+ if (stream) {
2552
+ stream.getTracks().forEach((track) => track.stop());
2553
+ stream = null;
2554
+ }
2555
+ video.srcObject = null;
2530
2556
  overlay.remove();
2531
2557
  styleTag.remove();
2532
2558
  document.body.style.overflow = prevOverflow;
@@ -2545,19 +2571,50 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2545
2571
  };
2546
2572
  closeBtn.onclick = () => closeScanner(null);
2547
2573
  try {
2548
- await scanner.start(
2549
- { facingMode: "environment" },
2550
- {
2551
- fps: 10,
2552
- qrbox: { width: 250, height: 250 },
2553
- aspectRatio: 1
2554
- },
2555
- (decodedText) => {
2556
- closeScanner(decodedText);
2557
- },
2558
- () => {
2574
+ const constraints = [
2575
+ { video: { facingMode: { ideal: "environment" } }, audio: false },
2576
+ { video: { facingMode: "environment" }, audio: false },
2577
+ { video: true, audio: false }
2578
+ ];
2579
+ let lastError = null;
2580
+ for (const constraint of constraints) {
2581
+ try {
2582
+ stream = await navigator.mediaDevices.getUserMedia(constraint);
2583
+ break;
2584
+ } catch (error) {
2585
+ lastError = error;
2586
+ }
2587
+ }
2588
+ if (!stream) {
2589
+ throw lastError instanceof Error ? lastError : new Error("Unable to access camera");
2590
+ }
2591
+ video.srcObject = stream;
2592
+ await video.play();
2593
+ const scanFrame = (now) => {
2594
+ if (closed) {
2595
+ return;
2559
2596
  }
2560
- );
2597
+ if (now - lastScanAt >= 100 && context && video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
2598
+ const width = video.videoWidth;
2599
+ const height = video.videoHeight;
2600
+ if (width > 0 && height > 0) {
2601
+ canvas.width = width;
2602
+ canvas.height = height;
2603
+ context.drawImage(video, 0, 0, width, height);
2604
+ const imageData = context.getImageData(0, 0, width, height);
2605
+ const result = (0, import_jsqr.default)(imageData.data, width, height, {
2606
+ inversionAttempts: "attemptBoth"
2607
+ });
2608
+ if (result?.data) {
2609
+ closeScanner(result.data);
2610
+ return;
2611
+ }
2612
+ lastScanAt = now;
2613
+ }
2614
+ }
2615
+ rafId = requestAnimationFrame(scanFrame);
2616
+ };
2617
+ rafId = requestAnimationFrame(scanFrame);
2561
2618
  } catch (error) {
2562
2619
  console.error("QR Start error", error);
2563
2620
  closeScanner(null);
@@ -2610,6 +2667,8 @@ ${url}` : url;
2610
2667
  };
2611
2668
 
2612
2669
  // src/adapters/index.ts
2670
+ var CONFIRMED_PLATFORM_STORAGE_KEY = "mini-app-adapter:confirmed-platform";
2671
+ var CONFIRMED_PLATFORM_TTL_MS = 30 * 60 * 1e3;
2613
2672
  function detectPlatform() {
2614
2673
  if (typeof window === "undefined") {
2615
2674
  return "web";
@@ -2621,30 +2680,76 @@ function detectPlatform() {
2621
2680
  })();
2622
2681
  const getParam = (name) => searchParams.get(name) ?? hashParams.get(name);
2623
2682
  const hasParam = (...names) => names.some((name) => getParam(name));
2683
+ const readConfirmedPlatform = () => {
2684
+ try {
2685
+ const raw = window.sessionStorage.getItem(CONFIRMED_PLATFORM_STORAGE_KEY);
2686
+ if (!raw) {
2687
+ return null;
2688
+ }
2689
+ const parsed = JSON.parse(raw);
2690
+ if (!parsed?.platform || typeof parsed.ts !== "number") {
2691
+ return null;
2692
+ }
2693
+ if (Date.now() - parsed.ts > CONFIRMED_PLATFORM_TTL_MS) {
2694
+ return null;
2695
+ }
2696
+ return parsed.platform;
2697
+ } catch {
2698
+ return null;
2699
+ }
2700
+ };
2701
+ const persistConfirmedPlatform = (platform) => {
2702
+ if (platform === "web") {
2703
+ return;
2704
+ }
2705
+ try {
2706
+ window.sessionStorage.setItem(
2707
+ CONFIRMED_PLATFORM_STORAGE_KEY,
2708
+ JSON.stringify({ platform, ts: Date.now() })
2709
+ );
2710
+ } catch {
2711
+ }
2712
+ };
2624
2713
  const shellPlatform = readShellPlatform();
2625
2714
  if (shellPlatform) {
2715
+ persistConfirmedPlatform(shellPlatform);
2626
2716
  return shellPlatform;
2627
2717
  }
2628
2718
  const userAgent = navigator.userAgent.toLowerCase();
2629
2719
  const hasNativeBridge = typeof window.NativeBridge?.postMessage === "function";
2630
2720
  if (hasNativeBridge) {
2631
2721
  if (userAgent.includes("android")) {
2722
+ persistConfirmedPlatform("shell_android");
2632
2723
  return "shell_android";
2633
2724
  }
2725
+ persistConfirmedPlatform("shell_ios");
2634
2726
  return "shell_ios";
2635
2727
  }
2636
- if (window.Telegram?.WebApp || hasParam("tgWebAppPlatform", "tgWebAppVersion", "tgWebAppData", "tgWebAppLanguage") || userAgent.includes("telegram")) {
2728
+ const telegramGlobals = window;
2729
+ const hasTelegramGlobal = Boolean(window.Telegram?.WebApp) || typeof telegramGlobals.TelegramWebviewProxy !== "undefined" || typeof telegramGlobals.TelegramGameProxy !== "undefined";
2730
+ const hasTelegramParams = hasParam("tgWebAppPlatform", "tgWebAppVersion", "tgWebAppData", "tgWebAppLanguage");
2731
+ if (hasTelegramGlobal || hasTelegramParams || userAgent.includes("telegram")) {
2732
+ persistConfirmedPlatform("telegram");
2637
2733
  return "telegram";
2638
2734
  }
2639
2735
  if (window.WebApp) {
2736
+ persistConfirmedPlatform("max");
2640
2737
  return "max";
2641
2738
  }
2642
2739
  if (window.MaxMiniApp) {
2740
+ persistConfirmedPlatform("max");
2643
2741
  return "max";
2644
2742
  }
2645
- if (hasParam("vk_app_id", "vk_platform")) {
2743
+ const hasVkParams = hasParam("vk_app_id", "vk_platform", "vk_user_id", "vk_language", "sign");
2744
+ const hasVkUserAgentSignal = userAgent.includes("vkclient") || userAgent.includes("vk-android") || userAgent.includes("vkontakte");
2745
+ if (hasVkParams || hasVkUserAgentSignal) {
2746
+ persistConfirmedPlatform("vk");
2646
2747
  return "vk";
2647
2748
  }
2749
+ const confirmedPlatform = readConfirmedPlatform();
2750
+ if (confirmedPlatform && confirmedPlatform !== "web") {
2751
+ return confirmedPlatform;
2752
+ }
2648
2753
  return "web";
2649
2754
  }
2650
2755
  function createAdapter(input) {
@@ -2845,14 +2950,18 @@ var cachedPlatform = null;
2845
2950
  function getPlatform() {
2846
2951
  const adapter = getActiveAdapter();
2847
2952
  if (adapter) {
2848
- cachedPlatform = adapter.platform;
2953
+ if (adapter.platform !== "web") {
2954
+ cachedPlatform = adapter.platform;
2955
+ }
2849
2956
  return adapter.platform;
2850
2957
  }
2851
- if (cachedPlatform) {
2958
+ if (cachedPlatform && cachedPlatform !== "web") {
2852
2959
  return cachedPlatform;
2853
2960
  }
2854
2961
  const detectedPlatform = detectPlatform();
2855
- cachedPlatform = detectedPlatform;
2962
+ if (detectedPlatform !== "web") {
2963
+ cachedPlatform = detectedPlatform;
2964
+ }
2856
2965
  return detectedPlatform;
2857
2966
  }
2858
2967