From 8eb195c648bafdfd7afc434ce3e5db370c21a2f8 Mon Sep 17 00:00:00 2001 From: muzuke <92723634+muzuke@users.noreply.github.com> Date: Thu, 27 Jul 2023 13:54:33 +0300 Subject: [PATCH 01/32] Fix conditional when deleting release environments --- .github/workflows/delete-release-env.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/delete-release-env.yaml b/.github/workflows/delete-release-env.yaml index 77ef299e..6a3d9b00 100644 --- a/.github/workflows/delete-release-env.yaml +++ b/.github/workflows/delete-release-env.yaml @@ -4,7 +4,7 @@ on: delete jobs: delete-release-env: - if: github.event.ref_type == 'branch' && contains('release', github.ref_name) + if: github.event.ref_type == 'branch' && contains(github.event.ref, 'release') runs-on: ubuntu-latest permissions: id-token: write From 5f9d0dc9d0fce4c8748c80ca639ab63507452e59 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Fri, 28 Jul 2023 16:53:10 +0200 Subject: [PATCH 02/32] docs: update docs (#81) * docs: update docs --- README.md | 314 ++++++++++++++++++++++++++++++++++++++++++-- examples/rdt/rdt.ts | 8 ++ 2 files changed, 313 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ac400a58..9d9a3955 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,26 @@ - [Installation](#installation) - [Usage](#usage) - [Getting started](#getting-started) - - [Wallet data](#wallet-data) + - [Login requests](#login-requests) + - [User authentication](#user-authentication) + - [User authentication management](#user-authentication-management) + - [Wallet data requests](#wallet-data-requests) - [Trigger wallet data request programmatically](#trigger-wallet-data-request-programmatically) - [Change requested data](#change-requested-data) + - [Data request builder](#data-request-builder) + - [`DataRequestBuilder.persona()`](#datarequestbuilderpersona) + - [`DataRequestBuilder.accounts()`](#datarequestbuilderaccounts) + - [`OneTimeDataRequestBuilderItem.accounts()`](#onetimedatarequestbuilderitemaccounts) + - [`DataRequestBuilder.personaData()`](#datarequestbuilderpersonadata) + - [`OneTimeDataRequestBuilderItem.personaData()`](#onetimedatarequestbuilderitempersonadata) + - [`DataRequestBuilder.config(input: DataRequestState)`](#datarequestbuilderconfiginput-datarequeststate) - [One Time Data Request](#one-time-data-request) - [State changes](#state-changes) + - [Transaction requests](#transaction-requests) + - [Build transaction manifest](#build-transaction-manifest) + - [sendTransaction](#sendtransaction) - [ROLA (Radix Off-Ledger Authentication)](#rola-radix-off-ledger-authentication) +- [√ Connect Button](#-connect-button) - [Setting up your dApp Definition](#setting-up-your-dapp-definition) - [Setting up a dApp Definition on the Radix Dashboard](#setting-up-a-dapp-definition-on-the-radix-dashboard) - [Data storage](#data-storage) @@ -19,7 +33,7 @@ # What is Radix dApp Toolkit? -Radix dApp Toolkit (RDT) is a TypeScript library that helps facilitate communication with the Radix Wallet and provides an easy-to-use interface over lower level APIs. +Radix dApp Toolkit (RDT) is a TypeScript library that automates getting users logged in to your dApp using a Persona, maintains a browser session for that login, and provides a local cache of data the user has given permission to your app to access associated with their Persona. It also provides an interface to request accounts and personal data from the user's wallet, either as a permission for ongoing access or as a one-time request, as well as to submit transaction manifest stubs for the user to review, sign, and submit in their wallet. The current version only supports desktop browser webapps with requests made via the Radix Wallet Connector browser extension. It is intended to later add support for mobile browser webapps using deep linking with the same essential interface. @@ -70,14 +84,103 @@ const rdt = RadixDappToolkit({ - **requires** dAppDefinitionAddress - Specifies the dApp that is interacting with the wallet. Used in dApp verification process on the wallet side. [Read more](#setting-up-your-dapp-definition) - **requires** networkId - Target radix network ID. -## Wallet data +## Login requests -A data requests needs to be sent to the wallet in order to read wallet data. +The user's journey on your dApp always always starts with connecting their wallet and logging in with a Persona. The "Connect" button always requests a Persona login from the user's wallet. + +The default behavior is to request the login alone, but you may also choose to add additional requests for account information or personal data to get at the time of login. This is useful if there is information that you know your dApp always needs to be able to function. You can also however choose to keep the login simple and make other requests later, as needed. Doing it this way allows your dApp to provide a helpful description in its UI of what a given piece of requested information is needed for, such as "please share all of your accounts that you want to use with this dApp" or "providing your email address will let us keep you informed of new features". + +The Persona the user logs in with sets the context for all ongoing account and personal data requests for that session. The Radix Wallet keeps track of what permissions the user has provided for each dApp and each Persona they've used with that dApp. RDT automatically keeps track of the currently logged in Persona so that requests to the wallet are for the correct Persona. + +After login, RDT also provides your dApp with a local cache of all account information and personal data that a user has given permission to share with your dApp for their chosen Persona. + +For a pure frontend dApp (where you have no backend or user database), there is typically no reason for a Persona login to be verified and the login process is completely automated by RDT. + +### User authentication + +For a full-stack dApp there is also the user authentication flow. Typically, a full-stack dApp would request a persona together with a proof of ownership, which is then verified on the dApp backend using ROLA verification. + +**What is a proof of ownership?** + +A signature produced by the wallet used to verify that the wallet is in control of a persona or account. + +```typescript +// Signed challenge +{ + type: 'persona' | 'account' + challenge: string + proof: { + publicKey: string + signature: string + curve: 'curve25519' | 'secp256k1' + } + address: string +} +``` + +The signature is composed of: + +| | | +| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| **prefix** | "R" (as in ROLA) in ascii encoding | +| **challenge** | 32 random bytes provided by the dApp | +| **length of dApp definition address** | String length of the dApp definition address | +| **dApp definition address** | The dApp definition address of the requesting dApp | +| **origin** | The origin of the dApp (e.g. `https://dashboard.radixdlt.com`). This is a value that is added to the wallet data request by the Connector extension. | + +**Challenge** + +In order to request a persona or account with proof of ownership a challenge is needed. + +A challenge is a random 32 bytes hex encoded string that looks something like: `4ccb0555d6b4faad0d7f5ed40bf4e4f0665c8ba35929c638e232e09775d0fa0e` + +**Why do we need a challenge?** + +The challenge plays an important role in the authentication flow, namely preventing replay attacks from bad actors. The challenge ensures that an authentication request payload sent from the client can only be used once. After a challenge is claimed by a request, the subsequent requests can no longer be resolved successfully with the same payload. As a security best practice, a stored challenge should have a short expiration time. In this case, just enough time for a user to interact with the wallet. + +**Request persona with proof** + +In order to request a proof, it is required to provide a function to RDT that produces a challenge. + +```typescript +// type requestChallengeFromDappBackendFn = () => Promise + +rdt.walletApi.provideChallengeGenerator(requestChallengeFromDappBackendFn) + +rdt.walletApi.setRequestData(DataRequestBuilder.persona.withProof()) + +rdt.walletApi.walletData$.subscribe((walletData) => { + const personaProof = walletData.proofs.find( + (proof) => proof.type === 'persona' + ) + if (personaProof) handleLogin(personaProof) +}) +``` + +See [ROLA example](https://github.com/radixdlt/rola-examples) for an end-to-end implementation. + +### User authentication management + +After a successful ROLA verification it is up to the dApp's business logic to handle user authentication session in order to keep the user logged-in between requests. Although RDT is persisting state between page reloads, it is not aware of user authentication. The dApp logic needs to control the login state and sign out a user when needed. + +**Expired user auth session** + +If a user's auth session has expired it is recommended to logout the user in RDT as well. The dApp needs to call the `disconnect` method in order to but the user in a **not connected** state. + +```typescript +rdt.disconnect() +``` + +The `disconnect` method resets the RDT state, to login anew, a wallet data request needs to be triggered. + +## Wallet data requests + +For your dApp to access data from a user's wallet, whether account information or personal data, a request must be sent to the wallet. By default, the request will be "ongoing", meaning that the user will be asked for permission to share the information whenever they login to your dApp with their current Persona. A request may also be "one time" if it is for transient use and you do not require the permission to be retained by the user's wallet. There are two ways to trigger a data request: -1. By user action in the √ Connect button -2. Programmatically through `walletApi.sendRequest` method +1. As part of the login request when the user clicks the √ Connect button's "Connect" +2. Programmatically through the walletApi.sendRequest method #### Trigger wallet data request programmatically @@ -86,25 +189,129 @@ const result = await rdt.walletApi.sendRequest() if (result.isError()) return handleException() +// { +// persona?: Persona, +// accounts: Account[], +// personaData: WalletDataPersonaData[], +// proofs: SignedChallenge[], +// } const walletData = result.value ``` ### Change requested data -By default, a data request will ask the wallet for a persona login. +By default, a data request requires a Persona to set its context and so if the user is not already logged in, the data request will include a request for login. Use `walletApi.setRequestData` together with `DataRequestBuilder` to change the wallet data request. ```typescript rdt.walletApi.setRequestData( + DataRequestBuilder.persona().withProof(), DataRequestBuilder.accounts().exactly(1), DataRequestBuilder.personaData().fullName().emailAddresses() ) ``` +### Data request builder + +The `DataRequestBuilder` and `OneTimeDataRequestBuilder` is there to assist you in constructing a wallet data request. + +#### `DataRequestBuilder.persona()` + +```typescript +withProof: (value?: boolean) => PersonaRequestBuilder +``` + +Example: Request persona with proof of ownership + +```typescript +rdt.walletApi.setRequestData(DataRequestBuilder.persona().withProof()) +``` + +#### `DataRequestBuilder.accounts()` + +```typescript +atLeast: (n: number) => AccountsRequestBuilder +exactly: (n: number) => AccountsRequestBuilder +withProof: (value?: boolean) => AccountsRequestBuilder +reset: (value?: boolean) => AccountsRequestBuilder +``` + +Example: Request at least 1 account with proof of ownership + +```typescript +rdt.walletApi.setRequestData( + DataRequestBuilder.accounts().atLeast(1).withProof() +) +``` + +#### `OneTimeDataRequestBuilderItem.accounts()` + +```typescript +atLeast: (n: number) => OneTimeAccountsRequestBuilder +exactly: (n: number) => OneTimeAccountsRequestBuilder +withProof: (value?: boolean) => OneTimeAccountsRequestBuilder +``` + +Example: Exactly 2 accounts + +```typescript +rdt.walletApi.sendOneTimeRequest( + OneTimeDataRequestBuilder.accounts().exactly(2) +) +``` + +#### `DataRequestBuilder.personaData()` + +```typescript +fullName: (value?: boolean) => PersonaDataRequestBuilder +emailAddresses: (value?: boolean) => PersonaDataRequestBuilder +phoneNumbers: (value?: boolean) => PersonaDataRequestBuilder +reset: (value?: boolean) => PersonaDataRequestBuilder +``` + +Example: Request full name and email address + +```typescript +rdt.walletApi.setRequestData( + DataRequestBuilder.personaData().fullName().emailAddresses() +) +``` + +#### `OneTimeDataRequestBuilderItem.personaData()` + +```typescript +fullName: (value?: boolean) => PersonaDataRequestBuilder +emailAddresses: (value?: boolean) => PersonaDataRequestBuilder +phoneNumbers: (value?: boolean) => PersonaDataRequestBuilder +``` + +Example: Request phone number + +```typescript +rdt.walletApi.sendOneTimeRequest( + OneTimeDataRequestBuilder.personaData().phoneNumbers() +) +``` + +#### `DataRequestBuilder.config(input: DataRequestState)` + +Use this method if you prefer to provide a raw data request object. + +Example: Request at least 1 account and full name. + +```typescript +rdt.walletApi.setRequestData( + DataRequestBuilder.config({ + personaData: { fullName: true }, + accounts: { numberOfAccounts: { quantifier: 'atLeast', quantity: 1 } }, + }) +) +``` + ### One Time Data Request -One Time data requests will always result in the Radix Wallet asking for the user's permission to share the data with the dApp. The wallet response from a one time data request is meant to be discarded after usage. A typical use case would be to populate a web-form with user data. +One-time data requests do not have a Persona context, and so will always result in the Radix Wallet asking the user to select where to draw personal data from. The wallet response from a one time data request is meant to be discarded after usage. A typical use case would be to populate a web-form with user data. ```typescript const result = rdt.walletApi.sendOneTimeRequest( @@ -114,6 +321,11 @@ const result = rdt.walletApi.sendOneTimeRequest( if (result.isError()) return handleException() +// { +// accounts: Account[], +// personaData: WalletDataPersonaData[], +// proofs: SignedChallenge[], +// } const walletData = result.value ``` @@ -123,6 +335,12 @@ Listen to wallet data changes by subscribing to `walletApi.walletData$`. ```typescript const subscription = rdt.walletApi.walletData$.subscribe((walletData) => { + // { + // persona?: Persona, + // accounts: Account[], + // personaData: WalletDataPersonaData[], + // proofs: SignedChallenge[], + // } doSomethingWithAccounts(walletData.accounts) }) ``` @@ -136,12 +354,90 @@ subscription.unsubscribe() Get the latest wallet data by calling `walletApi.getWalletData()`. ```typescript +// { +// persona?: Persona, +// accounts: Account[], +// personaData: WalletDataPersonaData[], +// proofs: SignedChallenge[], +// } const walletData = rdt.walletApi.getWalletData() ``` +## Transaction requests + +Your dApp can send transactions to the user's Radix Wallet for them to review, sign, and submit them to the Radix Network. + +Radix transactions are built using "transaction manifests", that use a simple syntax to describe desired behavior. See [documentation on transaction manifest commands here](https://docs-babylon.radixdlt.com/main/scrypto/transaction-manifest/intro.html). + +It is important to note that what your dApp sends to the Radix Wallet is actually a "transaction manifest stub". It is completed before submission by the Radix Wallet. For example, the Radix Wallet will automatically add a command to lock the necessary amount of network fees from one of the user's accounts. It may also add "assert" commands to the manifest according to user desires for expected returns. + +**NOTE:** Information will be provided soon on a ["comforming" transaction manifest stub format](https://docs-babylon.radixdlt.com/main/standards/comforming-transactions.html) that ensures clear presentation and handling in the Radix Wallet. + +### Build transaction manifest + +We recommend using template strings for constructing simpler transaction manifests. If your dApp is sending complex manifests a manifest builder can be found in [TypeScript Radix Engine Toolkit](https://github.com/radixdlt/typescript-radix-engine-toolkit#building-manifests) + +### sendTransaction + +This sends the transaction manifest stub to a user's Radix Wallet, where it will be completed, presented to the user for review, signed as required, and submitted to the Radix network to be processed. + +```typescript +type SendTransactionInput = { + transactionManifest: string + version: number + blobs?: string[] + message?: string +} +``` + +- **requires** transactionManifest - specify the transaction manifest +- **requires** version - specify the version of the transaction manifest +- **optional** blobs - used for deploying packages +- **optional** message - message to be included in the transaction + +
+ +sendTransaction example + +```typescript +const result = await rdt.walletApi.sendTransaction({ + version: 1, + transactionManifest: '...', +}) + +if (result.isErr()) { + // code to handle the exception +} + +const transactionIntentHash = result.value.transactionIntentHash +``` + +
+ # ROLA (Radix Off-Ledger Authentication) -[End-to-end ROLA verification example](https://github.com/radixdlt/rola-examples) using RDT in a [full-stack dApp](https://docs-babylon.radixdlt.com/main/getting-started-developers/dapp-backend/building-a-full-stack-dapp.html). +ROLA is method of authenticating something claimed by the user connected to your dApp with the Radix Wallet. It uses the capabilities of the Radix Network to make this possible in a way that is decentralized and flexible for the user. + +ROLA is intended for use in the server backend portion of a Full Stack dApp. It runs "off-ledger" alongside backend business and user management logic, providing reliable authentication of claims of user control using "on-ledger" data from the Radix Network. + +The primary use for ROLA is to authenticate the user's Persona login with the user's control of account(s) on Radix. Let's say that Alice is subscribed to an online streaming service on the Radix network called Radflix, which requires a subscription badge to enter the website. Alice logs in with her Persona to Radflix and now needs to prove that she owns an account that contains a Radflix subscription badge. By using Rola we can verify that Alice is the owner of the account that contains the Radflix subscription badge. Once we have verified that Alice is the owner of the account, we can then use the account to check for the Radflix subscription badge and verify that Alice has a valid subscription. + +**Read more** + +- [ROLA example](https://github.com/radixdlt/rola-examples) +- [Full-stack dApp](https://docs-babylon.radixdlt.com/main/getting-started-developers/dapp-backend/building-a-full-stack-dapp.html) + +# √ Connect Button + +Provides a consistent and delightful user experience between radix dApps. Although complex by itself, RDT is off-loading the developer burden of having to handle the logic of all its internal states. + +Just add the HTML element in your code, and you're all set. + +```html + +``` + +Currently you as the developer have no control over the styling. A complete make-over is coming shortly with more customization options to fit your dApp's branding needs. # Setting up your dApp Definition diff --git a/examples/rdt/rdt.ts b/examples/rdt/rdt.ts index 9e19f217..a264fd5b 100644 --- a/examples/rdt/rdt.ts +++ b/examples/rdt/rdt.ts @@ -2,6 +2,7 @@ import { BehaviorSubject } from 'rxjs' import { DataRequestBuilder, DataRequestStateClient, + OneTimeDataRequestBuilder, RadixDappToolkit, } from '../../src' import { appLogger } from '../logger/state' @@ -106,3 +107,10 @@ rdt.walletApi.walletData$.subscribe((state) => { }) rdt.walletApi.provideChallengeGenerator(async () => createChallenge()) + +rdt.walletApi.setRequestData( + DataRequestBuilder.config({ + personaData: { fullName: true }, + accounts: { numberOfAccounts: { quantifier: 'atLeast', quantity: 1 } }, + }) +) From 6fd0abc2a46c6798ec28fd3c9ea6b8e4793bc022 Mon Sep 17 00:00:00 2001 From: Dawid Sowa Date: Fri, 4 Aug 2023 14:54:21 +0200 Subject: [PATCH 03/32] chore: update deps within semver range - npm audit --- package-lock.json | 489 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 425 insertions(+), 64 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3caa59cb..4914a23a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1798,21 +1798,23 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "17.4.2", + "version": "17.6.7", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.6.7.tgz", + "integrity": "sha512-vqyNRqtbq72P2JadaoWiuoLtXIs9SaAWDqdtef6G2zsoXqKFc7vqj1f+thzVgosXG3X/5K9jNp+iYijmvOfc/g==", "dev": true, - "license": "MIT", "dependencies": { - "@commitlint/types": "^17.4.0", - "semver": "7.3.8" + "@commitlint/types": "^17.4.4", + "semver": "7.5.2" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/is-ignored/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -2018,9 +2020,10 @@ } }, "node_modules/@commitlint/types": { - "version": "17.4.0", + "version": "17.4.4", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz", + "integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.1.0" }, @@ -2224,13 +2227,62 @@ "dev": true, "license": "MIT" }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.17.tgz", + "integrity": "sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz", + "integrity": "sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.17.tgz", + "integrity": "sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.18", + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz", + "integrity": "sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2239,6 +2291,294 @@ "node": ">=12" } }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz", + "integrity": "sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz", + "integrity": "sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz", + "integrity": "sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz", + "integrity": "sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz", + "integrity": "sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz", + "integrity": "sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz", + "integrity": "sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz", + "integrity": "sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz", + "integrity": "sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz", + "integrity": "sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz", + "integrity": "sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz", + "integrity": "sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz", + "integrity": "sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz", + "integrity": "sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz", + "integrity": "sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz", + "integrity": "sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz", + "integrity": "sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz", + "integrity": "sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@fontsource/public-sans": { "version": "4.5.12", "dev": true, @@ -2958,9 +3298,9 @@ } }, "node_modules/@radixdlt/radix-engine-toolkit": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@radixdlt/radix-engine-toolkit/-/radix-engine-toolkit-0.2.3.tgz", - "integrity": "sha512-vIBZbnMw4NBvRACzipbaS6Rnokm+rxePo2Djd/y+yw+ba0YjmFLTj0oDTvzPJNh4LqjMea9Yi/PEfyXRMUmI6A==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@radixdlt/radix-engine-toolkit/-/radix-engine-toolkit-0.2.4.tgz", + "integrity": "sha512-1ecHJCQFngR5o4qlcjTX2WLamKyaNy6IfjNJmCF2Yto/A/4dgulH90AzHjg6HpVG4Geo978x346cuAQjFXdR5w==", "dev": true, "dependencies": { "@noble/ed25519": "2.0.0", @@ -4556,10 +4896,11 @@ } }, "node_modules/esbuild": { - "version": "0.17.18", + "version": "0.18.17", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.17.tgz", + "integrity": "sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -4567,28 +4908,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.18", - "@esbuild/android-arm64": "0.17.18", - "@esbuild/android-x64": "0.17.18", - "@esbuild/darwin-arm64": "0.17.18", - "@esbuild/darwin-x64": "0.17.18", - "@esbuild/freebsd-arm64": "0.17.18", - "@esbuild/freebsd-x64": "0.17.18", - "@esbuild/linux-arm": "0.17.18", - "@esbuild/linux-arm64": "0.17.18", - "@esbuild/linux-ia32": "0.17.18", - "@esbuild/linux-loong64": "0.17.18", - "@esbuild/linux-mips64el": "0.17.18", - "@esbuild/linux-ppc64": "0.17.18", - "@esbuild/linux-riscv64": "0.17.18", - "@esbuild/linux-s390x": "0.17.18", - "@esbuild/linux-x64": "0.17.18", - "@esbuild/netbsd-x64": "0.17.18", - "@esbuild/openbsd-x64": "0.17.18", - "@esbuild/sunos-x64": "0.17.18", - "@esbuild/win32-arm64": "0.17.18", - "@esbuild/win32-ia32": "0.17.18", - "@esbuild/win32-x64": "0.17.18" + "@esbuild/android-arm": "0.18.17", + "@esbuild/android-arm64": "0.18.17", + "@esbuild/android-x64": "0.18.17", + "@esbuild/darwin-arm64": "0.18.17", + "@esbuild/darwin-x64": "0.18.17", + "@esbuild/freebsd-arm64": "0.18.17", + "@esbuild/freebsd-x64": "0.18.17", + "@esbuild/linux-arm": "0.18.17", + "@esbuild/linux-arm64": "0.18.17", + "@esbuild/linux-ia32": "0.18.17", + "@esbuild/linux-loong64": "0.18.17", + "@esbuild/linux-mips64el": "0.18.17", + "@esbuild/linux-ppc64": "0.18.17", + "@esbuild/linux-riscv64": "0.18.17", + "@esbuild/linux-s390x": "0.18.17", + "@esbuild/linux-x64": "0.18.17", + "@esbuild/netbsd-x64": "0.18.17", + "@esbuild/openbsd-x64": "0.18.17", + "@esbuild/sunos-x64": "0.18.17", + "@esbuild/win32-arm64": "0.18.17", + "@esbuild/win32-ia32": "0.18.17", + "@esbuild/win32-x64": "0.18.17" } }, "node_modules/escalade": { @@ -5841,9 +6182,10 @@ } }, "node_modules/jest-runtime/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5889,9 +6231,10 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6485,6 +6828,8 @@ }, "node_modules/nanoid": { "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, "funding": [ { @@ -6492,7 +6837,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6561,9 +6905,10 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6850,7 +7195,9 @@ } }, "node_modules/postcss": { - "version": "8.4.23", + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", "dev": true, "funding": [ { @@ -6866,7 +7213,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -7132,9 +7478,10 @@ } }, "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } @@ -7364,9 +7711,10 @@ } }, "node_modules/rollup": { - "version": "3.21.6", + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.27.2.tgz", + "integrity": "sha512-YGwmHf7h2oUHkVBT248x0yt6vZkYQ3/rvE5iQuVBh3WO8GcJ6BNeOkpoX1yMHIiBm18EMLjBPIoUDkhgnyxGOQ==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -7444,9 +7792,10 @@ } }, "node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7519,8 +7868,9 @@ }, "node_modules/source-map-js": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7768,9 +8118,10 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -7875,9 +8226,10 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -8132,13 +8484,14 @@ } }, "node_modules/vite": { - "version": "4.3.5", + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.8.tgz", + "integrity": "sha512-LONawOUUjxQridNWGQlNizfKH89qPigK36XhMI7COMGztz8KNY0JHim7/xDd71CZwGT4HtSRgI7Hy+RlhG0Gvg==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.26", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -8146,12 +8499,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -8164,6 +8521,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -8251,9 +8611,10 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } From c44ad3f72620d2c5331c78aa15e4e38b0bea40c5 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Mon, 7 Aug 2023 12:04:32 +0200 Subject: [PATCH 04/32] build: update export config --- package.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7c76b8de..49637b70 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,18 @@ "main": "dist/radix-dapp-toolkit.mjs", "license": "Apache-2.0", "exports": { - ".": "./dist/radix-dapp-toolkit.mjs" + ".": { + "import": { + "types": "./types/index.d.ts", + "default": "./dist/radix-dapp-toolkit.mjs" + }, + "require": { + "types": "./types/index.d.ts", + "default": "./dist/radix-dapp-toolkit.js" + } + } }, - "types": "types/index.d.ts", + "types": "./types/index.d.ts", "files": [ "dist", "types" From 96b11b2bbe4249d720b18dc546cae9308b71141b Mon Sep 17 00:00:00 2001 From: Dawid Sowa Date: Sat, 5 Aug 2023 00:54:27 +0200 Subject: [PATCH 05/32] feat(sandbox): add pools page --- examples/account/SelectAccount.tsx | 46 ++-- examples/components/InfoBox.tsx | 22 ++ examples/entity/state.ts | 221 ++++++++++-------- examples/helpers/find-metadata.ts | 4 + .../InstantiateGumballMachineCard.tsx | 1 - examples/layouts/Sidebar.tsx | 1 + examples/network/state.ts | 15 +- examples/pools/CreatePoolCard.tsx | 211 +++++++++++++++++ examples/pools/PoolCard.tsx | 212 +++++++++++++++++ examples/pools/PoolsPage.tsx | 22 ++ examples/pools/manifests.ts | 81 +++++++ examples/pools/state.ts | 62 +++++ examples/router.tsx | 5 + 13 files changed, 784 insertions(+), 119 deletions(-) create mode 100644 examples/components/InfoBox.tsx create mode 100644 examples/helpers/find-metadata.ts create mode 100644 examples/pools/CreatePoolCard.tsx create mode 100644 examples/pools/PoolCard.tsx create mode 100644 examples/pools/PoolsPage.tsx create mode 100644 examples/pools/manifests.ts create mode 100644 examples/pools/state.ts diff --git a/examples/account/SelectAccount.tsx b/examples/account/SelectAccount.tsx index a170d982..eec26ba3 100644 --- a/examples/account/SelectAccount.tsx +++ b/examples/account/SelectAccount.tsx @@ -1,4 +1,4 @@ -import { Select, Option } from '@mui/joy' +import { Select, Option, FormControl, FormLabel } from '@mui/joy' import React from 'react' import { useAccounts } from './state' import { SxProps } from '@mui/joy/styles/types' @@ -6,36 +6,44 @@ import { addEntities } from '../entity/state' import { shortenAddress } from '../helpers/shorten-address' export const SelectAccount = ({ + label = '', placeholder = 'Select account…', sx = {}, onChange, + variant, value, }: { placeholder?: string + label?: string sx?: SxProps + variant?: 'plain' | 'outlined' onChange: (account: string) => void value?: string }) => { const accounts = useAccounts() return ( - + + {label ? {label} : ''} + + ) } diff --git a/examples/components/InfoBox.tsx b/examples/components/InfoBox.tsx new file mode 100644 index 00000000..0b7d1aa5 --- /dev/null +++ b/examples/components/InfoBox.tsx @@ -0,0 +1,22 @@ +import { Box, Typography } from '@mui/joy' +import React from 'react' +import { shortenAddress } from '../helpers/shorten-address' + +import { Clipboard } from '../components/Clipboard' +export const InfoBox = ({ + label, + address, +}: { + label: string + address: string +}) => { + return ( + + + {label} + + + {shortenAddress(address)} + + ) +} diff --git a/examples/entity/state.ts b/examples/entity/state.ts index 6c2d40ba..799ff3bc 100644 --- a/examples/entity/state.ts +++ b/examples/entity/state.ts @@ -1,3 +1,4 @@ +import { shortenAddress } from './../helpers/shorten-address' import { BehaviorSubject } from 'rxjs' import { ResultAsync, errAsync, okAsync } from 'neverthrow' import { gatewayApi, rdt } from '../rdt/rdt' @@ -8,6 +9,7 @@ import { StateEntityDetailsResponseComponentDetails, } from '@radixdlt/babylon-gateway-api-sdk' import { createObservableHook } from '../helpers/create-observable-hook' +import { getStringMetadata } from '../helpers/find-metadata' const entityType = { account: 'account', @@ -35,6 +37,7 @@ type Entity = { entityType: typeof entityType.fungibleToken address: string value: number + displayLabel?: string metadata: EntityMetadataItem[] } [entityType.nftCollection]: { @@ -138,112 +141,138 @@ const setEntities = (entities: EntityCollections) => { entitiesState.next(entities) } -const fetchEntity = (entity: AddEntityToCollectionInput) => - gatewayApi - .getEntityDetails(entity.address) - .andThen( - ({ - fungible_resources, - non_fungible_resources, - metadata, - details, - }): ResultAsync => { - const fungibleTokens = fungible_resources.items.map( - transformFungibleResourceItemResponse - ) +const fetchEntities = (requestedEntities: AddEntityToCollectionInput[]) => { + const requestedEntitiesMap = requestedEntities.reduce((prev, next) => { + prev[next.address] = next + return prev + }, {}) + return gatewayApi + .getEntitiesDetails(requestedEntities.map((item) => item.address)) + .andThen((items) => + ResultAsync.combine( + items.map( + ({ + fungible_resources, + non_fungible_resources, + metadata, + details, + address, + }) => { + const entity = requestedEntitiesMap[address] + if (!entity) { + console.warn('didnt found matching entity!') + return okAsync([]) + } - const nftCollections = - entity.type === 'account' - ? non_fungible_resources.items.map( - (item: NonFungibleResourcesCollectionItemVaultAggregated) => ({ - ...transformNftResourceItemResponse(item), - ownerAddress: entity.address, - }) - ) - : [] + const fungibleTokens = fungible_resources.items.map( + transformFungibleResourceItemResponse + ) - switch (entity.type) { - case entityType.account: - return okAsync([ - { - entityType: entity.type, - address: entity.address, - metadata: metadata.items, - fungibleTokens, - nftCollections, - } satisfies Entity['account'], - ]) + const nftCollections = + entity.type === 'account' + ? non_fungible_resources.items.map( + ( + item: NonFungibleResourcesCollectionItemVaultAggregated + ) => ({ + ...transformNftResourceItemResponse(item), + ownerAddress: entity.address, + }) + ) + : [] - case entityType.component: - return okAsync([ - { - entityType: entity.type, - address: entity.address, - metadata: metadata.items, - fungibleTokens, - nftCollections, - details: details as Entity['component']['details'], - } satisfies Entity['component'], - ]) + switch (entity.type) { + case entityType.account: + return okAsync([ + { + entityType: entity.type, + address: entity.address, + metadata: metadata.items, + fungibleTokens, + nftCollections, + } satisfies Entity['account'], + ]) - case entityType.identity: - return okAsync([ - { - entityType: entity.type, - address: entity.address, - metadata: metadata.items, - } satisfies Entity['identity'], - ]) + case entityType.component: + return okAsync([ + { + entityType: entity.type, + address: entity.address, + metadata: metadata.items, + fungibleTokens, + nftCollections, + details: details as Entity['component']['details'], + } satisfies Entity['component'], + ]) - case entityType.fungibleToken: - return okAsync([ - { - entityType: entity.type, - address: entity.address, - value: entity.value, - metadata: metadata.items, - } satisfies Entity['fungibleToken'], - ]) + case entityType.identity: + return okAsync([ + { + entityType: entity.type, + address: entity.address, + metadata: metadata.items, + } satisfies Entity['identity'], + ]) - case entityType.nftCollection: - return gatewayApi - .getEntityNonFungibleIds({ - accountAddress: entity.ownerAddress, - nftAddress: entity.address, - vaultAddress: entity.vaultAddress, - }) - .map((response) => - response.items.map( - (item) => - ({ - entityType: entityType.nft, - nftId: item, - address: `${entity.address}:${item}`, - nftCollectionAddress: entity.address, - ownerAddress: entity.ownerAddress, - } satisfies Entity['nft']) - ) - ) - .map((items) => [ - { - entityType: entityType.nftCollection, - address: entity.address, + case entityType.fungibleToken: + const symbol = getStringMetadata('symbol', { metadata: metadata.items, - vaultAddress: entity.vaultAddress, - totalCount: entity.totalCount, - } satisfies Entity['nftCollection'], - ...items, - ]) + }) + const name = getStringMetadata('name', { + metadata: metadata.items, + }) + const displayLabel = + [symbol, name].filter(Boolean).join(' - ') || + shortenAddress(entity.address) + return okAsync([ + { + entityType: entity.type, + address: entity.address, + value: entity.value, + metadata: metadata.items, + displayLabel, + } satisfies Entity['fungibleToken'], + ]) - default: { - return errAsync(new Error('Invalid entity type')) + case entityType.nftCollection: + return gatewayApi + .getEntityNonFungibleIds({ + accountAddress: entity.ownerAddress, + nftAddress: entity.address, + vaultAddress: entity.vaultAddress, + }) + .map((response) => + response.items.map( + (item) => + ({ + entityType: entityType.nft, + nftId: item, + address: `${entity.address}:${item}`, + nftCollectionAddress: entity.address, + ownerAddress: entity.ownerAddress, + } satisfies Entity['nft']) + ) + ) + .map((items) => [ + { + entityType: entityType.nftCollection, + address: entity.address, + metadata: metadata.items, + vaultAddress: entity.vaultAddress, + totalCount: entity.totalCount, + } satisfies Entity['nftCollection'], + ...items, + ]) + + default: { + return errAsync(new Error('Invalid entity type')) + } + } } - } - } + ) + ) ) - -const fetchEntities = (items: AddEntityToCollectionInput[]) => - ResultAsync.combine(items.map(fetchEntity)).map((items) => items.flat()) + .map((items) => items.flat()) +} export const addEntities = ( input: AddEntityToCollectionInput[], diff --git a/examples/helpers/find-metadata.ts b/examples/helpers/find-metadata.ts new file mode 100644 index 00000000..f89992e7 --- /dev/null +++ b/examples/helpers/find-metadata.ts @@ -0,0 +1,4 @@ +export const getStringMetadata = (key: string, object: { metadata: any[] }) => { + const metadata = object.metadata.find((m) => m.key === key) + return metadata ? metadata.value.typed.value : undefined +} diff --git a/examples/integration-tests/GumballMachine/InstantiateGumballMachineCard.tsx b/examples/integration-tests/GumballMachine/InstantiateGumballMachineCard.tsx index 397c2681..cebae706 100644 --- a/examples/integration-tests/GumballMachine/InstantiateGumballMachineCard.tsx +++ b/examples/integration-tests/GumballMachine/InstantiateGumballMachineCard.tsx @@ -56,7 +56,6 @@ export const InstantiateGumballMachineCard = () => { addLog(`instantiating gumball machine component`) return instantiateComponent(state.ownerAccount) .map((values) => { - console.log(values) const state_updates = values.transaction.receipt?.state_updates as { new_global_entities: { entity_address: string; entity_type: string }[] } diff --git a/examples/layouts/Sidebar.tsx b/examples/layouts/Sidebar.tsx index 7ea147c9..7a686d7c 100644 --- a/examples/layouts/Sidebar.tsx +++ b/examples/layouts/Sidebar.tsx @@ -32,6 +32,7 @@ export const Sidebar = () => ( { path: 'data-request', label: 'Data Requests' }, { path: 'one-time-data-request', label: 'One Time Data Requests' }, { path: 'create-token', label: 'Create Token' }, + { path: 'pools', label: 'Pools' }, { path: 'send-transaction', label: 'Send Transaction', diff --git a/examples/network/state.ts b/examples/network/state.ts index ef4425d9..2d61fe41 100644 --- a/examples/network/state.ts +++ b/examples/network/state.ts @@ -16,13 +16,22 @@ export const bootstrapNetwork = (networkId: number) => { ...response, gateway: RadixNetworkConfigById[networkId].gatewayUrl, }) - return xrdAddress.next(response.well_known_addresses.xrd) + xrdAddress.next(response.well_known_addresses.xrd) + poolPackageAddress.next( + response.well_known_addresses.pool_package || + 'package_tdx_d_1pkgxxxxxxxxxplxxxxxxxxxxxxx020379220524xxxxxxxxxa0ecqd' + ) }) } -const xrdAddress = new BehaviorSubject(undefined) +const poolPackageAddress = new BehaviorSubject('') +const xrdAddress = new BehaviorSubject('') -export const useXrdAddress = createObservableHook(xrdAddress, '') +export const useXrdAddress = createObservableHook(xrdAddress, '') +export const usePoolPackageAddress = createObservableHook( + poolPackageAddress, + '' +) const getNetworkIdDefault = () => { const urlParams = new URLSearchParams(window.location.search) diff --git a/examples/pools/CreatePoolCard.tsx b/examples/pools/CreatePoolCard.tsx new file mode 100644 index 00000000..10d284ca --- /dev/null +++ b/examples/pools/CreatePoolCard.tsx @@ -0,0 +1,211 @@ +import { + FormControl, + Select, + Option, + Input, + Button, + FormLabel, + Stack, + Divider, + Chip, +} from '@mui/joy' +import { Card } from '../components/Card' +import * as React from 'react' +import { useState } from 'react' +import { usePoolPackageAddress, useXrdAddress } from '../network/state' +import { createPoolManifest } from './manifests' +import { useEntities } from '../entity/state' +import { useLogger } from '../components/Logger' +import { gatewayApi, rdt } from '../rdt/rdt' +import { TransactionStatus } from '@radixdlt/babylon-gateway-api-sdk' +import { addPoolComponent } from './state' +export const CreatePoolCard = () => { + const [isLoading, setIsLoading] = useState(false) + + const { addLog } = useLogger() + const entities = useEntities() + const [poolType, setPoolType] = useState< + 'OneResourcePool' | 'TwoResourcePool' | 'MultiResourcePool' + >('TwoResourcePool') + const poolPackageAddress = usePoolPackageAddress() + const xrdAddress = useXrdAddress() + const [resourceAddresses, setResourceAddresses] = useState< + Record + >({ + 0: '', + 1: '', + }) + const instantiatePool = () => { + setIsLoading(true) + const transactionManifest = createPoolManifest( + poolPackageAddress, + poolType, + ...Object.values(resourceAddresses) + ) + addLog(transactionManifest) + rdt.walletApi + .sendTransaction({ + transactionManifest, + version: 1, + }) + .andThen(({ transactionIntentHash }) => + gatewayApi.getTransactionDetails(transactionIntentHash) + ) + .map((response) => { + if ( + response.transaction.transaction_status === + TransactionStatus.CommittedSuccess + ) { + addLog('Pool created successfully') + const state_updates = response.transaction.receipt?.state_updates as { + new_global_entities: { + entity_address: string + entity_type: string + }[] + } + + const createdEntities = state_updates.new_global_entities.map( + (entity) => entity.entity_address + ) + + addPoolComponent({ + address: createdEntities[0], + poolUnit: createdEntities[1], + resources: Object.values(resourceAddresses), + transactions: [ + { + transactionIntentHash: + response.transaction.intent_hash_hex || '', + status: response.transaction.transaction_status, + }, + ], + }) + } + setIsLoading(false) + addLog(`transaction status: ${response.transaction.transaction_status}`) + }) + .mapErr((error) => { + setIsLoading(false) + addLog(JSON.stringify(error, null, 2)) + }) + } + + const updatePoolType = (numberOfResources: number) => { + if (numberOfResources === 1) { + setPoolType('OneResourcePool') + } else if (numberOfResources === 2) { + setPoolType('TwoResourcePool') + } else { + setPoolType('MultiResourcePool') + } + } + + const updateResourceAddress = (value: string, index: number) => { + setResourceAddresses({ + ...resourceAddresses, + [index]: value, + }) + } + return ( + + + {poolType.slice(0, -4)} + + + + + } + > + + {Object.values(resourceAddresses).map((address, index) => { + return ( + + Resource #{index + 1} + + { + updateResourceAddress(ev.target.value, index) + }} + endDecorator={ + + + + + + + } + /> + + ) + })} + + + + + ) +} diff --git a/examples/pools/PoolCard.tsx b/examples/pools/PoolCard.tsx new file mode 100644 index 00000000..5197b5e6 --- /dev/null +++ b/examples/pools/PoolCard.tsx @@ -0,0 +1,212 @@ +import * as React from 'react' +import { Card } from '../components/Card' +import { Clipboard } from '../components/Clipboard' + +import { + InstantiatedPool, + rememberPoolTransaction, + removePoolComponent, +} from './state' +import { + Button, + Divider, + FormControl, + FormLabel, + Input, + List, + ListItem, + Modal, + ModalClose, + Sheet, + Stack, + Tooltip, + Typography, +} from '@mui/joy' +import { contributeToPoolManifest } from './manifests' +import { useEntities } from '../entity/state' +import { useState } from 'react' +import { SelectAccount } from '../account/SelectAccount' +import { rdt } from '../rdt/rdt' +import { shortenAddress } from '../helpers/shorten-address' +import { InfoBox } from '../components/InfoBox' +export const PoolCard = ({ pool }: { pool: InstantiatedPool }) => { + const [isLoading, setIsLoading] = useState(false) + const [isModalOpen, setIsModalOpen] = useState(false) + const [account, setAccount] = useState('') + const [contributions, setContributiones] = useState< + Record< + string, + { + amount: string + accountToWithdrawFrom: string + } + > + >( + pool.resources.reduce((acc, resource) => { + acc[resource] = { + amount: '0', + accountToWithdrawFrom: '', + } + return acc + }, {}) + ) + const { fungibleToken } = useEntities() + + const contribute = () => { + setIsLoading(true) + const transactionManifest = contributeToPoolManifest( + account, + pool.address, + Object.entries(contributions).map(([resourceAddress, value]) => ({ + resourceAddress, + amount: value.amount, + accountToWithdrawFrom: value.accountToWithdrawFrom || account, + })) + ) + rdt.walletApi + .sendTransaction({ + transactionManifest, + version: 1, + }) + .map((result) => { + rememberPoolTransaction(pool.address, result) + setIsLoading(false) + }) + .mapErr((result) => setIsLoading(false)) + } + return ( + <> + setIsModalOpen(false)} + sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }} + > + + + + Transactions History + + + {pool.transactions.map((transaction) => { + return ( + + {shortenAddress(transaction.transactionIntentHash)} + + + ) + })} + + + + + + + + + } + > + + + + { + setContributiones({ + ...contributions, + }) + setAccount(value) + }} + > + + {pool.resources.map((resource, index) => { + return ( + + + {fungibleToken[resource] + ? fungibleToken[resource].displayLabel + : 'Unknown Token'}{' '} + Amount ({shortenAddress(resource)}) + + { + setContributiones({ + ...contributions, + [resource]: { + ...contributions[resource], + amount: ev.target.value, + }, + }) + }} + endDecorator={ + + + + { + setContributiones({ + ...contributions, + [resource]: { + ...contributions[resource], + accountToWithdrawFrom: value, + }, + }) + }} + > + + } + /> + + ) + })} + + + + + ) +} diff --git a/examples/pools/PoolsPage.tsx b/examples/pools/PoolsPage.tsx new file mode 100644 index 00000000..56f11bd0 --- /dev/null +++ b/examples/pools/PoolsPage.tsx @@ -0,0 +1,22 @@ +import * as React from 'react' +import { CreatePoolCard } from './CreatePoolCard' +import { usePoolsState } from './state' +import { PoolCard } from './PoolCard' +import { Grid } from '@mui/joy' + +export const PoolsPage = () => { + const pools = usePoolsState() + return ( + + + + + + {Object.values(pools).map((pool) => ( + + + + ))} + + ) +} diff --git a/examples/pools/manifests.ts b/examples/pools/manifests.ts new file mode 100644 index 00000000..d676a56f --- /dev/null +++ b/examples/pools/manifests.ts @@ -0,0 +1,81 @@ +export const createPoolManifest = ( + poolAddress: string, + poolType: 'OneResourcePool' | 'TwoResourcePool' | 'MultiResourcePool', + ...resourceAddresses: string[] +) => { + const resourcesParameters = { + OneResourcePool: (resourceAddresses: string[]) => + `Address("${resourceAddresses[0]}")`, + TwoResourcePool: (resourceAddresses: string[]) => + `Tuple(${resourceAddresses + .map((address) => `Address("${address}")`) + .join(', ')})`, + MultiResourcePool: (resourceAddresses: string[]) => + `Array
(${resourceAddresses + .map((address) => `Address("${address}")`) + .join(', ')})`, + } + + const manifest = `CALL_FUNCTION Address("${poolAddress}") + "${poolType}" + "instantiate" + Enum() + Enum() + ${resourcesParameters[poolType](resourceAddresses)};` + + console.log(manifest) + return manifest +} + +export const contributeToPoolManifest = ( + account: string, + poolComponent: string, + contributions: { + amount: string + resourceAddress: string + accountToWithdrawFrom?: string + }[] +) => { + const bucketIds = contributions.map(() => crypto.randomUUID()) + const bucketsCreation = contributions.map((contribution, index) => { + const accountToWithdrawFrom = contribution.accountToWithdrawFrom || account + return ` + CALL_METHOD + Address("${accountToWithdrawFrom}") + "withdraw" + Address("${contribution.resourceAddress}") + Decimal("${contribution.amount}"); + + TAKE_ALL_FROM_WORKTOP + Address("${contribution.resourceAddress}") + Bucket("${bucketIds[index]}");` + }) + const poolContributions = ` + CALL_METHOD + Address("${poolComponent}") + "contribute" + ${ + bucketIds.length === 1 + ? `Bucket("${bucketIds[0]}")` + : bucketIds.length === 2 + ? `Tuple( + ${bucketIds.map((bucketId) => `Bucket("${bucketId}")`).join(',')} + )` + : `Array(${bucketIds + .map((bucketId) => `Bucket("${bucketId}")`) + .join(',')} )` + } +; + ` + + const manifest = ` + ${bucketsCreation.join('\n')} + ${poolContributions} + CALL_METHOD + Address("${account}") + "try_deposit_batch_or_abort" + Expression("ENTIRE_WORKTOP"); + ` + console.log(manifest) + return manifest +} diff --git a/examples/pools/state.ts b/examples/pools/state.ts new file mode 100644 index 00000000..63649798 --- /dev/null +++ b/examples/pools/state.ts @@ -0,0 +1,62 @@ +import { BehaviorSubject } from 'rxjs' +import { createObservableHook } from '../helpers/create-observable-hook' +import { SendTransactionResult } from '../../src' +import { TransactionStatus } from '@radixdlt/babylon-gateway-api-sdk' + +const POOLS_STATE_KEY = 'poolsState' + +export type InstantiatedPool = { + address: string + poolUnit: string + resources: string[] + transactions: { + transactionIntentHash: string + status: TransactionStatus + }[] +} + +export type PoolsState = Record + +const getPoolsState = (): PoolsState => { + try { + const raw = localStorage.getItem(POOLS_STATE_KEY) + if (!raw) return {} + const parsed = JSON.parse(raw) as unknown as PoolsState + return parsed + } catch (_) { + return {} + } +} + +export const poolsState = new BehaviorSubject(getPoolsState()) + +export const usePoolsState = createObservableHook(poolsState, getPoolsState()) + +export const setPoolsState = (value: PoolsState) => { + localStorage.setItem(POOLS_STATE_KEY, JSON.stringify(value)) + poolsState.next(value) +} + +export const addPoolComponent = (value: InstantiatedPool) => { + const state = getPoolsState() + state[value.address] = value + setPoolsState(state) +} + +export const rememberPoolTransaction = ( + poolAddress: string, + transaction: { + transactionIntentHash: string + status: TransactionStatus + } +) => { + const state = getPoolsState() + state[poolAddress].transactions.unshift(transaction) + setPoolsState(state) +} + +export const removePoolComponent = ({ address }: InstantiatedPool) => { + const state = getPoolsState() + delete state[address] + setPoolsState(state) +} diff --git a/examples/router.tsx b/examples/router.tsx index 2bf7336f..35eaaaa4 100644 --- a/examples/router.tsx +++ b/examples/router.tsx @@ -8,6 +8,7 @@ import { SettingsPage } from './settings/SettingsPage' import { SendTransactionPage } from './send-transaction/SendTransactionPage' import { RolaPage } from './rola/RolaPage' import { OneTimeDataRequestsPage } from './one-time-data-request/OneTimeDataRequestsPage' +import { PoolsPage } from './pools/PoolsPage' export const router = createBrowserRouter([ { @@ -35,6 +36,10 @@ export const router = createBrowserRouter([ path: 'send-transaction', element: , }, + { + path: 'pools', + element: , + }, { path: 'settings', element: , From 712deee7787aa4765206936d20549ae4d4f9737a Mon Sep 17 00:00:00 2001 From: Dawid Sowa Date: Tue, 8 Aug 2023 00:15:27 +0200 Subject: [PATCH 06/32] feat(sandbox): create form for visual metadata --- .../GumballMachine/SelectGumballComponent.tsx | 1 - examples/layouts/Sidebar.tsx | 1 + examples/pools/CreatePoolCard.tsx | 2 +- examples/pools/PoolCard.tsx | 58 +++++++- examples/pools/manifests.ts | 2 +- examples/router.tsx | 5 + .../standard-metadata/StandardMetadata.tsx | 130 ++++++++++++++++++ .../StandardMetadataPage.tsx | 11 ++ examples/standard-metadata/manifests.ts | 46 +++++++ 9 files changed, 250 insertions(+), 6 deletions(-) create mode 100644 examples/standard-metadata/StandardMetadata.tsx create mode 100644 examples/standard-metadata/StandardMetadataPage.tsx create mode 100644 examples/standard-metadata/manifests.ts diff --git a/examples/integration-tests/GumballMachine/SelectGumballComponent.tsx b/examples/integration-tests/GumballMachine/SelectGumballComponent.tsx index 3a64adfe..b875666a 100644 --- a/examples/integration-tests/GumballMachine/SelectGumballComponent.tsx +++ b/examples/integration-tests/GumballMachine/SelectGumballComponent.tsx @@ -21,7 +21,6 @@ export const SelectGumballComponent = ({ sx={sx} value={value} onChange={(_, value) => { - console.log(value) if (value) { onChange(value) } diff --git a/examples/layouts/Sidebar.tsx b/examples/layouts/Sidebar.tsx index 7a686d7c..f59f8a9c 100644 --- a/examples/layouts/Sidebar.tsx +++ b/examples/layouts/Sidebar.tsx @@ -33,6 +33,7 @@ export const Sidebar = () => ( { path: 'one-time-data-request', label: 'One Time Data Requests' }, { path: 'create-token', label: 'Create Token' }, { path: 'pools', label: 'Pools' }, + { path: 'standard-metadata', label: 'Standard Metadata' }, { path: 'send-transaction', label: 'Send Transaction', diff --git a/examples/pools/CreatePoolCard.tsx b/examples/pools/CreatePoolCard.tsx index 10d284ca..b5d1c05b 100644 --- a/examples/pools/CreatePoolCard.tsx +++ b/examples/pools/CreatePoolCard.tsx @@ -166,7 +166,7 @@ export const CreatePoolCard = () => { { + setState({ ...state, [key]: ev.target.value }) + }} + /> + + ) + } + const sendTx = () => { + setIsLoading(true) + const transactionManifest = createMetadataManifest(state) + rdt.walletApi + .sendTransaction({ + transactionManifest, + version: 1, + }) + .map(() => setIsLoading(false)) + .mapErr(() => setIsLoading(false)) + console.log(transactionManifest) + } + + return ( + + {props.type ? null : ( + + Entity Type + + + )} + {props.address ? null : ( + + Entity Address + { + setState({ ...state, address: ev.target.value }) + }} + /> + + )} + + + + Visual Standard + + + + {(definition[state.type]?.visual || []).map((key) => renderField(key))} + + + ) +} diff --git a/examples/standard-metadata/StandardMetadataPage.tsx b/examples/standard-metadata/StandardMetadataPage.tsx new file mode 100644 index 00000000..18857084 --- /dev/null +++ b/examples/standard-metadata/StandardMetadataPage.tsx @@ -0,0 +1,11 @@ +import * as React from 'react' +import { Card } from '../components/Card' +import { StandardMetadata } from './StandardMetadata' + +export const StandardMetadataPage = () => { + return ( + + + + ) +} diff --git a/examples/standard-metadata/manifests.ts b/examples/standard-metadata/manifests.ts new file mode 100644 index 00000000..e90a5944 --- /dev/null +++ b/examples/standard-metadata/manifests.ts @@ -0,0 +1,46 @@ +export type StandardMetadata = { + type: 'fungible' | 'nonFungible' | 'account' | 'component' + address: string + name?: string + symbol?: string + description?: string + tags?: string + icon_url?: string + info_url?: string +} + +export const createMetadataManifest = (metadata: StandardMetadata) => { + let manifest = `` + console.log(metadata) + + if (metadata.name) { + manifest += `SET_METADATA Address("${metadata.address}") "name" Enum("${metadata.name}");` + } + + if (metadata.symbol) { + manifest += `SET_METADATA Address("${metadata.address}") "symbol" Enum("${metadata.symbol}");` + } + + if (metadata.description) { + manifest += `SET_METADATA Address("${metadata.address}") "description" Enum("${metadata.description}");` + } + + if (metadata.tags) { + manifest += `SET_METADATA Address("${ + metadata.address + }") "tags" Enum(Array(${metadata.tags + .split(',') + .map((tag) => `"${tag}"`) + .join(',')}));` + } + + if (metadata.icon_url) { + manifest += `SET_METADATA Address("${metadata.address}") "icon_url" Enum("${metadata.icon_url}");` + } + + if (metadata.info_url) { + manifest += `SET_METADATA Address("${metadata.address}") "info_url" Enum("${metadata.info_url}");` + } + + return manifest +} From 944c8e426c433ff9d0d2f2d25ed43b820d6854d9 Mon Sep 17 00:00:00 2001 From: Dawid Sowa Date: Fri, 4 Aug 2023 15:04:26 +0200 Subject: [PATCH 07/32] feat: update connect button --- examples/index.css | 7 ++ examples/rdt/rdt.ts | 1 - package-lock.json | 68 ++++++------- package.json | 4 +- src/_types.ts | 22 ++++- src/connect-button/connect-button-client.ts | 100 +++++++++++++++----- src/connect-button/subjects.ts | 17 +++- src/data-request/data-request.ts | 1 + src/radix-dapp-toolkit.ts | 99 ++++++++++++++++--- src/request-items/request-item-client.ts | 62 +++++++++--- src/request-items/subjects.ts | 8 +- src/state/state.ts | 9 +- src/state/subjects.ts | 1 + src/state/types.ts | 1 + src/test-helpers/context.ts | 17 +++- src/wallet/wallet-client.ts | 1 + 16 files changed, 322 insertions(+), 96 deletions(-) diff --git a/examples/index.css b/examples/index.css index 81fb7e36..f14a38a3 100644 --- a/examples/index.css +++ b/examples/index.css @@ -1,3 +1,10 @@ +@import url('../node_modules/@radixdlt/connect-button/dist/style.css'); + +body { + --radix-connect-button-width: 138px; + --radix-connect-button-height: 42px; + --radix-connect-button-border-radius: 12px; +} pre { margin: 0; } diff --git a/examples/rdt/rdt.ts b/examples/rdt/rdt.ts index a264fd5b..616a9315 100644 --- a/examples/rdt/rdt.ts +++ b/examples/rdt/rdt.ts @@ -2,7 +2,6 @@ import { BehaviorSubject } from 'rxjs' import { DataRequestBuilder, DataRequestStateClient, - OneTimeDataRequestBuilder, RadixDappToolkit, } from '../../src' import { appLogger } from '../logger/state' diff --git a/package-lock.json b/package-lock.json index 4914a23a..b9aed2a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.0-alpha.1", - "@radixdlt/wallet-sdk": "0.9.0-alpha.11", + "@radixdlt/connect-button": "0.13.0-alpha.11", + "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", @@ -2951,12 +2951,14 @@ } }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.0.0", - "license": "BSD-3-Clause" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz", + "integrity": "sha512-kXOeFbfCm4fFf2A3WwVEeQj55tMZa8c8/f9AKHMobQMkzNUfUj+antR3fRPaZJawsa1aZiP/Da3ndpZrwEe4rQ==" }, "node_modules/@lit/reactive-element": { - "version": "1.6.1", - "license": "BSD-3-Clause", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.2.tgz", + "integrity": "sha512-rDfl+QnCYjuIGf5xI2sVJWdYIi56CTCwWa+nidKYX6oIuBYwUbT/vX4qbUDlHiZKJ/3FRNQ/tWJui44p6/stSA==", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.0.0" } @@ -3283,18 +3285,14 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.0-alpha.1", - "license": "Apache-2.0", + "version": "0.13.0-alpha.11", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.0-alpha.11.tgz", + "integrity": "sha512-ZxxUtKk0RqEyQ2xhe4j009oQ41suG6e12ni1kq/cdw92mmCzZaFRj4akg+Cpxe4Aw2EeH69saXwnPGOfVi2Ajw==", "dependencies": { - "lit": "^2.6.1", - "rxjs": "^7.8.0" - } - }, - "node_modules/@radixdlt/connect-button/node_modules/rxjs": { - "version": "7.8.0", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "lit": "^2.7.5" + }, + "engines": { + "node": "20.3.0" } }, "node_modules/@radixdlt/radix-engine-toolkit": { @@ -3316,9 +3314,9 @@ } }, "node_modules/@radixdlt/wallet-sdk": { - "version": "0.9.0-alpha.11", - "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.9.0-alpha.11.tgz", - "integrity": "sha512-vW+wlriLm6qldjL97nuFL0S48MU/MvfcsIFHIGUcYP31BxCkLZhiCHBMm/MaTCTWeOG2ppp8qsqF5JkDFqCm/A==", + "version": "0.9.0-alpha.17", + "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.9.0-alpha.17.tgz", + "integrity": "sha512-B9Jtu2H2LzMnQ/368L16BHPfPACCHAwrWJ5pFIX6nq2/vBKR8QjK+77V7P+Ezwovj5FVJNRvUzfBwvhZUH7Qlw==", "dependencies": { "neverthrow": "^6.0.0", "rxjs": "^7.8.1", @@ -3745,8 +3743,9 @@ "license": "MIT" }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "license": "MIT" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, "node_modules/@types/unist": { "version": "2.0.6", @@ -6502,25 +6501,29 @@ "license": "MIT" }, "node_modules/lit": { - "version": "2.6.1", - "license": "BSD-3-Clause", + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.7.6.tgz", + "integrity": "sha512-1amFHA7t4VaaDe+vdQejSVBklwtH9svGoG6/dZi9JhxtJBBlqY5D1RV7iLUYY0trCqQc4NfhYYZilZiVHt7Hxg==", "dependencies": { "@lit/reactive-element": "^1.6.0", - "lit-element": "^3.2.0", - "lit-html": "^2.6.0" + "lit-element": "^3.3.0", + "lit-html": "^2.7.0" } }, "node_modules/lit-element": { - "version": "3.2.2", - "license": "BSD-3-Clause", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.3.2.tgz", + "integrity": "sha512-xXAeVWKGr4/njq0rGC9dethMnYCq5hpKYrgQZYTzawt9YQhMiXfD+T1RgrdY3NamOxwq2aXlb0vOI6e29CKgVQ==", "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.1.0", "@lit/reactive-element": "^1.3.0", - "lit-html": "^2.2.0" + "lit-html": "^2.7.0" } }, "node_modules/lit-html": { - "version": "2.6.1", - "license": "BSD-3-Clause", + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.7.5.tgz", + "integrity": "sha512-YqUzpisJodwKIlbMFCtyrp58oLloKGnnPLMJ1t23cbfIJjg/H9pvLWK4XS69YeubK5HUs1UE4ys9w5dP1zg6IA==", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -8289,7 +8292,8 @@ }, "node_modules/tslog": { "version": "4.8.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.8.2.tgz", + "integrity": "sha512-eAKIRjxfSKYLs06r1wT7oou6Uv9VN6NW9g0JPidBlqQwPBBl5+84dm7r8zSOPVq1kyfEw1P6B3/FLSpZCorAgA==", "engines": { "node": ">=16" }, diff --git a/package.json b/package.json index 49637b70..01919b86 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.0-alpha.1", - "@radixdlt/wallet-sdk": "0.9.0-alpha.11", + "@radixdlt/connect-button": "0.13.0-alpha.11", + "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", diff --git a/src/_types.ts b/src/_types.ts index 2240154d..531cf638 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -8,7 +8,11 @@ import { } from '@radixdlt/wallet-sdk' import { Observable } from 'rxjs' import { WalletClient } from './wallet/wallet-client' -import { RequestItem } from '@radixdlt/connect-button' +import { + RadixButtonStatus, + RadixButtonTheme, + RequestItem, +} from '@radixdlt/connect-button' import { GatewayClient } from './gateway/gateway' import { StateClient } from './state/state' import { RequestItemClient } from './request-items/request-item-client' @@ -35,15 +39,22 @@ export type ConnectButtonProvider = { onConnect$: Observable<{ challenge: string } | undefined> onDisconnect$: Observable onUpdateSharedData$: Observable + onShowPopover$: Observable onCancelRequestItem$: Observable - setLoading: (value: boolean) => void + setStatus: (value: RadixButtonStatus) => void + setMode: (value: 'light' | 'dark') => void + setTheme: (value: RadixButtonTheme) => void + setActiveTab: (value: 'sharing' | 'requests') => void + setIsMobile: (value: boolean) => void + setIsWalletLinked: (value: boolean) => void + setIsExtensionAvailable: (value: boolean) => void setConnected: (value: boolean) => void + setLoggedInTimestamp: (value: string) => void setRequestItems: (value: RequestItem[]) => void setAccounts: (value: Account[]) => void setPersonaData: (value: { value: string; field: string }[]) => void setPersonaLabel: (value: string) => void setDappName: (value: string) => void - setConnecting: (value: boolean) => void destroy: () => void } @@ -100,6 +111,11 @@ export type GatewayApi = { transaction: Transaction } +export type ButtonApi = { + setMode: (value: 'light' | 'dark') => void + setTheme: (value: RadixButtonTheme) => void +} + export type WalletDataRequestResult = ResultAsync< WalletData, { error: string; message?: string } diff --git a/src/connect-button/connect-button-client.ts b/src/connect-button/connect-button-client.ts index c8a020ab..3250505c 100644 --- a/src/connect-button/connect-button-client.ts +++ b/src/connect-button/connect-button-client.ts @@ -8,10 +8,21 @@ import { tap, } from 'rxjs' import { Logger } from 'tslog' -import { Account, ConnectButton, RequestItem } from '@radixdlt/connect-button' +import { + Account, + ConnectButton, + RadixButtonStatus, + RadixButtonTheme, + RequestItem, +} from '@radixdlt/connect-button' import { ConnectButtonProvider } from '../_types' import { ConnectButtonSubjects } from './subjects' +export const isMobile = () => + /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( + globalThis.navigator ? globalThis.navigator.userAgent : '' + ) + export type ConnectButtonClient = ReturnType export const ConnectButtonClient = (input: { @@ -43,9 +54,17 @@ export const ConnectButtonClient = (input: { if (input.explorer) connectButtonElement.explorer = input.explorer + connectButtonElement.isMobile = isMobile() + const onConnect$ = fromEvent(connectButtonElement, 'onConnect').pipe( tap(() => { onConnect((value) => { + if ( + !connectButtonElement.isWalletLinked || + !connectButtonElement.isExtensionAvailable + ) + return + subjects.onConnect.next(value) }) }) @@ -96,8 +115,12 @@ export const ConnectButtonClient = (input: { }) ) - const loading$ = subjects.loading.pipe( - tap((value) => (connectButtonElement.loading = value)) + const status$ = subjects.status.pipe( + tap((value) => (connectButtonElement.status = value)) + ) + + const mode$ = subjects.mode.pipe( + tap((value) => (connectButtonElement.mode = value)) ) const connected$ = subjects.connected.pipe( @@ -106,6 +129,36 @@ export const ConnectButtonClient = (input: { }) ) + const isMobile$ = subjects.isMobile.pipe( + tap((value) => { + connectButtonElement.isMobile = value + }) + ) + + const isWalletLinked$ = subjects.isWalletLinked.pipe( + tap((value) => { + connectButtonElement.isWalletLinked = value + }) + ) + + const isExtensionAvailable$ = subjects.isExtensionAvailable.pipe( + tap((value) => { + connectButtonElement.isExtensionAvailable = value + }) + ) + + const loggedInTimestamp$ = subjects.loggedInTimestamp.pipe( + tap((value) => { + connectButtonElement.loggedInTimestamp = value + }) + ) + + const activeTab$ = subjects.activeTab.pipe( + tap((value) => { + connectButtonElement.activeTab = value + }) + ) + const requestItems$ = subjects.requestItems.pipe( tap((items) => { connectButtonElement.requestItems = items @@ -131,45 +184,38 @@ export const ConnectButtonClient = (input: { }) ) - const connecting$ = subjects.connecting.pipe( - tap((connecting) => { - connectButtonElement.connecting = connecting - }) - ) - const dAppName$ = subjects.dAppName.pipe( tap((value) => { connectButtonElement.dAppName = value }) ) - const showNotification$ = merge( - subjects.showNotification, - subjects.onShowPopover.pipe(map(() => false)), - subjects.requestItems.pipe(map((items) => !!items.length)) - ).pipe( + const theme$ = subjects.theme.pipe( tap((value) => { - const showNotification = - !connectButtonElement.showPopover && value - connectButtonElement.showNotification = showNotification + connectButtonElement.theme = value }) ) return merge( onConnect$, - loading$, + status$, + theme$, + mode$, connected$, requestItems$, + loggedInTimestamp$, + isMobile$, + activeTab$, + isWalletLinked$, + isExtensionAvailable$, onDisconnect$, onCancelRequestItem$, accounts$, personaData$, personaLabel$, - connecting$, onDestroy$, onUpdateSharedData$, onShowPopover$, - showNotification$, dAppName$ ) }) @@ -180,10 +226,20 @@ export const ConnectButtonClient = (input: { return { onConnect$: subjects.onConnect.asObservable(), onDisconnect$: subjects.onDisconnect.asObservable(), + onShowPopover$: subjects.onShowPopover.asObservable(), onUpdateSharedData$: subjects.onUpdateSharedData.asObservable(), onCancelRequestItem$: subjects.onCancelRequestItem.asObservable(), - setLoading: (value: boolean) => subjects.loading.next(value), - setConnecting: (value: boolean) => subjects.connecting.next(value), + setStatus: (value: RadixButtonStatus) => subjects.status.next(value), + setTheme: (value: RadixButtonTheme) => subjects.theme.next(value), + setMode: (value: 'light' | 'dark') => subjects.mode.next(value), + setActiveTab: (value: 'sharing' | 'requests') => + subjects.activeTab.next(value), + setIsMobile: (value: boolean) => subjects.isMobile.next(value), + setIsWalletLinked: (value: boolean) => subjects.isWalletLinked.next(value), + setIsExtensionAvailable: (value: boolean) => + subjects.isExtensionAvailable.next(value), + setLoggedInTimestamp: (value: string) => + subjects.loggedInTimestamp.next(value), setConnected: (value: boolean) => subjects.connected.next(value), setRequestItems: (items: RequestItem[]) => subjects.requestItems.next(items), diff --git a/src/connect-button/subjects.ts b/src/connect-button/subjects.ts index 2953c035..342c08f5 100644 --- a/src/connect-button/subjects.ts +++ b/src/connect-button/subjects.ts @@ -6,14 +6,25 @@ export const ConnectButtonSubjects = () => ({ onConnect: new Subject<{ challenge: string } | undefined>(), onDisconnect: new Subject(), onUpdateSharedData: new Subject(), - loading: new BehaviorSubject(false), connected: new ReplaySubject(1), requestItems: new BehaviorSubject([]), onCancelRequestItem: new Subject(), accounts: new BehaviorSubject([]), - connecting: new BehaviorSubject(false), onShowPopover: new Subject(), - showNotification: new BehaviorSubject(false), + status: new BehaviorSubject<'pending' | 'success' | 'default' | 'error'>( + 'default' + ), + loggedInTimestamp: new BehaviorSubject(''), + isMobile: new BehaviorSubject(false), + isWalletLinked: new BehaviorSubject(false), + isExtensionAvailable: new BehaviorSubject(false), + fullWidth: new BehaviorSubject(false), + activeTab: new BehaviorSubject<'sharing' | 'requests'>('sharing'), + mode: new BehaviorSubject<'light' | 'dark'>('light'), + theme: new BehaviorSubject< + 'radix-blue' | 'black' | 'white' | 'white-with-outline' + >('radix-blue'), + avatarUrl: new BehaviorSubject(''), personaLabel: new BehaviorSubject(''), personaData: new BehaviorSubject<{ value: string; field: string }[]>([]), dAppName: new BehaviorSubject(''), diff --git a/src/data-request/data-request.ts b/src/data-request/data-request.ts index a6730977..c346de9b 100644 --- a/src/data-request/data-request.ts +++ b/src/data-request/data-request.ts @@ -108,6 +108,7 @@ export const DataRequestClient = ({ .map((walletData) => { if (!oneTime) stateClient.setState({ + loggedInTimestamp: Date.now().toString(), walletData, sharedData: transformWalletRequestToSharedData( walletDataRequest, diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index 62931245..af9be9f6 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -10,8 +10,10 @@ import { filter, map, merge, + of, switchMap, tap, + timer, } from 'rxjs' import { RequestItemClient } from './request-items/request-item-client' @@ -20,11 +22,18 @@ import { DataRequestClient } from './data-request/data-request' import { transformWalletDataToConnectButton } from './data-request/transformations/wallet-data-to-connect-button' import { DataRequestStateClient } from './data-request/data-request-state' import { RadixNetworkConfigById } from '@radixdlt/babylon-gateway-api-sdk' -import { GatewayApi, RadixDappToolkitOptions, WalletApi } from './_types' +import { + ButtonApi, + GatewayApi, + RadixDappToolkitOptions, + WalletApi, +} from './_types' +import { withLatestFrom } from 'rxjs/operators' export type RadixDappToolkit = { walletApi: WalletApi gatewayApi: GatewayApi + buttonApi: ButtonApi disconnect: () => void destroy: () => void } @@ -71,6 +80,8 @@ export const RadixDappToolkit = ( }), }) + const storageClient = providers?.storageClient ?? LocalStorageClient() + const walletSdk = providers?.walletSdk ?? WalletSdk({ @@ -81,7 +92,7 @@ export const RadixDappToolkit = ( const requestItemClient = providers?.requestItemClient ?? - RequestItemClient({ + RequestItemClient(storageKey, storageClient, { logger, }) @@ -95,8 +106,6 @@ export const RadixDappToolkit = ( requestItemClient, }) - const storageClient = providers?.storageClient ?? LocalStorageClient() - const stateClient = providers?.stateClient ?? StateClient(storageKey, storageClient, { @@ -137,6 +146,20 @@ export const RadixDappToolkit = ( .subscribe() ) + subscriptions.add( + walletClient.extensionStatus$ + .pipe( + tap((result) => { + connectButtonClient.setIsExtensionAvailable( + result.isExtensionAvailable + ) + connectButtonClient.setIsWalletLinked(result.isWalletLinked) + }) + ) + + .subscribe() + ) + subscriptions.add( connectButtonClient.onConnect$ .pipe( @@ -151,6 +174,19 @@ export const RadixDappToolkit = ( .subscribe() ) + subscriptions.add( + connectButtonClient.onShowPopover$ + .pipe( + withLatestFrom(walletClient.requestItems$), + tap(([_, items]) => { + if (items.filter((item) => item.status === 'pending').length > 0) { + connectButtonClient.setActiveTab('requests') + } + }) + ) + .subscribe() + ) + subscriptions.add( connectButtonClient.onDisconnect$ .pipe( @@ -169,6 +205,7 @@ export const RadixDappToolkit = ( tap((state) => { const { personaData, accounts, personaLabel, connected } = transformWalletDataToConnectButton(state.walletData) + connectButtonClient.setLoggedInTimestamp(state.loggedInTimestamp) connectButtonClient.setAccounts(accounts) connectButtonClient.setPersonaData(personaData) connectButtonClient.setPersonaLabel(personaLabel) @@ -180,18 +217,46 @@ export const RadixDappToolkit = ( subscriptions.add( walletClient.requestItems$ + .pipe(tap((items) => connectButtonClient.setRequestItems(items))) + .subscribe() + ) + + subscriptions.add( + requestItemClient.change$ .pipe( - tap((items) => { - connectButtonClient.setRequestItems(items) - connectButtonClient.setConnecting( - items.some( - (item) => - item.status === 'pending' && item.type === 'loginRequest' + withLatestFrom(requestItemClient.items$), + tap(([, items]) => { + const hasPendingItem = items.find((item) => item.status === 'pending') + + if (hasPendingItem) { + connectButtonClient.setStatus('pending') + } + }), + switchMap(([change]) => { + const newStatus = change.newValue?.status + const oldStatus = change.oldValue?.status + + if ( + oldStatus === 'pending' && + (newStatus === 'success' || newStatus === 'fail') + ) { + connectButtonClient.setStatus( + newStatus === 'success' ? 'success' : 'error' ) - ) - connectButtonClient.setLoading( - items.some((item) => item.status === 'pending') - ) + return timer(2000).pipe( + withLatestFrom(walletClient.requestItems$), + tap(([_, items]) => { + const pendingItem = items.find( + (item) => item.status === 'pending' + ) + connectButtonClient.setStatus( + pendingItem ? 'pending' : 'default' + ) + }) + ) + } + + return of() }) ) .subscribe() @@ -226,6 +291,11 @@ export const RadixDappToolkit = ( getWalletData: () => stateClient.getState().walletData, } + const buttonApi = { + setTheme: connectButtonClient.setTheme, + setMode: connectButtonClient.setMode, + } + const disconnect = () => { walletClient.resetRequestItems() stateClient.reset() @@ -241,6 +311,7 @@ export const RadixDappToolkit = ( return { walletApi, gatewayApi, + buttonApi, disconnect, destroy, } diff --git a/src/request-items/request-item-client.ts b/src/request-items/request-item-client.ts index 5070ee51..a7a8e68b 100644 --- a/src/request-items/request-item-client.ts +++ b/src/request-items/request-item-client.ts @@ -3,21 +3,45 @@ import { map, Subscription, tap } from 'rxjs' import { Logger } from 'tslog' import { RequestItemSubjects } from './subjects' import { errorType } from '@radixdlt/wallet-sdk' +import { StorageProvider } from '../_types' export type RequestItemClient = ReturnType -export const RequestItemClient = (input: { - subjects?: RequestItemSubjects - logger?: Logger -}) => { +export const RequestItemClient = ( + storageKey: string, + storageClient: StorageProvider, + input: { + subjects?: RequestItemSubjects + logger?: Logger + } +) => { const logger = input.logger - const requestsItemStore = new Map() const requestItemIds = new Set() const subscriptions = new Subscription() + const requestsItemStore = new Map() const subjects = input.subjects || RequestItemSubjects() + const requestItemStoreKey = `${storageKey}:requestItemStore` + + storageClient + .getData>(requestItemStoreKey) + .map((store) => { + if (store) { + Object.keys(store).forEach((key) => { + requestItemIds.add(key) + requestsItemStore.set(key, store[key]) + }) + } + subjects.items.next(getItemsList()) + }) + + const emitChange = ( + oldValue: RequestItem | undefined, + newValue: RequestItem | undefined + ) => subjects.onChange.next({ oldValue, newValue }) const createItem = (type: RequestItem['type']): RequestItem => ({ type, status: 'pending', + timestamp: Date.now(), id: crypto.randomUUID(), showCancel: true, }) @@ -26,7 +50,7 @@ export const RequestItemClient = (input: { const item = createItem(type) requestsItemStore.set(item.id, item) requestItemIds.add(item.id) - subjects.onChange.next() + emitChange(undefined, item) logger?.trace(`addRequestItem`, { id: item.id, status: item.status, @@ -36,9 +60,10 @@ export const RequestItemClient = (input: { const remove = (id: string) => { if (requestsItemStore.has(id)) { + const oldValue = requestsItemStore.get(id)! requestsItemStore.delete(id) requestItemIds.delete(id) - subjects.onChange.next() + emitChange(oldValue, undefined) logger?.trace(`removeRequestItem`, id) } } @@ -51,7 +76,7 @@ export const RequestItemClient = (input: { ...partialValue, } as RequestItem requestsItemStore.set(id, updated) - subjects.onChange.next() + emitChange(item, updated) logger?.trace(`patchRequestItemStatus`, updated) } } @@ -66,7 +91,7 @@ export const RequestItemClient = (input: { const reset = () => { requestsItemStore.clear() requestItemIds.clear() - subjects.onChange.next() + emitChange(undefined, undefined) logger?.trace(`resetRequestItems`) } @@ -94,12 +119,13 @@ export const RequestItemClient = (input: { updated.transactionIntentHash = transactionIntentHash! } requestsItemStore.set(id, updated) - subjects.onChange.next() + emitChange(item, updated) + logger?.trace(`updateRequestItemStatus`, updated) } } - const getIds = () => [...requestItemIds] + const getIds = () => [...requestItemIds].reverse() const getItemsList = () => getIds() @@ -110,7 +136,17 @@ export const RequestItemClient = (input: { subjects.onChange .pipe( map(() => getItemsList()), - tap((items) => subjects.items.next(items)) + tap((items) => subjects.items.next(items)), + tap(() => { + const entries = Array.from(requestsItemStore.entries()) + + storageClient.setData( + requestItemStoreKey, + Object.fromEntries( + entries.filter(([, value]) => value.status !== 'pending') + ) + ) + }) ) .subscribe() ) @@ -126,6 +162,6 @@ export const RequestItemClient = (input: { subscriptions.unsubscribe() }, items$: subjects.items.asObservable(), - subjects, + change$: subjects.onChange.asObservable(), } } diff --git a/src/request-items/subjects.ts b/src/request-items/subjects.ts index 1109fd5a..d27f2543 100644 --- a/src/request-items/subjects.ts +++ b/src/request-items/subjects.ts @@ -1,8 +1,14 @@ import { RequestItem } from '@radixdlt/connect-button' import { BehaviorSubject, Subject } from 'rxjs' +export type RequestItemChange = { + oldValue: RequestItem | undefined + newValue: RequestItem | undefined +} + export type RequestItemSubjects = ReturnType export const RequestItemSubjects = () => ({ - onChange: new Subject(), + initialized: new BehaviorSubject(false), + onChange: new Subject(), items: new BehaviorSubject([]), }) diff --git a/src/state/state.ts b/src/state/state.ts index 601ca845..4933d30b 100644 --- a/src/state/state.ts +++ b/src/state/state.ts @@ -41,7 +41,11 @@ export const StateClient = ( } const resetState = () => { - subjects.state.next({ walletData: walletDataDefault, sharedData: {} }) + subjects.state.next({ + walletData: walletDataDefault, + sharedData: {}, + loggedInTimestamp: '', + }) } const initializeState = () => @@ -91,6 +95,9 @@ export const StateClient = ( filter((initialized) => initialized), switchMap(() => subjects.state) ), + patchState: (state: Partial) => { + setState({ ...(subjects.state.value || {}), ...state }) + }, reset: resetState, stateInitialized$: subjects.initialized.asObservable(), destroy: () => { diff --git a/src/state/subjects.ts b/src/state/subjects.ts index 29e04756..e163114d 100644 --- a/src/state/subjects.ts +++ b/src/state/subjects.ts @@ -4,6 +4,7 @@ import { RdtState, walletDataDefault } from './types' export type StateSubjects = ReturnType export const StateSubjects = () => ({ state: new BehaviorSubject({ + loggedInTimestamp: '', walletData: walletDataDefault, sharedData: {}, }), diff --git a/src/state/types.ts b/src/state/types.ts index 2559e866..ca6ba70a 100644 --- a/src/state/types.ts +++ b/src/state/types.ts @@ -73,6 +73,7 @@ export const SharedData = object({ export type RdtState = z.infer export const RdtState = object({ + loggedInTimestamp: string(), walletData: WalletData, sharedData: SharedData, }) diff --git a/src/test-helpers/context.ts b/src/test-helpers/context.ts index ba253507..1440dbcc 100644 --- a/src/test-helpers/context.ts +++ b/src/test-helpers/context.ts @@ -12,6 +12,7 @@ import { StateSubjects } from '../state/subjects' import { RequestItemSubjects } from '../request-items/subjects' import { okAsync } from 'neverthrow' import { InMemoryClient } from '../storage/in-memory-storage-client' +import { of } from 'rxjs' export type Context = { stateClient: StateClient @@ -47,7 +48,13 @@ export const createMockContext = (): MockContext => { const gatewayApiClientMock = mockDeep() const connectButtonMock = mockDeep() - const walletSdkMock = mockDeep() + const walletSdkMock = { + ...mockDeep(), + extensionStatus$: of({ + isWalletLinked: true, + isExtensionAvailable: true, + }), + } const storageClient = InMemoryClient() const stateClient = StateClient(`rdt:test:12`, storageClient, { @@ -55,7 +62,7 @@ export const createMockContext = (): MockContext => { logger, }) - const requestItemClient = RequestItemClient({ + const requestItemClient = RequestItemClient(`rdt:test:12`, storageClient, { subjects: requestItemSubjects, logger, }) @@ -69,11 +76,13 @@ export const createMockContext = (): MockContext => { logger, requestItemClient, gatewayClient, - walletSdk: walletSdkMock, + walletSdk: walletSdkMock as any, onCancelRequestItem$: connectButtonSubjects.onCancelRequestItem.asObservable(), }) + connectButtonMock.onShowPopover$ = + connectButtonSubjects.onShowPopover.asObservable() as any connectButtonMock.onConnect$ = connectButtonSubjects.onConnect.asObservable() as any connectButtonMock.onDisconnect$ = @@ -97,7 +106,7 @@ export const createMockContext = (): MockContext => { gatewayClient, gatewayApiClient: gatewayApiClientMock, connectButton: connectButtonMock, - walletSdk: walletSdkMock, + walletSdk: walletSdkMock as any, storageClient, } } diff --git a/src/wallet/wallet-client.ts b/src/wallet/wallet-client.ts index a18fca39..2ae5a5b6 100644 --- a/src/wallet/wallet-client.ts +++ b/src/wallet/wallet-client.ts @@ -120,6 +120,7 @@ export const WalletClient = (input: { return { request: sendWalletRequest, sendTransaction, + extensionStatus$: walletSdk.extensionStatus$, requestItems$: requestItemClient.items$, resetRequestItems: requestItemClient.reset, destroy: () => { From 3db1737e4d5814d9b94eb24eed76eaaccd2a1a90 Mon Sep 17 00:00:00 2001 From: Dawid Sowa Date: Fri, 4 Aug 2023 17:04:57 +0200 Subject: [PATCH 08/32] feat(sandbox): add CB config in sandbox settings --- examples/rdt/rdt.ts | 12 ++++++++++- examples/rdt/state.ts | 33 ++++++++++++++++++++++++++++++ examples/settings/SettingsPage.tsx | 31 +++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 examples/rdt/state.ts diff --git a/examples/rdt/rdt.ts b/examples/rdt/rdt.ts index 616a9315..d0596243 100644 --- a/examples/rdt/rdt.ts +++ b/examples/rdt/rdt.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject } from 'rxjs' +import { BehaviorSubject, tap } from 'rxjs' import { DataRequestBuilder, DataRequestStateClient, @@ -19,6 +19,7 @@ import { RadixNetwork, RadixNetworkConfigById, } from '@radixdlt/babylon-gateway-api-sdk' +import { connectButtonConfigSubject } from './state' const networkId = networkIdSubject.value @@ -113,3 +114,12 @@ rdt.walletApi.setRequestData( accounts: { numberOfAccounts: { quantifier: 'atLeast', quantity: 1 } }, }) ) + +connectButtonConfigSubject + .pipe( + tap((value) => { + rdt.buttonApi.setTheme(value.theme as any) + rdt.buttonApi.setMode(value.mode as any) + }) + ) + .subscribe() diff --git a/examples/rdt/state.ts b/examples/rdt/state.ts new file mode 100644 index 00000000..1db2f750 --- /dev/null +++ b/examples/rdt/state.ts @@ -0,0 +1,33 @@ +import { BehaviorSubject } from 'rxjs' +import { createObservableHook } from '../helpers/create-observable-hook' + +export type ConnectButtonConfig = { + theme: string + mode: string + width: number + height: number + borderRadius: number +} + +const DEFAULT_CONNECT_BUTTON_CONFIG: ConnectButtonConfig = { + theme: 'radix-blue', + mode: 'light', + width: 140, + height: 40, + borderRadius: 8, +} + +export const connectButtonConfigSubject = + new BehaviorSubject(DEFAULT_CONNECT_BUTTON_CONFIG) + +export const useConnectButtonConfig = createObservableHook( + connectButtonConfigSubject, + DEFAULT_CONNECT_BUTTON_CONFIG +) + +export const patchConnectButtonConfig = ( + value: Partial +) => { + const currentValue = connectButtonConfigSubject.value + connectButtonConfigSubject.next({ ...currentValue, ...value }) +} diff --git a/examples/settings/SettingsPage.tsx b/examples/settings/SettingsPage.tsx index 950c58d8..f687debd 100644 --- a/examples/settings/SettingsPage.tsx +++ b/examples/settings/SettingsPage.tsx @@ -4,8 +4,12 @@ import { Card } from '../components/Card' import Button from '@mui/joy/Button' import Input from '@mui/joy/Input' import { setDAppDefinitionAddress, useDAppDefinitionAddress } from '../rdt/rdt' - +import Select from '@mui/joy/Select' +import Option from '@mui/joy/Option' +import { FormControl, FormLabel } from '@mui/joy' +import { patchConnectButtonConfig, useConnectButtonConfig } from '../rdt/state' export const SettingsPage = () => { + const connectButtonConfig = useConnectButtonConfig() const dAppDefinitionAddress = useDAppDefinitionAddress() const [state, setState] = React.useState<{ dAppDefinitionAddress: string }>({ dAppDefinitionAddress, @@ -44,6 +48,31 @@ export const SettingsPage = () => { + + + Theme + + + + + Mode + + + ) } From 4a1573796dd9679626507bf54bb0aa1c6e03505d Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Mon, 7 Aug 2023 14:15:29 +0200 Subject: [PATCH 09/32] fix: cancel data request at any time --- src/wallet/wallet-client.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/wallet/wallet-client.ts b/src/wallet/wallet-client.ts index 2ae5a5b6..5d9efae1 100644 --- a/src/wallet/wallet-client.ts +++ b/src/wallet/wallet-client.ts @@ -8,7 +8,7 @@ import { Subscription, filter, firstValueFrom, - merge, + map, switchMap, tap, } from 'rxjs' @@ -37,21 +37,25 @@ export const WalletClient = (input: { eventCallback: (event) => { messageLifeCycleEvent.next(event) }, - requestControl: ({ cancelRequest }) => { + requestControl: ({ cancelRequest, getRequest }) => { firstValueFrom( - merge( - messageLifeCycleEvent.pipe( - filter((event) => event === 'receivedByWallet'), - tap(() => { + messageLifeCycleEvent.pipe( + filter((event) => event === 'receivedByWallet'), + map(() => getRequest()), + tap((request) => { + if (request.items.discriminator === 'transaction') requestItemClient.patch(id, { showCancel: false }) - }) - ), - input.onCancelRequestItem$.pipe( - filter((requestItemId) => requestItemId === id), - switchMap(() => - cancelRequest().map(() => requestItemClient.cancel(id)) - ) - ) + }) + ) + ) + + firstValueFrom( + input.onCancelRequestItem$.pipe( + filter((requestItemId) => requestItemId === id), + switchMap(() => { + requestItemClient.cancel(id) + return cancelRequest() + }) ) ) }, From ea944069f0e3dc78c5821587353ff3c2e7b4a6c9 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Mon, 7 Aug 2023 14:27:38 +0200 Subject: [PATCH 10/32] fix: show nickname if available --- .../transformations/wallet-data-to-connect-button.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/data-request/transformations/wallet-data-to-connect-button.ts b/src/data-request/transformations/wallet-data-to-connect-button.ts index 2a766852..326c7ae3 100644 --- a/src/data-request/transformations/wallet-data-to-connect-button.ts +++ b/src/data-request/transformations/wallet-data-to-connect-button.ts @@ -11,8 +11,12 @@ export const transformWalletDataToConnectButton = (walletData: WalletData) => { return { value: variant === 'western' - ? `${givenNames} "${nickname}" ${familyName}` - : `${familyName} "${nickname}" ${givenNames}`, + ? `${givenNames}${ + nickname ? ` "${nickname}" ` : ' ' + }${familyName}` + : `${familyName}${ + nickname ? ` "${nickname}" ` : ' ' + }${givenNames}`, field: 'fullName', } } else if (item.entry === 'emailAddresses') { From 9d70649532e4a8ba98d7cb96f816839d536db338 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Mon, 7 Aug 2023 14:37:52 +0200 Subject: [PATCH 11/32] fix: change button status after cancel --- src/radix-dapp-toolkit.ts | 5 +++-- src/wallet/wallet-client.ts | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index af9be9f6..bb301aef 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -28,7 +28,7 @@ import { RadixDappToolkitOptions, WalletApi, } from './_types' -import { withLatestFrom } from 'rxjs/operators' +import { mergeMap, withLatestFrom } from 'rxjs/operators' export type RadixDappToolkit = { walletApi: WalletApi @@ -232,7 +232,7 @@ export const RadixDappToolkit = ( connectButtonClient.setStatus('pending') } }), - switchMap(([change]) => { + mergeMap(([change]) => { const newStatus = change.newValue?.status const oldStatus = change.oldValue?.status @@ -243,6 +243,7 @@ export const RadixDappToolkit = ( connectButtonClient.setStatus( newStatus === 'success' ? 'success' : 'error' ) + return timer(2000).pipe( withLatestFrom(walletClient.requestItems$), tap(([_, items]) => { diff --git a/src/wallet/wallet-client.ts b/src/wallet/wallet-client.ts index 5d9efae1..c240c450 100644 --- a/src/wallet/wallet-client.ts +++ b/src/wallet/wallet-client.ts @@ -54,6 +54,11 @@ export const WalletClient = (input: { filter((requestItemId) => requestItemId === id), switchMap(() => { requestItemClient.cancel(id) + requestItemClient.updateStatus({ + id, + status: 'fail', + error: 'userCancelledRequest', + }) return cancelRequest() }) ) From b0950be0aab6b382d7dce78fb40186f7ff400715 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 10:40:39 +0200 Subject: [PATCH 12/32] refactor: remove explorer from connect button client --- examples/index.css | 6 +---- package-lock.json | 8 +++---- package.json | 2 +- src/_types.ts | 1 + src/connect-button/connect-button-client.ts | 15 ++++++++---- src/connect-button/subjects.ts | 1 + src/radix-dapp-toolkit.ts | 26 +++++++++++++++++---- 7 files changed, 40 insertions(+), 19 deletions(-) diff --git a/examples/index.css b/examples/index.css index f14a38a3..ec0b32f2 100644 --- a/examples/index.css +++ b/examples/index.css @@ -1,9 +1,5 @@ -@import url('../node_modules/@radixdlt/connect-button/dist/style.css'); - body { - --radix-connect-button-width: 138px; - --radix-connect-button-height: 42px; - --radix-connect-button-border-radius: 12px; + --radix-connect-button-border-radius: 8px; } pre { margin: 0; diff --git a/package-lock.json b/package-lock.json index b9aed2a2..6648ed4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.0-alpha.11", + "@radixdlt/connect-button": "0.13.1-alpha.5", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.0-alpha.11", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.0-alpha.11.tgz", - "integrity": "sha512-ZxxUtKk0RqEyQ2xhe4j009oQ41suG6e12ni1kq/cdw92mmCzZaFRj4akg+Cpxe4Aw2EeH69saXwnPGOfVi2Ajw==", + "version": "0.13.1-alpha.5", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.5.tgz", + "integrity": "sha512-OcFYMEYwXs/mtv1ZquMo/wS7wy5HSdGE8JimXP755R4vezYjdaBlbn/n820qn2pT0E4ijomHw21D1LltHn8dkw==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index 01919b86..6a1a8d48 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.0-alpha.11", + "@radixdlt/connect-button": "0.13.1-alpha.5", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", diff --git a/src/_types.ts b/src/_types.ts index 531cf638..21baf02a 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -41,6 +41,7 @@ export type ConnectButtonProvider = { onUpdateSharedData$: Observable onShowPopover$: Observable onCancelRequestItem$: Observable + onLinkClick$: Observable<{ type: 'account' | 'transaction'; data: string }> setStatus: (value: RadixButtonStatus) => void setMode: (value: 'light' | 'dark') => void setTheme: (value: RadixButtonTheme) => void diff --git a/src/connect-button/connect-button-client.ts b/src/connect-button/connect-button-client.ts index 3250505c..38ab89eb 100644 --- a/src/connect-button/connect-button-client.ts +++ b/src/connect-button/connect-button-client.ts @@ -26,7 +26,6 @@ export const isMobile = () => export type ConnectButtonClient = ReturnType export const ConnectButtonClient = (input: { - explorer?: ConnectButton['explorer'] onConnect?: (done: (input?: { challenge: string }) => void) => void subjects?: ConnectButtonSubjects logger?: Logger @@ -52,8 +51,6 @@ export const ConnectButtonClient = (input: { switchMap((connectButtonElement) => { logger?.debug(`connectButtonDiscovered`) - if (input.explorer) connectButtonElement.explorer = input.explorer - connectButtonElement.isMobile = isMobile() const onConnect$ = fromEvent(connectButtonElement, 'onConnect').pipe( @@ -79,6 +76,14 @@ export const ConnectButtonClient = (input: { }) ) + const onLinkClick$ = fromEvent< + CustomEvent<{ type: 'account' | 'transaction'; data: string }> + >(connectButtonElement, 'onLinkClick').pipe( + tap((ev) => { + subjects.onLinkClick.next(ev.detail) + }) + ) + const onDestroy$ = fromEvent(connectButtonElement, 'onDestroy').pipe( tap(() => { logger?.debug(`connectButtonRemovedFromDOM`) @@ -216,7 +221,8 @@ export const ConnectButtonClient = (input: { onDestroy$, onUpdateSharedData$, onShowPopover$, - dAppName$ + dAppName$, + onLinkClick$ ) }) ) @@ -229,6 +235,7 @@ export const ConnectButtonClient = (input: { onShowPopover$: subjects.onShowPopover.asObservable(), onUpdateSharedData$: subjects.onUpdateSharedData.asObservable(), onCancelRequestItem$: subjects.onCancelRequestItem.asObservable(), + onLinkClick$: subjects.onLinkClick.asObservable(), setStatus: (value: RadixButtonStatus) => subjects.status.next(value), setTheme: (value: RadixButtonTheme) => subjects.theme.next(value), setMode: (value: 'light' | 'dark') => subjects.mode.next(value), diff --git a/src/connect-button/subjects.ts b/src/connect-button/subjects.ts index 342c08f5..f93d3c9e 100644 --- a/src/connect-button/subjects.ts +++ b/src/connect-button/subjects.ts @@ -28,4 +28,5 @@ export const ConnectButtonSubjects = () => ({ personaLabel: new BehaviorSubject(''), personaData: new BehaviorSubject<{ value: string; field: string }[]>([]), dAppName: new BehaviorSubject(''), + onLinkClick: new Subject<{ type: 'account' | 'transaction'; data: string }>(), }) diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index bb301aef..553275ec 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -62,11 +62,6 @@ export const RadixDappToolkit = ( providers?.connectButton ?? ConnectButtonClient({ logger, - explorer: explorer ?? { - baseUrl: RadixNetworkConfigById[networkId].dashboardUrl, - transactionPath: '/transaction/', - accountsPath: '/account/', - }, }) const gatewayClient = @@ -174,6 +169,27 @@ export const RadixDappToolkit = ( .subscribe() ) + subscriptions.add( + connectButtonClient.onLinkClick$ + .pipe( + tap(({ type, data }) => { + const { baseUrl, transactionPath, accountsPath } = explorer ?? { + baseUrl: RadixNetworkConfigById[networkId].dashboardUrl, + transactionPath: '/transaction/', + accountsPath: '/account/', + } + if (!baseUrl || !window) return + + const url = `${baseUrl}${ + type === 'transaction' ? transactionPath : accountsPath + }${data}` + + window.open(url) + }) + ) + .subscribe() + ) + subscriptions.add( connectButtonClient.onShowPopover$ .pipe( From 3ee234f9a3ac1f99f8e1b0859bb9bc85a7d5dde4 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 11:22:03 +0200 Subject: [PATCH 13/32] refactor: use atomic state --- src/radix-dapp-toolkit.ts | 4 +- src/state/state.ts | 99 ++++++++++++++++++++++++++++++--------- src/state/subjects.ts | 10 ++-- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index 553275ec..a6398de0 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -304,8 +304,8 @@ export const RadixDappToolkit = ( updateSharedData: () => dataRequestClient.updateSharedData(), sendOneTimeRequest: dataRequestClient.sendOneTimeRequest, sendTransaction: walletClient.sendTransaction, - walletData$: stateClient.state$.pipe(map((state) => state.walletData)), - getWalletData: () => stateClient.getState().walletData, + walletData$: stateClient.walletData$, + getWalletData: () => stateClient.getWalletData(), } const buttonApi = { diff --git a/src/state/state.ts b/src/state/state.ts index 4933d30b..84f63e8c 100644 --- a/src/state/state.ts +++ b/src/state/state.ts @@ -1,10 +1,21 @@ import { Logger } from 'tslog' import { StateSubjects } from './subjects' import { StorageProvider } from '../_types' -import { Subscription, filter, first, skip, switchMap, tap } from 'rxjs' -import { removeUndefined } from '../helpers/remove-undefined' +import { + Subscription, + combineLatest, + debounceTime, + filter, + first, + map, + skip, + switchMap, + tap, +} from 'rxjs' import { ResultAsync } from 'neverthrow' import { RdtState, walletDataDefault } from './types' +import { produce } from 'immer' +import isEqual from 'lodash.isequal' export type StateClient = ReturnType @@ -21,6 +32,18 @@ export const StateClient = ( const subscriptions = new Subscription() + const state = combineLatest([ + subjects.walletData, + subjects.sharedData, + subjects.loggedInTimestamp, + ]).pipe( + map(([walletData, sharedData, loggedInTimestamp]) => ({ + walletData, + sharedData, + loggedInTimestamp, + })) + ) + const readStateFromStorage = () => storageClient.getData(key).map((state) => { if (state) logger?.debug('readFromStorage') @@ -28,24 +51,36 @@ export const StateClient = ( }) const writeStateToStorage = (value: RdtState) => { - return storageClient.setData(key, value).map(() => { - logger?.trace('writeToStorage', value) - }) + return storageClient + .setData( + key, + produce(value, (draft) => { + draft.walletData.proofs = [] + }) + ) + .map(() => { + logger?.trace('writeToStorage', value) + }) } const setState = (state: Partial) => { - removeUndefined(state).map((data) => { - if (Object.keys(data).length) - subjects.state.next({ ...subjects.state.value, ...data }) - }) + const { walletData, sharedData, loggedInTimestamp } = state + if (walletData && !isEqual(subjects.walletData.value, walletData)) { + subjects.walletData.next(walletData) + } + + if (sharedData) subjects.sharedData.next(sharedData) + if (loggedInTimestamp !== undefined) + subjects.loggedInTimestamp.next(loggedInTimestamp) } const resetState = () => { - subjects.state.next({ - walletData: walletDataDefault, - sharedData: {}, - loggedInTimestamp: '', - }) + if (!isEqual(subjects.walletData.value, walletDataDefault)) { + subjects.walletData.next(walletDataDefault) + } + + subjects.sharedData.next({}) + subjects.loggedInTimestamp.next('') } const initializeState = () => @@ -63,7 +98,12 @@ export const StateClient = ( .map((storedState) => { if (storedState) { logger?.debug(`initializeStateFromStorage`) - return subjects.state.next(storedState) + return setState( + produce(storedState, (draft) => { + draft.walletData.persona = draft.walletData.persona ?? undefined + return draft + }) + ) } else { logger?.debug(`initializeStateFromDefault`) resetState() @@ -72,12 +112,10 @@ export const StateClient = ( initializeState() - subscriptions.add( - subjects.state.pipe(switchMap(writeStateToStorage)).subscribe() - ) + subscriptions.add(state.pipe(switchMap(writeStateToStorage)).subscribe()) subscriptions.add( - subjects.state + state .pipe( skip(1), first(), @@ -88,15 +126,32 @@ export const StateClient = ( .subscribe() ) + const getState = () => { + return { + walletData: subjects.walletData.value, + sharedData: subjects.sharedData.value, + loggedInTimestamp: subjects.loggedInTimestamp.value, + } + } + return { setState, - getState: () => subjects.state.value, + getState, + walletData$: combineLatest([ + subjects.initialized, + subjects.walletData, + ]).pipe( + debounceTime(1), + filter(([initialized]) => initialized), + map(() => subjects.walletData.value) + ), + getWalletData: () => subjects.walletData.value, state$: subjects.initialized.pipe( filter((initialized) => initialized), - switchMap(() => subjects.state) + switchMap(() => state) ), patchState: (state: Partial) => { - setState({ ...(subjects.state.value || {}), ...state }) + setState({ ...getState(), ...state }) }, reset: resetState, stateInitialized$: subjects.initialized.asObservable(), diff --git a/src/state/subjects.ts b/src/state/subjects.ts index e163114d..7f9aa05c 100644 --- a/src/state/subjects.ts +++ b/src/state/subjects.ts @@ -1,12 +1,10 @@ import { BehaviorSubject } from 'rxjs' -import { RdtState, walletDataDefault } from './types' +import { SharedData, WalletData, walletDataDefault } from './types' export type StateSubjects = ReturnType export const StateSubjects = () => ({ - state: new BehaviorSubject({ - loggedInTimestamp: '', - walletData: walletDataDefault, - sharedData: {}, - }), + walletData: new BehaviorSubject(walletDataDefault), + sharedData: new BehaviorSubject({}), + loggedInTimestamp: new BehaviorSubject(''), initialized: new BehaviorSubject(false), }) From 391778960d778bcf74b2ee962e5b00091a960436 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 11:56:45 +0200 Subject: [PATCH 14/32] feat: add data request control --- src/_types.ts | 1 + src/data-request/data-request.ts | 35 ++++++++++++++++++++++++++++++++ src/radix-dapp-toolkit.ts | 5 ++++- src/wallet/wallet-client.ts | 2 +- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/_types.ts b/src/_types.ts index 21baf02a..9420c029 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -126,6 +126,7 @@ export type WalletApi = { getWalletData: () => WalletDataState walletData$: Observable provideChallengeGenerator: (fn: () => Promise) => void + dataRequestControl: (fn: (walletResponse: WalletData) => Promise) => void updateSharedData: () => WalletDataRequestResult sendTransaction: (input: SendTransactionInput) => SendTransactionResult setRequestData: (...dataRequestBuilderItem: DataRequestBuilderItem[]) => void diff --git a/src/data-request/data-request.ts b/src/data-request/data-request.ts index c346de9b..c3d65894 100644 --- a/src/data-request/data-request.ts +++ b/src/data-request/data-request.ts @@ -11,6 +11,7 @@ import { transformWalletRequestToSharedData, } from './transformations/shared-data' import { DataRequestStateClient } from './data-request-state' +import { WalletData } from '../state/types' export type DataRequestClient = ReturnType @@ -31,6 +32,10 @@ export const DataRequestClient = ({ | (() => ResultAsync) | undefined + let dataRequestControl: ( + walletData: WalletData + ) => ResultAsync + const isChallengeNeeded = (dataRequestState: DataRequestState) => dataRequestState.accounts?.withProof || dataRequestState.persona?.withProof @@ -52,6 +57,16 @@ export const DataRequestClient = ({ })) } + const provideDataRequestControl = ( + fn: (walletData: WalletData) => Promise + ) => { + dataRequestControl = (walletData: WalletData) => + ResultAsync.fromPromise(fn(walletData), () => ({ + error: 'LoginRejectedByDapp', + message: 'Login rejected by dApp', + })) + } + const sendOneTimeRequest = (...items: DataRequestBuilderItem[]) => sendRequest({ dataRequestState: dataRequestStateClient.toDataRequestState(...items), @@ -105,6 +120,25 @@ export const DataRequestClient = ({ }) ) .andThen(transformWalletResponseToRdtWalletData) + .andThen((response) => { + if (dataRequestControl) + return dataRequestControl(response) + .map(() => { + requestItemClient.updateStatus({ id, status: 'success' }) + return response + }) + .mapErr((error) => { + requestItemClient.updateStatus({ + id, + status: 'fail', + error: error.error, + }) + return error + }) + + requestItemClient.updateStatus({ id, status: 'success' }) + return ok(response) + }) .map((walletData) => { if (!oneTime) stateClient.setState({ @@ -143,6 +177,7 @@ export const DataRequestClient = ({ return { provideChallengeGenerator, + provideDataRequestControl, sendOneTimeRequest, setState, sendRequest: ({ diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index a6398de0..f4abd61e 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -8,7 +8,6 @@ import { BehaviorSubject, Subscription, filter, - map, merge, of, switchMap, @@ -29,6 +28,7 @@ import { WalletApi, } from './_types' import { mergeMap, withLatestFrom } from 'rxjs/operators' +import { WalletData } from './state/types' export type RadixDappToolkit = { walletApi: WalletApi @@ -301,6 +301,9 @@ export const RadixDappToolkit = ( provideChallengeGenerator: ( input: Parameters[0] ) => dataRequestClient.provideChallengeGenerator(input), + dataRequestControl: (fn: (walletData: WalletData) => Promise) => { + dataRequestClient.provideDataRequestControl(fn) + }, updateSharedData: () => dataRequestClient.updateSharedData(), sendOneTimeRequest: dataRequestClient.sendOneTimeRequest, sendTransaction: walletClient.sendTransaction, diff --git a/src/wallet/wallet-client.ts b/src/wallet/wallet-client.ts index c240c450..18d7535e 100644 --- a/src/wallet/wallet-client.ts +++ b/src/wallet/wallet-client.ts @@ -75,7 +75,7 @@ export const WalletClient = (input: { .request(input, cancelRequestControl(requestItemId)) .map((response) => { logger?.debug(`⬇️walletSuccessResponse`, response) - requestItemClient.updateStatus({ id: requestItemId, status: 'success' }) + return response }) .mapErr((error) => { From e00ab66c5f4074cfb6e97229d4c3ca1af496cb58 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 12:01:38 +0200 Subject: [PATCH 15/32] test: update unit tests --- src/test-helpers/context.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test-helpers/context.ts b/src/test-helpers/context.ts index 1440dbcc..fd2c6822 100644 --- a/src/test-helpers/context.ts +++ b/src/test-helpers/context.ts @@ -91,6 +91,8 @@ export const createMockContext = (): MockContext => { connectButtonSubjects.onCancelRequestItem.asObservable() as any connectButtonMock.onUpdateSharedData$ = connectButtonSubjects.onUpdateSharedData.asObservable() as any + connectButtonMock.onLinkClick$ = + connectButtonSubjects.onLinkClick.asObservable() as any gatewayApiClientMock.getEntityDetails.mockReturnValue( okAsync(undefined) as any From 38996b341db0b41e9efb320436865cd350a1729a Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 12:11:21 +0200 Subject: [PATCH 16/32] chore: update connect button dep --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6648ed4e..c6980041 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.5", + "@radixdlt/connect-button": "0.13.1-alpha.6", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.5", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.5.tgz", - "integrity": "sha512-OcFYMEYwXs/mtv1ZquMo/wS7wy5HSdGE8JimXP755R4vezYjdaBlbn/n820qn2pT0E4ijomHw21D1LltHn8dkw==", + "version": "0.13.1-alpha.6", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.6.tgz", + "integrity": "sha512-UlSPo7/jqFfCsRAdrlOkgSIU3UbfY04MLykmarj3nSAZ/UXgBgknfv4cjfhhvYjvic1MMiue8mhiKD6APcjgjw==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index 6a1a8d48..45e58c56 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.5", + "@radixdlt/connect-button": "0.13.1-alpha.6", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From e85efcda0d78782b58c1e422dcae2d90ea393b7b Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Tue, 8 Aug 2023 12:29:12 +0200 Subject: [PATCH 17/32] chore: update connect button dep --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c6980041..988eae5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.6", + "@radixdlt/connect-button": "0.13.1-alpha.7", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.6", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.6.tgz", - "integrity": "sha512-UlSPo7/jqFfCsRAdrlOkgSIU3UbfY04MLykmarj3nSAZ/UXgBgknfv4cjfhhvYjvic1MMiue8mhiKD6APcjgjw==", + "version": "0.13.1-alpha.7", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.7.tgz", + "integrity": "sha512-YSSY2Q8HerhEBH+b05g8EmH99/68G1tJopB4Y8giouXbkmAgAqPYb11bphQprF37c2Jc6jH1hRxz3JRUaZobxg==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index 45e58c56..9a860b05 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.6", + "@radixdlt/connect-button": "0.13.1-alpha.7", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From 429e37e27b7ba5d72457ec536892ab219aedb171 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Wed, 9 Aug 2023 08:48:37 +0200 Subject: [PATCH 18/32] docs: include dataRequestControl and connect button styling (#91) * docs: include dataRequestControl and connect button styling --- README.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9d9a3955..5d25357b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - [Getting started](#getting-started) - [Login requests](#login-requests) - [User authentication](#user-authentication) + - [Handle user authentication](#handle-user-authentication) - [User authentication management](#user-authentication-management) - [Wallet data requests](#wallet-data-requests) - [Trigger wallet data request programmatically](#trigger-wallet-data-request-programmatically) @@ -26,6 +27,12 @@ - [sendTransaction](#sendtransaction) - [ROLA (Radix Off-Ledger Authentication)](#rola-radix-off-ledger-authentication) - [√ Connect Button](#-connect-button) + - [Styling](#styling) + - [Themes](#themes) + - [Modes](#modes) + - [CSS variables](#css-variables) + - [Compact mode](#compact-mode) + - [Sandbox](#sandbox) - [Setting up your dApp Definition](#setting-up-your-dapp-definition) - [Setting up a dApp Definition on the Radix Dashboard](#setting-up-a-dapp-definition-on-the-radix-dashboard) - [Data storage](#data-storage) @@ -149,11 +156,35 @@ rdt.walletApi.provideChallengeGenerator(requestChallengeFromDappBackendFn) rdt.walletApi.setRequestData(DataRequestBuilder.persona.withProof()) -rdt.walletApi.walletData$.subscribe((walletData) => { +// handle the wallet response +rdt.walletApi.dataRequestControl(async (walletData) => { const personaProof = walletData.proofs.find( (proof) => proof.type === 'persona' ) - if (personaProof) handleLogin(personaProof) + if (personaProof) await handleLogin(personaProof) +}) +``` + +### Handle user authentication + +A typical full stack dApp will require the user to provide proof of ownership. After sending a data request and getting the proof from the wallet, you need authenticate the user through ROLA on the dApp backend. + +Use `walletApi.dataRequestControl` to provide a callback function that intercepts the RDT data request response flow. If no error has been thrown inside of the callback function the RDT flow will proceed as usual. + +```typescript +rdt.walletApi.dataRequestControl(async (walletData) => { + const personaProof = walletData.proofs.find( + (proof) => proof.type === 'persona' + ) + if (personaProof) await handleLogin(personaProof) +}) +``` + +Throwing an error inside of `walletApi.dataRequestControl` callback will prevent RDT from getting into a logged in state. A full stack dApp may wish to do this to prevent RDT from treating the user as logged in because the ROLA authentication check failed, or for other application-specific reasons why a given user should not be allowed to login. + +```typescript +rdt.walletApi.dataRequestControl(async (walletData) => { + throw new Error('something bad happened...') }) ``` @@ -437,7 +468,58 @@ Just add the HTML element in your code, and you're all set. ``` -Currently you as the developer have no control over the styling. A complete make-over is coming shortly with more customization options to fit your dApp's branding needs. +## Styling + +Configure the √ Connect Button to fit your dApp's branding. + +### Themes + +Available themes: + +- `radix-blue` (default) +- `black` +- `white-with-outline` +- `white` + +```typescript +rdt.buttonApi.setTheme('black') +``` + +### Modes + +Available modes: + +- `light` (default) +- `dark` + +```typescript +rdt.buttonApi.setMode('dark') +``` + +### CSS variables + +There are three CSS variables available: + +- `--radix-connect-button-width` (default 138px) +- `--radix-connect-button-height` (default 42px) +- `--radix-connect-button-border-radius` (default 0px) + +```css +body { + --radix-connect-button-width: 200px; + --radix-connect-button-height: 42px; + --radix-connect-button-border-radius: 12px; +} +``` + +### Compact mode + +Setting `--radix-connect-button-width` below `138px` will enable compact mode. + +### Sandbox + +Play around with the different configurations on the +[sandbox environment](https://connect-button-storybook.radixdlt.com/) # Setting up your dApp Definition From a177718e0065614416f387188c5bdc6c05d299b9 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Thu, 10 Aug 2023 11:17:28 +0200 Subject: [PATCH 19/32] chore: update connect button --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 988eae5d..eeb079a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.7", + "@radixdlt/connect-button": "0.13.1-alpha.8", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.7", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.7.tgz", - "integrity": "sha512-YSSY2Q8HerhEBH+b05g8EmH99/68G1tJopB4Y8giouXbkmAgAqPYb11bphQprF37c2Jc6jH1hRxz3JRUaZobxg==", + "version": "0.13.1-alpha.8", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.8.tgz", + "integrity": "sha512-zwLlwPawKt6nLXuCTynfFYpwxnoJ1DTqqrSdyuXvqHFtPAhc9O7ICjlU7z/QidKmzsYGSRDfCaARbcEmQb9pcQ==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index 9a860b05..5d82a8a1 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.7", + "@radixdlt/connect-button": "0.13.1-alpha.8", "@radixdlt/wallet-sdk": "0.9.0-alpha.17", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From 6d15f1d7fddf143de8228ecc46915f165708b9df Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Thu, 10 Aug 2023 12:35:37 +0200 Subject: [PATCH 20/32] style: connect button resizing --- examples/index.css | 1 + package-lock.json | 16 ++++++++-------- package.json | 4 ++-- src/_types.ts | 5 ++++- src/radix-dapp-toolkit.ts | 30 +++++++++++++++++++----------- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/examples/index.css b/examples/index.css index ec0b32f2..8171ccb2 100644 --- a/examples/index.css +++ b/examples/index.css @@ -1,5 +1,6 @@ body { --radix-connect-button-border-radius: 8px; + --radix-connect-button-width: 8px; } pre { margin: 0; diff --git a/package-lock.json b/package-lock.json index eeb079a8..8b68c0ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.8", - "@radixdlt/wallet-sdk": "0.9.0-alpha.17", + "@radixdlt/connect-button": "0.13.1-alpha.10", + "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.8", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.8.tgz", - "integrity": "sha512-zwLlwPawKt6nLXuCTynfFYpwxnoJ1DTqqrSdyuXvqHFtPAhc9O7ICjlU7z/QidKmzsYGSRDfCaARbcEmQb9pcQ==", + "version": "0.13.1-alpha.10", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.10.tgz", + "integrity": "sha512-eXT0fLLAJinkoXG175YkEk8umISpX5VOiyUSW+byW5H6bFCeOnJnsVupamdkI0YBdsNBAstGgMaT47Am0RZ01A==", "dependencies": { "lit": "^2.7.5" }, @@ -3314,9 +3314,9 @@ } }, "node_modules/@radixdlt/wallet-sdk": { - "version": "0.9.0-alpha.17", - "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.9.0-alpha.17.tgz", - "integrity": "sha512-B9Jtu2H2LzMnQ/368L16BHPfPACCHAwrWJ5pFIX6nq2/vBKR8QjK+77V7P+Ezwovj5FVJNRvUzfBwvhZUH7Qlw==", + "version": "0.9.0-alpha.18", + "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.9.0-alpha.18.tgz", + "integrity": "sha512-0gLuQTaIZTe0yIC5IaTPzP1vcfn79LV5zWAdXaJU0FiiWxQEjUfaFavux+OefRlmuy9Kh/k2bp1R1CDhOQ17hQ==", "dependencies": { "neverthrow": "^6.0.0", "rxjs": "^7.8.1", diff --git a/package.json b/package.json index 5d82a8a1..9aa39fb2 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.8", - "@radixdlt/wallet-sdk": "0.9.0-alpha.17", + "@radixdlt/connect-button": "0.13.1-alpha.10", + "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", diff --git a/src/_types.ts b/src/_types.ts index 9420c029..9c68da17 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -41,7 +41,10 @@ export type ConnectButtonProvider = { onUpdateSharedData$: Observable onShowPopover$: Observable onCancelRequestItem$: Observable - onLinkClick$: Observable<{ type: 'account' | 'transaction'; data: string }> + onLinkClick$: Observable<{ + type: 'account' | 'transaction' | 'showQrCode' | 'setupGuide' + data: string + }> setStatus: (value: RadixButtonStatus) => void setMode: (value: 'light' | 'dark') => void setTheme: (value: RadixButtonTheme) => void diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index f4abd61e..24806d54 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -173,18 +173,26 @@ export const RadixDappToolkit = ( connectButtonClient.onLinkClick$ .pipe( tap(({ type, data }) => { - const { baseUrl, transactionPath, accountsPath } = explorer ?? { - baseUrl: RadixNetworkConfigById[networkId].dashboardUrl, - transactionPath: '/transaction/', - accountsPath: '/account/', + if (['account', 'transaction'].includes(type)) { + const { baseUrl, transactionPath, accountsPath } = explorer ?? { + baseUrl: RadixNetworkConfigById[networkId].dashboardUrl, + transactionPath: '/transaction/', + accountsPath: '/account/', + } + if (!baseUrl || !window) return + + const url = `${baseUrl}${ + type === 'transaction' ? transactionPath : accountsPath + }${data}` + + window.open(url) + } else if (type === 'setupGuide') + window.open( + 'https://docs-babylon.radixdlt.com/main/getting-started-developers/wallet/wallet-and-connector-installation.html' + ) + else if (type === 'showQrCode') { + walletSdk.openPopup() } - if (!baseUrl || !window) return - - const url = `${baseUrl}${ - type === 'transaction' ? transactionPath : accountsPath - }${data}` - - window.open(url) }) ) .subscribe() From 3482134a7087010308bc18bea8862674ae04bdd6 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Thu, 10 Aug 2023 12:36:13 +0200 Subject: [PATCH 21/32] style(sandbox): connect button default width --- examples/index.css | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/index.css b/examples/index.css index 8171ccb2..ec0b32f2 100644 --- a/examples/index.css +++ b/examples/index.css @@ -1,6 +1,5 @@ body { --radix-connect-button-border-radius: 8px; - --radix-connect-button-width: 8px; } pre { margin: 0; From e7131ca058860e1f68b81ca87903f0b896bee23a Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Thu, 10 Aug 2023 14:32:35 +0200 Subject: [PATCH 22/32] chore: bump connect button version --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b68c0ce..49622fdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.10", + "@radixdlt/connect-button": "0.13.1-alpha.11", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.10", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.10.tgz", - "integrity": "sha512-eXT0fLLAJinkoXG175YkEk8umISpX5VOiyUSW+byW5H6bFCeOnJnsVupamdkI0YBdsNBAstGgMaT47Am0RZ01A==", + "version": "0.13.1-alpha.11", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.11.tgz", + "integrity": "sha512-c+VcoK+5Pc92bHo9KQ3JNA1psvVcLijBtIvDcxA74UqtBOevvvtdMfMJxUApkPQ4MNyT3eQ1dSN4RI3qiDonfw==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index 9aa39fb2..fb52d0db 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.10", + "@radixdlt/connect-button": "0.13.1-alpha.11", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From 2b768f7a92f4f1fe774df5100af05d69808c4945 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Thu, 10 Aug 2023 15:49:03 +0200 Subject: [PATCH 23/32] chore: bump connect button --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49622fdf..a8cd3704 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.11", + "@radixdlt/connect-button": "0.13.1-alpha.13", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.11", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.11.tgz", - "integrity": "sha512-c+VcoK+5Pc92bHo9KQ3JNA1psvVcLijBtIvDcxA74UqtBOevvvtdMfMJxUApkPQ4MNyT3eQ1dSN4RI3qiDonfw==", + "version": "0.13.1-alpha.13", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.13.tgz", + "integrity": "sha512-UvUc9nnAswiZ3HrI2lXWYNyTKSVlMtTshRnh2pOThtaMh1KDB1BOG5Z3x89YShEBcewDcOWBFL4y55JJWff0zQ==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index fb52d0db..c3e42d41 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.11", + "@radixdlt/connect-button": "0.13.1-alpha.13", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From f4553bf229f4a8a0ce1f2470dcdd3de36f1138f8 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Fri, 11 Aug 2023 09:54:07 +0200 Subject: [PATCH 24/32] chore: bump connect button version --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index a8cd3704..3044635c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.13", + "@radixdlt/connect-button": "0.13.1-alpha.16", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.13", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.13.tgz", - "integrity": "sha512-UvUc9nnAswiZ3HrI2lXWYNyTKSVlMtTshRnh2pOThtaMh1KDB1BOG5Z3x89YShEBcewDcOWBFL4y55JJWff0zQ==", + "version": "0.13.1-alpha.16", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.16.tgz", + "integrity": "sha512-xJQjqx3ndXef4MNN9zMav7qIUJP/P29/pDALTAODCsf65NLTFpMxyqm/guWGgbl2n8atl7Sv3CT1UJUAgSg45g==", "dependencies": { "lit": "^2.7.5" }, diff --git a/package.json b/package.json index c3e42d41..4e2bf88e 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.13", + "@radixdlt/connect-button": "0.13.1-alpha.16", "@radixdlt/wallet-sdk": "0.9.0-alpha.18", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From 80096d8343992aa70eddb2a94e389e1928f20010 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Fri, 11 Aug 2023 14:04:02 +0200 Subject: [PATCH 25/32] fix: ux improvements --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- src/connect-button/connect-button-client.ts | 2 -- .../wallet-data-to-connect-button.ts | 16 +++++++--------- src/radix-dapp-toolkit.ts | 7 ++++++- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3044635c..f2c07d4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.16", - "@radixdlt/wallet-sdk": "0.9.0-alpha.18", + "@radixdlt/connect-button": "0.13.2-alpha.2", + "@radixdlt/wallet-sdk": "0.10.0-alpha.3", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.1-alpha.16", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.1-alpha.16.tgz", - "integrity": "sha512-xJQjqx3ndXef4MNN9zMav7qIUJP/P29/pDALTAODCsf65NLTFpMxyqm/guWGgbl2n8atl7Sv3CT1UJUAgSg45g==", + "version": "0.13.2-alpha.2", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.2-alpha.2.tgz", + "integrity": "sha512-4zhvB1D3pPSLsQMDnVqx11UgNMUfcx4kjwDpAGt2jJDAWlSqVUy+sEVhow3F45JiURtQbWjIVuEzgsEnXHyeCA==", "dependencies": { "lit": "^2.7.5" }, @@ -3314,9 +3314,9 @@ } }, "node_modules/@radixdlt/wallet-sdk": { - "version": "0.9.0-alpha.18", - "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.9.0-alpha.18.tgz", - "integrity": "sha512-0gLuQTaIZTe0yIC5IaTPzP1vcfn79LV5zWAdXaJU0FiiWxQEjUfaFavux+OefRlmuy9Kh/k2bp1R1CDhOQ17hQ==", + "version": "0.10.0-alpha.3", + "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.10.0-alpha.3.tgz", + "integrity": "sha512-0mlGcTi5/faBdaq5Jcmzs+0JjTMa7vEB4Xrz2gVii4OhvhHFJwT6aAuU7QR12KxCwcYyioVqZ5nEmp9NLk2Jtg==", "dependencies": { "neverthrow": "^6.0.0", "rxjs": "^7.8.1", diff --git a/package.json b/package.json index 4e2bf88e..2b3e076c 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.1-alpha.16", - "@radixdlt/wallet-sdk": "0.9.0-alpha.18", + "@radixdlt/connect-button": "0.13.2-alpha.2", + "@radixdlt/wallet-sdk": "0.10.0-alpha.3", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", diff --git a/src/connect-button/connect-button-client.ts b/src/connect-button/connect-button-client.ts index 38ab89eb..c485a8c6 100644 --- a/src/connect-button/connect-button-client.ts +++ b/src/connect-button/connect-button-client.ts @@ -51,8 +51,6 @@ export const ConnectButtonClient = (input: { switchMap((connectButtonElement) => { logger?.debug(`connectButtonDiscovered`) - connectButtonElement.isMobile = isMobile() - const onConnect$ = fromEvent(connectButtonElement, 'onConnect').pipe( tap(() => { onConnect((value) => { diff --git a/src/data-request/transformations/wallet-data-to-connect-button.ts b/src/data-request/transformations/wallet-data-to-connect-button.ts index 326c7ae3..189ffc35 100644 --- a/src/data-request/transformations/wallet-data-to-connect-button.ts +++ b/src/data-request/transformations/wallet-data-to-connect-button.ts @@ -8,15 +8,13 @@ export const transformWalletDataToConnectButton = (walletData: WalletData) => { .map((item) => { if (item.entry === 'fullName') { const { variant, givenNames, familyName, nickname } = item.fields + const value = + variant === 'western' + ? `${givenNames}${nickname ? ` "${nickname}" ` : ' '}${familyName}` + : `${familyName}${nickname ? ` "${nickname}" ` : ' '}${givenNames}` + return { - value: - variant === 'western' - ? `${givenNames}${ - nickname ? ` "${nickname}" ` : ' ' - }${familyName}` - : `${familyName}${ - nickname ? ` "${nickname}" ` : ' ' - }${givenNames}`, + value, field: 'fullName', } } else if (item.entry === 'emailAddresses') { @@ -40,7 +38,7 @@ export const transformWalletDataToConnectButton = (walletData: WalletData) => { ): item is { value: string field: string - } => !!item + } => !!item && !!item.value.trim() ) return { accounts, personaLabel, connected, personaData } diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index 24806d54..5e25d9e3 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -1,5 +1,8 @@ import { StateClient } from './state/state' -import { ConnectButtonClient } from './connect-button/connect-button-client' +import { + ConnectButtonClient, + isMobile, +} from './connect-button/connect-button-client' import { WalletClient } from './wallet/wallet-client' import { WalletSdk } from '@radixdlt/wallet-sdk' import { GatewayApiClient } from './gateway/gateway-api' @@ -64,6 +67,8 @@ export const RadixDappToolkit = ( logger, }) + connectButtonClient.setIsMobile(isMobile()) + const gatewayClient = providers?.gatewayClient ?? GatewayClient({ From de9cb6157b479c3794e83185e549df29098871e8 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Fri, 11 Aug 2023 15:12:57 +0200 Subject: [PATCH 26/32] chore: bump connect button and wallet sdk versions --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2c07d4d..7f093265 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.2-alpha.2", - "@radixdlt/wallet-sdk": "0.10.0-alpha.3", + "@radixdlt/connect-button": "0.13.2", + "@radixdlt/wallet-sdk": "0.10.0", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", @@ -3285,9 +3285,9 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.2-alpha.2", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.2-alpha.2.tgz", - "integrity": "sha512-4zhvB1D3pPSLsQMDnVqx11UgNMUfcx4kjwDpAGt2jJDAWlSqVUy+sEVhow3F45JiURtQbWjIVuEzgsEnXHyeCA==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.2.tgz", + "integrity": "sha512-4Xle5YOhm12GmCPQnmBimCUdD9fJQBzEPuSSIrbVBAUJ+Ao79VMkVl4MrUdHkb61zehcS3XvdNp9lPsZvdFuiQ==", "dependencies": { "lit": "^2.7.5" }, @@ -3314,9 +3314,9 @@ } }, "node_modules/@radixdlt/wallet-sdk": { - "version": "0.10.0-alpha.3", - "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.10.0-alpha.3.tgz", - "integrity": "sha512-0mlGcTi5/faBdaq5Jcmzs+0JjTMa7vEB4Xrz2gVii4OhvhHFJwT6aAuU7QR12KxCwcYyioVqZ5nEmp9NLk2Jtg==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.10.0.tgz", + "integrity": "sha512-BzW1X5cEq4Rax8WP+zPEC3hDQammdTtWrnyaqFRshhMEeFN1bPVc8OxEghwDDRWwCWVqAa9FSfLPsmcCXAbZmw==", "dependencies": { "neverthrow": "^6.0.0", "rxjs": "^7.8.1", diff --git a/package.json b/package.json index 2b3e076c..8d0f0e5a 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.2-alpha.2", - "@radixdlt/wallet-sdk": "0.10.0-alpha.3", + "@radixdlt/connect-button": "0.13.2", + "@radixdlt/wallet-sdk": "0.10.0", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", From 9fe96e89a2918f968aa0a9fbd52d0bd569bddda5 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Mon, 14 Aug 2023 09:01:42 +0200 Subject: [PATCH 27/32] build: update node version range --- package-lock.json | 13 ++++++++----- package.json | 5 ++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f093265..c42e6e84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.2", + "@radixdlt/connect-button": "0.13.3", "@radixdlt/wallet-sdk": "0.10.0", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", @@ -53,6 +53,9 @@ "ts-node": "^10.9.1", "typescript": "^5.0.4", "vite": "^4.3.5" + }, + "engines": { + "node": ">=16.0.0" } }, "node_modules/@ampproject/remapping": { @@ -3285,14 +3288,14 @@ "integrity": "sha512-oi+FMTwDXJeg62YewE2OxxyTrtGvs/R4aSaCPpSC8P7IZkVgcAxB6qvo1NwVwpbrcirBxBJ8hAAWP/MXIF7zmg==" }, "node_modules/@radixdlt/connect-button": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.2.tgz", - "integrity": "sha512-4Xle5YOhm12GmCPQnmBimCUdD9fJQBzEPuSSIrbVBAUJ+Ao79VMkVl4MrUdHkb61zehcS3XvdNp9lPsZvdFuiQ==", + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@radixdlt/connect-button/-/connect-button-0.13.3.tgz", + "integrity": "sha512-XUj47PBO81hW4gQ4ViNhJaoaOjGZcGEEMvVi+r0u8wPTpyeGRwd+lX8Kyp0nNpTLASaNdZadM6bO/18RNZ/yGg==", "dependencies": { "lit": "^2.7.5" }, "engines": { - "node": "20.3.0" + "node": ">=16.0.0" } }, "node_modules/@radixdlt/radix-engine-toolkit": { diff --git a/package.json b/package.json index 8d0f0e5a..19fe738b 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,9 @@ "Alex Stelea ", "Dawid Sowa " ], + "engines": { + "node": ">=16.0.0" + }, "bugs": "https://github.com/radixdlt/radix-dapp-toolkit/issues", "main": "dist/radix-dapp-toolkit.mjs", "license": "Apache-2.0", @@ -46,7 +49,7 @@ }, "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", - "@radixdlt/connect-button": "0.13.2", + "@radixdlt/connect-button": "0.13.3", "@radixdlt/wallet-sdk": "0.10.0", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", From 289826141d71a1b134f81518b4b5e2e0acb4dfa0 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Wed, 16 Aug 2023 10:05:46 +0200 Subject: [PATCH 28/32] fix: expose connect button status --- src/_types.ts | 2 ++ src/connect-button/connect-button-client.ts | 1 + src/radix-dapp-toolkit.ts | 1 + 3 files changed, 4 insertions(+) diff --git a/src/_types.ts b/src/_types.ts index 9c68da17..838d75f3 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -36,6 +36,7 @@ export type StorageProvider = { } export type ConnectButtonProvider = { + status$: Observable onConnect$: Observable<{ challenge: string } | undefined> onDisconnect$: Observable onUpdateSharedData$: Observable @@ -118,6 +119,7 @@ export type GatewayApi = { export type ButtonApi = { setMode: (value: 'light' | 'dark') => void setTheme: (value: RadixButtonTheme) => void + status$: Observable } export type WalletDataRequestResult = ResultAsync< diff --git a/src/connect-button/connect-button-client.ts b/src/connect-button/connect-button-client.ts index c485a8c6..eb5fb9b8 100644 --- a/src/connect-button/connect-button-client.ts +++ b/src/connect-button/connect-button-client.ts @@ -228,6 +228,7 @@ export const ConnectButtonClient = (input: { ) return { + status$: subjects.status.asObservable(), onConnect$: subjects.onConnect.asObservable(), onDisconnect$: subjects.onDisconnect.asObservable(), onShowPopover$: subjects.onShowPopover.asObservable(), diff --git a/src/radix-dapp-toolkit.ts b/src/radix-dapp-toolkit.ts index 5e25d9e3..e1886192 100644 --- a/src/radix-dapp-toolkit.ts +++ b/src/radix-dapp-toolkit.ts @@ -327,6 +327,7 @@ export const RadixDappToolkit = ( const buttonApi = { setTheme: connectButtonClient.setTheme, setMode: connectButtonClient.setMode, + status$: connectButtonClient.status$, } const disconnect = () => { From ca89bfc072e247b4289af75daf4bddcc794ddf41 Mon Sep 17 00:00:00 2001 From: Alex Stelea Date: Wed, 16 Aug 2023 11:04:47 +0200 Subject: [PATCH 29/32] chore: update dependencies --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index c42e6e84..2b6e0200 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", "@radixdlt/connect-button": "0.13.3", - "@radixdlt/wallet-sdk": "0.10.0", + "@radixdlt/wallet-sdk": "0.10.1-alpha.1", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", @@ -3317,9 +3317,9 @@ } }, "node_modules/@radixdlt/wallet-sdk": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.10.0.tgz", - "integrity": "sha512-BzW1X5cEq4Rax8WP+zPEC3hDQammdTtWrnyaqFRshhMEeFN1bPVc8OxEghwDDRWwCWVqAa9FSfLPsmcCXAbZmw==", + "version": "0.10.1-alpha.1", + "resolved": "https://registry.npmjs.org/@radixdlt/wallet-sdk/-/wallet-sdk-0.10.1-alpha.1.tgz", + "integrity": "sha512-kCmCLJ3Yi2VZhN7/B9nhnTi6JNuiy8+otQy3kh1JNb0pd9CV6o6YS/Vj85mVoGtePVog4UHmu6RKFbPR9TVrfg==", "dependencies": { "neverthrow": "^6.0.0", "rxjs": "^7.8.1", diff --git a/package.json b/package.json index 19fe738b..6f9cceb1 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "dependencies": { "@radixdlt/babylon-gateway-api-sdk": "^1.0.0-rc.2.4", "@radixdlt/connect-button": "0.13.3", - "@radixdlt/wallet-sdk": "0.10.0", + "@radixdlt/wallet-sdk": "0.10.1-alpha.1", "immer": "^10.0.2", "lodash.isequal": "^4.5.0", "neverthrow": "^6.0.0", From e79c7408084bff9b2da1a16c3b51242425cf42a3 Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:28:01 +0200 Subject: [PATCH 30/32] avoid using sha without prefix as a tag Examples of places where it went wrong for us: https://github.com/radixdlt/radix-dapp-toolkit/actions/runs/5820031970/job/15779615253#step:14:59 https://github.com/radixdlt/radix-dapp-toolkit/actions/runs/5820031970/job/15779677514#step:7:69 Related Issue: https://github.com/helm/helm/issues/3001 References for the new introduced tag: https://github.com/docker/metadata-action#global-expressions --- .github/workflows/build.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c92ebce7..9cae420f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,10 +96,6 @@ jobs: steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - name: Setup tags for docker image - id: setup_tags - run: echo "tag=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Use Node.js uses: actions/setup-node@7c29869aec4da703a571b27bcd84d4f15af0b56e with: @@ -130,8 +126,8 @@ jobs: image_registry: "docker.io" image_organization: "radixdlt" image_name: "private-radix-dapp-toolkit" - tag: ${{ needs.build.outputs.tag }} tags: | + type=raw,value={{branch}}-{{sha}} type=sha,event=branch type=sha,event=pr type=semver,pattern={{version}} @@ -171,7 +167,6 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') needs: - - build - push-docker-image permissions: id-token: write @@ -188,7 +183,7 @@ jobs: snyk_secret_name: ${{ secrets.AWS_SECRET_NAME_SNYK }} parse_json: true snyk_org_id: ${{ secrets.SNYK_ORG_ID }} - image: docker.io/radixdlt/private-radix-dapp-toolkit:sha-${{ needs.build.outputs.tag }} + image: docker.io/radixdlt/private-radix-dapp-toolkit:${{ fromJSON(needs.push-docker-image.outputs.json).labels['org.opencontainers.image.version'] }} target_ref: ${{ github.ref_name }} deploy-pr: From 11e94aeb4c33925bf77878fd8f5d5cd40a6f5204 Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:32:20 +0200 Subject: [PATCH 31/32] Update build.yml --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9cae420f..27495794 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,6 @@ jobs: image_organization: "radixdlt" image_name: "private-radix-dapp-toolkit" tags: | - type=raw,value={{branch}}-{{sha}} type=sha,event=branch type=sha,event=pr type=semver,pattern={{version}} From c0714c188f2d3c48af8c1fc2554cb1dbf2bc4e76 Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Thu, 10 Aug 2023 13:44:13 +0200 Subject: [PATCH 32/32] readd the tag specification snyk requires the tag field to be set. Future updates will allow snyk scan to work with the tags field also. --- .github/workflows/build.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 27495794..641fa4c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -95,7 +95,11 @@ jobs: tag: ${{ steps.setup_tags.outputs.tag }} steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - + + - name: Setup tags for docker image + id: setup_tags + run: echo "tag=sha-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Use Node.js uses: actions/setup-node@7c29869aec4da703a571b27bcd84d4f15af0b56e with: @@ -126,9 +130,8 @@ jobs: image_registry: "docker.io" image_organization: "radixdlt" image_name: "private-radix-dapp-toolkit" + tag: ${{ needs.build.outputs.tag }} tags: | - type=sha,event=branch - type=sha,event=pr type=semver,pattern={{version}} context: "./" dockerfile: "./Dockerfile"