@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.d.ts CHANGED
@@ -205,16 +205,81 @@ declare type LoadableReady<T> = {
205
205
  value: T;
206
206
  };
207
207
 
208
+ interface WalletsModal {
209
+ /**
210
+ * Open the modal.
211
+ */
212
+ open: () => void;
213
+ /**
214
+ * Close the modal.
215
+ */
216
+ close: () => void;
217
+ /**
218
+ * Subscribe to the modal window status changes.
219
+ */
220
+ onStateChange: (callback: (state: WalletsModalState) => void) => () => void;
221
+ /**
222
+ * Current modal window state.
223
+ */
224
+ state: WalletsModalState;
225
+ }
226
+ /**
227
+ * Opened modal window state.
228
+ */
229
+ declare type WalletModalOpened = {
230
+ /**
231
+ * Modal window status.
232
+ */
233
+ status: 'opened';
234
+ /**
235
+ * Always `null` for opened modal window.
236
+ */
237
+ closeReason: null;
238
+ };
239
+ /**
240
+ * Closed modal window state.
241
+ */
242
+ declare type WalletModalClosed = {
243
+ /**
244
+ * Modal window status.
245
+ */
246
+ status: 'closed';
247
+ /**
248
+ * Close reason, if the modal window was closed.
249
+ */
250
+ closeReason: WalletsModalCloseReason | null;
251
+ };
252
+ /**
253
+ * Modal window state.
254
+ */
255
+ declare type WalletsModalState = WalletModalOpened | WalletModalClosed;
256
+ /**
257
+ * Modal window close reason.
258
+ */
259
+ declare type WalletsModalCloseReason = 'action-cancelled' | 'wallet-selected';
260
+
208
261
  declare class TonConnectUI {
209
262
  static getWallets(): Promise<WalletInfo[]>;
210
263
  private readonly walletInfoStorage;
211
264
  private readonly preferredWalletStorage;
212
- readonly connector: ITonConnect;
213
265
  private walletInfo;
214
266
  private systemThemeChangeUnsubscribe;
215
267
  private actionsConfiguration?;
216
268
  private readonly walletsList;
217
269
  private connectRequestParametersCallback?;
270
+ /**
271
+ * TonConnect instance.
272
+ */
273
+ readonly connector: ITonConnect;
274
+ /**
275
+ * Manages the modal window state.
276
+ */
277
+ readonly modal: WalletsModal;
278
+ /**
279
+ * Manages the transaction modal window state.
280
+ * TODO: make it public when interface will be ready for external usage.
281
+ */
282
+ private readonly transactionModal;
218
283
  /**
219
284
  * Promise that resolves after end of th connection restoring process (promise will fire after `onStatusChange`, so you can get actual information about wallet and session after when promise resolved).
220
285
  * Resolved value `true`/`false` indicates if the session was restored successfully.
@@ -255,7 +320,26 @@ declare class TonConnectUI {
255
320
  */
256
321
  onStatusChange(callback: (wallet: ConnectedWallet | null) => void, errorsHandler?: (err: TonConnectError) => void): ReturnType<ITonConnect['onStatusChange']>;
257
322
  /**
323
+ * Opens the modal window, returns a promise that resolves after the modal window is opened.
324
+ */
325
+ openModal(): Promise<void>;
326
+ /**
327
+ * Closes the modal window.
328
+ */
329
+ closeModal(): void;
330
+ /**
331
+ * Subscribe to the modal window state changes, returns a function which has to be called to unsubscribe.
332
+ */
333
+ onModalStateChange(onChange: (state: WalletsModalState) => void): () => void;
334
+ /**
335
+ * Returns current modal window state.
336
+ */
337
+ get modalState(): WalletsModalState;
338
+ /**
339
+ * @deprecated Use `tonConnectUI.openModal()` instead. Will be removed in the next major version.
258
340
  * Opens the modal window and handles a wallet connection.
341
+ * @return Connected wallet.
342
+ * @throws TonConnectUIError if connection was aborted.
259
343
  */
260
344
  connectWallet(): Promise<ConnectedWallet>;
261
345
  /**
@@ -268,6 +352,47 @@ declare class TonConnectUI {
268
352
  * @param options modal and notifications behaviour settings. Default is show only 'before' modal and all notifications.
269
353
  */
270
354
  sendTransaction(tx: SendTransactionRequest, options?: ActionConfiguration): Promise<SendTransactionResponse>;
355
+ /**
356
+ * TODO: remove in the next major version.
357
+ * Initiates a connection with an embedded wallet, awaits its completion, and returns the connected wallet information.
358
+ * @param embeddedWallet - Information about the embedded wallet to connect to.
359
+ * @throws Error if the connection process fails.
360
+ * @internal
361
+ */
362
+ private connectEmbeddedWallet;
363
+ /**
364
+ * TODO: remove in the next major version.
365
+ * Initiates the connection process for an external wallet by opening the wallet modal
366
+ * and returns the connected wallet information upon successful connection.
367
+ * @throws Error if the user cancels the connection process or if the connection process fails.
368
+ * @internal
369
+ */
370
+ private connectExternalWallet;
371
+ /**
372
+ * TODO: remove in the next major version.
373
+ * Waits for a wallet connection based on provided options, returning connected wallet information.
374
+ * @param options - Configuration for connection statuses and errors handling.
375
+ * @options.ignoreErrors - If true, ignores errors during waiting, waiting continues until a valid wallet connects. Default is false.
376
+ * @options.abortSignal - Optional AbortSignal for external cancellation. Throws TonConnectUIError if aborted.
377
+ * @throws TonConnectUIError if waiting is aborted or no valid wallet connection is received and ignoreErrors is false.
378
+ * @internal
379
+ */
380
+ private waitForWalletConnection;
381
+ /**
382
+ * Waits for a transaction to be sent based on provided options, returning the transaction response.
383
+ * @param options - Configuration for transaction statuses and errors handling.
384
+ * @options.transaction - Transaction to send.
385
+ * @options.ignoreErrors - If true, ignores errors during waiting, waiting continues until a valid transaction is sent. Default is false.
386
+ * @options.abortSignal - Optional AbortSignal for external cancellation. Throws TonConnectUIError if aborted.
387
+ * @throws TonConnectUIError if waiting is aborted or no valid transaction response is received and ignoreErrors is false.
388
+ * @internal
389
+ */
390
+ private waitForSendTransaction;
391
+ /**
392
+ * Subscribe to the transaction modal window state changes, returns a function which has to be called to unsubscribe.
393
+ * @internal
394
+ */
395
+ private onTransactionModalStateChange;
271
396
  private subscribeToWalletChange;
272
397
  private setPreferredWalletAppName;
273
398
  private getSelectedWalletInfo;
@@ -283,4 +408,4 @@ declare class TonConnectUIError extends TonConnectError {
283
408
  constructor(...args: ConstructorParameters<typeof Error>);
284
409
  }
285
410
 
286
- export { ActionConfiguration, BorderRadius, Color, ColorsSet, ConnectedWallet, Loadable, LoadableLoading, LoadableReady, Locales, PartialColorsSet, ReturnStrategy, THEME, Theme, TonConnectUI, TonConnectUIError, TonConnectUiCreateOptions, TonConnectUiCreateOptionsBase, TonConnectUiOptions, TonConnectUiOptionsWithConnector, TonConnectUiOptionsWithManifest, UIPreferences, UIWallet, WalletInfoRemoteWithOpenMethod, WalletInfoWithOpenMethod, WalletOpenMethod, WalletsListConfiguration };
411
+ export { ActionConfiguration, BorderRadius, Color, ColorsSet, ConnectedWallet, Loadable, LoadableLoading, LoadableReady, Locales, PartialColorsSet, ReturnStrategy, THEME, Theme, TonConnectUI, TonConnectUIError, TonConnectUiCreateOptions, TonConnectUiCreateOptionsBase, TonConnectUiOptions, TonConnectUiOptionsWithConnector, TonConnectUiOptionsWithManifest, UIPreferences, UIWallet, WalletInfoRemoteWithOpenMethod, WalletInfoWithOpenMethod, WalletModalClosed, WalletModalOpened, WalletOpenMethod, WalletsListConfiguration, WalletsModal, WalletsModalCloseReason, WalletsModalState };
package/lib/index.js CHANGED
@@ -1766,9 +1766,7 @@ function fixMobileSafariActiveTransition() {
1766
1766
  }
1767
1767
  }
