@wowlabtech/mini-app-adapter 0.2.0 → 0.2.2

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
@@ -665,6 +665,27 @@ var BaseMiniAppAdapter = class {
665
665
  }
666
666
  requestFullscreen() {
667
667
  }
668
+ onViewportChange(callback) {
669
+ if (typeof window === "undefined") {
670
+ return () => {
671
+ };
672
+ }
673
+ const fallbackHeight = () => window.visualViewport?.height ?? window.innerHeight;
674
+ const notify = () => {
675
+ const height = fallbackHeight();
676
+ callback({ height, stableHeight: height });
677
+ };
678
+ notify();
679
+ const onResize = () => notify();
680
+ window.visualViewport?.addEventListener("resize", onResize);
681
+ window.visualViewport?.addEventListener("scroll", onResize);
682
+ window.addEventListener("resize", onResize);
683
+ return () => {
684
+ window.visualViewport?.removeEventListener("resize", onResize);
685
+ window.visualViewport?.removeEventListener("scroll", onResize);
686
+ window.removeEventListener("resize", onResize);
687
+ };
688
+ }
668
689
  getViewportInsets() {
669
690
  return void 0;
670
691
  }
@@ -739,6 +760,25 @@ var BaseMiniAppAdapter = class {
739
760
  return null;
740
761
  }
741
762
  async requestNotificationsPermission() {
763
+ if (typeof Notification === "undefined" || typeof Notification.requestPermission !== "function") {
764
+ return false;
765
+ }
766
+ try {
767
+ const permission = await Notification.requestPermission();
768
+ return permission === "granted";
769
+ } catch (error) {
770
+ console.warn("[tvm-app-adapter] requestNotificationsPermission fallback failed:", error);
771
+ return false;
772
+ }
773
+ }
774
+ async addToHomeScreen() {
775
+ return false;
776
+ }
777
+ async checkHomeScreenStatus() {
778
+ return "unknown";
779
+ }
780
+ async denyNotifications() {
781
+ console.warn("[tvm-app-adapter] denyNotifications fallback is not supported in this environment.");
742
782
  return false;
743
783
  }
744
784
  enableVerticalSwipes() {
@@ -1387,6 +1427,63 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1387
1427
  return void 0;
1388
1428
  }
1389
1429
  }
