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: mass listing #7076

Merged
merged 52 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
0c66f2c
feat: listing cart
stephenjason89 Aug 31, 2023
562d444
refactor: listing cart store
stephenjason89 Aug 31, 2023
3fc8993
fix: state was not passed in listing cart store getItems
stephenjason89 Aug 31, 2023
ca7d51c
feat: added border when in listing cart
stephenjason89 Aug 31, 2023
9d904d9
feat: added different list labels depending on price
stephenjason89 Aug 31, 2023
9778b82
feat: listingCartMini
stephenjason89 Aug 31, 2023
7f5bb94
feat: listingCartModal
stephenjason89 Sep 1, 2023
fe103bc
feat(listingCartModal): finalized ui
stephenjason89 Sep 1, 2023
8456e44
style: changed sizes to fit figma better
stephenjason89 Sep 1, 2023
07b81cc
feat: added trashcan in listingCartModal
stephenjason89 Sep 1, 2023
166d903
feat: returned and used floor from nftListWithSearch instead of using…
stephenjason89 Sep 1, 2023
6a3a408
feat: can now set fixedPrice
stephenjason89 Sep 1, 2023
c7f1ab3
feat: set all floor price
stephenjason89 Sep 1, 2023
7881a8d
feat: recording on chain and code cleanup
stephenjason89 Sep 1, 2023
b2e6d01
feat: added loading indicator while saving on chain
stephenjason89 Sep 1, 2023
3569225
feat: add all not listed to cart
stephenjason89 Sep 1, 2023
8e1a849
fix: only first nft get's updated
stephenjason89 Sep 1, 2023
e55bfea
fix: floor value incorrectly being returned by graphql, use useCollec…
stephenjason89 Sep 2, 2023
be73046
feat: multichain segregation
stephenjason89 Sep 2, 2023
32fbf99
refactor: transaction cleanup
stephenjason89 Sep 2, 2023
f9a4f5f
fix: token object to array initializer
stephenjason89 Sep 2, 2023
4136875
refactor: import alias from ~ to @
stephenjason89 Sep 2, 2023
03fec91
Update components/common/listingCart/ListingCartItem.vue
stephenjason89 Sep 2, 2023
b43c1c0
refactor: minor code cleanup
stephenjason89 Sep 3, 2023
85070bc
feat: close listing cart modal when empty
stephenjason89 Sep 3, 2023
d6b3a4f
revert: h-full not being applied properly
stephenjason89 Sep 3, 2023
9e5fbba
fix: potential earnings not calculating correctly
stephenjason89 Sep 3, 2023
875185b
style: listing cart price input active state
stephenjason89 Sep 3, 2023
fe0b712
fix: list price and meta not storing on listingCartStore
stephenjason89 Sep 3, 2023
6bd6fa3
feat: recursive list price floor adjustment
stephenjason89 Sep 3, 2023
cbfe25c
refactor: added more translations
stephenjason89 Sep 3, 2023
aee55f6
fix: round to 2 decimals also some minor bug fix
stephenjason89 Sep 3, 2023
6d44d99
refactor: used the existing composable to determine chainSymbol & for…
stephenjason89 Sep 3, 2023
05764c1
refactor: removed unnecessary formatting in set fixed price
stephenjason89 Sep 3, 2023
9614716
fix: removed @polkadot/util formatBalance as it was causing NaN on so…
stephenjason89 Sep 3, 2023
08563cb
fix: set a 4 decimal cap on setFloorPrice via percentage
stephenjason89 Sep 3, 2023
b2a7990
Merge branch 'main' into feat/mass-listing
stephenjason89 Sep 3, 2023
9bea8e0
feat: prevent non-numeric on listing cart price input
stephenjason89 Sep 3, 2023
533a18c
fix: include decimals in listing cart price input
stephenjason89 Sep 3, 2023
b95a807
refactor(cleanup): remove console.log
stephenjason89 Sep 3, 2023
db23f29
refactor: remove unused import
stephenjason89 Sep 4, 2023
e5926f3
style: removed unused styles & opted to use bulma styles
stephenjason89 Sep 4, 2023
e4ebc6e
refactor: storing of unlisted owned
stephenjason89 Sep 4, 2023
311e06d
refactor: stylistic changes
stephenjason89 Sep 4, 2023
6f9449e
refactor: minor code style changes
stephenjason89 Sep 5, 2023
054cc8b
feat: added missing translations
stephenjason89 Sep 5, 2023
f34cbe2
feat: added last missing translations
stephenjason89 Sep 5, 2023
db51eca
refactor: converted last for of loop to es6 forEach
stephenjason89 Sep 5, 2023
cdf091a
fix: typo
stephenjason89 Sep 5, 2023
a680522
refactor: for better code readability
stephenjason89 Sep 5, 2023
1f508e7
revert: shoppingCart/utils totalPriceUsd
stephenjason89 Sep 5, 2023
edd67df
type: asserted the type of token instead of doing a nullish coalescing
stephenjason89 Sep 5, 2023
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
89 changes: 89 additions & 0 deletions components/common/listingCart/ListingCartItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<template>
<div class="py-2 px-6">
<div class="py-2 border-top border-k-shade" />
<div class="is-flex is-justify-content-space-between">
<div class="is-flex">
<div>
<BasicImage
:src="avatar"
:alt="nft?.name"
class="border image is-48x48" />
</div>

