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..2a7ce151c --- /dev/null +++ b/src/assets/images/nft/books/collector-message-item-background-lg.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + 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..cfceae4af --- /dev/null +++ b/src/assets/images/nft/books/collector-message-item-background.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + 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 }} - + 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..1d789922f --- /dev/null +++ b/src/components/NFTPage/CollectorMessageList/Identity.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/components/NFTPage/CollectorMessageList/Item.vue b/src/components/NFTPage/CollectorMessageList/Item.vue new file mode 100644 index 000000000..6f832d35c --- /dev/null +++ b/src/components/NFTPage/CollectorMessageList/Item.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/components/NFTPage/CollectorMessageList/index.vue b/src/components/NFTPage/CollectorMessageList/index.vue new file mode 100644 index 000000000..9768d3410 --- /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..44fe05fa6 --- /dev/null +++ b/src/components/ScrollingList.vue @@ -0,0 +1,100 @@ + + + + diff --git a/src/locales/en.json b/src/locales/en.json index 6499feb07..0ea637c91 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -622,6 +622,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 144ef5230..9733edfd0 100644 --- a/src/locales/zh-Hant.json +++ b/src/locales/zh-Hant.json @@ -622,6 +622,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/pages/nft/class/_classId/index.vue b/src/pages/nft/class/_classId/index.vue index e8bc0888a..5dda2e2fd 100644 --- a/src/pages/nft/class/_classId/index.vue +++ b/src/pages/nft/class/_classId/index.vue @@ -1,5 +1,5 @@
+ + @@ -479,6 +525,10 @@ export default { customPrice: 0, isOpeningCheckoutPage: false, + + isHistoryInfoLoading: false, + isFinishedLoadingHistory: false, + isOpeningCollectorListDialog: false, }; }, async fetch({ route, store, redirect, error, localeLocation }) { @@ -936,6 +986,7 @@ export default { shouldShowFollowButton() { return Boolean(this.iscnOwner !== this.getAddress); }, + // for WNFT populatedCollectorsWithMemo() { if (!this.populatedDisplayEvents) { return this.populatedCollectors; @@ -967,6 +1018,7 @@ export default { } return collectorsWithMemo; }, + // for collector dialog populatedBuyerWithMessage() { if (!this.populatedDisplayEvents) { return this.populatedCollectors; @@ -1001,6 +1053,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 === @@ -1026,6 +1134,9 @@ export default { : 'nft_edition_select_compare_button_text' ); }, + overlayClasses() { + return ['h-full', 'w-[60px]', 'from-light-gray', 'to-transparent']; + }, }, async mounted() { try { @@ -1100,6 +1211,7 @@ export default { ]), parseNFTMetadataURL, async fetchTrimmedCollectorsInfo() { + this.isHistoryInfoLoading = true; const trimmedCollectors = this.sortedOwnerListId.slice( 0, this.trimmedCount @@ -1108,6 +1220,8 @@ export default { this.lazyGetUserInfoByAddresses(trimmedCollectors), this.updateNFTHistory({ getAllUserInfo: false }), ]); + this.isHistoryInfoLoading = false; + this.isFinishedLoadingHistory = true; }, getPurchaseEventParams(edition) { const customPriceInDecimal = this.customPrice @@ -1140,6 +1254,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 5e5ffee3a..cb0e5a8aa 100644 --- a/src/util/ui.js +++ b/src/util/ui.js @@ -22,6 +22,19 @@ export function ellipsis(value) { return value; } +export function ellipsisCollectorAddress(value) { + if (value) { + const len = value.length; + const dots = '...'; + if (!value) return ''; + if (value.length > 10) { + return value.substring(0, 8) + dots; + } + return value; + } + return value; +} + export function ellipsisNFTName(value) { if (value) { const len = value.length;