diff --git a/src/assets/images/nft/books/collector-message-item-background-lg.svg b/src/assets/images/nft/books/collector-message-item-background-lg.svg new file mode 100644 index 000000000..bf670be71 --- /dev/null +++ b/src/assets/images/nft/books/collector-message-item-background-lg.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/nft/books/collector-message-item-background.svg b/src/assets/images/nft/books/collector-message-item-background.svg new file mode 100644 index 000000000..86586ec7f --- /dev/null +++ b/src/assets/images/nft/books/collector-message-item-background.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/CollapsibleCard.vue b/src/components/CollapsibleCard.vue index d6cb36768..b377fcb05 100644 --- a/src/components/CollapsibleCard.vue +++ b/src/components/CollapsibleCard.vue @@ -83,10 +83,14 @@ export default { type: Boolean, default: true, }, + defaultOpen: { + type: Boolean, + default: true, + }, }, data() { return { - isOpen: true, + isOpen: this.defaultOpen, }; }, computed: { diff --git a/src/components/NFTMessage/Identity.vue b/src/components/NFTMessage/Identity.vue index b3a16ed22..7ffa19214 100644 --- a/src/components/NFTMessage/Identity.vue +++ b/src/components/NFTMessage/Identity.vue @@ -36,9 +36,12 @@ > {{ userLabel }} - {{ - userDisplayNameFull | ellipsis - }} + {{ userDisplayNameFull | ellipsis }} diff --git a/src/components/NFTPage/ChainDataSection/index.vue b/src/components/NFTPage/ChainDataSection/index.vue index c61b80bb4..47c3585ca 100644 --- a/src/components/NFTPage/ChainDataSection/index.vue +++ b/src/components/NFTPage/ChainDataSection/index.vue @@ -2,6 +2,7 @@ diff --git a/src/components/NFTPage/CollectorListDialog.vue b/src/components/NFTPage/CollectorListDialog.vue new file mode 100644 index 000000000..29d99319e --- /dev/null +++ b/src/components/NFTPage/CollectorListDialog.vue @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/NFTPage/CollectorMessageList/Identity.vue b/src/components/NFTPage/CollectorMessageList/Identity.vue new file mode 100644 index 000000000..18cca1434 --- /dev/null +++ b/src/components/NFTPage/CollectorMessageList/Identity.vue @@ -0,0 +1,103 @@ + + + + + + {{ formattedUserDisplayNameFull }} + + + + + + diff --git a/src/components/NFTPage/CollectorMessageList/Item.vue b/src/components/NFTPage/CollectorMessageList/Item.vue new file mode 100644 index 000000000..e91335a07 --- /dev/null +++ b/src/components/NFTPage/CollectorMessageList/Item.vue @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/NFTPage/CollectorMessageList/index.vue b/src/components/NFTPage/CollectorMessageList/index.vue new file mode 100644 index 000000000..42a9e57e6 --- /dev/null +++ b/src/components/NFTPage/CollectorMessageList/index.vue @@ -0,0 +1,77 @@ + + + + + + + + diff --git a/src/components/ScrollingList.vue b/src/components/ScrollingList.vue new file mode 100644 index 000000000..175e538d4 --- /dev/null +++ b/src/components/ScrollingList.vue @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + diff --git a/src/locales/en.json b/src/locales/en.json index 6f2b108ad..98c45d03c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -623,6 +623,8 @@ "nft_collect_modal_subtitle_select_collect_method": "Select a collect method", "nft_collect_modal_title_collect": "Collect NFT", "nft_collect_modal_title_collecting": "Collecting NFT", + "nft_collector_message_label": "Collector's message", + "nft_collector_message_view_all": "View all", "nft_collection_content_label": "Content in this collection", "nft_collection_hint": "This book is part of a collection", "nft_collection_label": "Collection", diff --git a/src/locales/zh-Hant.json b/src/locales/zh-Hant.json index 9dcbe8d87..b2e98bb9c 100644 --- a/src/locales/zh-Hant.json +++ b/src/locales/zh-Hant.json @@ -623,6 +623,8 @@ "nft_collect_modal_subtitle_select_collect_method": "請選擇收藏方法", "nft_collect_modal_title_collect": "收藏作品", "nft_collect_modal_title_collecting": "正在收藏作品", + "nft_collector_message_label": "收藏者的留言", + "nft_collector_message_view_all": "顯示全部", "nft_collection_content_label": "套裝內容", "nft_collection_hint": "此本書收錄在套裝中", "nft_collection_label": "組合套裝", diff --git a/src/package-lock.json b/src/package-lock.json index 565c78b81..468a94a30 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -8,6 +8,7 @@ "name": "liker-land", "version": "1.0.0", "dependencies": { + "@better-scroll/core": "^2.5.1", "@cosmjs/stargate": "^0.30.1", "@google-cloud/pubsub": "^3.2.1", "@likecoin/edm": "^8.2.0-beta.1", @@ -2525,6 +2526,19 @@ "node": ">=6.9.0" } }, + "node_modules/@better-scroll/core": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@better-scroll/core/-/core-2.5.1.tgz", + "integrity": "sha512-koKOuYA55dQ04FJRIVUpMGDr1hbCfWmfX0MGp1hKagkQSWSRpwblqACiwtggVauoj9aaJRJZ9hDsTM4weaavlg==", + "dependencies": { + "@better-scroll/shared-utils": "^2.5.1" + } + }, + "node_modules/@better-scroll/shared-utils": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@better-scroll/shared-utils/-/shared-utils-2.5.1.tgz", + "integrity": "sha512-AplkfSjXVYP9LZiD6JsKgmgQJ/mG4uuLmBuwLz8W5OsYc7AYTfN8kw6GqZ5OwCGoXkVhBGyd8NeC4xwYItp0aw==" + }, "node_modules/@cnakazawa/watch": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", diff --git a/src/package.json b/src/package.json index 2152f01f5..7bda8196b 100644 --- a/src/package.json +++ b/src/package.json @@ -28,6 +28,7 @@ "build-storybook": "build-storybook" }, "dependencies": { + "@better-scroll/core": "^2.5.1", "@cosmjs/stargate": "^0.30.1", "@google-cloud/pubsub": "^3.2.1", "@likecoin/edm": "^8.2.0-beta.1", diff --git a/src/pages/nft/class/_classId/index.vue b/src/pages/nft/class/_classId/index.vue index 063d74c86..a0a7c64ca 100644 --- a/src/pages/nft/class/_classId/index.vue +++ b/src/pages/nft/class/_classId/index.vue @@ -1,5 +1,5 @@ - + {{ $t('nft_details_page_label_loading') }} @@ -174,6 +174,65 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + @@ -480,6 +528,10 @@ export default { customPrice: 0, isOpeningCheckoutPage: false, + + isHistoryInfoLoading: false, + hasHistoryInfoLoaded: false, + isOpeningCollectorListDialog: false, }; }, async fetch({ route, store, redirect, error, localeLocation }) { @@ -937,6 +989,7 @@ export default { shouldShowFollowButton() { return Boolean(this.iscnOwner !== this.getAddress); }, + // for WNFT populatedCollectorsWithMemo() { if (!this.populatedDisplayEvents) { return this.populatedCollectors; @@ -968,6 +1021,7 @@ export default { } return collectorsWithMemo; }, + // for collector dialog populatedBuyerWithMessage() { if (!this.populatedDisplayEvents) { return this.populatedCollectors; @@ -1002,6 +1056,62 @@ export default { } return collectorsWithBuyerMessages; }, + // for collector message list + filterCollectorsWithReplies() { + if (!this.populatedDisplayEvents || !this.populatedCollectors) { + return []; + } + const collectorsWithReplies = this.populatedCollectors + .map(collector => { + const purchaseEvent = this.populatedDisplayEvents.find( + event => + event.event === 'purchase' && event.toWallet === collector.id + ); + + const transferEvent = this.populatedDisplayEvents.find( + event => + event.event === 'transfer' && event.toWallet === collector.id + ); + + let buyerMessage = null; + let authorReply = null; + + if (purchaseEvent) { + buyerMessage = purchaseEvent.buyerMessage || null; + } else if (transferEvent) { + buyerMessage = transferEvent.buyerMessage || null; + authorReply = transferEvent.memo || null; + } + + if (!buyerMessage) { + return null; + } + + return { + ...collector, + buyerMessage, + authorReply, + }; + }) + .filter(Boolean); + + if (collectorsWithReplies && collectorsWithReplies.length) { + collectorsWithReplies.sort((a, b) => { + if (a.buyerMessage && !b.buyerMessage) { + return -1; + } + if (!a.buyerMessage && b.buyerMessage) { + return 1; + } + return b.authorReply - a.authorReply; + }); + } + + return collectorsWithReplies; + }, + shouldShowCollectorMessage() { + return this.filterCollectorsWithReplies?.length > 3; + }, isShowBanner() { return ( this.classId === @@ -1027,6 +1137,16 @@ export default { : 'nft_edition_select_compare_button_text' ); }, + overlayClasses() { + return [ + 'h-full', + 'w-[60px]', + 'from-light-gray', + 'to-transparent', + 'hidden', + 'laptop:block', + ]; + }, }, async mounted() { try { @@ -1101,6 +1221,7 @@ export default { ]), parseNFTMetadataURL, async fetchTrimmedCollectorsInfo() { + this.isHistoryInfoLoading = true; const trimmedCollectors = this.sortedOwnerListId.slice( 0, this.trimmedCount @@ -1109,6 +1230,8 @@ export default { this.lazyGetUserInfoByAddresses(trimmedCollectors), this.updateNFTHistory({ getAllUserInfo: false }), ]); + this.isHistoryInfoLoading = false; + this.hasHistoryInfoLoaded = true; }, getPurchaseEventParams(edition) { const customPriceInDecimal = this.customPrice @@ -1141,6 +1264,17 @@ export default { ); await this.lazyGetUserInfoByAddresses(this.sortedOwnerListId); }, + async handleClickMoreCollectorMessage() { + this.isOpeningCollectorListDialog = true; + logTrackerEvent( + this, + 'NFT', + 'class_details_show_more_collector_clicked', + this.classId, + 1 + ); + await this.lazyGetUserInfoByAddresses(this.sortedOwnerListId); + }, async handleClickMoreHistory() { logTrackerEvent( this, diff --git a/src/util/ui.js b/src/util/ui.js index ceac14efe..ef0dc7026 100644 --- a/src/util/ui.js +++ b/src/util/ui.js @@ -9,16 +9,15 @@ const largeNumFormatter = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2, }); -export function ellipsis(value) { - if (value) { - const len = value.length; - const dots = '...'; - if (!value) return ''; - if (value.length > 20) { - return value.substring(0, 8) + dots + value.substring(len - 6, len); - } - return value; +export function ellipsis(value, maxLength = 20, endLength = 6) { + if (!value) return ''; + const len = value.length; + const dots = '...'; + + if (len > maxLength) { + return value.substring(0, 8) + dots + value.substring(len - endLength, len); } + return value; }
+ {{ formattedUserDisplayNameFull }} +