kodadot/nft-gallery

View on GitHub
components/profile/activityTab/TradeRow.vue

Summary

Maintainability
Test Coverage
<template>
  <div
    v-if="isDesktop"
    class="flex gap-3 py-[.6rem]"
  >
    <div class="flex-1 is-clipped">
      <div class="flex items-center">
        <nuxt-link
          :to="`/${urlPrefix}/gallery/${trade.desired.id}`"
          class="h-[50px]"
        >
          <BaseMediaItem
            class="border border-k-shade w-[3.125rem] h-[3.125rem]"
            alt="offer.Item.name"
            :src="image"
            :animation-src="!image ? animationUrl : undefined"
            preview
            is-detail
          />
        </nuxt-link>
        <nuxt-link
          class="is-ellipsis inline-block"
          :to="`/${urlPrefix}/gallery/${trade.desired.id}`"
        >
          <span class="ml-5 font-bold is-clipped">
            {{ trade.desired.name }}
          </span>
        </nuxt-link>
      </div>
    </div>

    <div class="w-1/12">
      <div class="h-[50px] flex items-center">
        <EventTag
          :interaction="interaction"
          :interaction-name="interactionName"
        />
      </div>
    </div>

    <div class="flex-1 is-ellipsis">
      <div class="h-[50px] flex items-center">
        <div
          v-if="parseInt(trade.price)"
          class="flex gap-2 items-center"
        >
          <span>{{ amount }}</span> <span class="text-k-grey text-sm">({{ price }})</span>
        </div>
        <div v-else>
          {{ blank }}
        </div>
      </div>
    </div>

    <div
      class="flex-1"
    >
      <div class="h-[50px] flex items-center gap-2">
        <ProfileAvatar
          :size="24"
          :address="targetAddress"
        />

        <nuxt-link
          :to="`/${urlPrefix}/u/${targetAddress}`"
          class="text-k-blue hover:text-k-blue-hover"
        >
          <IdentityIndex
            :address="targetAddress"
          />
        </nuxt-link>
      </div>
    </div>

    <div class="flex-1">
      <div class="h-[50px] flex items-center">
        <template v-if="trade.expirationDate">
          <div v-if="isExpired">
            <span>{{ $t('expired') }}</span>
          </div>
          <div v-else>
            <span>{{ format(trade.expirationDate, EXPIRATION_FORMAT) }}</span>
            <span class="text-k-grey ml-3">({{ formatToNow(trade.expirationDate, isExpired) }})</span>
          </div>
        </template>
        <span v-else>
          {{ blank }}
        </span>
      </div>
    </div>

    <div class="flex-1">
      <div class="h-[50px] flex items-center">
        <TradeOwnerButton
          class="max-md:!w-full"
          :trade="trade"
          @click="$emit('select')"
        />
      </div>
    </div>
  </div>
  <!-- Mobile -->
  <div
    v-else
    class="mb-6 flex flex-col"
  >
    <div class="flex flex-col gap-[10px]">
      <div class="flex h-[70px] leading-[1]">
        <nuxt-link :to="`/${urlPrefix}/gallery/${trade.desired.id}`">
          <div class="mr-5">
            <BaseMediaItem
              class="border border-k-shade w-[4.375rem] h-[4.375rem]"
              alt="offer.Item.name"
              :src="image"
              :animation-src="!image ? animationUrl : undefined"
              preview
              is-detail
            />
          </div>
        </nuxt-link>
        <div class="flex flex-col justify-center gap-[10px] flex-grow">
          <nuxt-link
            class="is-ellipsis inline-block w-60"
            :to="`/${urlPrefix}/gallery/${trade.desired.id}`"
          >
            <span class="font-bold">
              {{ trade.desired.name }}
            </span>
          </nuxt-link>

          <EventTag
            :interaction="interaction"
            :interaction-name="interactionName"
          />
        </div>
      </div>

      <div
        v-if="parseInt(trade.price)"
        class="flex gap-2 items-center"
      >
        <span>{{ amount }}</span> <span class="text-k-grey text-sm">({{ price }})</span>
      </div>
      <div v-else>
        {{ blank }}
      </div>
      <template v-if="trade.expirationDate">
        <div v-if="isExpired">
          <span>{{ $t('expired') }}</span>
        </div>
        <div v-else>
          <span>{{ format(trade.expirationDate, EXPIRATION_FORMAT) }}</span>
          <span class="text-k-grey ml-3">({{ formatToNow(trade.expirationDate, isExpired) }})</span>
        </div>
      </template>
      <span v-else>
        {{ blank }}
      </span>

      <div class="flex gap-4">
        <div
          class="flex items-center"
        >
          <span class="text-xs mr-3">{{ $t(`activity.event.${target}`) }}:</span>
          <div class="flex items-center gap-2">
            <ProfileAvatar
              :size="24"
              :address="targetAddress"
            />
            <nuxt-link
              :to="`/${urlPrefix}/u/${targetAddress}`"
              class="text-k-blue hover:text-k-blue-hover"
            >
              <IdentityIndex
                :address="targetAddress"
              />
            </nuxt-link>
          </div>
        </div>
      </div>
    </div>
    <TradeOwnerButton
      class="max-md:!w-full !mt-4"
      :trade="trade"
      @click="$emit('select')"
    />
  </div>
</template>

<script setup lang="ts">
import { format } from 'date-fns'
import { formatToNow } from '@/utils/format/time'
import {
  blank,
  interactionNameMap,
} from '@/components/collection/activity/events/eventRow/common'
import EventTag from '@/components/collection/activity/events/eventRow/EventTag.vue'
import { TradeInteraction } from '@/composables/collectionActivity/types'
import { fetchNft } from '@/components/items/ItemsGrid/useNftActions'

const EXPIRATION_FORMAT = 'dd.MM. HH:MM'

defineEmits(['select'])
const props = defineProps<{
  trade: TradeNftItem
  variant: ResponsiveVariant
  target: 'from' | 'to'
}>()

const interaction = {
  [TradeType.OFFER]: TradeInteraction.OFFER,
  [TradeType.SWAP]: TradeInteraction.SWAP,
}[props.trade.type]

const { urlPrefix } = usePrefix()
const { format: formatPrice } = useFormatAmount()
const { amount, price } = formatPrice(props.trade?.price)

const image = ref()
const animationUrl = ref()

const isDesktop = computed(() => props.variant === 'Desktop')
const isExpired = computed(() => props.trade.status === 'EXPIRED')
const targetAddress = computed(() => props.target === 'to' ? props.trade.desired.currentOwner : props.trade.caller)
const interactionName = computed(() => interactionNameMap()[interaction])

const getAvatar = async (nft) => {
  const meta = await getNftMetadata(nft)
  image.value = meta.image
  animationUrl.value = meta.animationUrl
}

// TODO imporve nft fetching
onBeforeMount(() => fetchNft(props.trade.desired.id).then(getAvatar))
</script>