1768
1768
  function defineStylesRoot() {
1769
- customElements.define(globalStylesTag, class TcRootElement extends HTMLDivElement {
1770
- }, {
1771
- extends: "div"
1769
+ customElements.define(globalStylesTag, class TcRootElement extends HTMLElement {
1772
1770
  });
1773
1771
  }
1774
1772
  function preloadImages(images) {
@@ -1895,10 +1893,17 @@ class LastSelectedWalletInfoStorage {
1895
1893
  this.localStorage.removeItem(this.storageKey);
1896
1894
  }
1897
1895
  }
1898
- const [walletsModalOpen, setWalletsModalOpen] = createSignal(false);
1899
- const lastSelectedWalletInfoStorage = new LastSelectedWalletInfoStorage();
1900
- const [lastSelectedWalletInfo, _setLastSelectedWalletInfo] = createSignal(lastSelectedWalletInfoStorage.getLastSelectedWalletInfo());
1896
+ const [walletsModalState, setWalletsModalState] = createSignal({
1897
+ status: "closed",
1898
+ closeReason: null
1899
+ });
1900
+ const getWalletsModalIsOpened = createMemo(() => walletsModalState().status === "opened");
1901
+ let lastSelectedWalletInfoStorage = typeof window !== "undefined" ? new LastSelectedWalletInfoStorage() : void 0;
1902
+ const [lastSelectedWalletInfo, _setLastSelectedWalletInfo] = createSignal((lastSelectedWalletInfoStorage == null ? void 0 : lastSelectedWalletInfoStorage.getLastSelectedWalletInfo()) || null);
1901
1903
  const setLastSelectedWalletInfo = (walletInfo) => {
1904
+ if (!lastSelectedWalletInfoStorage) {
1905
+ lastSelectedWalletInfoStorage = new LastSelectedWalletInfoStorage();
1906
+ }
1902
1907
  if (walletInfo) {
1903
1908
  lastSelectedWalletInfoStorage.setLastSelectedWalletInfo(walletInfo);
1904
1909
  } else {
@@ -2994,6 +2999,30 @@ function escPressed(_, accessor) {
2994
2999
  document.body.addEventListener("keydown", onKeyPress);
2995
3000
  onCleanup(() => document.body.removeEventListener("keydown", onKeyPress));
2996
3001
  }
3002
+ function androidBackHandler$1(_, accessor) {
3003
+ const userOSIsAndroid = getUserAgent().os === "android";
3004
+ if (!userOSIsAndroid) {
3005
+ return;
3006
+ }
3007
+ let historyEntryAdded = true;
3008
+ window.history.pushState({}, "");
3009
+ const popstateHandler = (event) => {
3010
+ var _a2;
3011
+ historyEntryAdded = false;
3012
+ event.preventDefault();
3013
+ (_a2 = accessor()) == null ? void 0 : _a2();
3014
+ };
3015
+ window.addEventListener("popstate", popstateHandler, {
3016
+ once: true
3017
+ });
3018
+ onCleanup(() => {
3019
+ window.removeEventListener("popstate", popstateHandler);
3020
+ if (historyEntryAdded) {
3021
+ historyEntryAdded = false;
3022
+ window.history.back();
3023
+ }
3024
+ });
3025
+ }
2997
3026
  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);
2998
3027
  const rotationDegrees = {
2999
3028
  left: 0,
@@ -3204,6 +3233,7 @@ const QuestionButtonStyled = styled(IconButton)`
3204
3233
  const _tmpl$$t = /* @__PURE__ */ template$1(`<div></div>`);
3205
3234
  const clickOutside = clickOutside$1;
3206
3235
  const keyPressed = escPressed;
3236
+ const androidBackHandler = androidBackHandler$1;
3207
3237
  const Modal = (props) => {
3208
3238
  const theme = useTheme();
3209
3239
  const dataAttrs = useDataAttributes(props);
@@ -3267,6 +3297,7 @@ const Modal = (props) => {
3267
3297
  }, dataAttrs, {
3268
3298
  get children() {
3269
3299
  const _el$ = _tmpl$$t.cloneNode(true);
3300
+ use(androidBackHandler, _el$, () => () => props.onClose());
3270
3301
  use(keyPressed, _el$, () => () => props.onClose());
3271
3302
  use(clickOutside, _el$, () => () => props.onClose());
3272
3303
  insert(_el$, createComponent(ModalBodyStyled, {
@@ -3300,6 +3331,7 @@ const Modal = (props) => {
3300
3331
  createRenderEffect(() => className(_el$, cn(ModalWrapperClass, u`
3301
3332
  border-radius: ${borders$4[theme.borderRadius]};
3302
3333
  background-color: ${theme.colors.background.tint};
3334
+
3303
3335
  ${media("mobile")} {
3304
3336
  border-radius: ${borders$4[theme.borderRadius]}
3305
3337
  ${borders$4[theme.borderRadius]} 0 0;
@@ -6549,26 +6581,45 @@ const NotificationClass = u`
6549
6581
  transform: translateY(-8px);
6550
6582
  margin-bottom: 12px;
6551
6583
  `;
6552
- const _tmpl$$4 = /* @__PURE__ */ template$1(`<div data-tc-list-notifications="true"></div>`);
6553
- const Notifications = (props) => {
6554
- const timeouts = [];
6584
+ const defaultConfig = {
6585
+ timeout: 4500
6586
+ };
6587
+ const [latestAction, setLatestAction] = createSignal(null);
6588
+ function useOpenedNotifications(config) {
6589
+ const { timeout } = __spreadValues(__spreadValues({}, defaultConfig), config);
6555
6590
  const [openedNotifications, setOpenedNotifications] = createSignal([]);
6556
- let lastId = -1;
6557
- const liveTimeoutMs = 4500;
6558
- createEffect(on(action, (action2) => {
6559
- if (action2 && action2.showNotification) {
6560
- lastId++;
6561
- const id = lastId;
6562
- setOpenedNotifications((notifications2) => notifications2.filter((notification) => notification.action !== "confirm-transaction").concat({
6563
- id,
6564
- action: action2.name
6565
- }));
6566
- timeouts.push(setTimeout(() => setOpenedNotifications((notifications2) => notifications2.filter((notification) => notification.id !== id)), liveTimeoutMs));
6567
- }
6568
- }));
6591
+ const [timeoutIds, setTimeoutIds] = createSignal([]);
6592
+ createEffect(
6593
+ on(action, (action2) => {
6594
+ if (!action2 || !action2.showNotification) {
6595
+ return;
6596
+ }
6597
+ if (latestAction() === action2) {
6598
+ return;
6599
+ }
6600
+ setLatestAction(action2);
6601
+ setOpenedNotifications(
6602
+ (openedNotifications2) => openedNotifications2.filter((n2) => n2.action !== "confirm-transaction")
6603
+ );
6604
+ const notification = { action: action2.name };
6605
+ setOpenedNotifications((openedNotifications2) => [...openedNotifications2, notification]);
6606
+ const timeoutId = setTimeout(() => {
6607
+ setOpenedNotifications(
6608
+ (openedNotifications2) => openedNotifications2.filter((n2) => n2 !== notification)
6609
+ );
6610
+ setTimeoutIds((timeoutIds2) => timeoutIds2.filter((id) => id !== timeoutId));
6611
+ }, timeout);
6612
+ setTimeoutIds((timeoutIds2) => [...timeoutIds2, timeoutId]);
6613
+ })
6614
+ );
6569
6615
  onCleanup(() => {
6570
- timeouts.forEach(clearTimeout);
6616
+ timeoutIds().forEach((id) => clearTimeout(id));
6571
6617
  });
6618
+ return openedNotifications;
6619
+ }
6620
+ const _tmpl$$4 = /* @__PURE__ */ template$1(`<div data-tc-list-notifications="true"></div>`);
6621
+ const Notifications = (props) => {
6622
+ const openedNotifications = useOpenedNotifications();
6572
6623
  return (() => {
6573
6624
  const _el$ = _tmpl$$4.cloneNode(true);
6574
6625
  insert(_el$, createComponent(TransitionGroup, {
@@ -9239,22 +9290,25 @@ const WalletsModal = () => {
9239
9290
  }
9240
9291
  return (_a2 = appState.connectRequestParameters) == null ? void 0 : _a2.value;
9241
9292
  });
9242
- const onClose = () => {
9243
- setWalletsModalOpen(false);
9293
+ const onClose = (closeReason) => {
9294
+ setWalletsModalState({
9295
+ status: "closed",
9296
+ closeReason
9297
+ });
9244
9298
  setSelectedWalletInfo(null);
9245
9299
  setInfoTab(false);
9246
9300
  };
9247
9301
  const unsubscribe = connector.onStatusChange((wallet) => {
9248
9302
  if (wallet) {
9249
- onClose();
9303
+ onClose("wallet-selected");
9250
9304
  }
9251
9305
  });
9252
9306
  onCleanup(unsubscribe);
9253
9307
  return createComponent(StyledModal, {
9254
9308
  get opened() {
9255
- return walletsModalOpen();
9309
+ return getWalletsModalIsOpened();
9256
9310
  },
9257
- onClose,
9311
+ onClose: () => onClose("action-cancelled"),
9258
9312
  onClickQuestion: () => setInfoTab((v) => !v),
9259
9313
  "data-tc-wallets-modal-container": "true",
9260
9314
  get children() {
@@ -9587,8 +9641,14 @@ const App = (props) => {
9587
9641
  });
9588
9642
  };
9589
9643
  const widgetController = {
9590
- openWalletsModal: () => void setTimeout(() => setWalletsModalOpen(true)),
9591
- closeWalletsModal: () => void setTimeout(() => setWalletsModalOpen(false)),
9644
+ openWalletsModal: () => void setTimeout(() => setWalletsModalState({
9645
+ status: "opened",
9646
+ closeReason: null
9647
+ })),
9648
+ closeWalletsModal: (reason) => void setTimeout(() => setWalletsModalState({
9649
+ status: "closed",
9650
+ closeReason: reason
9651
+ })),
9592
9652
  setAction: (action2) => void setTimeout(() => setAction(action2)),
9593
9653
  clearAction: () => void setTimeout(() => setAction(null)),
9594
9654
  getSelectedWalletInfo: () => lastSelectedWalletInfo(),
@@ -9597,16 +9657,96 @@ const widgetController = {
9597
9657
  tonConnectUI
9598
9658
  }), document.getElementById(root))
9599
9659
  };
9660
+ class WalletsModalManager {
9661
+ constructor(options) {
9662
+ __publicField(this, "connector");
9663
+ __publicField(this, "setConnectRequestParametersCallback");
9664
+ __publicField(this, "consumers", []);
9665
+ __publicField(this, "state", walletsModalState());
9666
+ this.connector = options.connector;
9667
+ this.setConnectRequestParametersCallback = options.setConnectRequestParametersCallback;
9668
+ createEffect(() => {
9669
+ const state = walletsModalState();
9670
+ this.state = state;
9671
+ this.consumers.forEach((consumer) => consumer(state));
9672
+ });
9673
+ }
9674
+ open() {
9675
+ return __async(this, null, function* () {
9676
+ const walletsList = yield this.connector.getWallets();
9677
+ const embeddedWallet = walletsList.find(isWalletInfoCurrentlyEmbedded);
9678
+ if (embeddedWallet) {
9679
+ return this.connectEmbeddedWallet(embeddedWallet);
9680
+ } else {
9681
+ return this.connectExternalWallet();
9682
+ }
9683
+ });
9684
+ }
9685
+ close() {
9686
+ widgetController.closeWalletsModal("action-cancelled");
9687
+ }
9688
+ onStateChange(onChange) {
9689
+ this.consumers.push(onChange);
9690
+ return () => {
9691
+ this.consumers = this.consumers.filter((consumer) => consumer !== onChange);
9692
+ };
9693
+ }
9694
+ connectEmbeddedWallet(embeddedWallet) {
9695
+ const connect = (parameters) => {
9696
+ setLastSelectedWalletInfo(embeddedWallet);
9697
+ this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9698
+ };
9699
+ const additionalRequest = appState.connectRequestParameters;
9700
+ if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9701
+ this.setConnectRequestParametersCallback(connect);
9702
+ } else {
9703
+ connect(additionalRequest == null ? void 0 : additionalRequest.value);
9704
+ }
9705
+ }
9706
+ connectExternalWallet() {
9707
+ return __async(this, null, function* () {
9708
+ widgetController.openWalletsModal();
9709
+ return new Promise((resolve) => {
9710
+ const unsubscribe = this.onStateChange((state) => {
9711
+ const { status } = state;
9712
+ if (status === "opened") {
9713
+ unsubscribe();
9714
+ resolve();
9715
+ }
9716
+ });
9717
+ });
9718
+ });
9719
+ }
9720
+ }
9721
+ class TransactionModalManager {
9722
+ constructor(options) {
9723
+ __publicField(this, "connector");
9724
+ __publicField(this, "consumers", []);
9725
+ this.connector = options.connector;
9726
+ createEffect(() => {
9727
+ const _ = action();
9728
+ this.consumers.forEach((consumer) => consumer(_));
9729
+ });
9730
+ }
9731
+ onStateChange(consumer) {
9732
+ this.consumers.push(consumer);
9733
+ return () => {
9734
+ this.consumers = this.consumers.filter((c2) => c2 !== consumer);
9735
+ };
9736
+ }
9737
+ }
9600
9738
  class TonConnectUI {
9601
9739
  constructor(options) {
9602
9740
  __publicField(this, "walletInfoStorage", new WalletInfoStorage());
9603
9741
  __publicField(this, "preferredWalletStorage", new PreferredWalletStorage());
9604
- __publicField(this, "connector");
9605
9742
  __publicField(this, "walletInfo", null);
9606
9743
  __publicField(this, "systemThemeChangeUnsubscribe", null);
9607
9744
  __publicField(this, "actionsConfiguration");
9608
9745
  __publicField(this, "walletsList");
9609
9746
  __publicField(this, "connectRequestParametersCallback");
9747
+ __publicField(this, "connector");
9748
+ __publicField(this, "modal");
9749
+ __publicField(this, "transactionModal");
9610
9750
  __publicField(this, "connectionRestored", Promise.resolve(false));
9611
9751
  if (options && "connector" in options && options.connector) {
9612
9752
  this.connector = options.connector;
@@ -9617,6 +9757,15 @@ class TonConnectUI {
9617
9757
  "You have to specify a `manifestUrl` or a `connector` in the options."
9618
9758
  );
9619
9759
  }
9760
+ this.modal = new WalletsModalManager({
9761
+ connector: this.connector,
9762
+ setConnectRequestParametersCallback: (callback) => {
9763
+ this.connectRequestParametersCallback = callback;
9764
+ }
9765
+ });
9766
+ this.transactionModal = new TransactionModalManager({
9767
+ connector: this.connector
9768
+ });
9620
9769
  this.walletsList = this.getWallets();
9621
9770
  this.walletsList.then((list) => preloadImages(uniq(list.map((item) => item.imageUrl))));
9622
9771
  const rootId = this.normalizeWidgetRoot(options == null ? void 0 : options.widgetRootId);
@@ -9715,35 +9864,29 @@ class TonConnectUI {
9715
9864
  }
9716
9865
  }), errorsHandler);
9717
9866
  }
9867
+ openModal() {
9868
+ return __async(this, null, function* () {
9869
+ return this.modal.open();
9870
+ });
9871
+ }
9872
+ closeModal() {
9873
+ this.modal.close();
9874
+ }
9875
+ onModalStateChange(onChange) {
9876
+ return this.modal.onStateChange(onChange);
9877
+ }
9878
+ get modalState() {
9879
+ return this.modal.state;
9880
+ }
9718
9881
  connectWallet() {
9719
9882
  return __async(this, null, function* () {
9720
9883
  const walletsList = yield this.getWallets();
9721
9884
  const embeddedWallet = walletsList.find(isWalletInfoCurrentlyEmbedded);
9722
9885
  if (embeddedWallet) {
9723
- const connect = (parameters) => {
9724
- setLastSelectedWalletInfo(embeddedWallet);
9725
- this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9726
- };
9727
- const additionalRequest = appState.connectRequestParameters;
9728
- if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9729
- this.connectRequestParametersCallback = connect;
9730
- } else {
9731
- connect(additionalRequest == null ? void 0 : additionalRequest.value);
9732
- }
9886
+ return yield this.connectEmbeddedWallet(embeddedWallet);
9733
9887
  } else {
9734
- widgetController.openWalletsModal();
9888
+ return yield this.connectExternalWallet();
9735
9889
  }
9736
- return new Promise((resolve, reject) => {
9737
- const unsubscribe = this.connector.onStatusChange((wallet) => __async(this, null, function* () {
9738
- unsubscribe();
9739
- if (wallet) {
9740
- const lastSelectedWalletInfo2 = yield this.getSelectedWalletInfo(wallet);
9741
- resolve(__spreadValues(__spreadValues({}, wallet), lastSelectedWalletInfo2 || this.walletInfoStorage.getWalletInfo()));
9742
- } else {
9743
- reject(new TonConnectUIError("Wallet was not connected"));
9744
- }
9745
- }), reject);
9746
- });
9747
9890
  });
9748
9891
  }
9749
9892
  disconnect() {
@@ -9772,8 +9915,21 @@ class TonConnectUI {
9772
9915
  showNotification: notifications2.includes("before"),
9773
9916
  openModal: modals.includes("before")
9774
9917
  });
9918
+ const abortController = new AbortController();
9919
+ const unsubscribe = this.onTransactionModalStateChange((action2) => {
9920
+ if (action2 == null ? void 0 : action2.openModal) {
9921
+ return;
9922
+ }
9923
+ unsubscribe();
9924
+ if (!action2) {
9925
+ abortController.abort();
9926
+ }
9927
+ });
9775
9928
  try {
9776
- const result = yield this.connector.sendTransaction(tx);
9929
+ const result = yield this.waitForSendTransaction({
9930
+ transaction: tx,
9931
+ abortSignal: abortController.signal
9932
+ });
9777
9933
  widgetController.setAction({
9778
9934
  name: "transaction-sent",
9779
9935
  showNotification: notifications2.includes("success"),
@@ -9792,9 +9948,110 @@ class TonConnectUI {
9792
9948
  console.error(e2);
9793
9949
  throw new TonConnectUIError("Unhandled error:" + e2);
9794
9950
  }
9951
+ } finally {
9952
+ unsubscribe();
9953
+ }
9954
+ });
9955
+ }
9956
+ connectEmbeddedWallet(embeddedWallet) {
9957
+ return __async(this, null, function* () {
9958
+ const connect = (parameters) => {
9959
+ setLastSelectedWalletInfo(embeddedWallet);
9960
+ this.connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }, parameters);
9961
+ };
9962
+ const additionalRequest = appState.connectRequestParameters;
9963
+ if ((additionalRequest == null ? void 0 : additionalRequest.state) === "loading") {
9964
+ this.connectRequestParametersCallback = connect;
9965
+ } else {
9966
+ connect(additionalRequest == null ? void 0 : additionalRequest.value);
9795
9967
  }
9968
+ return yield this.waitForWalletConnection({
9969
+ ignoreErrors: false
9970
+ });
9971
+ });
9972
+ }
9973
+ connectExternalWallet() {
9974
+ return __async(this, null, function* () {
9975
+ const abortController = new AbortController();
9976
+ widgetController.openWalletsModal();
9977
+ const unsubscribe = this.onModalStateChange((state) => {
9978
+ const { status, closeReason } = state;
9979
+ if (status === "opened") {
9980
+ return;
9981
+ }
9982
+ unsubscribe();
9983
+ if (closeReason === "action-cancelled") {
9984
+ abortController.abort();
9985
+ }
9986
+ });
9987
+ return yield this.waitForWalletConnection({
9988
+ ignoreErrors: true,
9989
+ abortSignal: abortController.signal
9990
+ });
9991
+ });
9992
+ }
9993
+ waitForWalletConnection(options) {
9994
+ return __async(this, null, function* () {
9995
+ return new Promise((resolve, reject) => {
9996
+ const { ignoreErrors = false, abortSignal = null } = options;
9997
+ if (abortSignal && abortSignal.aborted) {
9998
+ return reject(new TonConnectUIError("Wallet was not connected"));
9999
+ }
10000
+ const onStatusChangeHandler = (wallet) => __async(this, null, function* () {
10001
+ if (!wallet) {
10002
+ if (ignoreErrors) {
10003
+ return;
10004
+ }
10005
+ unsubscribe();
10006
+ reject(new TonConnectUIError("Wallet was not connected"));
10007
+ } else {
10008
+ unsubscribe();
10009
+ resolve(wallet);
10010
+ }
10011
+ });
10012
+ const onErrorsHandler = (reason) => {
10013
+ if (ignoreErrors) {
10014
+ return;
10015
+ }
10016
+ unsubscribe();
10017
+ reject(reason);
10018
+ };
10019
+ const unsubscribe = this.onStatusChange(
10020
+ (wallet) => onStatusChangeHandler(wallet),
10021
+ (reason) => onErrorsHandler(reason)
10022
+ );
10023
+ if (abortSignal) {
10024
+ abortSignal.addEventListener("abort", () => {
10025
+ unsubscribe();
10026
+ reject(new TonConnectUIError("Wallet was not connected"));
10027
+ });
10028
+ }
10029
+ });
10030
+ });
10031
+ }
10032
+ waitForSendTransaction(options) {
10033
+ return __async(this, null, function* () {
10034
+ return new Promise((resolve, reject) => {
10035
+ const { transaction, abortSignal } = options;
10036
+ if (abortSignal.aborted) {
10037
+ return reject(new TonConnectUIError("Transaction was not sent"));
10038
+ }
10039
+ const onTransactionHandler = (transaction2) => __async(this, null, function* () {
10040
+ resolve(transaction2);
10041
+ });
10042
+ const onErrorsHandler = (reason) => {
10043
+ reject(reason);
10044
+ };
10045
+ this.connector.sendTransaction(transaction).then((result) => onTransactionHandler(result)).catch((reason) => onErrorsHandler(reason));
10046
+ abortSignal.addEventListener("abort", () => {
10047
+ reject(new TonConnectUIError("Transaction was not sent"));
10048
+ });
10049
+ });
9796
10050
  });
9797
10051
  }
10052
+ onTransactionModalStateChange(onChange) {
10053
+ return this.transactionModal.onStateChange(onChange);
10054
+ }
9798
10055
  subscribeToWalletChange() {
9799
10056
  this.connector.onStatusChange((wallet) => __async(this, null, function* () {
9800
10057
  var _a2;