Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: improve EVM contact selection #2561

Merged
Merged
18 changes: 16 additions & 2 deletions packages/desktop/components/ContactAddressCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import { IContact, IContactAddress, IContactAddressMap, setSelectedContactNetworkAddress } from '@core/contact'
import { localize } from '@core/i18n'
import { resetLedgerPreparedOutput, resetShowInternalVerificationPopup } from '@core/ledger'
import { ExplorerEndpoint, getExplorerUrl, getNameFromNetworkId, getNetwork, NetworkId } from '@core/network'
import {
ExplorerEndpoint,
getExplorerUrl,
getNameFromNetworkId,
getNetwork,
NetworkId,
NetworkNamespace,
} from '@core/network'
import { Router } from '@core/router'
import { truncateString } from '@core/utils'
import { SendFlowType, setSendFlowParameters, SubjectType } from '@core/wallet'
Expand All @@ -23,6 +30,13 @@

const hasExplorer = !!getNetwork(networkId)?.explorerUrl

function getTitle(): string {
if (networkId.includes(NetworkNamespace.Evm)) {
return localize('general.evmAddress')
}
return getNameFromNetworkId(networkId) ?? ''
}

function onExplorerClick(address: string): void {
const url = getExplorerUrl(networkId, ExplorerEndpoint.Address, address)
openUrlInBrowser(url)
Expand Down Expand Up @@ -55,7 +69,7 @@
<contact-address-head class="flex justify-between">
<div class="flex items-center gap-2">
<NetworkAvatar {networkId} />
<Text type="body1">{getNameFromNetworkId(networkId)}</Text>
<Text type="body1">{getTitle()}</Text>
</div>
<ContactAddressMenu {drawerRouter} {networkId} />
</contact-address-head>
Expand All @@ -77,20 +91,20 @@
size="sm"
icon={IconName.Globe}
tooltip={localize('general.viewOnExplorer')}
on:click={() => onExplorerClick(contactAddress.address)}

Check warning on line 94 in packages/desktop/components/ContactAddressCard.svelte

View workflow job for this annotation

GitHub Actions / Lint

Missing return type on function
/>
{/if}
<IconButton
size="sm"
icon={IconName.QrCode}
tooltip={localize('general.viewQrCode')}
on:click={() => onQrCodeClick(contactAddress)}

Check warning on line 101 in packages/desktop/components/ContactAddressCard.svelte

View workflow job for this annotation

GitHub Actions / Lint

Missing return type on function
/>
{#if features.contacts.sendTo.enabled}
<IconButton
icon={IconName.Send}
tooltip={localize('actions.send')}
on:click={() => onSendClick(contactAddress.address)}

Check warning on line 107 in packages/desktop/components/ContactAddressCard.svelte

View workflow job for this annotation

GitHub Actions / Lint

Missing return type on function
/>
{/if}
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/components/ContactCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<ContactAvatar {contact} />
<div class="flex w-full text-left overflow-hidden">
<Text type="base" truncate>
{contact.name}
{contact?.name}
</Text>
</div>
{#if showBreadcrumb}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
}}
>
<div class="space-y-5 flex flex-auto flex-col shrink-0">
<NetworkInput bind:networkId={selectedNetworkId} />
<NetworkInput mergeLayer2Options bind:networkId={selectedNetworkId} />
{#if receiveAddress}
<AddressBox
address={receiveAddress}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
bind:this={networkSelectionInput}
bind:networkId={selectedNetworkId}
bind:error={validationErrors[ContactField.Network]}
mergeLayer2Options
/>
<TextInput
bind:this={addressInput}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
bind:this={networkSelectionInput}
bind:networkId={selectedNetworkId}
bind:error={validationErrors[AddressField.Network]}
mergeLayer2Options
/>
<TextInput
bind:value={address}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
getStardustNetwork,
getNetwork,
isEvmNetwork,
SupportedNetworkId,
} from '@core/network'
import { NftStandard } from '@core/nfts'
import { visibleActiveAccounts } from '@core/profile/stores'
Expand Down Expand Up @@ -95,7 +96,8 @@
}

