From 0b2d2af3ee38b8008f64e9d5199dd2aa69f06444 Mon Sep 17 00:00:00 2001 From: Sven Date: Tue, 5 Mar 2024 17:47:12 +0100 Subject: [PATCH 1/4] setting up blockchain api, adding ui components for account blance --- .../wui-list-description.stories.ts | 48 ++++++ .../composites/wui-list-token.stories.ts | 32 ++++ apps/gallery/utils/PresetUtils.ts | 1 + package-lock.json | 146 +++++++++--------- packages/common/src/utils/TypeUtil.ts | 15 ++ .../core/src/controllers/AccountController.ts | 9 +- .../controllers/BlockchainApiController.ts | 9 ++ packages/core/src/utils/TypeUtil.ts | 6 +- .../index.ts | 112 +++++++++++++- .../styles.ts | 14 ++ packages/ui/index.ts | 2 + packages/ui/src/components/wui-text/styles.ts | 10 +- .../composites/wui-list-description/index.ts | 66 ++++++++ .../composites/wui-list-description/styles.ts | 23 +++ .../ui/src/composites/wui-list-token/index.ts | 50 ++++++ .../src/composites/wui-list-token/styles.ts | 17 ++ packages/ui/src/composites/wui-tabs/index.ts | 12 +- packages/ui/src/composites/wui-tag/index.ts | 2 +- packages/ui/src/composites/wui-tag/styles.ts | 11 +- packages/ui/src/utils/JSXTypeUtil.ts | 4 + packages/ui/src/utils/ThemeUtil.ts | 2 + packages/ui/src/utils/TypeUtil.ts | 1 + 22 files changed, 509 insertions(+), 83 deletions(-) create mode 100644 apps/gallery/stories/composites/wui-list-description.stories.ts create mode 100644 apps/gallery/stories/composites/wui-list-token.stories.ts create mode 100644 packages/ui/src/composites/wui-list-description/index.ts create mode 100644 packages/ui/src/composites/wui-list-description/styles.ts create mode 100644 packages/ui/src/composites/wui-list-token/index.ts create mode 100644 packages/ui/src/composites/wui-list-token/styles.ts diff --git a/apps/gallery/stories/composites/wui-list-description.stories.ts b/apps/gallery/stories/composites/wui-list-description.stories.ts new file mode 100644 index 0000000000..1114b1984f --- /dev/null +++ b/apps/gallery/stories/composites/wui-list-description.stories.ts @@ -0,0 +1,48 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-list-description' +import type { WuiListDescription } from '@web3modal/ui/src/composites/wui-list-description' +import { html } from 'lit' +import '../../components/gallery-container' +import { colorOptions, iconOptions } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-list-description', + args: { + icon: 'card', + text: 'Buy Crypto', + tag: 'Popular', + description: 'Easy with card or bank account', + iconBackgroundColor: 'success-100', + iconColor: 'success-100' + }, + argTypes: { + icon: { + options: iconOptions, + control: { type: 'select' } + }, + iconBackgroundColor: { + options: colorOptions, + control: { type: 'select' } + }, + iconColor: { + options: colorOptions, + control: { type: 'select' } + } + } +} as Component + +export const Default: Component = { + render: args => + html` + + ` +} diff --git a/apps/gallery/stories/composites/wui-list-token.stories.ts b/apps/gallery/stories/composites/wui-list-token.stories.ts new file mode 100644 index 0000000000..eac87f809b --- /dev/null +++ b/apps/gallery/stories/composites/wui-list-token.stories.ts @@ -0,0 +1,32 @@ +import type { Meta } from '@storybook/web-components' +import '@web3modal/ui/src/composites/wui-list-token' +import type { WuiListToken } from '@web3modal/ui/src/composites/wui-list-token' +import { html } from 'lit' +import '../../components/gallery-container' +import { networkImageSrc } from '../../utils/PresetUtils' + +type Component = Meta + +export default { + title: 'Composites/wui-list-token', + args: { + tokenName: 'Ethereum', + tokenImageUrl: networkImageSrc, + tokenValue: '$1,740.72', + tokenAmount: 0.867, + tokenCurrency: 'ETH' + } +} as Component + +export const Default: Component = { + render: args => + html` + + ` +} diff --git a/apps/gallery/utils/PresetUtils.ts b/apps/gallery/utils/PresetUtils.ts index f9393e98aa..f3da773580 100644 --- a/apps/gallery/utils/PresetUtils.ts +++ b/apps/gallery/utils/PresetUtils.ts @@ -44,6 +44,7 @@ export const colorOptions: ColorType[] = [ export const textOptions: TextType[] = [ 'micro-700', 'micro-600', + 'mini-700', 'tiny-500', 'tiny-600', 'small-500', diff --git a/package-lock.json b/package-lock.json index 88b9316e43..1b0b8f4750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,10 +41,10 @@ }, "apps/gallery": { "name": "@apps/gallery", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { - "@web3modal/common": "4.0.12", - "@web3modal/ui": "4.0.12", + "@web3modal/common": "4.0.13", + "@web3modal/ui": "4.0.13", "lit": "3.1.0", "storybook": "7.6.7" }, @@ -70,7 +70,7 @@ }, "apps/laboratory": { "name": "@apps/laboratory", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { "@chakra-ui/react": "2.8.2", "@emotion/react": "11.11.3", @@ -78,9 +78,9 @@ "@sentry/browser": "7.92.0", "@sentry/react": "7.92.0", "@tanstack/react-query": "5.17.19", - "@web3modal/ethers": "4.0.12", - "@web3modal/siwe": "4.0.12", - "@web3modal/wagmi": "4.0.12", + "@web3modal/ethers": "4.0.13", + "@web3modal/siwe": "4.0.13", + "@web3modal/wagmi": "4.0.13", "ethers": "6.9.0", "framer-motion": "10.17.9", "next": "14.0.4", @@ -112,9 +112,9 @@ }, "examples/html-ethers5": { "name": "@examples/html-ethers5", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { - "@web3modal/ethers5": "4.0.12", + "@web3modal/ethers5": "4.0.13", "ethers": "5.7.2" }, "devDependencies": { @@ -170,11 +170,11 @@ }, "examples/html-wagmi": { "name": "@examples/html-wagmi", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { "@wagmi/connectors": "4.1.14", "@wagmi/core": "2.6.5", - "@web3modal/wagmi": "4.0.12", + "@web3modal/wagmi": "4.0.13", "react": "18.2.0", "react-dom": "18.2.0" }, @@ -184,10 +184,10 @@ }, "examples/next-wagmi": { "name": "@examples/next-wagmi", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { "@tanstack/react-query": "5.17.19", - "@web3modal/wagmi": "4.0.12", + "@web3modal/wagmi": "4.0.13", "next": "14.1.0", "react": "18.2.0", "react-dom": "18.2.0", @@ -250,9 +250,9 @@ }, "examples/react-ethers5": { "name": "@examples/react-ethers5", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { - "@web3modal/ethers5": "4.0.12", + "@web3modal/ethers5": "4.0.13", "ethers": "5.7.2", "react": "18.2.0", "react-dom": "18.2.0" @@ -311,10 +311,10 @@ }, "examples/react-wagmi": { "name": "@examples/react-wagmi", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { "@tanstack/react-query": "5.17.19", - "@web3modal/wagmi": "4.0.12", + "@web3modal/wagmi": "4.0.13", "react": "18.2.0", "react-dom": "18.2.0", "viem": "2.7.13", @@ -328,9 +328,9 @@ }, "examples/vue-ethers5": { "name": "@examples/vue-ethers5", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { - "@web3modal/ethers5": "4.0.12", + "@web3modal/ethers5": "4.0.13", "ethers": "5.7.2" }, "devDependencies": { @@ -386,11 +386,11 @@ }, "examples/vue-wagmi": { "name": "@examples/vue-wagmi", - "version": "4.0.12", + "version": "4.0.13", "dependencies": { "@wagmi/connectors": "4.1.14", "@wagmi/core": "2.6.5", - "@web3modal/wagmi": "4.0.12" + "@web3modal/wagmi": "4.0.13" }, "devDependencies": { "@vitejs/plugin-vue": "5.0.2" @@ -30112,7 +30112,7 @@ }, "packages/common": { "name": "@web3modal/common", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "dayjs": "1.11.10" @@ -30120,11 +30120,11 @@ }, "packages/core": { "name": "@web3modal/core", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/common": "4.0.12", - "@web3modal/wallet": "4.0.12", + "@web3modal/common": "4.0.13", + "@web3modal/wallet": "4.0.13", "valtio": "1.11.2" }, "devDependencies": { @@ -30133,33 +30133,33 @@ }, "packages/ethers": { "name": "@web3modal/ethers", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "@coinbase/wallet-sdk": "3.9.1", "@walletconnect/ethereum-provider": "2.11.1", - "@web3modal/polyfills": "4.0.12", - "@web3modal/scaffold": "4.0.12", - "@web3modal/scaffold-react": "4.0.12", - "@web3modal/scaffold-utils": "4.0.12", - "@web3modal/scaffold-vue": "4.0.12", + "@web3modal/polyfills": "4.0.13", + "@web3modal/scaffold": "4.0.13", + "@web3modal/scaffold-react": "4.0.13", + "@web3modal/scaffold-utils": "4.0.13", + "@web3modal/scaffold-vue": "4.0.13", "valtio": "1.11.2" }, "devDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "ethers": "6.9.0", "react": "18.2.0", "react-dom": "18.2.0", "vue": "3.4.3" }, "optionalDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "react": ">=17", "react-dom": ">=17", "vue": ">=3" }, "peerDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "ethers": ">=6.0.0", "react": ">=17", "react-dom": ">=17", @@ -30330,33 +30330,33 @@ }, "packages/ethers5": { "name": "@web3modal/ethers5", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "@coinbase/wallet-sdk": "3.9.1", "@walletconnect/ethereum-provider": "2.11.1", - "@web3modal/polyfills": "4.0.12", - "@web3modal/scaffold": "4.0.12", - "@web3modal/scaffold-react": "4.0.12", - "@web3modal/scaffold-utils": "4.0.12", - "@web3modal/scaffold-vue": "4.0.12", + "@web3modal/polyfills": "4.0.13", + "@web3modal/scaffold": "4.0.13", + "@web3modal/scaffold-react": "4.0.13", + "@web3modal/scaffold-utils": "4.0.13", + "@web3modal/scaffold-vue": "4.0.13", "valtio": "1.11.2" }, "devDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "ethers": "5.7.2", "react": "18.2.0", "react-dom": "18.2.0", "vue": "3.4.3" }, "optionalDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "react": ">=17", "react-dom": ">=17", "vue": ">=3" }, "peerDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "ethers": ">=5.0.0 <6.0.0", "react": ">=17", "react-dom": ">=17", @@ -30575,7 +30575,7 @@ }, "packages/polyfills": { "name": "@web3modal/polyfills", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "buffer": "6.0.3" @@ -30583,23 +30583,23 @@ }, "packages/scaffold": { "name": "@web3modal/scaffold", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/common": "4.0.12", - "@web3modal/core": "4.0.12", - "@web3modal/ui": "4.0.12", + "@web3modal/common": "4.0.13", + "@web3modal/core": "4.0.13", + "@web3modal/ui": "4.0.13", "lit": "3.1.0" }, "devDependencies": { - "@web3modal/siwe": "4.0.12", - "@web3modal/wallet": "4.0.12" + "@web3modal/siwe": "4.0.13", + "@web3modal/wallet": "4.0.13" }, "optionalDependencies": { - "@web3modal/siwe": "4.0.12" + "@web3modal/siwe": "4.0.13" }, "peerDependencies": { - "@web3modal/siwe": ">=4.0.12" + "@web3modal/siwe": ">=4.0.13" }, "peerDependenciesMeta": { "@web3modal/siwe": { @@ -30609,10 +30609,10 @@ }, "packages/scaffold-react": { "name": "@web3modal/scaffold-react", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/scaffold": "4.0.12" + "@web3modal/scaffold": "4.0.13" }, "peerDependencies": { "react": ">=17", @@ -30621,20 +30621,20 @@ }, "packages/scaffold-utils": { "name": "@web3modal/scaffold-utils", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/core": "4.0.12", - "@web3modal/polyfills": "4.0.12", + "@web3modal/core": "4.0.13", + "@web3modal/polyfills": "4.0.13", "valtio": "1.11.2" } }, "packages/scaffold-vue": { "name": "@web3modal/scaffold-vue", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/scaffold": "4.0.12" + "@web3modal/scaffold": "4.0.13" }, "peerDependencies": { "vue": ">=3" @@ -30652,11 +30652,11 @@ }, "packages/siwe": { "name": "@web3modal/siwe", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/core": "4.0.12", - "@web3modal/scaffold-utils": "4.0.12" + "@web3modal/core": "4.0.13", + "@web3modal/scaffold-utils": "4.0.13" }, "devDependencies": { "lit": "3.1.0", @@ -30680,7 +30680,7 @@ }, "packages/ui": { "name": "@web3modal/ui", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "lit": "3.1.0", @@ -30688,7 +30688,7 @@ }, "devDependencies": { "@types/qrcode": "1.5.5", - "@web3modal/common": "4.0.12", + "@web3modal/common": "4.0.13", "eslint-plugin-lit": "1.11.0", "eslint-plugin-wc": "2.0.4" } @@ -30705,14 +30705,14 @@ }, "packages/wagmi": { "name": "@web3modal/wagmi", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { - "@web3modal/polyfills": "4.0.12", - "@web3modal/scaffold": "4.0.12", - "@web3modal/scaffold-react": "4.0.12", - "@web3modal/scaffold-utils": "4.0.12", - "@web3modal/scaffold-vue": "4.0.12" + "@web3modal/polyfills": "4.0.13", + "@web3modal/scaffold": "4.0.13", + "@web3modal/scaffold-react": "4.0.13", + "@web3modal/scaffold-utils": "4.0.13", + "@web3modal/scaffold-vue": "4.0.13" }, "devDependencies": { "@wagmi/connectors": "4.1.14", @@ -30723,7 +30723,7 @@ "vue": "3.4.3" }, "optionalDependencies": { - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "react": ">=17", "react-dom": ">=17", "vue": ">=3" @@ -30731,7 +30731,7 @@ "peerDependencies": { "@wagmi/connectors": ">=4.0.0", "@wagmi/core": ">=2.0.0", - "@web3modal/siwe": "4.0.12", + "@web3modal/siwe": "4.0.13", "react": ">=17", "react-dom": ">=17", "viem": ">=2.0.0", @@ -30902,7 +30902,7 @@ }, "packages/wallet": { "name": "@web3modal/wallet", - "version": "4.0.12", + "version": "4.0.13", "license": "Apache-2.0", "dependencies": { "zod": "3.22.4" diff --git a/packages/common/src/utils/TypeUtil.ts b/packages/common/src/utils/TypeUtil.ts index 9a199626dd..0722a7cff8 100644 --- a/packages/common/src/utils/TypeUtil.ts +++ b/packages/common/src/utils/TypeUtil.ts @@ -72,3 +72,18 @@ export interface TransactionDetail { export interface TransactionQuantity { numeric: string } + +export interface Balance { + name: string + symbol: string + chainId: string + value: string + price: number + quantity: BalanceQuantity + iconUrl: string +} + +type BalanceQuantity = { + decimals: number + quantity: number +} diff --git a/packages/core/src/controllers/AccountController.ts b/packages/core/src/controllers/AccountController.ts index 20262aba7e..6e988cd5fc 100644 --- a/packages/core/src/controllers/AccountController.ts +++ b/packages/core/src/controllers/AccountController.ts @@ -6,6 +6,7 @@ import type { CaipAddress } from '../utils/TypeUtil.js' // -- Types --------------------------------------------- // export interface AccountControllerState { isConnected: boolean + currentTab: number caipAddress?: CaipAddress address?: string balance?: string @@ -20,7 +21,8 @@ type StateKey = keyof AccountControllerState // -- State --------------------------------------------- // const state = proxy({ - isConnected: false + isConnected: false, + currentTab: 0 }) // -- Controller ---------------------------------------- // @@ -68,6 +70,10 @@ export const AccountController = { state.smartAccountDeployed = isDeployed }, + setCurrentTab(currentTab: AccountControllerState['currentTab']) { + state.currentTab = currentTab + }, + resetAccount() { state.isConnected = false state.caipAddress = undefined @@ -78,5 +84,6 @@ export const AccountController = { state.profileImage = undefined state.addressExplorerUrl = undefined state.smartAccountDeployed = undefined + state.currentTab = 0 } } diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index 05afa5fc1a..77189c98ec 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -122,6 +122,15 @@ export const BlockchainApiController = { }) }, + async getBalance(address: string) { + return api.get({ + path: `/v1/account/${address}/balance`, + params: { + currency: 'USD' + } + }) + }, + async generateOnRampURL({ destinationWallets, partnerUserId, diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 8299f95093..492714caed 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -1,5 +1,5 @@ import type { W3mFrameProvider } from '@web3modal/wallet' -import type { Transaction } from '@web3modal/common' +import type { Balance, Transaction } from '@web3modal/common' export type CaipAddress = `${string}:${string}:${string}` @@ -147,6 +147,10 @@ export interface BlockchainApiTransactionsResponse { next: string | null } +export interface BlockchainApiBalanceResponse { + balances: Balance[] +} + // -- OptionsController Types --------------------------------------------------- export interface Token { address: string diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts index 761119a258..cc4e88025d 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -28,6 +28,8 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { @state() private network = NetworkController.state.caipNetwork + @state() private currentTab = AccountController.state.currentTab + public constructor() { super() this.unsubscribe.push( @@ -37,6 +39,7 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { this.address = val.address this.profileImage = val.profileImage this.profileName = val.profileName + this.currentTab = val.currentTab } else { ModalController.close() } @@ -66,7 +69,7 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { flexDirection="column" .padding=${['0', 'xl', 'm', 'xl'] as const} alignItems="center" - gap="l" + gap="m" > ${this.activateAccountTemplate()} - + + ${this.listContentTemplate()} ` } // -- Private ------------------------------------------- // + private listContentTemplate() { + if (this.currentTab === 0) { + return this.tokenTemplate() + } else if (this.currentTab === 1) { + return this.nftTemplate() + } else if (this.currentTab === 2) { + return this.activityTemplate() + } + + return this.tokenTemplate() + } + + private tokenTemplate() { + return html` ` + } + + private nftTemplate() { + return html` + + + No NFTs yet + Transfer from another wallets to get started + + Receive NFTs + ` + } + + private activityTemplate() { + return html` + + + No activity yet + Your next transactions will appear here + + Trade + ` + } private activateAccountTemplate() { // eslint-disable-next-line no-warning-comments @@ -106,6 +210,10 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { return html` ` } + private onTabChange(index: number) { + AccountController.setCurrentTab(index) + } + private onProfileButtonClick() { RouterController.push('AccountSettings') } diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts index 264076216f..212d02255a 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/styles.ts @@ -21,4 +21,18 @@ export default css` wui-tabs { width: 100%; } + + .contentContainer { + height: 280px; + } + + .contentContainer > wui-icon-box { + width: 40px; + height: 40px; + border-radius: var(--wui-border-radius-xxs); + } + + .contentContainer > .textContent { + width: 65%; + } ` diff --git a/packages/ui/index.ts b/packages/ui/index.ts index e0bb70cd56..5d661225ec 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -56,6 +56,8 @@ export * from './src/composites/wui-profile-button/index.js' export * from './src/composites/wui-chip-button/index.js' export * from './src/composites/wui-compatible-network/index.js' export * from './src/composites/wui-banner/index.js' +export * from './src/composites/wui-list-token/index.js' +export * from './src/composites/wui-list-description/index.js' export * from './src/layout/wui-flex/index.js' export * from './src/layout/wui-grid/index.js' diff --git a/packages/ui/src/components/wui-text/styles.ts b/packages/ui/src/components/wui-text/styles.ts index 6712d95a41..73081751bb 100644 --- a/packages/ui/src/components/wui-text/styles.ts +++ b/packages/ui/src/components/wui-text/styles.ts @@ -6,6 +6,7 @@ export default css` } slot { + width: 100%; display: inline-block; font-style: normal; font-family: var(--wui-font-family); @@ -31,6 +32,12 @@ export default css` letter-spacing: var(--wui-letter-spacing-medium-title); } + .wui-font-mini-700 { + font-size: var(--wui-font-size-mini); + letter-spacing: var(--wui-letter-spacing-mini); + text-transform: uppercase; + } + .wui-font-large-500, .wui-font-large-600, .wui-font-large-700 { @@ -81,7 +88,8 @@ export default css` .wui-font-large-700, .wui-font-paragraph-700, - .wui-font-micro-700 { + .wui-font-micro-700, + .wui-font-mini-700 { font-weight: var(--wui-font-weight-bold); } diff --git a/packages/ui/src/composites/wui-list-description/index.ts b/packages/ui/src/composites/wui-list-description/index.ts new file mode 100644 index 0000000000..1cab542490 --- /dev/null +++ b/packages/ui/src/composites/wui-list-description/index.ts @@ -0,0 +1,66 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-text/index.js' +import '../../components/wui-image/index.js' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' +import type { ColorType, IconType } from '../../utils/TypeUtil.js' + +@customElement('wui-list-description') +export class WuiListDescription extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public icon: IconType = 'card' + + @property() public text = '' + + @property() public description = '' + + @property() public tag?: string = undefined + + @property() public iconBackgroundColor: ColorType = 'accent-100' + + @property() public iconColor: ColorType = 'accent-100' + + @property({ type: Boolean }) public disabled = false + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ` + } + + // -- Private ------------------------------------------- // + private titleTemplate() { + if (this.tag) { + return html` ${this.text}${this.tag} + ` + } + + return html`${this.text}` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-list-description': WuiListDescription + } +} diff --git a/packages/ui/src/composites/wui-list-description/styles.ts b/packages/ui/src/composites/wui-list-description/styles.ts new file mode 100644 index 0000000000..38f1512983 --- /dev/null +++ b/packages/ui/src/composites/wui-list-description/styles.ts @@ -0,0 +1,23 @@ +import { css } from 'lit' + +export default css` + button { + width: 100%; + display: flex; + gap: var(--wui-spacing-s); + align-items: center; + justify-content: flex-start; + padding: var(--wui-spacing-s) var(--wui-spacing-m) var(--wui-spacing-s) var(--wui-spacing-s); + background-color: var(--wui-gray-glass-002); + border-radius: var(--wui-border-radius-xs); + } + + wui-icon-box { + width: var(--wui-spacing-2xl); + height: var(--wui-spacing-2xl); + } + + wui-flex { + width: auto; + } +` diff --git a/packages/ui/src/composites/wui-list-token/index.ts b/packages/ui/src/composites/wui-list-token/index.ts new file mode 100644 index 0000000000..4d362d5338 --- /dev/null +++ b/packages/ui/src/composites/wui-list-token/index.ts @@ -0,0 +1,50 @@ +import { html, LitElement } from 'lit' +import { property } from 'lit/decorators.js' +import '../../components/wui-text/index.js' +import '../../components/wui-image/index.js' +import '../../layout/wui-flex/index.js' +import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' +import { customElement } from '../../utils/WebComponentsUtil.js' +import styles from './styles.js' + +@customElement('wui-list-token') +export class WuiListToken extends LitElement { + public static override styles = [resetStyles, elementStyles, styles] + + // -- State & Properties -------------------------------- // + @property() public tokenName = '' + + @property() public tokenImageUrl = '' + + @property() public tokenValue = '' + + @property({ type: Number }) public tokenAmount = 0.0 + + @property() public tokenCurrency = '' + + // -- Render -------------------------------------------- // + public override render() { + return html` + + ` + } + + // -- Private ------------------------------------------- // +} + +declare global { + interface HTMLElementTagNameMap { + 'wui-list-token': WuiListToken + } +} diff --git a/packages/ui/src/composites/wui-list-token/styles.ts b/packages/ui/src/composites/wui-list-token/styles.ts new file mode 100644 index 0000000000..9c2aaac3b5 --- /dev/null +++ b/packages/ui/src/composites/wui-list-token/styles.ts @@ -0,0 +1,17 @@ +import { css } from 'lit' + +export default css` + button { + pointer-events: none; + padding: 6.5px var(--wui-spacing-l) 6.5px var(--wui-spacing-xs); + display: flex; + justify-content: space-between; + width: 100%; + } + + wui-image { + width: var(--wui-spacing-3xl); + height: var(--wui-spacing-3xl); + border-radius: var(--wui-border-radius-3xl); + } +` diff --git a/packages/ui/src/composites/wui-tabs/index.ts b/packages/ui/src/composites/wui-tabs/index.ts index bd67ff7f32..3f12860c17 100644 --- a/packages/ui/src/composites/wui-tabs/index.ts +++ b/packages/ui/src/composites/wui-tabs/index.ts @@ -2,6 +2,7 @@ import { html, LitElement } from 'lit' import { property, state } from 'lit/decorators.js' import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' import type { IconType } from '../../utils/TypeUtil.js' +import '../../components/wui-icon/index.js' import { customElement } from '../../utils/WebComponentsUtil.js' import styles from './styles.js' @@ -20,7 +21,7 @@ export class WuiTabs extends LitElement { @state() public activeTab = 0 - @state() public localTabWidth = '100px' + @property() public localTabWidth = '100px' @state() public isDense = false @@ -45,7 +46,7 @@ export class WuiTabs extends LitElement { data-active=${isActive} data-testid="tab-${tab.label?.toLowerCase()}" > - + ${this.iconTemplate(tab)} ${tab.label} ` @@ -62,6 +63,13 @@ export class WuiTabs extends LitElement { } // -- Private ------------------------------------------- // + private iconTemplate(tab: { icon?: IconType; label: string }) { + if (tab.icon) { + return html`` + } + + return null + } private onTabClick(index: number) { if (this.buttons) { this.animateTabs(index, false) diff --git a/packages/ui/src/composites/wui-tag/index.ts b/packages/ui/src/composites/wui-tag/index.ts index a0190f50fd..8348d76c79 100644 --- a/packages/ui/src/composites/wui-tag/index.ts +++ b/packages/ui/src/composites/wui-tag/index.ts @@ -18,7 +18,7 @@ export class WuiTag extends LitElement { this.dataset['variant'] = this.variant return html` - + ` diff --git a/packages/ui/src/composites/wui-tag/styles.ts b/packages/ui/src/composites/wui-tag/styles.ts index a402d5162f..a47dfb4b28 100644 --- a/packages/ui/src/composites/wui-tag/styles.ts +++ b/packages/ui/src/composites/wui-tag/styles.ts @@ -2,11 +2,18 @@ import { css } from 'lit' export default css` :host { - display: block; - padding: 3.5px 5px !important; + display: flex; + justify-content: center; + align-items: center; + height: var(--wui-spacing-m); + padding: 0 var(--wui-spacing-3xs) !important; border-radius: var(--wui-border-radius-5xs); } + :host > wui-text { + transform: translateY(5%); + } + :host([data-variant='main']) { background-color: var(--wui-accent-glass-015); color: var(--wui-color-accent-100); diff --git a/packages/ui/src/utils/JSXTypeUtil.ts b/packages/ui/src/utils/JSXTypeUtil.ts index 81b9a3e2a6..4b3495c97d 100644 --- a/packages/ui/src/utils/JSXTypeUtil.ts +++ b/packages/ui/src/utils/JSXTypeUtil.ts @@ -55,6 +55,8 @@ import type { WuiTooltipSelect } from '../composites/wui-tooltip-select/index.js import type { WuiProfileButton } from '../composites/wui-profile-button/index.js' import type { WuiBanner } from '../composites/wui-banner/index.js' import type { WuiCompatibleNetwork } from '../composites/wui-compatible-network/index.js' +import type { WuiListToken } from '../composites/wui-list-token/index.js' +import type { WuiListDescription } from '../composites/wui-list-description/index.js' import type { WuiFlex } from '../layout/wui-flex/index.js' import type { WuiGrid } from '../layout/wui-grid/index.js' @@ -125,6 +127,8 @@ declare global { 'wui-profile-button': CustomElement 'wui-banner': CustomElement 'wui-compatible-network': CustomElement + 'wui-list-token': CustomElement + 'wui-list-description': CustomElement } } } diff --git a/packages/ui/src/utils/ThemeUtil.ts b/packages/ui/src/utils/ThemeUtil.ts index b00491052b..6b19842753 100644 --- a/packages/ui/src/utils/ThemeUtil.ts +++ b/packages/ui/src/utils/ThemeUtil.ts @@ -62,6 +62,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-font-family: var(--w3m-font-family); + --wui-font-size-mini: calc(var(--w3m-font-size-master) * 0.8); --wui-font-size-micro: var(--w3m-font-size-master); --wui-font-size-tiny: calc(var(--w3m-font-size-master) * 1.2); --wui-font-size-small: calc(var(--w3m-font-size-master) * 1.4); @@ -94,6 +95,7 @@ function createRootStyles(themeVariables?: ThemeVariables) { --wui-letter-spacing-small: -0.56px; --wui-letter-spacing-tiny: -0.48px; --wui-letter-spacing-micro: -0.2px; + --wui-letter-spacing-mini: -0.16px; --wui-spacing-0: 0px; --wui-spacing-4xs: 2px; diff --git a/packages/ui/src/utils/TypeUtil.ts b/packages/ui/src/utils/TypeUtil.ts index b5582c6d9e..ac9c03fe60 100644 --- a/packages/ui/src/utils/TypeUtil.ts +++ b/packages/ui/src/utils/TypeUtil.ts @@ -19,6 +19,7 @@ export type TextType = | 'medium-title-600' | 'micro-600' | 'micro-700' + | 'mini-700' | 'paragraph-400' | 'paragraph-500' | 'paragraph-600' From 265fa2862b2b8e0332fdf54b8d0764c99243e91c Mon Sep 17 00:00:00 2001 From: Sven Date: Tue, 5 Mar 2024 17:51:16 +0100 Subject: [PATCH 2/4] adjust wui tabs --- packages/ui/src/composites/wui-tabs/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/composites/wui-tabs/index.ts b/packages/ui/src/composites/wui-tabs/index.ts index 3f12860c17..f118b50baf 100644 --- a/packages/ui/src/composites/wui-tabs/index.ts +++ b/packages/ui/src/composites/wui-tabs/index.ts @@ -19,10 +19,10 @@ export class WuiTabs extends LitElement { @property({ type: Boolean }) public disabled = false - @state() public activeTab = 0 - @property() public localTabWidth = '100px' + @state() public activeTab = 0 + @state() public isDense = false // -- Render -------------------------------------------- // From 810bf33af6fc732323c22b8a75d9717f6ce23b25 Mon Sep 17 00:00:00 2001 From: Sven Date: Wed, 6 Mar 2024 16:22:00 +0100 Subject: [PATCH 3/4] integrate account balance --- packages/common/src/utils/TypeUtil.ts | 6 +- .../core/src/controllers/AccountController.ts | 27 +- .../controllers/BlockchainApiController.ts | 8 +- packages/core/src/utils/CoreHelperUtil.ts | 15 + packages/ethers/src/client.ts | 1 + packages/scaffold/index.ts | 4 + packages/scaffold/src/client.ts | 4 + .../w3m-account-activity-widget/index.ts | 20 ++ .../w3m-account-activity-widget/styles.ts | 10 + .../partials/w3m-account-nfts-widget/index.ts | 57 ++++ .../w3m-account-nfts-widget/styles.ts | 17 + .../w3m-account-tokens-widget/index.ts | 96 ++++++ .../w3m-account-tokens-widget/styles.ts | 17 + .../index.ts | 107 ++---- .../src/partials/w3m-activity-list/index.ts | 323 ++++++++++++++++++ .../src/partials/w3m-activity-list/styles.ts | 21 ++ .../src/views/w3m-transactions-view/index.ts | 261 +------------- .../ui/src/composites/wui-list-token/index.ts | 10 +- packages/ui/src/utils/UiHelperUtil.ts | 5 + packages/wagmi/src/client.ts | 1 + 20 files changed, 653 insertions(+), 357 deletions(-) create mode 100644 packages/scaffold/src/partials/w3m-account-activity-widget/index.ts create mode 100644 packages/scaffold/src/partials/w3m-account-activity-widget/styles.ts create mode 100644 packages/scaffold/src/partials/w3m-account-nfts-widget/index.ts create mode 100644 packages/scaffold/src/partials/w3m-account-nfts-widget/styles.ts create mode 100644 packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts create mode 100644 packages/scaffold/src/partials/w3m-account-tokens-widget/styles.ts create mode 100644 packages/scaffold/src/partials/w3m-activity-list/index.ts create mode 100644 packages/scaffold/src/partials/w3m-activity-list/styles.ts diff --git a/packages/common/src/utils/TypeUtil.ts b/packages/common/src/utils/TypeUtil.ts index 0722a7cff8..b2ece14b61 100644 --- a/packages/common/src/utils/TypeUtil.ts +++ b/packages/common/src/utils/TypeUtil.ts @@ -77,13 +77,13 @@ export interface Balance { name: string symbol: string chainId: string - value: string + value: number price: number quantity: BalanceQuantity iconUrl: string } type BalanceQuantity = { - decimals: number - quantity: number + decimals: string + numeric: string } diff --git a/packages/core/src/controllers/AccountController.ts b/packages/core/src/controllers/AccountController.ts index 6e988cd5fc..c32819e57a 100644 --- a/packages/core/src/controllers/AccountController.ts +++ b/packages/core/src/controllers/AccountController.ts @@ -1,7 +1,10 @@ import { subscribeKey as subKey } from 'valtio/utils' -import { proxy, subscribe as sub } from 'valtio/vanilla' +import { proxy, ref, subscribe as sub } from 'valtio/vanilla' import { CoreHelperUtil } from '../utils/CoreHelperUtil.js' import type { CaipAddress } from '../utils/TypeUtil.js' +import type { Balance } from '@web3modal/common' +import { BlockchainApiController } from './BlockchainApiController.js' +import { SnackController } from './SnackController.js' // -- Types --------------------------------------------- // export interface AccountControllerState { @@ -15,6 +18,7 @@ export interface AccountControllerState { profileImage?: string | null addressExplorerUrl?: string smartAccountDeployed?: boolean + tokenBalance?: Balance[] } type StateKey = keyof AccountControllerState @@ -22,7 +26,8 @@ type StateKey = keyof AccountControllerState // -- State --------------------------------------------- // const state = proxy({ isConnected: false, - currentTab: 0 + currentTab: 0, + tokenBalance: [] }) // -- Controller ---------------------------------------- // @@ -74,6 +79,24 @@ export const AccountController = { state.currentTab = currentTab }, + setTokenBalance(tokenBalance: AccountControllerState['tokenBalance']) { + if (tokenBalance) { + state.tokenBalance = ref(tokenBalance) + } + }, + + async fetchTokenBalance() { + try { + if (state.address) { + const response = await BlockchainApiController.getBalance(state.address) + + this.setTokenBalance(response.balances) + } + } catch (error) { + SnackController.showError('Failed to fetch token balance') + } + }, + resetAccount() { state.isConnected = false state.caipAddress = undefined diff --git a/packages/core/src/controllers/BlockchainApiController.ts b/packages/core/src/controllers/BlockchainApiController.ts index 77189c98ec..a75641b6f5 100644 --- a/packages/core/src/controllers/BlockchainApiController.ts +++ b/packages/core/src/controllers/BlockchainApiController.ts @@ -9,7 +9,8 @@ import type { GetQuoteArgs, OnrampQuote, PaymentCurrency, - PurchaseCurrency + PurchaseCurrency, + BlockchainApiBalanceResponse } from '../utils/TypeUtil.js' import { OptionsController } from './OptionsController.js' @@ -123,10 +124,11 @@ export const BlockchainApiController = { }, async getBalance(address: string) { - return api.get({ + return api.get({ path: `/v1/account/${address}/balance`, params: { - currency: 'USD' + currency: 'usd', + projectId: OptionsController.state.projectId } }) }, diff --git a/packages/core/src/utils/CoreHelperUtil.ts b/packages/core/src/utils/CoreHelperUtil.ts index 2955891b14..d260d5a227 100644 --- a/packages/core/src/utils/CoreHelperUtil.ts +++ b/packages/core/src/utils/CoreHelperUtil.ts @@ -1,3 +1,4 @@ +import type { Balance } from '@web3modal/common' import { ConstantsUtil } from './ConstantsUtil.js' import type { CaipAddress, LinkingRecord, CaipNetwork } from './TypeUtil.js' @@ -242,5 +243,19 @@ export const CoreHelperUtil = { } return requestedNetworks + }, + calculateBalance(array: Balance[]) { + let sum = 0 + for (const item of array) { + sum += item.value + } + + return sum + }, + formatTokenBalance(number: number) { + const roundedNumber = number.toFixed(2) + const [dollars, pennies] = roundedNumber.split('.') + + return { dollars, pennies } } } diff --git a/packages/ethers/src/client.ts b/packages/ethers/src/client.ts index ed529aabfd..28fb539015 100644 --- a/packages/ethers/src/client.ts +++ b/packages/ethers/src/client.ts @@ -835,6 +835,7 @@ export class Web3Modal extends Web3ModalScaffold { await Promise.all([ this.syncProfile(address), this.syncBalance(address), + this.fetchTokenBalance(), this.getApprovedCaipNetworksData() ]) diff --git a/packages/scaffold/index.ts b/packages/scaffold/index.ts index 41a47453ec..f8df152827 100644 --- a/packages/scaffold/index.ts +++ b/packages/scaffold/index.ts @@ -56,6 +56,10 @@ export * from './src/partials/w3m-snackbar/index.js' export * from './src/partials/w3m-email-login-widget/index.js' export * from './src/partials/w3m-account-default-widget/index.js' export * from './src/partials/w3m-account-wallet-features-widget/index.js' +export * from './src/partials/w3m-account-activity-widget/index.js' +export * from './src/partials/w3m-account-nfts-widget/index.js' +export * from './src/partials/w3m-account-tokens-widget/index.js' +export * from './src/partials/w3m-activity-list/index.js' export { Web3ModalScaffold } from './src/client.js' export type { LibraryOptions, ScaffoldOptions } from './src/client.js' diff --git a/packages/scaffold/src/client.ts b/packages/scaffold/src/client.ts index 2c386b5128..788282766d 100644 --- a/packages/scaffold/src/client.ts +++ b/packages/scaffold/src/client.ts @@ -145,6 +145,10 @@ export class Web3ModalScaffold { AccountController.setBalance(balance, balanceSymbol) } + protected fetchTokenBalance = () => { + AccountController.fetchTokenBalance() + } + protected setProfileName: (typeof AccountController)['setProfileName'] = profileName => { AccountController.setProfileName(profileName) } diff --git a/packages/scaffold/src/partials/w3m-account-activity-widget/index.ts b/packages/scaffold/src/partials/w3m-account-activity-widget/index.ts new file mode 100644 index 0000000000..30af383552 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-activity-widget/index.ts @@ -0,0 +1,20 @@ +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' + +import styles from './styles.js' + +@customElement('w3m-account-activity-widget') +export class W3mAccountActivityWidget extends LitElement { + public static override styles = styles + + // -- Render -------------------------------------------- // + public override render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-account-activity-widget': W3mAccountActivityWidget + } +} diff --git a/packages/scaffold/src/partials/w3m-account-activity-widget/styles.ts b/packages/scaffold/src/partials/w3m-account-activity-widget/styles.ts new file mode 100644 index 0000000000..18f1b1a01f --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-activity-widget/styles.ts @@ -0,0 +1,10 @@ +import { css } from 'lit' + +export default css` + :host { + width: 100%; + max-height: 280px; + overflow: scroll; + scrollbar-width: none; + } +` diff --git a/packages/scaffold/src/partials/w3m-account-nfts-widget/index.ts b/packages/scaffold/src/partials/w3m-account-nfts-widget/index.ts new file mode 100644 index 0000000000..f37f5d89c9 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-nfts-widget/index.ts @@ -0,0 +1,57 @@ +import { RouterController } from '@web3modal/core' +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' + +import styles from './styles.js' + +@customElement('w3m-account-nfts-widget') +export class W3mAccountNftsWidget extends LitElement { + public static override styles = styles + + // -- Render -------------------------------------------- // + public override render() { + return html`${this.nftTemplate()}` + } + + // -- Private ------------------------------------------- // + private nftTemplate() { + return html` + + + No NFTs yet + Transfer from another wallets to get started + + Receive NFTs + ` + } + + private onReceiveClick() { + RouterController.push('WalletReceive') + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-account-nfts-widget': W3mAccountNftsWidget + } +} diff --git a/packages/scaffold/src/partials/w3m-account-nfts-widget/styles.ts b/packages/scaffold/src/partials/w3m-account-nfts-widget/styles.ts new file mode 100644 index 0000000000..072ce33206 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-nfts-widget/styles.ts @@ -0,0 +1,17 @@ +import { css } from 'lit' + +export default css` + .contentContainer { + height: 280px; + } + + .contentContainer > wui-icon-box { + width: 40px; + height: 40px; + border-radius: var(--wui-border-radius-xxs); + } + + .contentContainer > .textContent { + width: 65%; + } +` diff --git a/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts new file mode 100644 index 0000000000..808f7077ad --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-tokens-widget/index.ts @@ -0,0 +1,96 @@ +import { AccountController, RouterController } from '@web3modal/core' +import { customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import styles from './styles.js' +import { state } from 'lit/decorators.js' + +@customElement('w3m-account-tokens-widget') +export class W3mAccountTokensWidget extends LitElement { + public static override styles = styles + + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + // -- State & Properties -------------------------------- // + @state() private tokenBalance = AccountController.state.tokenBalance + + public constructor() { + super() + this.unsubscribe.push( + ...[ + AccountController.subscribe(val => { + this.tokenBalance = val.tokenBalance + }) + ] + ) + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + public override firstUpdated() { + AccountController.fetchTokenBalance() + } + + // -- Render -------------------------------------------- // + public override render() { + return html`${this.tokenTemplate()}` + } + + // -- Private ------------------------------------------- // + private tokenTemplate() { + if (this.tokenBalance && this.tokenBalance?.length > 0) { + return html` + ${this.tokenItemTemplate()} + ` + } + + return html` ` + } + + private tokenItemTemplate() { + return this.tokenBalance?.map( + token => + html`` + ) + } + + private onReceiveClick() { + RouterController.push('WalletReceive') + } + + private onBuyClick() { + RouterController.push('OnRampProviders') + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-account-tokens-widget': W3mAccountTokensWidget + } +} diff --git a/packages/scaffold/src/partials/w3m-account-tokens-widget/styles.ts b/packages/scaffold/src/partials/w3m-account-tokens-widget/styles.ts new file mode 100644 index 0000000000..6e4efb2183 --- /dev/null +++ b/packages/scaffold/src/partials/w3m-account-tokens-widget/styles.ts @@ -0,0 +1,17 @@ +import { css } from 'lit' + +export default css` + :host { + width: 100%; + } + + wui-flex { + width: 100%; + } + + .contentContainer { + max-height: 280px; + overflow: scroll; + scrollbar-width: none; + } +` diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts index cc4e88025d..784f64d607 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -3,7 +3,8 @@ import { ModalController, NetworkController, AssetUtil, - RouterController + RouterController, + CoreHelperUtil } from '@web3modal/core' import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' @@ -30,6 +31,8 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { @state() private currentTab = AccountController.state.currentTab + @state() private tokenBalance = AccountController.state.tokenBalance + public constructor() { super() this.unsubscribe.push( @@ -40,6 +43,7 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { this.profileImage = val.profileImage this.profileName = val.profileName this.currentTab = val.currentTab + this.tokenBalance = val.tokenBalance } else { ModalController.close() } @@ -80,7 +84,7 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { avatarSrc=${ifDefined(this.profileImage ? this.profileImage : undefined)} ?isprofilename=${Boolean(this.profileName)} > - + ${this.tokenBalanceTemplate()} ` + } + if (this.currentTab === 1) { + return html`` + } + if (this.currentTab === 2) { + return html`` } return this.tokenTemplate() } - private tokenTemplate() { - return html` ` - } + private tokenBalanceTemplate() { + if (this.tokenBalance && this.tokenBalance?.length >= 0) { + const value = CoreHelperUtil.calculateBalance(this.tokenBalance) + const { dollars = '0', pennies = '00' } = CoreHelperUtil.formatTokenBalance(value) - private nftTemplate() { - return html` - - - No NFTs yet - Transfer from another wallets to get started - - Receive NFTs - ` - } + return html`` + } - private activityTemplate() { - return html` - - - No activity yet - Your next transactions will appear here - - Trade - ` + return html`` } private activateAccountTemplate() { diff --git a/packages/scaffold/src/partials/w3m-activity-list/index.ts b/packages/scaffold/src/partials/w3m-activity-list/index.ts new file mode 100644 index 0000000000..2a4eb3276d --- /dev/null +++ b/packages/scaffold/src/partials/w3m-activity-list/index.ts @@ -0,0 +1,323 @@ +import { DateUtil } from '@web3modal/common' +import type { Transaction, TransactionImage } from '@web3modal/common' +import { + AccountController, + EventsController, + OptionsController, + RouterController, + TransactionsController +} from '@web3modal/core' +import { TransactionUtil, customElement } from '@web3modal/ui' +import { LitElement, html } from 'lit' +import { property, state } from 'lit/decorators.js' +import type { TransactionType } from '@web3modal/ui/src/utils/TypeUtil.js' +import styles from './styles' + +// -- Helpers --------------------------------------------- // +const PAGINATOR_ID = 'last-transaction' +const LOADING_ITEM_COUNT = 7 + +@customElement('w3m-activity-list') +export class W3mActivityList extends LitElement { + public static override styles = styles + + // -- Members ------------------------------------------- // + private unsubscribe: (() => void)[] = [] + + private paginationObserver?: IntersectionObserver = undefined + + // -- State & Properties -------------------------------- // + @property() public page: 'account' | 'activity' = 'activity' + + @state() private address: string | undefined = AccountController.state.address + + @state() private transactionsByYear = TransactionsController.state.transactionsByYear + + @state() private loading = TransactionsController.state.loading + + @state() private empty = TransactionsController.state.empty + + @state() private next = TransactionsController.state.next + + // -- Lifecycle ----------------------------------------- // + public constructor() { + super() + this.unsubscribe.push( + ...[ + AccountController.subscribe(val => { + if (val.isConnected) { + if (this.address !== val.address) { + this.address = val.address + TransactionsController.resetTransactions() + TransactionsController.fetchTransactions(val.address) + } + } + }), + TransactionsController.subscribe(val => { + this.transactionsByYear = val.transactionsByYear + this.loading = val.loading + this.empty = val.empty + this.next = val.next + }) + ] + ) + } + + public override firstUpdated() { + TransactionsController.fetchTransactions(this.address) + this.createPaginationObserver() + } + + public override updated() { + this.setPaginationObserver() + } + + public override disconnectedCallback() { + this.unsubscribe.forEach(unsubscribe => unsubscribe()) + } + + // -- Render -------------------------------------------- // + public override render() { + return html` ${this.empty ? null : this.templateTransactionsByYear()} + ${this.loading ? this.templateLoading() : null} + ${!this.loading && this.empty ? this.templateEmpty() : null}` + } + + // -- Private ------------------------------------------- // + private templateTransactionsByYear() { + const sortedYearKeys = Object.keys(this.transactionsByYear).sort().reverse() + + return sortedYearKeys.map((year, index) => { + const isLastGroup = index === sortedYearKeys.length - 1 + const yearInt = parseInt(year, 10) + + const sortedMonthIndexes = new Array(12) + .fill(null) + .map((_, idx) => idx) + .reverse() + + return sortedMonthIndexes.map(month => { + const groupTitle = TransactionUtil.getTransactionGroupTitle(yearInt, month) + const transactions = this.transactionsByYear[yearInt]?.[month] + + if (!transactions) { + return null + } + + return html` + + + ${groupTitle} + + + ${this.templateTransactions(transactions, isLastGroup)} + + + ` + }) + }) + } + + private templateRenderTransaction(transaction: Transaction, isLastTransaction: boolean) { + const { date, descriptions, direction, isAllNFT, images, status, transfers, type } = + this.getTransactionListItemProps(transaction) + const haveMultipleTransfers = transfers?.length > 1 + const haveTwoTransfers = transfers?.length === 2 + + if (haveTwoTransfers && !isAllNFT) { + return html` + + ` + } + + if (haveMultipleTransfers) { + return transfers.map((transfer, index) => { + const description = TransactionUtil.getTransferDescription(transfer) + const isLastTransfer = isLastTransaction && index === transfers.length - 1 + + return html` ` + }) + } + + return html` + + ` + } + + private templateTransactions(transactions: Transaction[], isLastGroup: boolean) { + return transactions.map((transaction, index) => { + const isLastTransaction = isLastGroup && index === transactions.length - 1 + + return html`${this.templateRenderTransaction(transaction, isLastTransaction)}` + }) + } + + private emptyStateActivity() { + return html` + + + No Transactions yet + Start trading on dApps
+ to grow your wallet!
+
+
` + } + + private emptyStateAccount() { + return html` + + + No activity yet + Your next transactions will appear here + + Trade + ` + } + + private templateEmpty() { + if (this.page === 'account') { + return html`${this.emptyStateAccount()}` + } + + return html`${this.emptyStateActivity()}` + } + + private templateLoading() { + if (this.page === 'activity') { + return Array(LOADING_ITEM_COUNT) + .fill(html` `) + .map(item => item) + } + + return null + } + + private onReceiveClick() { + RouterController.push('WalletReceive') + } + + private createPaginationObserver() { + const { projectId } = OptionsController.state + + this.paginationObserver = new IntersectionObserver(([element]) => { + if (element?.isIntersecting && !this.loading) { + TransactionsController.fetchTransactions(this.address) + EventsController.sendEvent({ + type: 'track', + event: 'LOAD_MORE_TRANSACTIONS', + properties: { + address: this.address, + projectId, + cursor: this.next + } + }) + } + }, {}) + this.setPaginationObserver() + } + + private setPaginationObserver() { + this.paginationObserver?.disconnect() + + const lastItem = this.shadowRoot?.querySelector(`#${PAGINATOR_ID}`) + if (lastItem) { + this.paginationObserver?.observe(lastItem) + } + } + + private getTransactionListItemProps(transaction: Transaction) { + const date = DateUtil.formatDate(transaction?.metadata?.minedAt) + const descriptions = TransactionUtil.getTransactionDescriptions(transaction) + + const transfers = transaction?.transfers + const transfer = transaction?.transfers?.[0] + const isAllNFT = + Boolean(transfer) && transaction?.transfers?.every(item => Boolean(item.nft_info)) + const images = TransactionUtil.getTransactionImages(transfers) + + return { + date, + direction: transfer?.direction, + descriptions, + isAllNFT, + images, + status: transaction.metadata?.status, + transfers, + type: transaction.metadata?.operationType as TransactionType + } + } +} + +declare global { + interface HTMLElementTagNameMap { + 'w3m-activity-list': W3mActivityList + } +} diff --git a/packages/scaffold/src/partials/w3m-activity-list/styles.ts b/packages/scaffold/src/partials/w3m-activity-list/styles.ts new file mode 100644 index 0000000000..cb9d5e808c --- /dev/null +++ b/packages/scaffold/src/partials/w3m-activity-list/styles.ts @@ -0,0 +1,21 @@ +import { css } from 'lit' + +export default css` + :host { + height: 100%; + } + + .contentContainer { + height: 280px; + } + + .contentContainer > wui-icon-box { + width: 40px; + height: 40px; + border-radius: var(--wui-border-radius-xxs); + } + + .contentContainer > .textContent { + width: 65%; + } +` diff --git a/packages/scaffold/src/views/w3m-transactions-view/index.ts b/packages/scaffold/src/views/w3m-transactions-view/index.ts index 0841315777..a5b246b7d6 100644 --- a/packages/scaffold/src/views/w3m-transactions-view/index.ts +++ b/packages/scaffold/src/views/w3m-transactions-view/index.ts @@ -1,275 +1,20 @@ -import { DateUtil } from '@web3modal/common' -import type { Transaction, TransactionImage } from '@web3modal/common' -import { - AccountController, - EventsController, - OptionsController, - TransactionsController -} from '@web3modal/core' -import { TransactionUtil, customElement } from '@web3modal/ui' +import { customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' -import { state } from 'lit/decorators.js' -import styles from './styles.js' -import type { TransactionType } from '@web3modal/ui/src/utils/TypeUtil.js' -// -- Helpers --------------------------------------------- // -const PAGINATOR_ID = 'last-transaction' -const LOADING_ITEM_COUNT = 7 +import styles from './styles.js' @customElement('w3m-transactions-view') export class W3mTransactionsView extends LitElement { public static override styles = styles - // -- Members ------------------------------------------- // - private unsubscribe: (() => void)[] = [] - - private paginationObserver?: IntersectionObserver = undefined - - // -- State & Properties -------------------------------- // - @state() private address: string | undefined = AccountController.state.address - - @state() private transactionsByYear = TransactionsController.state.transactionsByYear - - @state() private loading = TransactionsController.state.loading - - @state() private empty = TransactionsController.state.empty - - @state() private next = TransactionsController.state.next - - // -- Lifecycle ----------------------------------------- // - public constructor() { - super() - this.unsubscribe.push( - ...[ - AccountController.subscribe(val => { - if (val.isConnected) { - if (this.address !== val.address) { - this.address = val.address - TransactionsController.resetTransactions() - TransactionsController.fetchTransactions(val.address) - } - } - }), - TransactionsController.subscribe(val => { - this.transactionsByYear = val.transactionsByYear - this.loading = val.loading - this.empty = val.empty - this.next = val.next - }) - ] - ) - } - - public override firstUpdated() { - TransactionsController.fetchTransactions(this.address) - this.createPaginationObserver() - } - - public override updated() { - this.setPaginationObserver() - } - - public override disconnectedCallback() { - this.unsubscribe.forEach(unsubscribe => unsubscribe()) - } - // -- Render -------------------------------------------- // public override render() { return html` - ${this.empty ? null : this.templateTransactionsByYear()} - ${this.loading ? this.templateLoading() : null} - ${!this.loading && this.empty ? this.templateEmpty() : null} - - ` - } - - // -- Private ------------------------------------------- // - private templateTransactionsByYear() { - const sortedYearKeys = Object.keys(this.transactionsByYear).sort().reverse() - - return sortedYearKeys.map((year, index) => { - const isLastGroup = index === sortedYearKeys.length - 1 - const yearInt = parseInt(year, 10) - - const sortedMonthIndexes = new Array(12) - .fill(null) - .map((_, idx) => idx) - .reverse() - - return sortedMonthIndexes.map(month => { - const groupTitle = TransactionUtil.getTransactionGroupTitle(yearInt, month) - const transactions = this.transactionsByYear[yearInt]?.[month] - - if (!transactions) { - return null - } - - return html` - - - ${groupTitle} - - - ${this.templateTransactions(transactions, isLastGroup)} - - - ` - }) - }) - } - - private templateRenderTransaction(transaction: Transaction, isLastTransaction: boolean) { - const { date, descriptions, direction, isAllNFT, images, status, transfers, type } = - this.getTransactionListItemProps(transaction) - const haveMultipleTransfers = transfers?.length > 1 - const haveTwoTransfers = transfers?.length === 2 - - if (haveTwoTransfers && !isAllNFT) { - return html` - - ` - } - - if (haveMultipleTransfers) { - return transfers.map((transfer, index) => { - const description = TransactionUtil.getTransferDescription(transfer) - const isLastTransfer = isLastTransaction && index === transfers.length - 1 - - return html` ` - }) - } - - return html` - - ` - } - - private templateTransactions(transactions: Transaction[], isLastGroup: boolean) { - return transactions.map((transaction, index) => { - const isLastTransaction = isLastGroup && index === transactions.length - 1 - - return html`${this.templateRenderTransaction(transaction, isLastTransaction)}` - }) - } - - private templateEmpty() { - return html` - - - - No Transactions yet - Start trading on dApps
- to grow your wallet!
-
+
` } - - private templateLoading() { - return Array(LOADING_ITEM_COUNT) - .fill(html` `) - .map(item => item) - } - - private createPaginationObserver() { - const { projectId } = OptionsController.state - - this.paginationObserver = new IntersectionObserver(([element]) => { - if (element?.isIntersecting && !this.loading) { - TransactionsController.fetchTransactions(this.address) - EventsController.sendEvent({ - type: 'track', - event: 'LOAD_MORE_TRANSACTIONS', - properties: { - address: this.address, - projectId, - cursor: this.next - } - }) - } - }, {}) - this.setPaginationObserver() - } - - private setPaginationObserver() { - this.paginationObserver?.disconnect() - - const lastItem = this.shadowRoot?.querySelector(`#${PAGINATOR_ID}`) - if (lastItem) { - this.paginationObserver?.observe(lastItem) - } - } - - private getTransactionListItemProps(transaction: Transaction) { - const date = DateUtil.formatDate(transaction?.metadata?.minedAt) - const descriptions = TransactionUtil.getTransactionDescriptions(transaction) - - const transfers = transaction?.transfers - const transfer = transaction?.transfers?.[0] - const isAllNFT = - Boolean(transfer) && transaction?.transfers?.every(item => Boolean(item.nft_info)) - const images = TransactionUtil.getTransactionImages(transfers) - - return { - date, - direction: transfer?.direction, - descriptions, - isAllNFT, - images, - status: transaction.metadata?.status, - transfers, - type: transaction.metadata?.operationType as TransactionType - } - } } declare global { diff --git a/packages/ui/src/composites/wui-list-token/index.ts b/packages/ui/src/composites/wui-list-token/index.ts index 4d362d5338..de3327b613 100644 --- a/packages/ui/src/composites/wui-list-token/index.ts +++ b/packages/ui/src/composites/wui-list-token/index.ts @@ -6,6 +6,7 @@ import '../../layout/wui-flex/index.js' import { elementStyles, resetStyles } from '../../utils/ThemeUtil.js' import { customElement } from '../../utils/WebComponentsUtil.js' import styles from './styles.js' +import { UiHelperUtil } from '../../utils/UiHelperUtil.js' @customElement('wui-list-token') export class WuiListToken extends LitElement { @@ -16,9 +17,9 @@ export class WuiListToken extends LitElement { @property() public tokenImageUrl = '' - @property() public tokenValue = '' + @property({ type: Number }) public tokenValue = 0.0 - @property({ type: Number }) public tokenAmount = 0.0 + @property() public tokenAmount = '0.0' @property() public tokenCurrency = '' @@ -31,11 +32,12 @@ export class WuiListToken extends LitElement { ${this.tokenName} ${this.tokenAmount} ${this.tokenCurrency}${UiHelperUtil.roundNumber(Number(this.tokenAmount), 6, 5)} + ${this.tokenCurrency}
- ${this.tokenValue} + $${this.tokenValue.toFixed(2)} ` } diff --git a/packages/ui/src/utils/UiHelperUtil.ts b/packages/ui/src/utils/UiHelperUtil.ts index 3a90e53c10..7c09e82a57 100644 --- a/packages/ui/src/utils/UiHelperUtil.ts +++ b/packages/ui/src/utils/UiHelperUtil.ts @@ -114,5 +114,10 @@ export const UiHelperUtil = { } return ['0', '00'] + }, + roundNumber(number: number, threshold: number, fixed: number) { + const roundedNumber = Math.abs(number) >= threshold ? Number(number.toFixed(fixed)) : number + + return roundedNumber } } diff --git a/packages/wagmi/src/client.ts b/packages/wagmi/src/client.ts index b6753d6fb5..5b28ec2101 100644 --- a/packages/wagmi/src/client.ts +++ b/packages/wagmi/src/client.ts @@ -242,6 +242,7 @@ export class Web3Modal extends Web3ModalScaffold { await Promise.all([ this.syncProfile(address, chainId), this.syncBalance(address, chainId), + this.fetchTokenBalance(), this.getApprovedCaipNetworksData() ]) this.hasSyncedConnectedAccount = true From 4cda9d3815bb3bfd25d2a5158635f6bc0c10298e Mon Sep 17 00:00:00 2001 From: Sven Date: Wed, 6 Mar 2024 16:24:15 +0100 Subject: [PATCH 4/4] fix lint & build --- .../src/partials/w3m-account-wallet-features-widget/index.ts | 2 +- packages/scaffold/src/partials/w3m-activity-list/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts index 784f64d607..7ce22d743f 100644 --- a/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts +++ b/packages/scaffold/src/partials/w3m-account-wallet-features-widget/index.ts @@ -122,7 +122,7 @@ export class W3mAccountWalletFeaturesWidget extends LitElement { return html`` } - return this.tokenTemplate() + return html`` } private tokenBalanceTemplate() { diff --git a/packages/scaffold/src/partials/w3m-activity-list/index.ts b/packages/scaffold/src/partials/w3m-activity-list/index.ts index 2a4eb3276d..9455b0e7cc 100644 --- a/packages/scaffold/src/partials/w3m-activity-list/index.ts +++ b/packages/scaffold/src/partials/w3m-activity-list/index.ts @@ -11,7 +11,7 @@ import { TransactionUtil, customElement } from '@web3modal/ui' import { LitElement, html } from 'lit' import { property, state } from 'lit/decorators.js' import type { TransactionType } from '@web3modal/ui/src/utils/TypeUtil.js' -import styles from './styles' +import styles from './styles.js' // -- Helpers --------------------------------------------- // const PAGINATOR_ID = 'last-transaction'