@wowlabtech/mini-app-adapter 0.2.1 → 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 +242 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +242 -35
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -56,6 +56,10 @@ interface MiniAppViewportInsets {
|
|
|
56
56
|
safeArea: MiniAppSafeAreaInsets;
|
|
57
57
|
contentSafeArea: MiniAppSafeAreaInsets;
|
|
58
58
|
}
|
|
59
|
+
interface MiniAppViewportState {
|
|
60
|
+
height: number;
|
|
61
|
+
stableHeight: number;
|
|
62
|
+
}
|
|
59
63
|
interface MiniAppInitOptions {
|
|
60
64
|
/**
|
|
61
65
|
* Enables verbose logs for platforms that support it.
|
|
@@ -170,6 +174,10 @@ interface MiniAppAdapter {
|
|
|
170
174
|
* Requests fullscreen mode if supported by the platform.
|
|
171
175
|
*/
|
|
172
176
|
requestFullscreen?(): void;
|
|
177
|
+
/**
|
|
178
|
+
* Subscribes to viewport size changes. Returns disposer.
|
|
179
|
+
*/
|
|
180
|
+
onViewportChange?(callback: (state: MiniAppViewportState) => void): () => void;
|
|
173
181
|
/**
|
|
174
182
|
* Returns viewport safe area insets if supported by the platform.
|
|
175
183
|
*/
|
|
@@ -307,6 +315,10 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
|
|
|
307
315
|
getLaunchParams(): unknown;
|
|
308
316
|
decodeStartParam(_param: string): unknown;
|
|
309
317
|
requestFullscreen(): void;
|
|
318
|
+
onViewportChange(callback: (state: {
|
|
319
|
+
height: number;
|
|
320
|
+
stableHeight: number;
|
|
321
|
+
}) => void): () => void;
|
|
310
322
|
getViewportInsets(): MiniAppViewportInsets | undefined;
|
|
311
323
|
shareMessage(_message: string): Promise<void>;
|
|
312
324
|
shareUrl(_url: string, _text: string): void;
|
|
@@ -402,6 +414,10 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
|
|
|
402
414
|
decodeStartParam(param: string): unknown;
|
|
403
415
|
requestFullscreen(): void;
|
|
404
416
|
getViewportInsets(): MiniAppViewportInsets | undefined;
|
|
417
|
+
onViewportChange(callback: (state: {
|
|
418
|
+
height: number;
|
|
419
|
+
stableHeight: number;
|
|
420
|
+
}) => void): () => void;
|
|
405
421
|
onAppearanceChange(callback: (appearance: 'dark' | 'light' | undefined) => void): () => void;
|
|
406
422
|
setBackButtonVisibility(visible: boolean): void;
|
|
407
423
|
enableVerticalSwipes(): void;
|
|
@@ -420,6 +436,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
|
|
|
420
436
|
private prepareViewport;
|
|
421
437
|
private requestFullscreenInternal;
|
|
422
438
|
private getViewportMountOptions;
|
|
439
|
+
private safeHeightFromSdk;
|
|
440
|
+
private stableHeightFromSdk;
|
|
423
441
|
private notifyViewHide;
|
|
424
442
|
private notifyViewRestore;
|
|
425
443
|
protected onDestroy(): void;
|
|
@@ -536,4 +554,4 @@ declare function trackPixelEvent(event: string, payload?: Record<string, unknown
|
|
|
536
554
|
|
|
537
555
|
declare function getPlatform(): MiniAppPlatform;
|
|
538
556
|
|
|
539
|
-
export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
|
|
557
|
+
export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, type MiniAppViewportState, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
|
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,10 @@ interface MiniAppViewportInsets {
|
|
|
56
56
|
safeArea: MiniAppSafeAreaInsets;
|
|
57
57
|
contentSafeArea: MiniAppSafeAreaInsets;
|
|
58
58
|
}
|
|
59
|
+
interface MiniAppViewportState {
|
|
60
|
+
height: number;
|
|
61
|
+
stableHeight: number;
|
|
62
|
+
}
|
|
59
63
|
interface MiniAppInitOptions {
|
|
60
64
|
/**
|
|
61
65
|
* Enables verbose logs for platforms that support it.
|
|
@@ -170,6 +174,10 @@ interface MiniAppAdapter {
|
|
|
170
174
|
* Requests fullscreen mode if supported by the platform.
|
|
171
175
|
*/
|
|
172
176
|
requestFullscreen?(): void;
|
|
177
|
+
/**
|
|
178
|
+
* Subscribes to viewport size changes. Returns disposer.
|
|
179
|
+
*/
|
|
180
|
+
onViewportChange?(callback: (state: MiniAppViewportState) => void): () => void;
|
|
173
181
|
/**
|
|
174
182
|
* Returns viewport safe area insets if supported by the platform.
|
|
175
183
|
*/
|
|
@@ -307,6 +315,10 @@ declare abstract class BaseMiniAppAdapter implements MiniAppAdapter {
|
|
|
307
315
|
getLaunchParams(): unknown;
|
|
308
316
|
decodeStartParam(_param: string): unknown;
|
|
309
317
|
requestFullscreen(): void;
|
|
318
|
+
onViewportChange(callback: (state: {
|
|
319
|
+
height: number;
|
|
320
|
+
stableHeight: number;
|
|
321
|
+
}) => void): () => void;
|
|
310
322
|
getViewportInsets(): MiniAppViewportInsets | undefined;
|
|
311
323
|
shareMessage(_message: string): Promise<void>;
|
|
312
324
|
shareUrl(_url: string, _text: string): void;
|
|
@@ -402,6 +414,10 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
|
|
|
402
414
|
decodeStartParam(param: string): unknown;
|
|
403
415
|
requestFullscreen(): void;
|
|
404
416
|
getViewportInsets(): MiniAppViewportInsets | undefined;
|
|
417
|
+
onViewportChange(callback: (state: {
|
|
418
|
+
height: number;
|
|
419
|
+
stableHeight: number;
|
|
420
|
+
}) => void): () => void;
|
|
405
421
|
onAppearanceChange(callback: (appearance: 'dark' | 'light' | undefined) => void): () => void;
|
|
406
422
|
setBackButtonVisibility(visible: boolean): void;
|
|
407
423
|
enableVerticalSwipes(): void;
|
|
@@ -420,6 +436,8 @@ declare class TelegramMiniAppAdapter extends BaseMiniAppAdapter {
|
|
|
420
436
|
private prepareViewport;
|
|
421
437
|
private requestFullscreenInternal;
|
|
422
438
|
private getViewportMountOptions;
|
|
439
|
+
private safeHeightFromSdk;
|
|
440
|
+
private stableHeightFromSdk;
|
|
423
441
|
private notifyViewHide;
|
|
424
442
|
private notifyViewRestore;
|
|
425
443
|
protected onDestroy(): void;
|
|
@@ -536,4 +554,4 @@ declare function trackPixelEvent(event: string, payload?: Record<string, unknown
|
|
|
536
554
|
|
|
537
555
|
declare function getPlatform(): MiniAppPlatform;
|
|
538
556
|
|
|
539
|
-
export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
|
|
557
|
+
export { AdapterProvider, BaseMiniAppAdapter, type CreateAdapterOptions, MaxMiniAppAdapter, type MiniAppAdapter, type MiniAppCapability, type MiniAppEnvironmentInfo, type MiniAppInitOptions, type MiniAppPlatform, type MiniAppPopupOptions, type MiniAppQrScanOptions, type MiniAppViewportState, ShellMiniAppAdapter, TelegramMiniAppAdapter, VKMiniAppAdapter, WebMiniAppAdapter, configureVkPixel, createAdapter, createShellAPI, detectPlatform, getActiveAdapter, getPlatform, isShell, isShellAndroid, isShellIOS, readShellPlatform, requestShellPushPermission, shell, storeShellToken, trackConversionEvent, trackPixelEvent, useAdapterTheme, useMiniAppAdapter, useSafeArea };
|
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;
|
|
@@ -607,6 +607,27 @@ var BaseMiniAppAdapter = class {
|
|
|
607
607
|
}
|
|
608
608
|
requestFullscreen() {
|
|
609
609
|
}
|
|
610
|
+
onViewportChange(callback) {
|
|
611
|
+
if (typeof window === "undefined") {
|
|
612
|
+
return () => {
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
const fallbackHeight = () => window.visualViewport?.height ?? window.innerHeight;
|
|
616
|
+
const notify = () => {
|
|
617
|
+
const height = fallbackHeight();
|
|
618
|
+
callback({ height, stableHeight: height });
|
|
619
|
+
};
|
|
620
|
+
notify();
|
|
621
|
+
const onResize = () => notify();
|
|
622
|
+
window.visualViewport?.addEventListener("resize", onResize);
|
|
623
|
+
window.visualViewport?.addEventListener("scroll", onResize);
|
|
624
|
+
window.addEventListener("resize", onResize);
|
|
625
|
+
return () => {
|
|
626
|
+
window.visualViewport?.removeEventListener("resize", onResize);
|
|
627
|
+
window.visualViewport?.removeEventListener("scroll", onResize);
|
|
628
|
+
window.removeEventListener("resize", onResize);
|
|
629
|
+
};
|
|
630
|
+
}
|
|
610
631
|
getViewportInsets() {
|
|
611
632
|
return void 0;
|
|
612
633
|
}
|
|
@@ -1379,6 +1400,63 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
1379
1400
|
return void 0;
|
|
1380
1401
|
}
|
|
1381
1402
|
}
|
|
1403
|
+
onViewportChange(callback) {
|
|
1404
|
+
const disposers = [];
|
|
1405
|
+
const fallbackHeight = () => typeof window !== "undefined" ? window.visualViewport?.height ?? window.innerHeight : 0;
|
|
1406
|
+
const notify = (state) => {
|
|
1407
|
+
const heightCandidate = state?.height ?? this.safeHeightFromSdk();
|
|
1408
|
+
const stableCandidate = state?.stableHeight ?? this.stableHeightFromSdk();
|
|
1409
|
+
const height = Number.isFinite(heightCandidate) ? heightCandidate : fallbackHeight();
|
|
1410
|
+
const stableHeight = Number.isFinite(stableCandidate) && stableCandidate > 0 ? stableCandidate : height;
|
|
1411
|
+
callback({ height, stableHeight });
|
|
1412
|
+
};
|
|
1413
|
+
const ensureMounted = async () => {
|
|
1414
|
+
try {
|
|
1415
|
+
await ensureViewportMounted(this.getViewportMountOptions());
|
|
1416
|
+
} catch (error) {
|
|
1417
|
+
console.warn("[tvm-app-adapter] ensureViewportMounted failed:", error);
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
void ensureMounted().finally(() => notify());
|
|
1421
|
+
const { sdkViewport } = this.getViewportMountOptions();
|
|
1422
|
+
if (typeof sdkViewport.on === "function") {
|
|
1423
|
+
try {
|
|
1424
|
+
const off2 = sdkViewport.on("change", (next) => notify(next));
|
|
1425
|
+
if (typeof off2 === "function") {
|
|
1426
|
+
disposers.push(off2);
|
|
1427
|
+
}
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
console.warn("[tvm-app-adapter] viewport.on(change) subscription failed:", error);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
try {
|
|
1433
|
+
if (typeof sdkViewport.height?.sub === "function") {
|
|
1434
|
+
disposers.push(sdkViewport.height.sub(() => notify()));
|
|
1435
|
+
}
|
|
1436
|
+
if (typeof sdkViewport.stableHeight?.sub === "function") {
|
|
1437
|
+
disposers.push(sdkViewport.stableHeight.sub(() => notify()));
|
|
1438
|
+
}
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
console.warn("[tvm-app-adapter] viewport signal subscriptions failed:", error);
|
|
1441
|
+
}
|
|
1442
|
+
if (typeof window !== "undefined") {
|
|
1443
|
+
const onResize = () => notify();
|
|
1444
|
+
window.visualViewport?.addEventListener("resize", onResize);
|
|
1445
|
+
window.addEventListener("resize", onResize);
|
|
1446
|
+
disposers.push(() => {
|
|
1447
|
+
window.visualViewport?.removeEventListener("resize", onResize);
|
|
1448
|
+
window.removeEventListener("resize", onResize);
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
return () => {
|
|
1452
|
+
disposers.forEach((dispose) => {
|
|
1453
|
+
try {
|
|
1454
|
+
dispose();
|
|
1455
|
+
} catch {
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1382
1460
|
onAppearanceChange(callback) {
|
|
1383
1461
|
this.appearanceListeners.add(callback);
|
|
1384
1462
|
callback(this.environment.appearance);
|
|
@@ -1609,6 +1687,26 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
1609
1687
|
}
|
|
1610
1688
|
};
|
|
1611
1689
|
}
|
|
1690
|
+
safeHeightFromSdk() {
|
|
1691
|
+
try {
|
|
1692
|
+
if (typeof rawViewport.height === "function") {
|
|
1693
|
+
return rawViewport.height();
|
|
1694
|
+
}
|
|
1695
|
+
} catch {
|
|
1696
|
+
return void 0;
|
|
1697
|
+
}
|
|
1698
|
+
return void 0;
|
|
1699
|
+
}
|
|
1700
|
+
stableHeightFromSdk() {
|
|
1701
|
+
try {
|
|
1702
|
+
if (typeof rawViewport.stableHeight === "function") {
|
|
1703
|
+
return rawViewport.stableHeight();
|
|
1704
|
+
}
|
|
1705
|
+
} catch {
|
|
1706
|
+
return void 0;
|
|
1707
|
+
}
|
|
1708
|
+
return void 0;
|
|
1709
|
+
}
|
|
1612
1710
|
notifyViewHide() {
|
|
1613
1711
|
for (const listener of this.viewHideListeners) {
|
|
1614
1712
|
try {
|
|
@@ -2278,7 +2376,7 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2278
2376
|
};
|
|
2279
2377
|
|
|
2280
2378
|
// src/adapters/webAdapter.ts
|
|
2281
|
-
import
|
|
2379
|
+
import jsQR from "jsqr";
|
|
2282
2380
|
var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
2283
2381
|
constructor() {
|
|
2284
2382
|
super("web", {
|
|
@@ -2348,14 +2446,31 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2348
2446
|
closeBtn.style.cursor = "pointer";
|
|
2349
2447
|
closeBtn.style.zIndex = "9999999999";
|
|
2350
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);
|
|
2351
2458
|
const scanArea = document.createElement("div");
|
|
2352
|
-
scanArea.
|
|
2353
|
-
scanArea.style.
|
|
2354
|
-
scanArea.style.
|
|
2355
|
-
scanArea.style.
|
|
2356
|
-
scanArea
|
|
2357
|
-
|
|
2358
|
-
|
|
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);
|
|
2359
2474
|
const frame = document.createElement("div");
|
|
2360
2475
|
frame.style.position = "absolute";
|
|
2361
2476
|
frame.style.top = "0";
|
|
@@ -2365,8 +2480,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2365
2480
|
frame.style.border = "3px solid rgba(255,255,255,0.9)";
|
|
2366
2481
|
frame.style.borderRadius = "18px";
|
|
2367
2482
|
frame.style.pointerEvents = "none";
|
|
2368
|
-
frame.style.zIndex = "
|
|
2369
|
-
|
|
2483
|
+
frame.style.zIndex = "3";
|
|
2484
|
+
scanBox.appendChild(frame);
|
|
2370
2485
|
const line = document.createElement("div");
|
|
2371
2486
|
line.style.position = "absolute";
|
|
2372
2487
|
line.style.left = "0";
|
|
@@ -2375,8 +2490,8 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2375
2490
|
line.style.background = "rgba(255,255,255,0.85)";
|
|
2376
2491
|
line.style.borderRadius = "2px";
|
|
2377
2492
|
line.style.animation = "qr-line 2s infinite";
|
|
2378
|
-
line.style.zIndex = "
|
|
2379
|
-
|
|
2493
|
+
line.style.zIndex = "4";
|
|
2494
|
+
scanBox.appendChild(line);
|
|
2380
2495
|
const styleTag = document.createElement("style");
|
|
2381
2496
|
styleTag.innerHTML = `
|
|
2382
2497
|
@keyframes qr-line {
|
|
@@ -2393,17 +2508,26 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2393
2508
|
hint.style.fontSize = "17px";
|
|
2394
2509
|
hint.style.opacity = "0.9";
|
|
2395
2510
|
overlay.appendChild(hint);
|
|
2396
|
-
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;
|
|
2397
2516
|
let closed = false;
|
|
2398
2517
|
const finalize = async (result) => {
|
|
2399
2518
|
if (closed) {
|
|
2400
2519
|
return;
|
|
2401
2520
|
}
|
|
2402
2521
|
closed = true;
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2522
|
+
if (rafId !== null) {
|
|
2523
|
+
cancelAnimationFrame(rafId);
|
|
2524
|
+
rafId = null;
|
|
2406
2525
|
}
|
|
2526
|
+
if (stream) {
|
|
2527
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
2528
|
+
stream = null;
|
|
2529
|
+
}
|
|
2530
|
+
video.srcObject = null;
|
|
2407
2531
|
overlay.remove();
|
|
2408
2532
|
styleTag.remove();
|
|
2409
2533
|
document.body.style.overflow = prevOverflow;
|
|
@@ -2422,19 +2546,50 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
|
|
|
2422
2546
|
};
|
|
2423
2547
|
closeBtn.onclick = () => closeScanner(null);
|
|
2424
2548
|
try {
|
|
2425
|
-
|
|
2426
|
-
{ facingMode: "environment" },
|
|
2427
|
-
{
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
()
|
|
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;
|
|
2436
2561
|
}
|
|
2437
|
-
|
|
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;
|
|
2571
|
+
}
|
|
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);
|
|
2438
2593
|
} catch (error) {
|
|
2439
2594
|
console.error("QR Start error", error);
|
|
2440
2595
|
closeScanner(null);
|
|
@@ -2487,6 +2642,8 @@ ${url}` : url;
|
|
|
2487
2642
|
};
|
|
2488
2643
|
|
|
2489
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;
|
|
2490
2647
|
function detectPlatform() {
|
|
2491
2648
|
if (typeof window === "undefined") {
|
|
2492
2649
|
return "web";
|
|
@@ -2498,30 +2655,76 @@ function detectPlatform() {
|
|
|
2498
2655
|
})();
|
|
2499
2656
|
const getParam = (name) => searchParams.get(name) ?? hashParams.get(name);
|
|
2500
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
|
+
};
|
|
2501
2688
|
const shellPlatform = readShellPlatform();
|
|
2502
2689
|
if (shellPlatform) {
|
|
2690
|
+
persistConfirmedPlatform(shellPlatform);
|
|
2503
2691
|
return shellPlatform;
|
|
2504
2692
|
}
|
|
2505
2693
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
2506
2694
|
const hasNativeBridge = typeof window.NativeBridge?.postMessage === "function";
|
|
2507
2695
|
if (hasNativeBridge) {
|
|
2508
2696
|
if (userAgent.includes("android")) {
|
|
2697
|
+
persistConfirmedPlatform("shell_android");
|
|
2509
2698
|
return "shell_android";
|
|
2510
2699
|
}
|
|
2700
|
+
persistConfirmedPlatform("shell_ios");
|
|
2511
2701
|
return "shell_ios";
|
|
2512
2702
|
}
|
|
2513
|
-
|
|
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");
|
|
2514
2708
|
return "telegram";
|
|
2515
2709
|
}
|
|
2516
2710
|
if (window.WebApp) {
|
|
2711
|
+
persistConfirmedPlatform("max");
|
|
2517
2712
|
return "max";
|
|
2518
2713
|
}
|
|
2519
2714
|
if (window.MaxMiniApp) {
|
|
2715
|
+
persistConfirmedPlatform("max");
|
|
2520
2716
|
return "max";
|
|
2521
2717
|
}
|
|
2522
|
-
|
|
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");
|
|
2523
2722
|
return "vk";
|
|
2524
2723
|
}
|
|
2724
|
+
const confirmedPlatform = readConfirmedPlatform();
|
|
2725
|
+
if (confirmedPlatform && confirmedPlatform !== "web") {
|
|
2726
|
+
return confirmedPlatform;
|
|
2727
|
+
}
|
|
2525
2728
|
return "web";
|
|
2526
2729
|
}
|
|
2527
2730
|
function createAdapter(input) {
|
|
@@ -2722,14 +2925,18 @@ var cachedPlatform = null;
|
|
|
2722
2925
|
function getPlatform() {
|
|
2723
2926
|
const adapter = getActiveAdapter();
|
|
2724
2927
|
if (adapter) {
|
|
2725
|
-
|
|
2928
|
+
if (adapter.platform !== "web") {
|
|
2929
|
+
cachedPlatform = adapter.platform;
|
|
2930
|
+
}
|
|
2726
2931
|
return adapter.platform;
|
|
2727
2932
|
}
|
|
2728
|
-
if (cachedPlatform) {
|
|
2933
|
+
if (cachedPlatform && cachedPlatform !== "web") {
|
|
2729
2934
|
return cachedPlatform;
|
|
2730
2935
|
}
|
|
2731
2936
|
const detectedPlatform = detectPlatform();
|
|
2732
|
-
|
|
2937
|
+
if (detectedPlatform !== "web") {
|
|
2938
|
+
cachedPlatform = detectedPlatform;
|
|
2939
|
+
}
|
|
2733
2940
|
return detectedPlatform;
|
|
2734
2941
|
}
|
|
2735
2942
|
|