kodadot/nft-gallery

View on GitHub
components/common/ConnectWallet/WalletMenuItem.vue

Summary

Maintainability
Test Coverage
<template>
  <div class="wallet-menu-item">
    <button
      class="button my-0 !px-8 py-2.5 flex justify-between items-center"
      @click="onClickWallet(wallet)"
    >
      <span>
        <div class="flex justify-between items-center">
          <span class="flex items-center">
            <img
              :src="wallet.img"
              :alt="wallet.extensionName"
              width="32"
              style="vertical-align: middle"
            >
            <span class="text-base ml-2 capitalize">{{ wallet.name }}</span>

            <NeoTag
              v-if="isRecent(wallet)"
              class="capitalize ml-2"
              variant="transparent"
              size="small"
            >
              {{ $t('recent') }}
            </NeoTag>
          </span>

          <div
            v-if="!wallet.installed"
            class="text-xs capitalize text-neutral-7"
          >
            {{ $t('moreActions.download') }}
            <NeoIcon icon="download" />
          </div>

          <NeoIcon
            v-else-if="showAccountList"
            icon="chevron-down"
          />

          <NeoIcon
            v-else
            icon="chevron-right"
          />
        </div>
      </span>
    </button>
    <div
      v-if="isAuth && walletAccounts.length === 0"
      class="pl-5 pt-2 pb-2 flex items-center auth-tip"
    >
      <NeoIcon icon="spinner-third" />
      <span class="text-k-grey text-xs pl-4">
        {{ $t('walletConnect.authTip') }}
      </span>
    </div>

    <div
      v-if="walletAccountsWithProfile.length && showAccountList"
      class="account-list"
    >
      <div
        v-for="option in walletAccountsWithProfile"
        :key="option.address"
        class="account-item"
      >
        <a
          class="pl-5 flex items-center justify-between"
          :value="option.address"
          @click="emitAccountChange(option)"
        >
          <div class="flex items-center">
            <Avatar
              :size="33"
              :value="option.address"
              class="mr-2 image-outline"
            />
            <div class="flex flex-col">
              <span class="text-k-grey text-xs account-name">{{
                option.name
              }}</span>
              <div class="account-address">
                {{ shortAddress(option.address, 6, -3) }}
              </div>
            </div>
          </div>
          <NeoSkeleton
            v-if="isProfilesLoading"
            class="!w-[73px] !h-[22px] mr-1 rounded-full overflow-hidden"
            width="100%"
            height="100%"
            no-margin
          />
          <div
            v-else-if="option.profile"
            class="flex items-center rounded-full bg-neutral-3 dark:bg-neutral-11 px-[6px] py-[3px] h-[22px] gap-[0.375rem] mr-1"
          >
            <ProfileAvatar
              :size="12.5"
              :address="option.address"
              :profile-image="option.profile?.image"
            />
            <span
              class="text-sm max-w-[80px] text-ellipsis whitespace-nowrap overflow-hidden"
            >{{ option.profile?.name }}</span>
          </div>
        </a>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { NeoIcon, NeoSkeleton } from '@kodadot1/brick'
import { DEFAULT_VM_PREFIX } from '@kodadot1/static'
import { chainPropListOf } from '@/utils/config/chain.config'
import type { WalletAccount } from '@/utils/config/wallets'
import type { BaseDotsamaWallet } from '@/utils/config/wallets/BaseDotsamaWallet'
import shouldUpdate from '@/utils/shouldUpdate'
import { formatAddress } from '@/utils/account'
import shortAddress from '@/utils/shortAddress'
import { useWalletStore } from '@/stores/wallet'
import Avatar from '@/components/shared/Avatar.vue'
import NeoTag from '@/components/shared/gallery/NeoTag.vue'
import { toSubstrateAddress } from '@/services/profile'

defineProps<{
  wallet: BaseDotsamaWallet
}>()

const { chainProperties } = useChain()
const { $consola } = useNuxtApp()
const { urlPrefix } = usePrefix()
const hasWalletProviderExtension = ref(false)
const walletAccounts = ref<WalletAccount[]>([])
const showAccountList = ref(false)
const emit = defineEmits(['setWallet', 'setAccount'])
const walletStore = useWalletStore()
const isAuth = ref(false)

const isRecent = (wallet: BaseDotsamaWallet) =>
  walletStore.getRecentWallet === wallet.source

const emitAccountChange = (account): void => {
  emit('setAccount', account)
}

const ss58Format = computed(() => chainProperties.value?.ss58Format)

const walletAccountsWithProfile = computed(() => {
  return walletAccounts.value.map((account) => {
    return {
      ...account,
      profile: existingProfiles.value?.find(
        p => p.address === toSubstrateAddress(account.address),
      ),
    }
  })
})

watch(walletAccounts, (newValue) => {
  if (shouldUpdate(newValue, walletAccounts.value)) {
    walletAccounts.value = newValue
  }
})

const { data: existingProfiles, isLoading: isProfilesLoading } = useProfiles('account-profiles', computed(() => walletAccounts.value.map(a => a.address)))

const formatAccount = (account: WalletAccount): WalletAccount => {
  return {
    ...account,
    address: formatAddress(account.address, !isPrefixVmOf(urlPrefix.value, 'SUB') ? chainPropListOf(DEFAULT_VM_PREFIX['SUB']).ss58Format : ss58Format.value),
  }
}

const onClickWallet = (wallet: BaseDotsamaWallet): void => {
  if (wallet.installed) {
    setWallet(wallet)
    showAccountList.value = !showAccountList.value
  }
  else {
    window.open(wallet.walletUrl, '_blank')
  }
}

const setWallet = async (wallet: BaseDotsamaWallet) => {
  emit('setWallet', wallet)
  // web3 wallet connect logic here & show accountSelect, async or not?
  isAuth.value = true

  await wallet.enable()
  // init account
  try {
    const data = await wallet.getAccounts()
    walletAccounts.value = data ? data.map(formatAccount) : []
  }
  catch (e) {
    isAuth.value = false
    $consola.error('init account error', e)
  }

  // subscribe change
  wallet.subscribeAccounts((accounts) => {
    // list of supported accounts for this wallet to show in AccoutSelect
    if (accounts) {
      walletAccounts.value = accounts.map(formatAccount)
    }
    isAuth.value = false
  })
  hasWalletProviderExtension.value = true
  //
}
</script>