@tonconnect/ui 2.0.0-beta.1 → 2.0.0-beta.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/lib/index.umd.js CHANGED
@@ -1769,9 +1769,7 @@ var __async = (__this, __arguments, generator) => {
1769
1769
  }
1770
1770
  }
1771
1771
  function defineStylesRoot() {
1772
- customElements.define(globalStylesTag, class TcRootElement extends HTMLDivElement {
1773
- }, {
1774
- extends: "div"
1772
+ customElements.define(globalStylesTag, class TcRootElement extends HTMLElement {
1775
1773
  });
1776
1774
  }
1777
1775
  function preloadImages(images) {
@@ -1898,10 +1896,17 @@ var __async = (__this, __arguments, generator) => {
1898
1896
  this.localStorage.removeItem(this.storageKey);
1899
1897
  }
1900
1898
  }
1901
- const [walletsModalOpen, setWalletsModalOpen] = createSignal(false);
1902
- const lastSelectedWalletInfoStorage = new LastSelectedWalletInfoStorage();
1903
- const [lastSelectedWalletInfo, _setLastSelectedWalletInfo] = createSignal(lastSelectedWalletInfoStorage.getLastSelectedWalletInfo());
1899
+ const [walletsModalState, setWalletsModalState] = createSignal({
1900
+ status: "closed",
1901
+ closeReason: null
1902
+ });
1903
+ const getWalletsModalIsOpened = createMemo(() => walletsModalState().status === "opened");
1904
+ let lastSelectedWalletInfoStorage = typeof window !== "undefined" ? new LastSelectedWalletInfoStorage() : void 0;
1905
+ const [lastSelectedWalletInfo, _setLastSelectedWalletInfo] = createSignal((lastSelectedWalletInfoStorage == null ? void 0 : lastSelectedWalletInfoStorage.getLastSelectedWalletInfo()) || null);
1904
1906
  const setLastSelectedWalletInfo = (walletInfo) => {
1907
+ if (!lastSelectedWalletInfoStorage) {
1908
+ lastSelectedWalletInfoStorage = new LastSelectedWalletInfoStorage();
1909
+ }
1905
1910
  if (walletInfo) {
1906
1911
  lastSelectedWalletInfoStorage.setLastSelectedWalletInfo(walletInfo);
1907
1912
  } else {
@@ -2997,6 +3002,30 @@ var __async = (__this, __arguments, generator) => {
2997
3002
  document.body.addEventListener("keydown", onKeyPress);
2998
3003
  onCleanup(() => document.body.removeEventListener("keydown", onKeyPress));
2999
3004
  }
3005
+ function androidBackHandler$1(_, accessor) {
3006
+ const userOSIsAndroid = getUserAgent().os === "android";
3007
+ if (!userOSIsAndroid) {
3008
+ return;
3009
+ }
3010
+ let historyEntryAdded = true;
3011
+ window.history.pushState({}, "");
3012
+ const popstateHandler = (event) => {
3013
+ var _a2;
3014
+ historyEntryAdded = false;
3015
+ event.preventDefault();
3016
+ (_a2 = accessor()) == null ? void 0 : _a2();
3017
+ };
3018
+ window.addEventListener("popstate", popstateHandler, {
3019
+ once: true
3020
+ });
3021
+ onCleanup(() => {
3022
+ window.removeEventListener("popstate", popstateHandler);
3023
+ if (historyEntryAdded) {
3024
+ historyEntryAdded = false;
3025
+ window.history.back();
3026
+ }
3027
+ });
3028
+ }
3000
3029
  const _tmpl$$v = /* @__PURE__ */ template$1(`<svg><path fill-rule="evenodd" clip-rule="evenodd" d="M10.2122 14.3407C10.5384 14.0854 10.5959 13.614 10.3406 13.2878L6.20237 8.00003L10.3406 2.71227C10.5959 2.38607 10.5384 1.91469 10.2122 1.6594C9.88604 1.40412 9.41465 1.46161 9.15937 1.7878L4.65937 7.5378C4.44688 7.80932 4.44688 8.19074 4.65937 8.46226L9.15937 14.2123C9.41465 14.5385 9.88604 14.5959 10.2122 14.3407Z"></path></svg>`, 4, true);
3001
3030
  const rotationDegrees = {
3002
3031
  left: 0,
@@ -3207,6 +3236,7 @@ var __async = (__this, __arguments, generator) => {
3207
3236
  const _tmpl$$t = /* @__PURE__ */ template$1(`<div></div>`);
3208
3237
  const clickOutside = clickOutside$1;
3209
3238
  const keyPressed = escPressed;
3239
+ const androidBackHandler = androidBackHandler$1;
3210
3240
  const Modal = (props) => {
3211
3241
  const theme = useTheme();
3212
3242
  const dataAttrs = useDataAttributes(props);
@@ -3270,6 +3300,7 @@ var __async = (__this, __arguments, generator) => {
3270
3300
  }, dataAttrs, {
3271
3301
  get children() {
3272
3302
  const _el$ = _tmpl$$t.cloneNode(true);
3303
+ use(androidBackHandler, _el$, () => () => props.onClose());
3273
3304
  use(keyPressed, _el$, () => () => props.onClose());
3274
3305
  use(clickOutside, _el$, () => () => props.onClose());
3275
3306
  insert(_el$, createComponent(ModalBodyStyled, {
@@ -3303,6 +3334,7 @@ var __async = (__this, __arguments, generator) => {
3303
3334
  createRenderEffect(() => className(_el$, cn__default.default(ModalWrapperClass, u`
3304
3335
  border-radius: ${borders$4[theme.borderRadius]};
3305
3336
  background-color: ${theme.colors.background.tint};
3337
+
3306
3338
  ${media("mobile")} {
3307
3339
  border-radius: ${borders$4[theme.borderRadius]}
3308
3340
  ${borders$4[theme.borderRadius]} 0 0;
@@ -6552,26 +6584,45 @@ var __async = (__this, __arguments, generator) => {
6552
6584
  transform: translateY(-8px);
6553
6585
  margin-bottom: 12px;
6554
6586
  `;
6555
- const _tmpl$$4 = /* @__PURE__ */ template$1(`<div data-tc-list-notifications="true"></div>`);
6556
- const Notifications = (props) => {
6557
- const timeouts = [];
6587
+ const defaultConfig = {
6588
+ timeout: 4500
6589
+ };
6590
+ const [latestAction, setLatestAction] = createSignal(null);
6591
+ function useOpenedNotifications(config) {
6592
+ const { timeout } = __spreadValues(__spreadValues({}, defaultConfig), config);
6558
6593
  const [openedNotifications, setOpenedNotifications] = createSignal([]);
6559
- let lastId = -1;
6560
- const liveTimeoutMs = 4500;
6561
- createEffect(on(action, (action2) => {
6562
- if (action2 && action2.showNotification) {
6563
- lastId++;
6564
- const id = lastId;
6565
- setOpenedNotifications((notifications2) => notifications2.filter((notification) => notification.action !== "confirm-transaction").concat({
6566
- id,
6567
- action: action2.name
6568
- }));
6569
- timeouts.push(setTimeout(() => setOpenedNotifications((notifications2) => notifications2.filter((notification) => notification.id !== id)), liveTimeoutMs));
6570
- }
6571
- }));
6594
+ const [timeoutIds, setTimeoutIds] = createSignal([]);
6595
+ createEffect(
6596
+ on(action, (action2) => {
6597
+ if (!action2 || !action2.showNotification) {
6598
+ return;
6599
+ }
6600
+ if (latestAction() === action2) {
6601
+ return;
6602
+ }
6603
+ setLatestAction(action2);
6604
+ setOpenedNotifications(
6605
+ (openedNotifications2) => openedNotifications2.filter((n2) => n2.action !== "confirm-transaction")
6606
+ );
6607
+ const notification = { action: action2.name };
6608
+ setOpenedNotifications((openedNotifications2) => [...openedNotifications2, notification]);
6609
+ const timeoutId = setTimeout(() => {
6610
+ setOpenedNotifications(
6611
+ (openedNotifications2) => openedNotifications2.filter((n2) => n2 !== notification)
6612
+ );
6613
+ setTimeoutIds((timeoutIds2) => timeoutIds2.filter((id) => id !== timeoutId));
6614
+ }, timeout);
6615
+ setTimeoutIds((timeoutIds2) => [...timeoutIds2, timeoutId]);
6616
+ })
6617
+ );
6572
6618
  onCleanup(() => {
6573
- timeouts.forEach(clearTimeout);
6619
+ timeoutIds().forEach((id) => clearTimeout(id));
6574
6620
  });
6621
+ return openedNotifications;
6622
+ }
6623
+ const _tmpl$$4 = /* @__PURE__ */ template$1(`<div data-tc-list-notifications="true"></div>`);
6624
+ const Notifications = (props) => {
6625
+ const openedNotifications = useOpenedNotifications();
6575
6626
  return (() => {
6576
6627
  const _el$ = _tmpl$$4.cloneNode(true);
6577
6628
  insert(_el$, createComponent(TransitionGroup, {
@@ -9242,22 +9293,25 @@ var __async = (__this, __arguments, generator) => {
9242
9293
  }
9243
9294
  return (_a2 = appState.connectRequestParameters) == null ? void 0 : _a2.value;
9244
9295
  });
9245
- const onClose = () => {
9246
- setWalletsModalOpen(false);
9296
+ const onClose = (closeReason) => {
9297
+ setWalletsModalState({
9298
+ status: "closed",
9299
+ closeReason
9300
+ });
9247
9301
  setSelectedWalletInfo(null);
9248
9302
  setInfoTab(false);
9249
9303
  };
9250
9304
  const unsubscribe = connector.onStatusChange((wallet) => {
9251
9305
  if (wallet) {
9252
- onClose();
9306
+ onClose("wallet-selected");
9253
9307
  }
9254
9308
  });
9255
9309
  onCleanup(unsubscribe);
9256
9310
  return createComponent(StyledModal, {
9257
9311
  get opened() {
9258
- return walletsModalOpen();
9312
+ return getWalletsModalIsOpened();
9259
9313
  },
9260
- onClose,
9314
+ onClose: () => onClose("action-cancelled"),
9261
9315
  onClickQuestion: () => setInfoTab((v) => !v),
9262
9316
  "data-tc-wallets-modal-container": "true",
9263
9317
  get children() {
@@ -9590,8 +9644,14 @@ var __async = (__this, __arguments, generator) => {
9590
9644
  });
9591
9645
  };
9592
9646
  const widgetController = {
9593
- openWalletsModal: () => void setTimeout(() => setWalletsModalOpen(true)),
9594
- closeWalletsModal: () => void setTimeout(() => setWalletsModalOpen(false)),
9647
+ openWalletsModal: () => void setTimeout(() => setWalletsModalState({
9648
+ status: "opened",
9649
+ closeReason: null
9650
+ })),
9651
+ closeWalletsModal: (reason) => void setTimeout(() => setWalletsModalState({
9652
+ status: "closed",
9653
+ closeReason: reason
9654
+ })),
9595
9655
  setAction: (action2) => void setTimeout(() => setAction(action2)),
9596
9656
  clearAction: () => void setTimeout(() => setAction(null)),
9597
9657
  getSelectedWalletInfo: () => lastSelectedWalletInfo(),
@@ -9600,16 +9660,96 @@ var __async = (__this, __arguments, generator) => {
9600
9660
  tonConnectUI
9601
9661
  }), document.getElementById(root))
9602
9662
  };
9663
+ class WalletsModalManager {
9664
+ constructor(options) {
9665
+ __publicField(this, "connector");
9666
+ __publicField(this, "setConnectRequestParametersCallback");
9667
+ __publicField(this, "consumers", []);
9668
+ __publicField(this, "state", walletsModalState());
9669
+ this.connector = options.connector;
9670
+ this.setConnectRequestParametersCallback = options.setConnectRequestParametersCallback;
9671
+ createEffect(() => {
9672
+ const state = walletsModalState();
9673
+ this.state = state;
9674
+ this.consumers.forEach((consumer) => consumer(state));
9675
+ });
9676
+ }
9677
+ open() {
9678
+ return __async(this, null, function* () {
9679
+ const walletsList = yield this.connector.getWallets();
9680
+ const embeddedWallet = walletsList.find(sdk.isWalletInfoCurrentlyEmbedded);
9681
+ if (embeddedWallet) {
9682
+ return this.connectEmbeddedWallet(embeddedWallet);
9683
+ } else {
9684
+ return this.connectExternalWallet();
9685
+ }
9686
+ });
9687
+ }
9688
+ close() {
9689
+ widgetController.closeWalletsModal("action-cancelled");
9690
+ }
9691
+ onStateChange(onChange) {
9692
+ this.consumers.push(onChange);
9693
+ return () => {
9694
+ this.consumers = this.consumers.filter((consumer) => consumer !== onChange);
9695
+ };
9696
+ }
9697
+ connectEmbeddedWallet(embeddedWallet) {
9698
+ const connect = (parameters) => {
9699
+ setLastSelectedWalletInfo(embeddedWallet);
9700
+ this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9701
+ };
9702
+ const additionalRequest = appState.connectRequestParameters;
9703
+ if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9704
+ this.setConnectRequestParametersCallback(connect);
9705
+ } else {
9706
+ connect(additionalRequest == null ? void 0 : additionalRequest.value);
9707
+ }
9708
+ }
9709
+ connectExternalWallet() {
9710
+ return __async(this, null, function* () {
9711
+ widgetController.openWalletsModal();
9712
+ return new Promise((resolve) => {
9713
+ const unsubscribe = this.onStateChange((state) => {
9714
+ const { status } = state;
9715
+ if (status === "opened") {
9716
+ unsubscribe();
9717
+ resolve();
9718
+ }
9719
+ });
9720
+ });
9721
+ });
9722
+ }
9723
+ }
9724
+ class TransactionModalManager {
9725
+ constructor(options) {
9726
+ __publicField(this, "connector");
9727
+ __publicField(this, "consumers", []);
9728
+ this.connector = options.connector;
9729
+ createEffect(() => {
9730
+ const _ = action();
9731
+ this.consumers.forEach((consumer) => consumer(_));
9732
+ });
9733
+ }
9734
+ onStateChange(consumer) {
9735
+ this.consumers.push(consumer);
9736
+ return () => {
9737
+ this.consumers = this.consumers.filter((c2) => c2 !== consumer);
9738
+ };
9739
+ }
9740
+ }
9603
9741
  class TonConnectUI {
9604
9742
  constructor(options) {
9605
9743
  __publicField(this, "walletInfoStorage", new WalletInfoStorage());
9606
9744
  __publicField(this, "preferredWalletStorage", new PreferredWalletStorage());
9607
- __publicField(this, "connector");
9608
9745
  __publicField(this, "walletInfo", null);
9609
9746
  __publicField(this, "systemThemeChangeUnsubscribe", null);
9610
9747
  __publicField(this, "actionsConfiguration");
9611
9748
  __publicField(this, "walletsList");
9612
9749
  __publicField(this, "connectRequestParametersCallback");
9750
+ __publicField(this, "connector");
9751
+ __publicField(this, "modal");
9752
+ __publicField(this, "transactionModal");
9613
9753
  __publicField(this, "connectionRestored", Promise.resolve(false));
9614
9754
  if (options && "connector" in options && options.connector) {
9615
9755
  this.connector = options.connector;
@@ -9620,6 +9760,15 @@ var __async = (__this, __arguments, generator) => {
9620
9760
  "You have to specify a `manifestUrl` or a `connector` in the options."
9621
9761
  );
9622
9762
  }
9763
+ this.modal = new WalletsModalManager({
9764
+ connector: this.connector,
9765
+ setConnectRequestParametersCallback: (callback) => {
9766
+ this.connectRequestParametersCallback = callback;
9767
+ }
9768
+ });
9769
+ this.transactionModal = new TransactionModalManager({
9770
+ connector: this.connector
9771
+ });
9623
9772
  this.walletsList = this.getWallets();
9624
9773
  this.walletsList.then((list) => preloadImages(uniq(list.map((item) => item.imageUrl))));
9625
9774
  const rootId = this.normalizeWidgetRoot(options == null ? void 0 : options.widgetRootId);
@@ -9718,35 +9867,29 @@ var __async = (__this, __arguments, generator) => {
9718
9867
  }
9719
9868
  }), errorsHandler);
9720
9869
  }
9870
+ openModal() {
9871
+ return __async(this, null, function* () {
9872
+ return this.modal.open();
9873
+ });
9874
+ }
9875
+ closeModal() {
9876
+ this.modal.close();
9877
+ }
9878
+ onModalStateChange(onChange) {
9879
+ return this.modal.onStateChange(onChange);
9880
+ }
9881
+ get modalState() {
9882
+ return this.modal.state;
9883
+ }
9721
9884
  connectWallet() {
9722
9885
  return __async(this, null, function* () {
9723
9886
  const walletsList = yield this.getWallets();
9724
9887
  const embeddedWallet = walletsList.find(sdk.isWalletInfoCurrentlyEmbedded);
9725
9888
  if (embeddedWallet) {
9726
- const connect = (parameters) => {
9727
- setLastSelectedWalletInfo(embeddedWallet);
9728
- this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9729
- };
9730
- const additionalRequest = appState.connectRequestParameters;
9731
- if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9732
- this.connectRequestParametersCallback = connect;
9733
- } else {
9734
- connect(additionalRequest == null ? void 0 : additionalRequest.value);
9735
- }
9889
+ return yield this.connectEmbeddedWallet(embeddedWallet);
9736
9890
  } else {
9737
- widgetController.openWalletsModal();
9891
+ return yield this.connectExternalWallet();
9738
9892
  }
9739
- return new Promise((resolve, reject) => {
9740
- const unsubscribe = this.connector.onStatusChange((wallet) => __async(this, null, function* () {
9741
- unsubscribe();
9742
- if (wallet) {
9743
- const lastSelectedWalletInfo2 = yield this.getSelectedWalletInfo(wallet);
9744
- resolve(__spreadValues(__spreadValues({}, wallet), lastSelectedWalletInfo2 || this.walletInfoStorage.getWalletInfo()));
9745
- } else {
9746
- reject(new TonConnectUIError("Wallet was not connected"));
9747
- }
9748
- }), reject);
9749
- });
9750
9893
  });
9751
9894
  }
9752
9895
  disconnect() {
@@ -9775,8 +9918,21 @@ var __async = (__this, __arguments, generator) => {
9775
9918
  showNotification: notifications2.includes("before"),
9776
9919
  openModal: modals.includes("before")
9777
9920
  });
9921
+ const abortController = new AbortController();
9922
+ const unsubscribe = this.onTransactionModalStateChange((action2) => {
9923
+ if (action2 == null ? void 0 : action2.openModal) {
9924
+ return;
9925
+ }
9926
+ unsubscribe();
9927
+ if (!action2) {
9928
+ abortController.abort();
9929
+ }
9930
+ });
9778
9931
  try {
9779
- const result = yield this.connector.sendTransaction(tx);
9932
+ const result = yield this.waitForSendTransaction({
9933
+ transaction: tx,
9934
+ abortSignal: abortController.signal
9935
+ });
9780
9936
  widgetController.setAction({
9781
9937
  name: "transaction-sent",
9782
9938
  showNotification: notifications2.includes("success"),
@@ -9795,9 +9951,110 @@ var __async = (__this, __arguments, generator) => {
9795
9951
  console.error(e2);
9796
9952
  throw new TonConnectUIError("Unhandled error:" + e2);
9797
9953
  }
9954
+ } finally {
9955
+ unsubscribe();
9956
+ }
9957
+ });
9958
+ }
9959
+ connectEmbeddedWallet(embeddedWallet) {
9960
+ return __async(this, null, function* () {
9961
+ const connect = (parameters) => {
9962
+ setLastSelectedWalletInfo(embeddedWallet);
9963
+ this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9964
+ };
9965
+ const additionalRequest = appState.connectRequestParameters;
9966
+ if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9967
+ this.connectRequestParametersCallback = connect;
9968
+ } else {
9969
+ connect(additionalRequest == null ? void 0 : additionalRequest.value);
9798
9970
  }
9971
+ return yield this.waitForWalletConnection({
9972
+ ignoreErrors: false
9973
+ });
9974
+ });
9975
+ }
9976
+ connectExternalWallet() {
9977
+ return __async(this, null, function* () {
9978
+ const abortController = new AbortController();
9979
+ widgetController.openWalletsModal();
9980
+ const unsubscribe = this.onModalStateChange((state) => {
9981
+ const { status, closeReason } = state;
9982
+ if (status === "opened") {
9983
+ return;
9984
+ }
9985
+ unsubscribe();
9986
+ if (closeReason === "action-cancelled") {
9987
+ abortController.abort();
9988
+ }
9989
+ });
9990
+ return yield this.waitForWalletConnection({
9991
+ ignoreErrors: true,
9992
+ abortSignal: abortController.signal
9993
+ });
9994
+ });
9995
+ }
9996
+ waitForWalletConnection(options) {
9997
+ return __async(this, null, function* () {
9998
+ return new Promise((resolve, reject) => {
9999
+ const { ignoreErrors = false, abortSignal = null } = options;
10000
+ if (abortSignal && abortSignal.aborted) {
10001
+ return reject(new TonConnectUIError("Wallet was not connected"));
10002
+ }
10003
+ const onStatusChangeHandler = (wallet) => __async(this, null, function* () {
10004
+ if (!wallet) {
10005
+ if (ignoreErrors) {
10006
+ return;
10007
+ }
10008
+ unsubscribe();
10009
+ reject(new TonConnectUIError("Wallet was not connected"));
10010
+ } else {
10011
+ unsubscribe();
10012
+ resolve(wallet);
10013
+ }
10014
+ });
10015
+ const onErrorsHandler = (reason) => {
10016
+ if (ignoreErrors) {
10017
+ return;
10018
+ }
10019
+ unsubscribe();
10020
+ reject(reason);
10021
+ };
10022
+ const unsubscribe = this.onStatusChange(
10023
+ (wallet) => onStatusChangeHandler(wallet),
10024
+ (reason) => onErrorsHandler(reason)
10025
+ );
10026
+ if (abortSignal) {
10027
+ abortSignal.addEventListener("abort", () => {
10028
+ unsubscribe();
10029
+ reject(new TonConnectUIError("Wallet was not connected"));
10030
+ });
10031
+ }
10032
+ });
10033
+ });
10034
+ }
10035
+ waitForSendTransaction(options) {
10036
+ return __async(this, null, function* () {
10037
+ return new Promise((resolve, reject) => {
10038
+ const { transaction, abortSignal } = options;
10039
+ if (abortSignal.aborted) {
10040
+ return reject(new TonConnectUIError("Transaction was not sent"));
10041
+ }
10042
+ const onTransactionHandler = (transaction2) => __async(this, null, function* () {
10043
+ resolve(transaction2);
10044
+ });
10045
+ const onErrorsHandler = (reason) => {
10046
+ reject(reason);
10047
+ };
10048
+ this.connector.sendTransaction(transaction).then((result) => onTransactionHandler(result)).catch((reason) => onErrorsHandler(reason));
10049
+ abortSignal.addEventListener("abort", () => {
10050
+ reject(new TonConnectUIError("Transaction was not sent"));
10051
+ });
10052
+ });
9799
10053
  });
9800
10054
  }
10055
+ onTransactionModalStateChange(onChange) {
10056
+ return this.transactionModal.onStateChange(onChange);
10057
+ }
9801
10058
  subscribeToWalletChange() {
9802
10059
  this.connector.onStatusChange((wallet) => __async(this, null, function* () {
9803
10060
  var _a2;