<div
class="is-flex is-flex-direction-column is-justify-content-space-between ml-4 limit-width">
<div
class="has-text-weight-bold has-text-color line-height-1 no-wrap is-clipped is-ellipsis">
{{ nft.name }}
</div>
<div class="line-height-1 no-wrap is-clipped is-ellipsis">
{{ nft.collection?.name || nft.collection.id }}
</div>
</div>
</div>

<NeoButton
class="has-text-grey pt-4"
variant="text"
no-shadow
icon="trash"
icon-pack="far"
@click.native="listingCartStore.removeItem(nft.id)" />
</div>
<div class="is-flex is-justify-content-space-between pt-2">
<div class="is-flex is-flex-direction-column">
<div class="has-text-grey is-size-7">{{ $t('activity.floor') }}</div>
<span>{{ floor }}</span>
</div>

<div class="is-flex is-align-items-end pt-2">
<ListingCartPriceInput
v-model="listingCartStore.getItem(nft.id).listPrice" />
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { parseNftAvatar } from '@/utils/nft'
import BasicImage from '@/components/shared/view/BasicImage.vue'
import ListingCartPriceInput from '@/components/common/listingCart/ListingCartPriceInput.vue'
import { NeoButton } from '@kodadot1/brick'
import { ListCartItem, useListingCartStore } from '@/stores/listingCart'
import { formatBalance } from '@polkadot/util'
const { decimals, chainSymbol } = useChain()

const listingCartStore = useListingCartStore()

const avatar = ref<string>()

const props = defineProps<{
nft: ListCartItem
}>()

const getAvatar = async () => {
if (props.nft) {
avatar.value = await parseNftAvatar(props.nft)
}
}

const floor = computed(() =>
formatBalance(props.nft.collection.floor, {
decimals: decimals.value,
withUnit: chainSymbol.value,
})
)

onMounted(() => {
getAvatar()
})
</script>
<style scoped lang="scss">
.limit-width {
max-width: 170px;
}

