Skip to main content

UI Elements

caution

We strongly recommend keeping the default UI Elements. Consistent UI helps users to interact with different dApps more easily.

The default UI elements have been designed with all wallets and user setups in mind. It handles a lot of cases that are not obvious at first glance. If you really must overwrite the default UI.

Custom UI Guidelines

  1. When the user wants to connect to a wallet, a selection of wallets must be shown. The selection depends on the device the user is using and should display different options on different devices.

On desktop there is only one view with some wallets and the QR code.

https://mydapp.com
Beacon Pairing
  1. Every message sent by the dApp should trigger a persistent element on screen, indicating that a request is in progress.
https://mydapp.com
Beacon Loading
  • If no message has been received for a certain amount of time (eg. 5 seconds), the UI should indicate that there are potential connection issues. The dApp should NOT do any automatic action at this point, because some delays are normal, especially when working with wallets that communicate over the P2P network.
  • In the UI element that is shown, the user SHOULD have the option to reset his connection, meaning to disconnect a wallet. This will abort the whole flow. The user can start the action again, which will now trigger a new "pairing" alert because the previous connection was reset.
https://mydapp.com
Beacon Loading Open
  1. Once the Wallet receives the request, it will immediately send back an Acknowledgement Message. When this message is received in the dApp, the dApp knows that the connection is still valid and the user can handle the request. The persistent UI element that is shown on screen should now be updated, indicating that we are waiting for user input on the wallet.
https://mydapp.com
Beacon Awaiting
  1. When the response is received, the persistent element can be removed again and the successful (or error) response can be displayed to the user and the application flow can be continued.

Overwriting Default UI elements

You can overwrite all of the default UI elements by doing the following.

Example
import {
  BeaconEvent,
  DAppClient,
  defaultEventCallbacks,
} from "@airgap/beacon-sdk";

const dAppClient = new DAppClient({
  name: "Beacon Docs",
  disableDefaultEvents: true, // Disable all events / UI. This also disables the pairing alert.
  eventHandlers: {
    // To keep the pairing alert, we have to add the following default event handlers back
    [BeaconEvent.PAIR_INIT]: {
      handler: defaultEventCallbacks.PAIR_INIT,
    },
    [BeaconEvent.PAIR_SUCCESS]: {
      handler: defaultEventCallbacks.PAIR_SUCCESS,
    },
  },
});

try {
  console.log("Requesting permissions...");
  const permissions = await dAppClient.requestPermissions();
  console.log("Got permissions:", permissions.address);
} catch (error) {
  console.error("Got error:", error);
}
Loading...

You can also add your own logic to specific events and still keep the original behaviour.

The same can be achieved without overwriting the default event handler by subscribing to an event. This method is preferred, if possible.

Example
import {
  BeaconEvent,
  DAppClient,
  defaultEventCallbacks,
  P2PPairingRequest,
  PostMessagePairingRequest,
  NetworkType,
  WalletConnectPairingRequest,
  AnalyticsInterface,
} from "@airgap/beacon-sdk";

const dAppClient = new DAppClient({
  name: "Beacon Docs",
  eventHandlers: {
    [BeaconEvent.PAIR_INIT]: {
      // Every BeaconEvent can be overriden by passing a handler here.
      // The default will not be executed anymore. To keep the default,
      // you will have to call it again.
      handler: async (
        data: {
          p2pPeerInfo: () => Promise<P2PPairingRequest>;
          postmessagePeerInfo: () => Promise<PostMessagePairingRequest>;
          walletConnectPeerInfo: () => Promise<WalletConnectPairingRequest>;
          networkType: NetworkType;
          abortedHandler?(): void;
          disclaimerText?: string;
          analytics: AnalyticsInterface;
          featuredWallets?: string[];
        },
        eventCallback?: any[] | undefined,
      ): Promise<void> => {
        await defaultEventCallbacks.PAIR_INIT(data); // Add this if you want to keep the default behaviour.
        console.log("syncInfo", data, eventCallback);
      },
    },
  },
});

try {
  console.log("Requesting permissions...");
  const permissions = await dAppClient.requestPermissions();
  console.log("Got permissions:", permissions.address);
} catch (error) {
  console.error("Got error:", error);
}
Loading...

The closing of the pairing alert can not be listened to by default. The reason for this is the delay in the P2P connections. It is possible that a user scans the pairing QR code with his wallet and then closes the alert while waiting for the connection to be established. If the dApp interprets the "closing" of the alert as an abort, and a few seconds later the connection is established successfully, the behaviour could be unexpected.

If you still want to be notified of the closing of the pairing window, you can do it in the following way, while keeping the default behaviour.

Example
import {
  BeaconEvent,
  DAppClient,
  defaultEventCallbacks,
  P2PPairingRequest,
  PostMessagePairingRequest,
  NetworkType,
  WalletConnectPairingRequest,
  AnalyticsInterface,
} from "@airgap/beacon-sdk";

const dAppClient = new DAppClient({
    name: "Beacon Docs",
    eventHandlers: {
      [BeaconEvent.PAIR_INIT]: {
        // Every BeaconEvent can be overriden by passing a handler here.
        // The default will not be executed anymore. To keep the default,
        // you will have to call it again.
        handler: async (data: {
          p2pPeerInfo: () => Promise<P2PPairingRequest>;
          postmessagePeerInfo: () => Promise<PostMessagePairingRequest>;
          walletConnectPeerInfo: () => Promise<WalletConnectPairingRequest>;
          networkType: NetworkType;
          abortedHandler?(): void;
          disclaimerText?: string;
          analytics: AnalyticsInterface;
          featuredWallets?: string[];
        }): Promise<void> => {
          // If you want to attach your own "on alert closed" handler
          // eslint-disable-next-line @typescript-eslint/unbound-method
          const oldHandler = data.abortedHandler;
          const newHandler = (): void => {
            if (oldHandler) {
              // Make sure to call the internal abortedHandler
              oldHandler();
            }
            // Add your own logic here
            console.log("My logic");

          data.abortedHandler = newHandler; // Replace the internal abortedHandler with the new one
          await defaultEventCallbacks.PAIR_INIT(data); // Add this if you want to keep the default behaviour.
          console.log("syncInfo", data);
        },
      },
    },
  });

  try {
    console.log("Requesting permissions...");
    const permissions = await dAppClient.requestPermissions();
    console.log("Got permissions:", permissions.address);
  } catch (error) {
    console.error("Got error:", error);
  }

Loading...