1430
+ onViewportChange(callback) {
1431
+ const disposers = [];
1432
+ const fallbackHeight = () => typeof window !== "undefined" ? window.visualViewport?.height ?? window.innerHeight : 0;
1433
+ const notify = (state) => {
1434
+ const heightCandidate = state?.height ?? this.safeHeightFromSdk();
1435
+ const stableCandidate = state?.stableHeight ?? this.stableHeightFromSdk();
1436
+ const height = Number.isFinite(heightCandidate) ? heightCandidate : fallbackHeight();
1437
+ const stableHeight = Number.isFinite(stableCandidate) && stableCandidate > 0 ? stableCandidate : height;
1438
+ callback({ height, stableHeight });
1439
+ };
1440
+ const ensureMounted = async () => {
1441
+ try {
1442
+ await ensureViewportMounted(this.getViewportMountOptions());
1443
+ } catch (error) {
1444
+ console.warn("[tvm-app-adapter] ensureViewportMounted failed:", error);
1445
+ }
1446
+ };
1447
+ void ensureMounted().finally(() => notify());
1448
+ const { sdkViewport } = this.getViewportMountOptions();
1449
+ if (typeof sdkViewport.on === "function") {
1450
+ try {
1451
+ const off2 = sdkViewport.on("change", (next) => notify(next));
1452
+ if (typeof off2 === "function") {
1453
+ disposers.push(off2);
1454
+ }
1455
+ } catch (error) {
1456
+ console.warn("[tvm-app-adapter] viewport.on(change) subscription failed:", error);
1457
+ }
1458
+ }
1459
+ try {
1460
+ if (typeof sdkViewport.height?.sub === "function") {
1461
+ disposers.push(sdkViewport.height.sub(() => notify()));
1462
+ }
1463
+ if (typeof sdkViewport.stableHeight?.sub === "function") {
1464
+ disposers.push(sdkViewport.stableHeight.sub(() => notify()));
1465
+ }
1466
+ } catch (error) {
1467
+ console.warn("[tvm-app-adapter] viewport signal subscriptions failed:", error);
1468
+ }
1469
+ if (typeof window !== "undefined") {
1470
+ const onResize = () => notify();
1471
+ window.visualViewport?.addEventListener("resize", onResize);
1472
+ window.addEventListener("resize", onResize);
1473
+ disposers.push(() => {
1474
+ window.visualViewport?.removeEventListener("resize", onResize);
1475
+ window.removeEventListener("resize", onResize);
1476
+ });
1477
+ }
1478
+ return () => {
1479
+ disposers.forEach((dispose) => {
1480
+ try {
1481
+ dispose();
1482
+ } catch {
1483
+ }
1484
+ });
1485
+ };
1486
+ }
1390
1487
  onAppearanceChange(callback) {
1391
1488
  this.appearanceListeners.add(callback);
1392
1489
  callback(this.environment.appearance);
@@ -1464,6 +1561,50 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1464
1561
  async shareStory(mediaUrl, options) {
1465
1562
  (0, import_sdk.shareStory)(mediaUrl, options);
1466
1563
  }
1564
+ async addToHomeScreen() {
1565
+ const isAvailable = typeof import_sdk.addToHomeScreen?.isAvailable === "function" ? import_sdk.addToHomeScreen.isAvailable() : true;
1566
+ if (!isAvailable) {
1567
+ return super.addToHomeScreen();
1568
+ }
1569
+ return new Promise((resolve) => {
1570
+ const cleanup = () => {
1571
+ (0, import_sdk.off)("home_screen_added", handleSuccess);
1572
+ (0, import_sdk.off)("home_screen_failed", handleFail);
1573
+ };
1574
+ const handleSuccess = () => {
1575
+ cleanup();
1576
+ resolve(true);
1577
+ };
1578
+ const handleFail = () => {
1579
+ cleanup();
1580
+ resolve(false);
1581
+ };
1582
+ (0, import_sdk.on)("home_screen_added", handleSuccess);
1583
+ (0, import_sdk.on)("home_screen_failed", handleFail);
1584
+ try {
1585
+ (0, import_sdk.addToHomeScreen)();
1586
+ } catch (error) {
1587
+ cleanup();
1588
+ console.warn("[tvm-app-adapter] Telegram addToHomeScreen failed:", error);
1589
+ resolve(false);
1590
+ }
1591
+ });
1592
+ }
1593
+ async checkHomeScreenStatus() {
1594
+ try {
1595
+ const status = await (0, import_sdk.checkHomeScreenStatus)();
1596
+ if (typeof status === "string") {
1597
+ return status;
1598
+ }
1599
+ if (typeof status === "boolean") {
1600
+ return status ? "added" : "not_added";
1601
+ }
1602
+ return "unknown";
1603
+ } catch (error) {
1604
+ console.warn("[tvm-app-adapter] Telegram checkHomeScreenStatus failed:", error);
1605
+ return "unknown";
1606
+ }
1607
+ }
1467
1608
  async requestPhone() {
1468
1609
  const contactFeature = ensureFeature(import_sdk.requestContact);
1469
1610
  if (!contactFeature.ok) {
@@ -1573,6 +1714,26 @@ var TelegramMiniAppAdapter = class extends BaseMiniAppAdapter {
1573
1714
  }
1574
1715
  };
1575
1716
  }
1717
+ safeHeightFromSdk() {
1718
+ try {
1719
+ if (typeof import_sdk.viewport.height === "function") {
1720
+ return import_sdk.viewport.height();
1721
+ }
1722
+ } catch {
1723
+ return void 0;
1724
+ }
1725
+ return void 0;
1726
+ }
1727
+ stableHeightFromSdk() {
1728
+ try {
1729
+ if (typeof import_sdk.viewport.stableHeight === "function") {
1730
+ return import_sdk.viewport.stableHeight();
1731
+ }
1732
+ } catch {
1733
+ return void 0;
1734
+ }
1735
+ return void 0;
1736
+ }
1576
1737
  notifyViewHide() {
1577
1738
  for (const listener of this.viewHideListeners) {
1578
1739
  try {
@@ -1803,6 +1964,39 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
1803
1964
  return false;
1804
1965
  }
1805
1966
  }
1967
+ async addToHomeScreen() {
1968
+ const supported = await this.supportsBridgeMethod("VKWebAppAddToHomeScreen");
1969
+ if (!supported) {
1970
+ console.warn("[tvm-app-adapter] VK addToHomeScreen not supported");
1971
+ return super.addToHomeScreen();
1972
+ }
1973
+ try {
1974
+ const response = await import_vk_bridge.default.send("VKWebAppAddToHomeScreen");
1975
+ if (response && typeof response === "object" && "result" in response) {
1976
+ return Boolean(response.result);
1977
+ }
1978
+ return true;
1979
+ } catch (error) {
1980
+ console.warn("[tvm-app-adapter] VK addToHomeScreen failed:", error);
1981
+ return false;
1982
+ }
1983
+ }
1984
+ async denyNotifications() {
1985
+ const supported = await this.supportsBridgeMethod("VKWebAppDenyNotifications");
1986
+ if (!supported) {
1987
+ return super.denyNotifications();
1988
+ }
1989
+ try {
1990
+ const response = await import_vk_bridge.default.send("VKWebAppDenyNotifications");
1991
+ if (response && typeof response === "object" && "result" in response) {
1992
+ return Boolean(response.result);
1993
+ }
1994
+ return true;
1995
+ } catch (error) {
1996
+ console.warn("[tvm-app-adapter] VK deny notifications failed:", error);
1997
+ return false;
1998
+ }
1999
+ }
1806
2000
  async scanQRCode(options) {
1807
2001
  const supportsQrScanner = await this.supportsBridgeMethod("VKWebAppOpenCodeReader");
1808
2002
  if (!supportsQrScanner) {
@@ -1838,6 +2032,25 @@ var VKMiniAppAdapter = class extends BaseMiniAppAdapter {
1838
2032
  };
1839
2033
  await import_vk_bridge.default.send("VKWebAppShowStoryBox", bridgeOptions);
1840
2034
  }
2035
+ shareUrl(url, text) {
2036
+ void this.shareUrlInternal(url, text);
2037
+ }
2038
+ async shareUrlInternal(url, text) {
2039
+ const supported = await this.supportsBridgeMethod("VKWebAppShare");
2040
+ if (!supported) {
2041
+ super.shareUrl(url, text ?? "");
2042
+ return;
2043
+ }
2044
+ try {
2045
+ await import_vk_bridge.default.send("VKWebAppShare", {
2046
+ link: url,
2047
+ ...text ? { text } : {}
2048
+ });
2049
+ } catch (error) {
2050
+ console.warn("[tvm-app-adapter] VK shareUrl failed:", error);
2051
+ super.shareUrl(url, text ?? "");
2052
+ }
2053
+ }
1841
2054
  async downloadFile(url, filename) {
1842
2055
  const supported = await this.supportsBridgeMethod("VKWebAppDownloadFile");
1843
2056
  if (!supported) {
@@ -2196,6 +2409,13 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2196
2409
  languageCode: navigator.language,
2197
2410
  isWebView: false
2198
2411
  });
2412
+ __publicField(this, "deferredPrompt", null);
2413
+ if (typeof window !== "undefined") {
2414
+ window.addEventListener("beforeinstallprompt", (event) => {
2415
+ event.preventDefault();
2416
+ this.deferredPrompt = event;
2417
+ });
2418
+ }
2199
2419
  }
2200
2420
  async downloadFile(url, filename) {
2201
2421
  try {
@@ -2344,6 +2564,49 @@ var WebMiniAppAdapter = class extends BaseMiniAppAdapter {
2344
2564
  }
2345
2565
  });
2346
2566
  }
2567
+ shareUrl(url, text) {
2568
+ if (navigator.share) {
2569
+ try {
2570
+ navigator.share({ title: text, text, url });
2571
+ return;
2572
+ } catch (err) {
2573
+ console.warn("Share cancelled or failed:", err);
2574
+ }
2575
+ }
2576
+ const payload = text ? `${text}
2577
+ ${url}` : url;
2578
+ this.copyTextToClipboard(payload).catch((err) => {
2579
+ console.warn("Share fallback (clipboard) failed:", err);
2580
+ });
2581
+ }
2582
+ async addToHomeScreen() {
2583
+ const isAndroid = /android/i.test(navigator.userAgent);
2584
+ if (!isAndroid || !this.deferredPrompt) {
2585
+ return super.addToHomeScreen();
2586
+ }
2587
+ try {
2588
+ this.deferredPrompt.prompt();
2589
+ const choice = await this.deferredPrompt.userChoice;
2590
+ this.deferredPrompt = null;
2591
+ return choice?.outcome === "accepted";
2592
+ } catch (error) {
2593
+ console.warn("[tvm-app-adapter] Web addToHomeScreen failed:", error);
2594
+ this.deferredPrompt = null;
2595
+ return false;
2596
+ }
2597
+ }
2598
+ async checkHomeScreenStatus() {
2599
+ try {
2600
+ const isStandalone = typeof window !== "undefined" && window.matchMedia?.("(display-mode: standalone)").matches || // iOS Safari specific flag
2601
+ typeof navigator !== "undefined" && navigator.standalone === true;
2602
+ if (isStandalone) {
2603
+ return "added";
2604
+ }
2605
+ return "unknown";
2606
+ } catch {
2607
+ return "unknown";
2608
+ }
2609
+ }
2347
2610
  };
2348
2611
 
2349
2612
  // src/adapters/index.ts