.line-height-1 {
line-height: 1;
}
</style>
51 changes: 51 additions & 0 deletions components/common/listingCart/ListingCartMini.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<transition v-if="listingCartStore.count" name="slide">
<div class="listing-container">
<div class="is-inline-flex is-align-items-center">
<div class="k-shadow theme-background-color border py-2">
<div class="is-inline-flex is-align-items-center mx-5">
<div>
<b>{{ listingCartStore.count }}</b> {{ $t('items') }}
</div>
<div class="mx-4" />
<NeoButton
:disabled="!listingCartStore.count"
variant="text"
no-shadow
@click.native="listingCartStore.clear">
{{ $t('sort.clearAll') }}
</NeoButton>
<div class="mx-4" />
<NeoButton
variant="text"
no-shadow
@click.native="listingCartStore.addAllToCart">
{{ $t('listingCart.selectAll') }}
</NeoButton>
</div>
</div>
<NeoButton
class="h-full"
:variant="'k-accent'"
@click.native="preferencesStore.listingCartModalOpen = true">
{{ $t('listingCart.listItem') }}
</NeoButton>
</div>
</div>
</transition>
</template>
<script setup lang="ts">
import { NeoButton } from '@kodadot1/brick'
import { useListingCartStore } from '@/stores/listingCart'
import { usePreferencesStore } from '@/stores/preferences'
const listingCartStore = useListingCartStore()
const preferencesStore = usePreferencesStore()
</script>
<style scoped lang="scss">
.listing-container {
position: fixed;
right: 80px;
bottom: 50px;
z-index: 998;
}
</style>
210 changes: 210 additions & 0 deletions components/common/listingCart/ListingCartModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
<template>
<div>
<Loader v-model="isLoading" :status="status" />
<NeoModal
v-model="preferencesStore.listingCartModalOpen"
scroll="clip"
@close="preferencesStore.listingCartModalOpen = false">
<div class="modal-width">
<header
class="py-5 px-6 is-flex is-justify-content-space-between border-bottom">
<span class="modal-card-title is-size-6 has-text-weight-bold">
List {{ listingCartStore.count }} {{ $t('items') }}
</span>

<NeoButton
variant="text"
no-shadow
icon="close"
@click.native="preferencesStore.listingCartModalOpen = false" />
</header>
<div class="px-6 pt-4">
<div
class="rounded border border-k-shade is-flex is-justify-content-start is-flex-grow-1 pl-3">
<IdentityItem
v-if="isLogIn"
:label="$t('confirmPurchase.connectedWith')"
hide-identity-popover
disable-identity-link
:prefix="urlPrefix"
:account="accountId"
class="identity-name-font-weight-regular"
data-cy="item-creator" />
</div>
<div class="pt-4 has-text-weight-bold">Set All To</div>
<div class="pt-4">
{{ $t('listingCart.collectionFloorPrice') }}
<span v-if="floorPricePercentAdjustment !== 1" class="has-text-grey"
>{{ (floorPricePercentAdjustment * 100 - 100).toFixed(0) }}%</span
>
</div>
<div class="py-2 is-flex is-justify-content-start is-flex-grow-1">
<NeoButton
class="mr-2"
label="-5%"
rounded
no-shadow
@click.native="
floorPricePercentAdjustment > 0.05 &&
(floorPricePercentAdjustment -= 0.05)
listingCartStore.setFloorPrice(floorPricePercentAdjustment)
" />
<NeoButton
class="mr-2"
:label="$t('statsOverview.floorPrice')"
rounded
no-shadow
@click.native="
floorPricePercentAdjustment = 1
listingCartStore.setFloorPrice(floorPricePercentAdjustment)
" />
<NeoButton
label="+5%"
rounded
no-shadow
@click.native="
floorPricePercentAdjustment += 0.05
listingCartStore.setFloorPrice(floorPricePercentAdjustment)
" />
</div>
<div class="pt-3 has-text-grey">-Or-</div>
<div class="pt-3">{{ $t('listingCart.fixedPrice') }}</div>
<ListingCartPriceInput
v-model="fixedPrice"
check
class="pt-2"
@confirm="setFixedPrice" />
</div>
<div class="py-2">
<ListingCartItem
v-for="nft in listingCartStore.itemsInChain"
:key="nft.id"
:nft="nft" />
</div>
<div
class="is-flex border-top is-justify-content-space-between py-4 px-6">
{{ $t('listingCart.potentialEarnings') }}
<div class="is-flex">
<span class="ml-2 has-text-grey"
>{{ totalNFTsPrice.toFixed(4) }} {{ chainSymbol }}</span
>
<span class="has-text-weight-bold ml-2"> ${{ priceUSD }} </span>
</div>
</div>

