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

feat: Offers tab in profile and gallery item #10911

Merged
merged 43 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
832d6c0
add(GalleryItem): `offers` tab
hassnian Aug 27, 2024
dda9bdc
fix(GalleryItemOffersTable.vue): loading state
hassnian Aug 27, 2024
9c5912b
add(offers): withdraw offer
hassnian Aug 28, 2024
b4e727d
Merge branch 'main' into issue-10845-0
hassnian Aug 28, 2024
b9ba537
fix(GalleryItemOffersTable.vue): mobile full width button
hassnian Aug 28, 2024
782c20b
fix(offers): withdraw offer button styles
hassnian Aug 28, 2024
c967af0
fix(GalleryItemOffersTable.vue): offers not syncing in real time
hassnian Aug 28, 2024
5c25c88
ref(GalleryItem): better fiat price fetching
hassnian Aug 28, 2024
be3b5fe
fix(GalleryItemOffersTable.vue): `useOffers` computed not reactive
hassnian Aug 28, 2024
2bacd5c
add(offers): claim offer & porfile offers tab
hassnian Aug 29, 2024
f971a4f
add(Offers.vue): offer overview modal
hassnian Aug 29, 2024
84caf19
add(Offers.vue): tabs total count
hassnian Aug 29, 2024
de1b058
add(OfferRow.vue): nft image fetching and tag `offer` tag
hassnian Aug 29, 2024
e242f33
fix(OfferRow.vue): `amount` column
hassnian Aug 29, 2024
d76d319
fix(GalleryItemOffersTable.vue): offer table responsiveness
hassnian Aug 30, 2024
b1dc6e2
add(ProfileDetail.vue): `offers` tab active tab check
hassnian Aug 30, 2024
3a15608
fix(GalleryItemOffersTable.vue): expiration withouth prefix
hassnian Aug 30, 2024
d5e5e39
add(OfferOverviewModal.vue): loading notification
hassnian Aug 30, 2024
7076de1
add(OfferOverviewModal.vue): on cancel reset modal
hassnian Aug 31, 2024
bec90e2
add(OfferOverviewModal.vue): close modal on fail
hassnian Aug 31, 2024
d2abaf0
fix(Offers.vue): empty offer table when it shouldn't
hassnian Sep 2, 2024
fbd788f
ref(Offers.vue): unnecesary computed variables
hassnian Sep 2, 2024
97be792
fix(OwnerButton.vue): `loading` as optional prop
hassnian Sep 2, 2024
7e07c63
ref(useTransactionNotification.ts): Remove this redundant jump.
hassnian Sep 2, 2024
28767d5
ref(useOffers.ts): remove lazy loading
hassnian Sep 2, 2024
ef6862c
ref(ResponsiveTable.vue): skeleton loader with cols
hassnian Sep 2, 2024
4e2fe33
ref(Offers.vue): use `NeoButton` instate of `FilterButton`
hassnian Sep 2, 2024
6307880
Merge branch 'main' into issue-10845-0
hassnian Sep 3, 2024
2e8b85c
fix(ProfileDetail.vue): hide profile offer tab if not available
hassnian Sep 3, 2024
f07228a
fix(ProfileDetail.vue): `activeTab` filer invalid tabs
hassnian Sep 3, 2024
8d4ec4e
Merge branch 'issue-10845-0' of https://github.com/hassnian/nft-galle…
hassnian Sep 3, 2024
280de3d
fix(ProfileDetail.vue): mobile tabs unresponsive
hassnian Sep 3, 2024
82031bc
fix(OfferRow.vue): more precise price
hassnian Sep 3, 2024
5046d50
ref(GalleryItemActivityTable.vue): reuse `useFormatNumber` composable
hassnian Sep 3, 2024
d8ba5ca
ref(Offers.vue): show expired offers & add `time ago` in expiration date
hassnian Sep 3, 2024
6c345ff
ref: remove nested template literals.
hassnian Sep 3, 2024
fcac6ec
fix(Offers.vue): tab offers not syncing after action is performed
hassnian Sep 3, 2024
6c12091
ref(Offers.vue): cleaner where condiiton
hassnian Sep 4, 2024
a6104ed
Merge branch 'main' into issue-10845-0
vikiival Sep 9, 2024
eb9c440
fix(galleryitem.spec.ts): chart tab index
hassnian Sep 9, 2024
b5242f2
Merge branch 'issue-10845-0' of https://github.com/hassnian/nft-galle…
hassnian Sep 9, 2024
4ef7ba3
fix(profile.spec.ts): profile activity tab
hassnian Sep 9, 2024
00d13ba
Merge branch 'main' into issue-10845-0
hassnian Sep 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions components/gallery/GalleryItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ const mediaItemRef = ref<{
const galleryDescriptionRef = ref<{ isLewd: boolean } | null>(null)
const preferencesStore = usePreferencesStore()
const pageViewCount = usePageViews()
const fiatStore = useFiatStore()

const galleryItem = useGalleryItem()
const {
Expand Down Expand Up @@ -385,6 +386,8 @@ function toggleFallback() {
isFullscreen.value = isCurrentlyFullscreen
}
}

onBeforeMount(() => fiatStore.fetchFiatPrice())
</script>

<style lang="scss">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@
v-slot="props"
width="20%"
field="meta"
:label="`${$t(`tabs.tabActivity.price`)} (${chainSymbol})`"
:label="$t(`amount`)"
>
<p v-if="Number(props.row.meta)">
{{ formatPrice(props.row.meta)[0] }}
<span class="text-k-grey">
${{ formatPrice(props.row.meta)[1] }}</span>
{{ formatPrice(props.row.meta).amount }}
<span class="text-k-grey">{{ formatPrice(props.row.meta).price }}</span>
</p>
</NeoTableColumn>

Expand Down Expand Up @@ -135,10 +134,6 @@ import itemEvents from '@/queries/subsquid/general/itemEvents.graphql'

import Identity from '@/components/identity/IdentityIndex.vue'
import { formatToNow } from '@/utils/format/time'
import formatBalance, {
formatNumber,
withoutDigitSeparator,
} from '@/utils/format/balance'
import { parseDate } from '@/utils/datetime'

import type { Interaction } from '@/components/rmrk/service/scheme'
Expand All @@ -149,13 +144,8 @@ const dprops = defineProps<{
interactions: string[]
}>()

const { decimals, chainSymbol } = useChain()
const { urlPrefix, client } = usePrefix()
const tokenPrice = ref(0)

onMounted(async () => {
tokenPrice.value = await getApproximatePriceOf(chainSymbol.value)
})
const { format: formatPrice } = useFormatAmount()

const interaction = computed(() =>
dprops.interactions.map((key) => {
Expand Down Expand Up @@ -213,14 +203,6 @@ watchEffect(() => {
events.value = itemEvents.events
}
})

const formatPrice = (price) => {
const tokenAmount = formatBalance(price, decimals.value, false)
const flatPrice = `${formatNumber(
Number(withoutDigitSeparator(tokenAmount)) * tokenPrice.value,
)}`
return [formatNumber(tokenAmount), flatPrice]
}
</script>

<style lang="scss" scoped>
Expand Down
15 changes: 15 additions & 0 deletions components/gallery/GalleryItemTabsPanel/GalleryItemOffers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<div class="h-full flex flex-col">
<GalleryItemOffersTable
:nft-id="nftId"
/>
</div>
</template>

<script setup lang="ts">
import GalleryItemOffersTable from './GalleryItemOffersTable.vue'

defineProps<{
nftId: string
}>()
</script>
198 changes: 198 additions & 0 deletions components/gallery/GalleryItemTabsPanel/GalleryItemOffersTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<template>
<div
class="gallery-item-offers-table flex flex-col !py-6"
data-testid="gallery-item-offers-table"
>
<div
v-if="loading"
class="p-5"
>
<NeoSkeleton
animated
size="large"
:count="3"
/>
</div>
<NeoTable
v-else-if="offers.length"
:data="offers"
hoverable
class="py-5 max-md:!top-0"
>
<!-- price -->
<NeoTableColumn
v-slot="{ row }: {row: NFTOfferItem}"
width="20%"
field="price"
:label="$t('amount')"
>
<p
v-if="Number(row.price)"
class="flex gap-2"
>
<span> {{ format(row.price).amount }} </span>
<span class="text-k-grey"> {{ format(row.price).price }} </span>
</p>
</NeoTableColumn>

<!-- expiration -->
<NeoTableColumn
v-slot="{ row }: {row: NFTOfferItem}"
width="15%"
field="expiration"
:label="$t('expiration')"
>
<p class="capitalize">
{{ row.expirationDate ? formatToNow(row.expirationDate, false) : '--' }}
</p>
</NeoTableColumn>

<!-- from -->
<NeoTableColumn
v-slot="{ row }: {row: NFTOfferItem}"
width="20%"
field="caller"
:label="$t('tabs.tabActivity.from')"
>
<div class="flex items-center gap-2">
<ProfileAvatar
:size="24"
:address="row.caller"
/>

<nuxt-link
:to="`/${urlPrefix}/u/${row.caller}`"
class="text-k-blue hover:text-k-blue-hover"
>
<Identity
:address="row.caller"
/>
</nuxt-link>
</div>
</NeoTableColumn>

<!-- action -->
<NeoTableColumn
v-slot="{ row }"
width="10%"
>
<OfferOwnerButton
class="max-md:!w-full"
:offer="row as NFTOfferItem"
@click="selectOffer"
/>
</NeoTableColumn>
</NeoTable>
<div
v-else
class="p-5"
>
{{ $t('tabs.tabActivity.empty') }}
</div>
</div>

<OfferOverviewModal
v-model="isWithdrawOfferModalOpen"
:offer="selectedOffer!"
@close="closeOfferOverviewModal"
/>
</template>

<script setup lang="ts">
import {
NeoSkeleton,
NeoTable,
NeoTableColumn,
} from '@kodadot1/brick'
import type { UnwrapRef } from 'vue'
import { formatToNow } from '@/utils/format/time'
import Identity from '@/components/identity/IdentityIndex.vue'
import useSubscriptionGraphql from '@/composables/useSubscriptionGraphql'

const props = defineProps<{
nftId: string
}>()

const { urlPrefix } = usePrefix()
const { format } = useFormatAmount()

const isWithdrawOfferModalOpen = ref(false)
const loading = ref(false)
const offers = ref<UnwrapRef<ReturnType<typeof useOffers>['offers']>>([])
const selectedOffer = ref<NFTOfferItem>()
const stopWatch = ref(() => {})

useSubscriptionGraphql({
query: `
offers (
where: { status_eq: ACTIVE, desired: { id_eq: "${props.nftId}" } }
orderBy: blockNumber_DESC
) {
id
}`,
onChange: ({ data: { offers: newOffers } }) => {
stopWatch.value?.()
offers.value = []

const { offers: offersData, loading: offersLoading } = useOffers({
where: { id_in: newOffers.map(offer => offer.id) },
})

stopWatch.value = watchEffect(() => {
loading.value = offersLoading.value
offers.value = offersData.value
})
},
})

const selectOffer = (offer: NFTOfferItem) => {
selectedOffer.value = offer
isWithdrawOfferModalOpen.value = true
}

const closeOfferOverviewModal = () => {
isWithdrawOfferModalOpen.value = false
selectedOffer.value = undefined
}
</script>

<style lang="scss" scoped>
@import '@/assets/styles/abstracts/variables';

.gallery-item-offers-table {
overflow-y: auto;

@include desktop {
:deep(table tr > *:first-child) {
padding-left: 2rem;
}
:deep(table tbody > tr > td) {
vertical-align: middle;
height: 3.25rem;
&:last-child {
padding: 0;
}
}
}
}

@include touch {
.gallery-item-offers-table {
:deep(.o-table__td) {
@apply border-inherit;
padding: 0;
&:before {
font-weight: 400 !important;
}
}

:deep(.o-table__td:not(:nth-last-child(1)):not(:nth-last-child(2))) {
padding: 0 0 1rem 0;
}

.o-table__td:last-child button {
margin-top: 1.5rem
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,22 @@
/>
</NeoTabItem>

<!-- chart -->
<!-- offers -->
<NeoTabItem
v-if="offerVisible(urlPrefix)"
value="2"
:label="$t('offers')"
data-testid="offers-activity"
>
<GalleryItemOffers
v-if="nft?.id"
:nft-id="nft.id"
/>
</NeoTabItem>

<!-- chart -->
<NeoTabItem
value="3"
:label="$t('tabs.chart')"
class="p-5"
>
Expand All @@ -31,10 +44,11 @@

<script setup lang="ts">
import { NeoTabItem, NeoTabs } from '@kodadot1/brick'

import type { GalleryItem } from '../useGalleryItem'
import GalleryItemActivity from './GalleryItemActivity.vue'
import GalleryItemOffers from './GalleryItemOffers.vue'
import GalleryItemChart from './GalleryItemChart.vue'
import { offerVisible } from '@/utils/config/permission.config'

const props = withDefaults(
defineProps<{
Expand All @@ -46,6 +60,8 @@ const props = withDefaults(
},
)

const { urlPrefix } = usePrefix()

const nft = computed(() => props.galleryItem.nft.value)

const active = ref('1')
Expand Down
Loading
Loading