function getContactRecipientsForNetwork(networkId: NetworkId): Subject[] {
const recipients: Subject[] = ContactManager.listContactAddressesForNetwork(networkId).map((address) => {
const _networkId = isEvmNetwork(networkId) ? SupportedNetworkId.GenericEvm : networkId
const recipients: Subject[] = ContactManager.listContactAddressesForNetwork(_networkId).map((address) => {
const contact = ContactManager.getContact(address.contactId)
return {
type: SubjectType.Contact,
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/components/avatars/NetworkAvatar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
[SupportedNetworkId.TestnetEvm]: 'shimmer-evm-background/90',
[SupportedNetworkId.Ethereum]: '#627eea',
[SupportedNetworkId.Sepolia]: 'text-secondary',
[SupportedNetworkId.GenericEvm]: '#627eea',
}

const AVATAR_TEXT_COLOR: { [id in NetworkId]?: string } = {
Expand All @@ -33,6 +34,7 @@
[SupportedNetworkId.TestnetEvm]: '#FFFFFF',
[SupportedNetworkId.Ethereum]: '#FFFFFF',
[SupportedNetworkId.Sepolia]: '#FFFFFF',
[SupportedNetworkId.GenericEvm]: '#FFFFFF',
}

let anchor: HTMLElement
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/components/avatars/TokenAvatar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
[SupportedNetworkId.Sepolia]: {
[BASE_TOKEN_ID]: 'text-secondary',
},
[SupportedNetworkId.GenericEvm]: {
[BASE_TOKEN_ID]: 'text-secondary',
},
}

const AVATAR_TEXT_COLOR: { [networkId: string]: { [tokenId: string]: string } } = {
Expand Down Expand Up @@ -77,6 +80,9 @@
[SupportedNetworkId.Sepolia]: {
[BASE_TOKEN_ID]: '#FFFFFF',
},
[SupportedNetworkId.GenericEvm]: {
[BASE_TOKEN_ID]: 'text-secondary',
},
}

const IMAGE_SIZES: Record<typeof size, keyof CoinGeckoCoinImage> = {
Expand Down
16 changes: 14 additions & 2 deletions packages/shared/src/components/inputs/NetworkInput.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script lang="ts">
import { IOption, SelectInput } from '@bloomwalletio/ui'
import { localize } from '@core/i18n'
import { NetworkId, getEvmNetworks, getStardustNetwork } from '@core/network'
import { NetworkId, SupportedNetworkId, getEvmNetworks, getStardustNetwork } from '@core/network'

export let networkId: NetworkId | undefined
export let error: string | undefined
export let showLayer1: boolean = true
export let showLayer2: boolean = true
export let mergeLayer2Options: boolean = false

export let validationFunction: ((arg: string) => void) | undefined = undefined

export function validate(): void {
Expand All @@ -32,13 +34,23 @@
options.push({ label: l1Network.name, value: l1Network.id })
}

if (showLayer2) {
if (!showLayer2) {
return options
}

if (mergeLayer2Options) {
options.push({
label: 'EVM',
value: SupportedNetworkId.GenericEvm,
})
} else {
const layer2Networks: IOption[] = getEvmNetworks().map((evmNetwork) => ({
label: evmNetwork.name,
value: evmNetwork.id,
}))
options.push(...layer2Networks)
}

return options
}
</script>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { Icon, IOption, IconName, Text, Tile } from '@bloomwalletio/ui'
import { isEvmNetwork } from '@core/network'
import { SupportedNetworkId, isEvmNetwork } from '@core/network'
import { Subject, SubjectType } from '@core/wallet'
import { NetworkAvatar, RecipientInput } from '@ui'
import { INetworkRecipientSelectorOption } from '../interfaces'
Expand Down Expand Up @@ -35,8 +35,9 @@
},
]
case SubjectType.Contact: {
const networkId = isEvmNetwork(item.networkId) ? SupportedNetworkId.GenericEvm : item.networkId
const addresses = Object.values(
ContactManager.getNetworkContactAddressMapForContact(recipient.contact.id)[item.networkId] ?? {}
ContactManager.getNetworkContactAddressMapForContact(recipient.contact.id)[networkId] ?? {}
)
return addresses.map<IOption>((address) => ({
label: `${recipient.contact.name} (${address.addressName})`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
export let selectedIndex = -1
export let hasError: boolean = false

const reipientItems: Record<number, NetworkRecipientItem> = {}
const recipientItems: Record<number, NetworkRecipientItem> = {}

export function validate(): void {
reipientItems[selectedIndex]?.validate()
recipientItems[selectedIndex]?.validate()
}

function onItemClick(index: number): void {
Expand All @@ -22,7 +22,7 @@
{#each options as item, index}
<NetworkRecipientItem
hasError={selectedIndex === index && hasError}
bind:this={reipientItems[index]}
bind:this={recipientItems[index]}
bind:item
selected={index === selectedIndex}
onChange={() => onItemClick(index)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const DEFAULT_NETWORK_ICON: { [id in NetworkId]?: IconName } = {
[SupportedNetworkId.TestnetEvm]: IconName.Shimmer,
[SupportedNetworkId.Ethereum]: IconName.Ethereum,
[SupportedNetworkId.Sepolia]: IconName.Ethereum,
[SupportedNetworkId.GenericEvm]: IconName.Ethereum,
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ export const DEFAULT_TOKEN_ICON: { [networkId in NetworkId]?: { [tokenId: string
[SupportedNetworkId.Sepolia]: {
[BASE_TOKEN_ID]: IconName.Ethereum,
},
[SupportedNetworkId.GenericEvm]: {
[BASE_TOKEN_ID]: IconName.Ethereum,
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,14 @@ export class ContactManager {
return profile && profile.contacts ? Object.values(profile.contacts) : []
}

static addContactAddress(contactId: string, networkId: NetworkId, addressName: string, address: string): void {
static addContactAddress(
contactId: string | undefined,
networkId: NetworkId,
addressName: string,
address: string
): void {
const profile = getActiveProfile()
if (!profile) {
if (!profile || !contactId) {
throw new Error('Profile is not available.')
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ export const DEFAULT_BASE_TOKEN: Readonly<{ [id in NetworkId]: IBaseToken }> = {
[SupportedNetworkId.TestnetEvm]: SHIMMER_EVM_BASE_TOKEN,
[SupportedNetworkId.Ethereum]: EVM_BASE_TOKEN,
[SupportedNetworkId.Sepolia]: EVM_BASE_TOKEN,
[SupportedNetworkId.GenericEvm]: EVM_BASE_TOKEN,
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export const DEFAULT_COIN_TYPE: Readonly<{ [key in NetworkId]: number }> = {
[SupportedNetworkId.TestnetEvm]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.Ethereum]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.Sepolia]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.GenericEvm]: ETHEREUM_COIN_TYPE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const SupportedStardustNetworkId: Record<string, StardustNetworkId> = {
}

export const SupportedL1EvmNetworkId: Record<string, EvmNetworkId> = {
GenericEvm: `${NetworkNamespace.Evm}:*`,
Ethereum: `${NetworkNamespace.Evm}:${ChainId.Ethereum}`,
Sepolia: `${NetworkNamespace.Evm}:${ChainId.Sepolia}`,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppStage } from '@core/app/enums'

export const PROFILE_VERSION: Record<AppStage, number> = {
[AppStage.ALPHA]: 19,
[AppStage.ALPHA]: 20,
[AppStage.BETA]: 1,
[AppStage.PROD]: 10,
[AppStage.PROD]: 11,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { IContactAddressMap } from '@core/contact'
import { isEvmNetwork, NetworkId, SupportedNetworkId } from '@core/network'
import { IPersistedProfile } from '@core/profile/interfaces'

export function migrateEvmContacts(profile: IPersistedProfile): void {
const evmNetworks = Object.keys(profile.networkContactAddresses).filter((networkId) =>
isEvmNetwork(networkId as NetworkId)
)

const migratedContacts: IContactAddressMap = structuredClone(
profile.networkContactAddresses[SupportedNetworkId.GenericEvm] ?? {}
)

evmNetworks.forEach((network) => {
const networkContacts: IContactAddressMap = profile.networkContactAddresses[network] ?? {}
Object.entries(networkContacts).forEach(([address, contactAddress]) => {
migratedContacts[address] = contactAddress
})
delete profile.networkContactAddresses[network]
})

profile.networkContactAddresses[SupportedNetworkId.GenericEvm] = migratedContacts
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IPersistedProfile } from '../../interfaces'
import { migrateEvmContacts } from '../../migrations/actions/migrateEvmContacts'

export function alphaProfileMigration19To20(existingProfile: unknown): Promise<void> {
const profile = existingProfile as IPersistedProfile

migrateEvmContacts(profile)

return Promise.resolve()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { alphaProfileMigration15To16 } from './alpha-profile-migration-15-to-16'
import { alphaProfileMigration16To17 } from './alpha-profile-migration-16-to-17'
import { alphaProfileMigration17To18 } from './alpha-profile-migration-17-to-18'
import { alphaProfileMigration18To19 } from './alpha-profile-migration-18-to-19'
import { alphaProfileMigration19To20 } from './alpha-profile-migration-19-to-20'
import { alphaProfileMigration2To3 } from './alpha-profile-migration-2-to-3'
import { alphaProfileMigration3To4 } from './alpha-profile-migration-3-to-4'
import { alphaProfileMigration4To5 } from './alpha-profile-migration-4-to-5'
Expand Down Expand Up @@ -41,4 +42,5 @@ export const ALPHA_PROFILE_MIGRATION_MAP: ProfileMigrationMap = {
17: alphaProfileMigration17To18,
18: alphaProfileMigration18To19,
// ^^^ release 1.0.3 ^^^
19: alphaProfileMigration19To20,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IPersistedProfile } from '../../interfaces'
import { migrateEvmContacts } from '../actions/migrateEvmContacts'

export function prodProfileMigration10To11(existingProfile: unknown): Promise<void> {
const profile = existingProfile as IPersistedProfile

migrateEvmContacts(profile)

return Promise.resolve()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { prodProfileMigration6To7 } from './prod-profile-migration-6-to-7'
import { prodProfileMigration7To8 } from './prod-profile-migration-7-to-8'
import { prodProfileMigration9To10 } from './prod-profile-migration-9-to-10'
import { prodProfileMigration8To9 } from './prod-profile-migration-8-to-9'
import { prodProfileMigration10To11 } from './prod-profile-migration-10-to-11'

export const PROD_PROFILE_MIGRATION_MAP: ProfileMigrationMap = {
0: prodProfileMigration0To1,
Expand All @@ -24,4 +25,5 @@ export const PROD_PROFILE_MIGRATION_MAP: ProfileMigrationMap = {
8: prodProfileMigration8To9, // Migration was removed and replaced with 10To11 after 9To10
// ^^^ release 1.0.3 ^^^
9: prodProfileMigration9To10, // rechecking some previous migrations due to a bug during migrations
10: prodProfileMigration10To11,
}
Loading