kodadot/nft-gallery

View on GitHub
components/migrate/steps/SignLoader2.vue

Summary

Maintainability
Test Coverage
<template>
  <div class="mb-5">
    <div class="flex items-center mb-4">
      <div class="mr-5">
        <NeoIcon
          v-bind="whichIcon()"
          class="fa-2x"
        />
      </div>
      <div>
        <p class="font-bold text-xl">
          {{ $t('migrate.signStep.migratingItems') }}
        </p>
        <p class="text-k-grey">
          {{ $t('migrate.signStep.signtx') }}
        </p>
      </div>
    </div>
    <div class="flex">
      <div class="v-border" />
      <div class="mb-4">
        <p v-if="iterations">
          {{ iterations }}/{{ maxIterations }} {{ $t('migrate.signStep.left') }}
        </p>
        <p v-else>
          {{ $t('migrate.signStep.done') }}
        </p>
      </div>
    </div>
    <div
      v-for="(_, index) in maxIterations"
      :key="index"
      class="flex"
    >
      <div class="v-border" />
      <div class="mb-4 flex w-full">
        <NeoIcon
          v-bind="itemLeftIcons(index)"
          class="mr-4"
        />
        <div :class="itemLeftIcons(index).textColor">
          <p>{{ $t('migrate.signStep.migratingNItems', itemLeft(index)) }}</p>
        </div>
        <div
          v-if="isError || status === TransactionStatus.Cancelled"
          class="flex-1 text-right"
        >
          <NeoButton
            variant="outlined-rounded"
            size="small"
            @click="tryAgain()"
          >
            {{ $t('helper.tryAgain') }}
          </NeoButton>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { Prefix } from '@kodadot1/static'
import { NeoButton, NeoIcon } from '@kodadot1/brick'
import {
  BATCH_SIZE,
  type Steps,
  calculateIterations,
  iconIdle,
  iconLoading,
  iconSuccess,
  useCollectionReady,
} from '@/composables/useMigrate'
import waifuApi from '@/services/waifu'

const { accountId } = useAuth()
const { apiInstance } = useApi()
const { howAboutToExecute, status, isError } = useMetaTransaction()
const { $consola } = useNuxtApp()
const route = useRoute()
const router = useRouter()

const from = route.query.source as Prefix
const fromAccountId = route.query.accountId?.toString()
const itemCount = route.query.itemCount?.toString()
const collectionOwner = route.query.collectionOwner?.toString()
const collectionId = route.query.collectionId

const { collections } = await useCollectionReady(from, fromAccountId)
const fromCollection = collections.value.find(
  collection => collection.id === route.query.collectionId,
)

const api = await apiInstance.value
const { steps, updateSteps } = inject('steps') as {
  steps: Ref<Steps>
  updateSteps: (step: Steps) => void
}

const maxIterations = calculateIterations(itemCount)
const iterations = ref(maxIterations)
const batchPresigned = reactive({})

const itemLeft = (index) => {
  if (index === maxIterations - 1) {
    return parseInt(itemCount || '0') % BATCH_SIZE
  }

  return BATCH_SIZE
}

const itemLeftIcons = (index) => {
  if (isError.value || status.value === TransactionStatus.Cancelled) {
    return iconError
  }

  if (steps.value.includes('init') || steps.value.includes('step1')) {
    return iconIdle
  }

  if (iterations.value < maxIterations - index) {
    return iconSuccess
  }

  if (iterations.value <= maxIterations - index) {
    return iconLoading
  }

  return iconIdle
}

const tryAgain = () => {
  iterations.value += 1
  startStep2()
}

const startStep2 = async () => {
  let nextCollectionId
  try {
    for (let index = 0; index < iterations.value; index++) {
      const fromCollectionId = collectionOwner
        ? collectionId
        : fromCollection?.id
      const checkSign = await waifuApi(
        `/relocations/${from}/${fromCollectionId}/iterations/${index}`,
        {
          method: 'PUT',
        },
      )

      const ownerSign = checkSign.data.filter(
        item => item.account === accountId.value,
      )

      if (!nextCollectionId) {
        nextCollectionId = ownerSign[0]?.to_collection
      }

      const presigned = ownerSign.map((item) => {
        const preSignInfo = api.createType('PalletNftsPreSignedMint', item.data)
        const create = api.tx.nfts.mintPreSigned(
          preSignInfo,
          {
            Ed25519: item.signature,
          },
          item.signer,
        )

        return create
      })

      batchPresigned[index] = presigned
    }

    if (collectionOwner && nextCollectionId) {
      router.push({
        query: {
          ...route.query,
          nextCollectionId,
        },
      })
    }

    executeStep2()
  }
  catch (error) {
    $consola.error('error step2', error)
  }
}

const executeStep2 = async () => {
  updateSteps('step2-migrate')

  if (
    status.value
    && status.value !== TransactionStatus.Finalized
    && status.value !== TransactionStatus.Cancelled
  ) {
    await delay(DETAIL_TIMEOUT)
    executeStep2()
    return
  }

  if (iterations.value) {
    const cb = api.tx.utility.batch
    const args = [toRaw(batchPresigned[maxIterations - iterations.value])]
    iterations.value -= 1

    await howAboutToExecute(accountId.value, cb, args)
    await delay(DETAIL_TIMEOUT)

    if (iterations.value) {
      executeStep2()
    }
  }
}

watchEffect(() => {
  $consola.info('SignLoader2.vue', steps.value, status.value)

  if (steps.value === 'step2') {
    startStep2()
  }

  // Done, move to step3
  if (
    steps.value === 'step2-migrate'
    && !iterations.value
    && status.value === TransactionStatus.Finalized
  ) {
    updateSteps('step3')
  }
})

const whichIcon = () => {
  if (isError.value || status.value === TransactionStatus.Cancelled) {
    return iconError
  }

  if (iterations.value === 0) {
    return iconSuccess
  }

  if (steps.value.includes('step2')) {
    return iconLoading
  }

  return iconIdle
}
</script>