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 cec1e90bac..92677d1db2 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..a1b4105a4a 100644 --- a/components/gallery/GalleryItemAction/GalleryItemAction.vue +++ b/components/gallery/GalleryItemAction/GalleryItemAction.vue @@ -7,8 +7,10 @@ /> @@ -34,11 +36,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 | undefined }>() const { urlPrefix } = usePrefix() diff --git a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue index 7d36d0a382..c8e654c639 100644 --- a/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue +++ b/components/gallery/GalleryItemAction/GalleryItemActionType/GalleryItemOffer.vue @@ -1,14 +1,21 @@ diff --git a/components/offer/OfferPriceInput.vue b/components/offer/OfferPriceInput.vue new file mode 100644 index 0000000000..088cd98cba --- /dev/null +++ b/components/offer/OfferPriceInput.vue @@ -0,0 +1,98 @@ + + + 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/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/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/transaction/transactionOffer.ts b/composables/transaction/transactionOffer.ts index 88817a846b..ae896b360a 100644 --- a/composables/transaction/transactionOffer.ts +++ b/composables/transaction/transactionOffer.ts @@ -15,11 +15,13 @@ export const getOfferCollectionId = (prefix: Prefix) => { const OFFER_MINT_PRICE = '500000000' +const BLOCKS_PER_DAY = 300 * 24 // 12sec /block --> 300blocks/hr + 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 = getOfferCollectionId(item.urlPrefix as Prefix) const nextId = Number.parseInt(await generateId()) const create = api.tx.nfts.mint( @@ -31,7 +33,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 +42,7 @@ async function execMakingOffer(item: ActionOffer, api, executeTransaction) { amount: Number(price) || 0, direction: 'Send', }, - duration, + BLOCKS_PER_DAY * duration, ) return [create, offer] diff --git a/composables/transaction/types.ts b/composables/transaction/types.ts index 90cda2848d..2039f0a03c 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/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/useNft.ts b/composables/useNft.ts index 6ec3a0d0d5..70398782f2 100644 --- a/composables/useNft.ts +++ b/composables/useNft.ts @@ -64,6 +64,13 @@ export type TokenEntity = { } } +export type NFTOffer = { + id: string + price: string + expiration: number + status: string +} + export const isTokenEntity = ( entity: NFTWithMetadata | TokenEntity, ): entity is TokenEntity => diff --git a/composables/useSubscriptionGraphql.ts b/composables/useSubscriptionGraphql.ts index 522d73e032..116270f392 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 db59d42b89..c4b6f77ab2 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1363,6 +1363,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", @@ -1636,12 +1637,20 @@ }, "offer": { "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", + "highestOffer": "Highest Offer", + "invalidPrice": "Your offer must greater than 0.0001", + "emptyInput": "Please enter your offer", "yourOffer": "Your Offer", "cancelOffer": "Cancel Offer", "floorDifference": "Floor Difference", - "newOffer": "New Offer", "incomingOffer": "Incoming Offer", - "manageOffers": "Manage Offer", "offerWithdrawl": "Offer Cancelation", "offerAccept": "Accepting Offer" }, @@ -1821,6 +1830,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..20c47b8845 --- /dev/null +++ b/queries/subsquid/general/highestOfferByNftId.graphql @@ -0,0 +1,8 @@ +query highestOfferByNftId($id: String!) { + offers(where: {status_eq: ACTIVE, desired: {id_eq: $id}}, orderBy: price_DESC, limit: 1) { + expiration + status + 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) }, },