<div class="is-flex is-justify-content-space-between pb-5 px-6">
<NeoButton
:disabled="Boolean(listingCartStore.incompleteListPrices)"
:label="confirmListingLabel"
variant="k-accent"
no-shadow
class="is-flex is-flex-grow-1 py-5"
@click.native="confirm" />
</div>
</div>
</NeoModal>
</div>
</template>
<script setup lang="ts">
import { Interaction } from '@kodadot1/minimark/v1'
import ListingCartPriceInput from '@/components/common/listingCart/ListingCartPriceInput.vue'
import { prefixToToken } from '@/components/common/shoppingCart/utils'
import IdentityItem from '@/components/identity/IdentityItem.vue'
import { NeoButton, NeoModal } from '@kodadot1/brick'
import { usePreferencesStore } from '@/stores/preferences'
import { TokenToList } from '@/composables/transaction/types'
import { useListingCartStore } from '@/stores/listingCart'
import { calculateBalance } from '@/utils/format/balance'
import { warningMessage } from '@/utils/notification'
import { useFiatStore } from '@/stores/fiat'
import { calculateExactUsdFromToken } from '@/utils/calculation'
const { isLogIn, accountId } = useAuth()
const { urlPrefix } = usePrefix()
const preferencesStore = usePreferencesStore()
const listingCartStore = useListingCartStore()
const { transaction, isLoading, status } = useTransaction()
const { $i18n } = useNuxtApp()

const { chainSymbol } = useChain()
const fixedPrice = ref<number | null>(null)
const floorPricePercentAdjustment = ref(1)
function setFixedPrice() {
listingCartStore.setFixedPrice(fixedPrice.value)
fixedPrice.value = null
}
const fiatStore = useFiatStore()
const priceUSD = computed(() =>
calculateExactUsdFromToken(
totalNFTsPrice.value,
Number(fiatStore.getCurrentTokenValue(prefixToToken[urlPrefix.value]))
)
)

const totalNFTsPrice = computed(() =>
listingCartStore.itemsInChain.reduce((acc, nft) => {
return Number((acc + Number(nft.listPrice || 0)).toFixed(4))
}, 0)
)
stephenjason89 marked this conversation as resolved.
Show resolved Hide resolved

const confirmListingLabel = computed(() => {
switch (listingCartStore.incompleteListPrices) {
case 0:
return $i18n.t('listingCart.complete')
case 1:
return $i18n.t('listingCart.missing1')
default:
return `${listingCartStore.incompleteListPrices} ${$i18n.t(
'listingCart.missingMultiple'
)}`
}
})
async function confirm() {
const token = listingCartStore.itemsInChain
.filter((item) => item.listPrice)
stephenjason89 marked this conversation as resolved.
Show resolved Hide resolved
.map((item) => ({
price: String(calculateBalance(item.listPrice ?? 0)),
stephenjason89 marked this conversation as resolved.
Show resolved Hide resolved
nftId: item.id,
})) as TokenToList[]

try {
await transaction({
interaction: Interaction.LIST,
urlPrefix: urlPrefix.value,
token,
successMessage: $i18n.t('transaction.price.success') as string,
errorMessage: $i18n.t('transaction.price.error') as string,
})
listingCartStore.clear()
preferencesStore.listingCartModalOpen = false
} catch (error) {
warningMessage(error)
}
}

watch(
() => listingCartStore.count,
() => {
if (listingCartStore.count === 0) {
preferencesStore.listingCartModalOpen = false
}
}
)
</script>
<style lang="scss" scoped>
@import '@/styles/abstracts/variables';

.rounded {
border-radius: 10rem;
}

.modal-width {
width: 25rem;
max-width: 30rem;
}

:deep .identity-name-font-weight-regular {
.identity-name {
font-weight: unset !important;
}
}
</style>
Loading