From 3716fd414fa7ce91cc666030b76de043b244741d Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:51:48 +0200 Subject: [PATCH 01/12] feat: Create offer modal design --- components/common/shoppingCart/utils.ts | 3 +- components/gallery/GalleryItem.vue | 6 +- .../GalleryItemAction/GalleryItemAction.vue | 3 + .../GalleryItemOffer.vue | 34 ++++--- components/gallery/useGalleryItem.ts | 19 +++- components/offer/MakeOffer.vue | 31 ++++-- components/offer/MakingOfferSingleItem.vue | 51 ++++++++-- components/offer/OfferExpirationSelector.vue | 81 ++++++++++++++++ components/offer/OfferPriceInput.vue | 97 +++++++++++++++++++ components/offer/types.ts | 2 + composables/transaction/transactionOffer.ts | 5 +- composables/transaction/types.ts | 2 +- composables/useNft.ts | 9 ++ locales/en.json | 13 ++- .../general/highestOfferByNftId.graphql | 14 +++ stores/makeOffer.ts | 2 +- 16 files changed, 337 insertions(+), 35 deletions(-) create mode 100644 components/offer/OfferExpirationSelector.vue create mode 100644 components/offer/OfferPriceInput.vue create mode 100644 queries/subsquid/general/highestOfferByNftId.graphql diff --git a/components/common/shoppingCart/utils.ts b/components/common/shoppingCart/utils.ts index 0a26a181fe..e6edf56fdf 100644 --- a/components/common/shoppingCart/utils.ts +++ b/components/common/shoppingCart/utils.ts @@ -91,13 +91,14 @@ export const nftToListingCartItem = ( } } -export const nftToOfferItem = (nft: NFT & TokenId): MakingOfferItem => { +export const nftToOfferItem = (nft: NFT & TokenId, highestOffer?: string): MakingOfferItem => { const { urlPrefix } = usePrefix() return { id: nft.id, name: nameWithIndex(nft.name, nft.sn), price: nft.price ?? '0', + highestOffer, urlPrefix: urlPrefix.value, collection: nft.collection, metadata: nft.metadata, diff --git a/components/gallery/GalleryItem.vue b/components/gallery/GalleryItem.vue index 9391a0f8f3..3d82946840 100644 --- a/components/gallery/GalleryItem.vue +++ b/components/gallery/GalleryItem.vue @@ -166,7 +166,10 @@ v-if="nft && isAssetHub" :nft="nft" /> - + nft.value?.collection) diff --git a/components/gallery/GalleryItemAction/GalleryItemAction.vue b/components/gallery/GalleryItemAction/GalleryItemAction.vue index b4ccd70561..fd5fef10d7 100644 --- a/components/gallery/GalleryItemAction/GalleryItemAction.vue +++ b/components/gallery/GalleryItemAction/GalleryItemAction.vue @@ -9,6 +9,7 @@ @@ -34,11 +35,13 @@ import GalleryItemOffer from './GalleryItemActionType/GalleryItemOffer.vue' import GalleryItemPriceRelist from './GalleryItemActionType/GalleryItemRelist.vue' import GalleryItemPriceTransfer from './GalleryItemActionType/GalleryItemTransfer.vue' import { listVisible, offerVisible } from '@/utils/config/permission.config' +import type { NFTOffer } from '@/composables/useNft' import type { NFT } from '@/components/rmrk/service/scheme' const props = defineProps<{ nft: NFT | undefined + highestOffer: NFTOffer }>() const { urlPrefix } = usePrefix() diff --git a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue index 7d36d0a382..431c03b8a3 100644 --- a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue +++ b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue @@ -1,14 +1,20 @@ diff --git a/components/offer/OfferPriceInput.vue b/components/offer/OfferPriceInput.vue new file mode 100644 index 0000000000..57551b634d --- /dev/null +++ b/components/offer/OfferPriceInput.vue @@ -0,0 +1,97 @@ + + + diff --git a/components/offer/types.ts b/components/offer/types.ts index 3f07015b3c..65da5c908e 100644 --- a/components/offer/types.ts +++ b/components/offer/types.ts @@ -7,7 +7,9 @@ import type { export type MakingOfferItem = { urlPrefix: string price?: string + highestOffer?: string offerPrice?: string + offerExpiration?: number id: string name: string currentOwner: string diff --git a/composables/transaction/transactionOffer.ts b/composables/transaction/transactionOffer.ts index 9ff8c1f215..89ac30ec79 100644 --- a/composables/transaction/transactionOffer.ts +++ b/composables/transaction/transactionOffer.ts @@ -19,7 +19,7 @@ async function execMakingOffer(item: ActionOffer, api, executeTransaction) { const { accountId } = useAuth() const nfts = Array.isArray(item.token) ? item.token : [item.token] const transactions = await Promise.all( - nfts.map(async ({ price, nftSn, collectionId }) => { + nfts.map(async ({ price, nftSn, collectionId, duration }) => { const offerId = getOfferId(item.urlPrefix as Prefix) const nextId = Number.parseInt(await generateId()) const create = api.tx.nfts.mint( @@ -31,7 +31,6 @@ async function execMakingOffer(item: ActionOffer, api, executeTransaction) { }, ) - const duration = 300 * 24 * 7 // Temporarily set to one week (12sec /block --> 300blocks/hr) const offer = api.tx.nfts.createSwap( offerId, nextId, @@ -41,7 +40,7 @@ async function execMakingOffer(item: ActionOffer, api, executeTransaction) { amount: Number(price) || 0, direction: 'Send', }, - duration, + 300 * 24 * duration, // 12sec /block --> 300blocks/hr ) return [create, offer] diff --git a/composables/transaction/types.ts b/composables/transaction/types.ts index 0a06bdfe27..ab310fbeaf 100644 --- a/composables/transaction/types.ts +++ b/composables/transaction/types.ts @@ -154,6 +154,7 @@ export type TokenToOffer = { price: string collectionId: string nftSn: string + duration: number } export type ActionList = { @@ -182,7 +183,6 @@ export type ActionOffer = { interaction: typeof ShoppingActions.MAKE_OFFER urlPrefix: string token: TokenToOffer | TokenToOffer[] - duration: number successMessage?: string | ((blockNumber: string) => string) errorMessage?: string } diff --git a/composables/useNft.ts b/composables/useNft.ts index 6ec3a0d0d5..4e0ceb56c0 100644 --- a/composables/useNft.ts +++ b/composables/useNft.ts @@ -64,6 +64,15 @@ export type TokenEntity = { } } +export type NFTOffer = { + id: string + price: string + expiration: number + status: string + currentOwner: string + royalty?: Royalty +} + export const isTokenEntity = ( entity: NFTWithMetadata | TokenEntity, ): entity is TokenEntity => diff --git a/locales/en.json b/locales/en.json index 05f6045a4e..b8c6047d5b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1631,7 +1631,14 @@ }, "offer": { "yourOfferAmount": "Your Offer Amount" , - "newOffer": "New Offer" + "newOffer": "New Offer", + "bestOffer": "Best Offer", + "collectionFloorPrice": "Collection Floor", + "expiration": "Offer Expiration", + "typeOffer": "Type An Offer", + "highestOffer": "Highest Offer", + "invalidPrice": "Your offer must greater than 0.0001", + "emptyInput": "Please enter your offer" }, "migrate": { "cta": "Migrate Now", @@ -1809,6 +1816,10 @@ "consume": { "confirm": "Teleport and Burn NFT", "title": "Burning NFT" + }, + "make_offer": { + "confirm": "Teleport and Create Offer", + "title": "Creating Offer" } } }, diff --git a/queries/subsquid/general/highestOfferByNftId.graphql b/queries/subsquid/general/highestOfferByNftId.graphql new file mode 100644 index 0000000000..43d238367f --- /dev/null +++ b/queries/subsquid/general/highestOfferByNftId.graphql @@ -0,0 +1,14 @@ +query highestOfferByNftId($id: String!) { + offers(where: {status_eq: ACTIVE, desired: {id_eq: $id}}, orderBy: price_DESC) { + expiration + status + price + id + caller + desired { + name + price + id + } + } +} \ No newline at end of file diff --git a/stores/makeOffer.ts b/stores/makeOffer.ts index cf9c542308..edcd55197d 100644 --- a/stores/makeOffer.ts +++ b/stores/makeOffer.ts @@ -26,7 +26,7 @@ export const useMakingOfferStore = defineStore('makingOffer', { return this.itemsInChain.length }, hasInvalidOfferPrices(): boolean { - return this.itemsInChain.some(item => Number(item.offerPrice || 0) <= 0) + return this.itemsInChain.some(item => Number(item.offerPrice || 0) <= 0.0001) }, }, From 960b6e1a74b138be273ef7cd1f194a1913f2163f Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Mon, 26 Aug 2024 22:57:12 +0200 Subject: [PATCH 02/12] fix: do after login --- .../GalleryItemActionType/GalleryItemOffer.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue index 431c03b8a3..8d7a03005e 100644 --- a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue +++ b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue @@ -10,7 +10,7 @@ variant="k-blue" size="large" class="w-[calc(10rem+55px)]" - @click="openOfferModal" + @click="onMakeOfferClick" /> @@ -35,6 +35,7 @@ const props = defineProps<{ const preferencesStore = usePreferencesStore() const makeOfferStore = useMakingOfferStore() +const { doAfterLogin } = useDoAfterlogin() const highestOfferPrice = computed(() => props.highestOffer?.price || '') @@ -46,6 +47,12 @@ const openOfferModal = () => { preferencesStore.setMakeOfferModalOpen(true) } +const onMakeOfferClick = () => { + doAfterLogin({ + onLoginSuccess: openOfferModal, + }) +} + useModalIsOpenTracker({ isOpen: computed(() => preferencesStore.makeOfferModalOpen), onChange: () => makeOfferStore.clear(), From ee886d0e51081bf18c805f37a3e2037dd3fab8d5 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:50:20 +0200 Subject: [PATCH 03/12] fix: disabled ahk offer --- components/gallery/useGalleryItem.ts | 2 +- components/offer/MakingOfferSingleItem.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index 8ea397e805..7dc478bdcc 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -57,7 +57,7 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { const { data: nftOfferData } = useGraphql({ queryName: 'highestOfferByNftId', - queryPrefix: queryPath[urlPrefix.value], + disabled: computed(() => urlPrefix.value !== 'ahp'), variables: { id, }, diff --git a/components/offer/MakingOfferSingleItem.vue b/components/offer/MakingOfferSingleItem.vue index 69079a9f88..e4595b257b 100644 --- a/components/offer/MakingOfferSingleItem.vue +++ b/components/offer/MakingOfferSingleItem.vue @@ -90,7 +90,7 @@ const offerExpirationStoreItem = computed({ const highestOfferPrice = computed(() => formatWithBlank(Number(item.value.highestOffer) || 0) || '--') const collectionFloorPrice = computed(() => - formatWithBlank(Number(item.value.collection.floorPrice[0]?.price || '0')) || '--', + formatWithBlank(Number(item.value.collection.floorPrice?.[0]?.price || '0')) || '--', ) watch( From 568a1947303cf027bd8edd794c4e94931c9b3742 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:24:40 +0200 Subject: [PATCH 04/12] fix: offer query --- components/gallery/useGalleryItem.ts | 2 +- composables/useNft.ts | 2 -- queries/subsquid/general/highestOfferByNftId.graphql | 8 +------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index 7dc478bdcc..239a0b7292 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -20,7 +20,7 @@ export interface GalleryItem { nftAnimationMimeType: Ref nftImage: Ref nftResources: Ref - nftHighestOffer: Ref + nftHighestOffer: Ref } export const useGalleryItem = (nftId?: string): GalleryItem => { diff --git a/composables/useNft.ts b/composables/useNft.ts index 4e0ceb56c0..70398782f2 100644 --- a/composables/useNft.ts +++ b/composables/useNft.ts @@ -69,8 +69,6 @@ export type NFTOffer = { price: string expiration: number status: string - currentOwner: string - royalty?: Royalty } export const isTokenEntity = ( diff --git a/queries/subsquid/general/highestOfferByNftId.graphql b/queries/subsquid/general/highestOfferByNftId.graphql index 43d238367f..20c47b8845 100644 --- a/queries/subsquid/general/highestOfferByNftId.graphql +++ b/queries/subsquid/general/highestOfferByNftId.graphql @@ -1,14 +1,8 @@ query highestOfferByNftId($id: String!) { - offers(where: {status_eq: ACTIVE, desired: {id_eq: $id}}, orderBy: price_DESC) { + offers(where: {status_eq: ACTIVE, desired: {id_eq: $id}}, orderBy: price_DESC, limit: 1) { expiration status price id - caller - desired { - name - price - id - } } } \ No newline at end of file From 2729ddd2e785b6f846257f978ce54d82add2b31d Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Tue, 27 Aug 2024 13:52:56 +0200 Subject: [PATCH 05/12] fix: offer modal --- components/offer/MakeOffer.vue | 3 ++- components/offer/OfferPriceInput.vue | 10 +++++----- locales/en.json | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/offer/MakeOffer.vue b/components/offer/MakeOffer.vue index f77f3952eb..80d9517031 100644 --- a/components/offer/MakeOffer.vue +++ b/components/offer/MakeOffer.vue @@ -32,6 +32,7 @@ @close="onClose" > { nftSn: item.sn, } as TokenToOffer)), successMessage: $i18n.t('transaction.price.offer') as string, - errorMessage: $i18n.t('transaction.price.error') as string, + errorMessage: $i18n.t('transaction.offerError') as string, } } diff --git a/components/offer/OfferPriceInput.vue b/components/offer/OfferPriceInput.vue index 57551b634d..ffb4eaffc3 100644 --- a/components/offer/OfferPriceInput.vue +++ b/components/offer/OfferPriceInput.vue @@ -8,7 +8,7 @@ v-model="model" type="number" step="0.01" - min="0.001" + min="0.0001" pattern="[0-9]+([\.,][0-9]+)?" class="indent-2.5 border-none outline-none w-20 bg-background-color text-text-color w-full" :placeholder="$t('offer.typeOffer')" @@ -74,24 +74,24 @@ const model = computed({ set: (value) => { const newTokenAmount = value === '' ? undefined - : (isSymbolMode.value ? value : (Number(value) / tokenPrice.value).toFixed(2)) + : (isSymbolMode.value ? value : (Number(value) / tokenPrice.value)) emit('update:modelValue', newTokenAmount) tokenAmount.value = value }, }) watch(urlPrefix, async () => { - tokenPrice.value = await getApproximatePriceOf(chainSymbol.value) + tokenPrice.value = Number((await getApproximatePriceOf(chainSymbol.value)).toFixed(2)) }, { immediate: true, }) watch(isSymbolMode, async (isSymbol) => { if (isSymbol) { - model.value = (Number(model.value) / tokenPrice.value).toFixed(2) + model.value = (Number(model.value) / tokenPrice.value) } else { - model.value = (Number(model.value) * tokenPrice.value).toFixed(2) + model.value = (Number(model.value) * tokenPrice.value) } }) diff --git a/locales/en.json b/locales/en.json index 2c68d8b39e..88360ca251 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1359,6 +1359,7 @@ "addressIncorrect": "Address Incorrect", "wrongAddressCannotRecoveredWarning": "Items sent to the wrong address cannot be recovered.", "selfTransfer": "You can't transfer to yourself", + "offerError": "Offer creation failed", "price": { "change": "Change Price", "new": "Your New Price", From 24b423005d7e5cff0ae4fb5d2982da55b482697b Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:49:10 +0200 Subject: [PATCH 06/12] fix: teleport modal disappear --- .../GalleryItemActionType/GalleryItemOffer.vue | 14 +++++++++----- components/offer/MakeOffer.vue | 9 +++++---- components/offer/OfferExpirationSelector.vue | 10 ++++------ components/offer/OfferPriceInput.vue | 11 ++++++----- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue index 8d7a03005e..5fbf0b26f0 100644 --- a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue +++ b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue @@ -25,17 +25,17 @@ import { usePreferencesStore } from '@/stores/preferences' import { useMakingOfferStore } from '@/stores/makeOffer' import MakeOffer from '@/components/offer/MakeOffer.vue' import GalleryItemPriceSection from '@/components/gallery/GalleryItemAction/GalleryItemActionSection.vue' +import type { NFTOffer } from '@/components/useNft' const props = defineProps<{ nft: NFT - highestOffer?: { - price: string - } + highestOffer?: NFTOffer }>() - const preferencesStore = usePreferencesStore() const makeOfferStore = useMakingOfferStore() const { doAfterLogin } = useDoAfterlogin() +const { isCurrentOwner } = useAuth() +const isOwner = computed(() => isCurrentOwner(props.nft?.currentOwner)) const highestOfferPrice = computed(() => props.highestOffer?.price || '') @@ -49,7 +49,11 @@ const openOfferModal = () => { const onMakeOfferClick = () => { doAfterLogin({ - onLoginSuccess: openOfferModal, + onLoginSuccess: () => { + if (!isOwner.value) { + openOfferModal() + } + }, }) } diff --git a/components/offer/MakeOffer.vue b/components/offer/MakeOffer.vue index 80d9517031..58b12757ee 100644 --- a/components/offer/MakeOffer.vue +++ b/components/offer/MakeOffer.vue @@ -32,7 +32,6 @@ @close="onClose" > -
+
@@ -223,8 +225,7 @@ useModalIsOpenTracker({ }, }) -const closeMakingOfferModal = () => - (preferencesStore.makeOfferModalOpen = false) +const closeMakingOfferModal = () => (preferencesStore.makeOfferModalOpen = false) onBeforeMount(closeMakingOfferModal) onUnmounted(closeMakingOfferModal) diff --git a/components/offer/OfferExpirationSelector.vue b/components/offer/OfferExpirationSelector.vue index 2326dff49f..a0f18ece9e 100644 --- a/components/offer/OfferExpirationSelector.vue +++ b/components/offer/OfferExpirationSelector.vue @@ -59,16 +59,14 @@ const props = defineProps<{ }>() const emit = defineEmits(['update:modelValue']) -const selected = computed({ - get: () => props.modelValue || 7, - set: (value) => { - emit('update:modelValue', value) - }, + +const selected = useVModel(props, 'modelValue', emit, { + defaultValue: 7, }) const formattedExpirationTime = computed(() => { const date = new Date() - date.setDate(date.getDate() + selected.value) + date.setDate(date.getDate() + selected.value!) return date.toLocaleString(undefined, { month: 'numeric', day: '2-digit', diff --git a/components/offer/OfferPriceInput.vue b/components/offer/OfferPriceInput.vue index ffb4eaffc3..150151fe60 100644 --- a/components/offer/OfferPriceInput.vue +++ b/components/offer/OfferPriceInput.vue @@ -55,10 +55,11 @@ const props = defineProps<{ const { chainSymbol } = useChain() const { urlPrefix } = usePrefix() const { balance } = useDeposit(urlPrefix) +const fiatStore = useFiatStore() const emit = defineEmits(['update:modelValue']) const isSymbolMode = ref(true) -const tokenPrice = ref(0) +const tokenPrice = computed(() => fiatStore.getCurrentTokenValue(chainSymbol.value as Token) as number) const switchSymbolMode = () => { isSymbolMode.value = !isSymbolMode.value } @@ -80,10 +81,10 @@ const model = computed({ }, }) -watch(urlPrefix, async () => { - tokenPrice.value = Number((await getApproximatePriceOf(chainSymbol.value)).toFixed(2)) -}, { - immediate: true, +onMounted(() => { + if (fiatStore.incompleteFiatValues) { + fiatStore.fetchFiatPrice() + } }) watch(isSymbolMode, async (isSymbol) => { From 029e9faf79bfe667d73c4cc968c5a3d30d82ab30 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:51:40 +0200 Subject: [PATCH 07/12] chore: remove blank --- components/offer/OfferExpirationSelector.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/components/offer/OfferExpirationSelector.vue b/components/offer/OfferExpirationSelector.vue index a0f18ece9e..e2af28733f 100644 --- a/components/offer/OfferExpirationSelector.vue +++ b/components/offer/OfferExpirationSelector.vue @@ -55,7 +55,6 @@ const selectedItem = computed(() => options.find(option => option.value === sele const props = defineProps<{ modelValue?: number - }>() const emit = defineEmits(['update:modelValue']) From 0c0c6a3a55b7f79486a55cb8d27a58df36cda323 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Thu, 29 Aug 2024 00:02:44 +0200 Subject: [PATCH 08/12] feat: creating offer signing design --- components/gallery/useGalleryItem.ts | 13 ++- components/offer/MakeOffer.vue | 81 +++++++++++-------- .../offer/SuccessfulMakingOfferBody.vue | 76 ----------------- components/profile/create/Modal.vue | 1 + composables/useMetaTransaction.ts | 3 +- composables/useSubscriptionGraphql.ts | 4 +- locales/en.json | 2 + utils/notification.ts | 6 +- 8 files changed, 69 insertions(+), 117 deletions(-) delete mode 100644 components/offer/SuccessfulMakingOfferBody.vue diff --git a/components/gallery/useGalleryItem.ts b/components/gallery/useGalleryItem.ts index 239a0b7292..d801a6079e 100644 --- a/components/gallery/useGalleryItem.ts +++ b/components/gallery/useGalleryItem.ts @@ -34,6 +34,7 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { const nftMetadata = ref() const nftResources = ref() const nftHighestOffer = ref() + const isOfferIndexerDisabled = computed(() => urlPrefix.value !== 'ahp') const { params } = useRoute() const id = nftId || params.id @@ -55,9 +56,9 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { }, }) - const { data: nftOfferData } = useGraphql({ + const { data: nftOfferData, refetch: refetchHighestOffer } = useGraphql({ queryName: 'highestOfferByNftId', - disabled: computed(() => urlPrefix.value !== 'ahp'), + disabled: isOfferIndexerDisabled, variables: { id, }, @@ -76,6 +77,14 @@ export const useGalleryItem = (nftId?: string): GalleryItem => { onChange: refetch, }) + useSubscriptionGraphql({ + query: `offers(where: {status_eq: ACTIVE, desired: {id_eq: "${id}"}}, orderBy: price_DESC, limit: 1) { + id + }`, + disabled: isOfferIndexerDisabled, + onChange: refetchHighestOffer, + }) + watch(data as unknown as NFTData, async (newData) => { const nftEntity = newData?.nftEntity if (!nftEntity) { diff --git a/components/offer/MakeOffer.vue b/components/offer/MakeOffer.vue index 58b12757ee..fb98411381 100644 --- a/components/offer/MakeOffer.vue +++ b/components/offer/MakeOffer.vue @@ -9,23 +9,6 @@ @try-again="submitOffer" /> - - - - - - void }>({ + state: 'loading', +}) +const { itemsInChain, hasInvalidOfferPrices, count } = storeToRefs(offerStore) const { decimals } = useChain() const { $i18n } = useNuxtApp() const items = ref([]) -const isSuccessModalOpen = computed( - () => Boolean(items.value.length) && isTransactionSuccessful.value, -) - const getAction = (items: MakingOfferItem[]): Actions => { return { interaction: ShoppingActions.MAKE_OFFER, @@ -126,8 +105,6 @@ const getAction = (items: MakingOfferItem[]): Actions => { duration: item.offerExpiration || 7, nftSn: item.sn, } as TokenToOffer)), - successMessage: $i18n.t('transaction.price.offer') as string, - errorMessage: $i18n.t('transaction.offerError') as string, } } @@ -179,7 +156,7 @@ const confirmListingLabel = computed(() => { }) const submitOffer = () => { - return transaction(getAction(itemsInChain.value)) + return transaction(getAction(items.value)) } async function confirm({ autoteleport }: AutoTeleportActionButtonConfirmEvent) { @@ -204,9 +181,28 @@ async function confirm({ autoteleport }: AutoTeleportActionButtonConfirmEvent) { const onClose = () => { closeMakingOfferModal() } +const closeMakingOfferModal = () => (preferencesStore.makeOfferModalOpen = false) -const handleSuccessModalClose = () => { - items.value = [] +const showOfferCreationNotification = (session) => { + const isSessionState = (state: LoadingNotificationState) => + session.value?.state === state + + session.value.closeNotification = loadingMessage({ + title: ref($i18n.t('offer.offerCreation')), + message: ref(undefined), + state: computed(() => session?.value.state as LoadingNotificationState), + action: computed(() => { + if (isSessionState('succeeded')) { + return { + label: $i18n.t('offer.manageOffers'), + icon: 'arrow-up-right', + url: `/${urlPrefix.value}/u/${accountId.value}`, + } + } + + return undefined + }), + }) } watch( @@ -225,7 +221,22 @@ useModalIsOpenTracker({ }, }) -const closeMakingOfferModal = () => (preferencesStore.makeOfferModalOpen = false) +watch(isError, (error) => { + if (error) { + offerSession.value.closeNotification?.() + } +}) + +watch(status, (status) => { + switch (status) { + case TransactionStatus.Casting: + showOfferCreationNotification(offerSession) + break + case TransactionStatus.Finalized: + offerSession.value.state = 'succeeded' + break + } +}) onBeforeMount(closeMakingOfferModal) onUnmounted(closeMakingOfferModal) diff --git a/components/offer/SuccessfulMakingOfferBody.vue b/components/offer/SuccessfulMakingOfferBody.vue deleted file mode 100644 index fac0c2ff7e..0000000000 --- a/components/offer/SuccessfulMakingOfferBody.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - diff --git a/components/profile/create/Modal.vue b/components/profile/create/Modal.vue index f883269fbd..01205eedb3 100644 --- a/components/profile/create/Modal.vue +++ b/components/profile/create/Modal.vue @@ -39,6 +39,7 @@ import type { ProfileFormData } from './stages/index' import { Form, Introduction, Select } from './stages/index' import { deleteProfile } from '@/services/profile' import { appClient, createChannel } from '@/services/farcaster' +import type { NotificationAction } from '@/utils/notification' type SessionState = { state: LoadingNotificationState diff --git a/composables/useMetaTransaction.ts b/composables/useMetaTransaction.ts index ddc6f5022d..256a84ec0f 100644 --- a/composables/useMetaTransaction.ts +++ b/composables/useMetaTransaction.ts @@ -107,7 +107,8 @@ function useMetaTransaction() { const onCatchError = (e) => { if (e instanceof Error) { - const isCancelled = e.message === 'Cancelled' + const errorMessage = e.message?.toLowerCase() || '' + const isCancelled = errorMessage.includes('cancelled') || errorMessage.includes('rejected') if (isCancelled) { warningMessage($i18n.t('general.tx.cancelled'), { reportable: false }) diff --git a/composables/useSubscriptionGraphql.ts b/composables/useSubscriptionGraphql.ts index 9fee33e06b..cc2ff9ba3a 100644 --- a/composables/useSubscriptionGraphql.ts +++ b/composables/useSubscriptionGraphql.ts @@ -7,19 +7,21 @@ export default function ({ onChange, onError, pollingInterval = 6000, + disabled, }: { clientName?: string query: string onChange: (data) => void onError?: (error) => void pollingInterval?: number + disabled?: ComputedRef }) { const { client: prefixClient } = usePrefix() const { $consola } = useNuxtApp() const client = clientName || prefixClient.value const httpUrl = apolloClientConfig[client]?.httpEndpoint - if (!httpUrl) { + if (disabled?.value || !httpUrl) { return () => {} } diff --git a/locales/en.json b/locales/en.json index 88360ca251..e18a7f7708 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1635,6 +1635,8 @@ "yourOfferAmount": "Your Offer Amount" , "newOffer": "New Offer", "bestOffer": "Best Offer", + "manageOffers": "Manage Offers", + "offerCreation": "Offer Creation", "collectionFloorPrice": "Collection Floor", "expiration": "Offer Expiration", "typeOffer": "Type An Offer", diff --git a/utils/notification.ts b/utils/notification.ts index 9b1d2da5cf..92b27199c1 100644 --- a/utils/notification.ts +++ b/utils/notification.ts @@ -49,7 +49,7 @@ export const showNotification = ({ action?: MaybeRef holdTimer?: Ref icon?: Ref -}): void => { +}): () => void => { if (params === notificationTypes.danger) { consola.error('[Notification Error]', message) } @@ -71,6 +71,8 @@ export const showNotification = ({ } Notif.open(componentParams) + + return Notif.closeAll } export const showLargeNotification = ({ @@ -201,7 +203,7 @@ export const loadingMessage = ({ const isLoadingState = computed(() => state.value === 'loading') - showNotification({ + return showNotification({ title, message: stateMessage, variant: computed(() => NotificationStateToVariantMap[state.value]), From f54fbeeb960209241474c6c9af10647f911f8948 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Thu, 29 Aug 2024 00:08:49 +0200 Subject: [PATCH 09/12] fix: modal hide time --- components/offer/MakeOffer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/offer/MakeOffer.vue b/components/offer/MakeOffer.vue index fb98411381..7fc5ead0ff 100644 --- a/components/offer/MakeOffer.vue +++ b/components/offer/MakeOffer.vue @@ -229,7 +229,7 @@ watch(isError, (error) => { watch(status, (status) => { switch (status) { - case TransactionStatus.Casting: + case TransactionStatus.Block: showOfferCreationNotification(offerSession) break case TransactionStatus.Finalized: From 06486687a9e5c9a0df623f58d7da089fd7135f77 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:42:40 +0200 Subject: [PATCH 10/12] fix: hide offer button for owner --- components/gallery/GalleryItemAction/GalleryItemAction.vue | 3 ++- .../GalleryItemActionType/GalleryItemOffer.vue | 2 ++ components/offer/MakeOffer.vue | 1 - composables/transaction/transactionOffer.ts | 4 +++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/gallery/GalleryItemAction/GalleryItemAction.vue b/components/gallery/GalleryItemAction/GalleryItemAction.vue index fd5fef10d7..a06c913b67 100644 --- a/components/gallery/GalleryItemAction/GalleryItemAction.vue +++ b/components/gallery/GalleryItemAction/GalleryItemAction.vue @@ -7,7 +7,8 @@ /> From e5dec25c63b698b46bf3cd2f3e5ceff04fd1b4c9 Mon Sep 17 00:00:00 2001 From: Jarsen <31397967+Jarsen136@users.noreply.github.com> Date: Sat, 7 Sep 2024 11:27:27 +0200 Subject: [PATCH 12/12] fix: type import --- .../GalleryItemActionType/GalleryItemOffer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue index 3ad8fe74b1..c8e654c639 100644 --- a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue +++ b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue @@ -26,7 +26,7 @@ import { usePreferencesStore } from '@/stores/preferences' import { useMakingOfferStore } from '@/stores/makeOffer' import MakeOffer from '@/components/offer/MakeOffer.vue' import GalleryItemPriceSection from '@/components/gallery/GalleryItemAction/GalleryItemActionSection.vue' -import type { NFTOffer } from '@/components/useNft' +import type { NFTOffer } from '@/composables/useNft' const props = defineProps<{ nft: NFT