From 81c9eb44ccc519a41b20984d403434e536bc29b6 Mon Sep 17 00:00:00 2001 From: "Ng Wing Tat, David" Date: Thu, 7 Nov 2024 23:33:49 +0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=94=20Update=20listing=20page=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.json | 2 +- src/locales/zh-Hant.json | 2 +- src/pages/store/index.vue | 85 +++++++++++++++++++++------------ src/server/api/util/airtable.js | 9 ++-- src/store/modules/nft.js | 3 +- 5 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index b99d3244b..5de52f7af 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -486,7 +486,7 @@ "listing_page_select_language_ch": "Simplified Chinese", "listing_page_select_language_en": "English", "listing_page_select_language_title": "Language", - "listing_page_select_language_zh": "Traditional Chinese", + "listing_page_select_language_zh": "Chinese", "listing_page_sorter": "Sort", "listing_page_tag_not_found": "Tag is not found", "Locale": { diff --git a/src/locales/zh-Hant.json b/src/locales/zh-Hant.json index 63721084a..013440ae2 100644 --- a/src/locales/zh-Hant.json +++ b/src/locales/zh-Hant.json @@ -486,7 +486,7 @@ "listing_page_select_language_ch": "簡體中文", "listing_page_select_language_en": "英文", "listing_page_select_language_title": "語言", - "listing_page_select_language_zh": "繁體中文", + "listing_page_select_language_zh": "中文", "listing_page_sorter": "排序", "listing_page_tag_not_found": "找不到此標籤", "Locale": { diff --git a/src/pages/store/index.vue b/src/pages/store/index.vue index 44ef16a72..ce95bbfc8 100644 --- a/src/pages/store/index.vue +++ b/src/pages/store/index.vue @@ -304,7 +304,7 @@ -
  • +
  • { + if (locale.includes(a)) return -1; + if (locale.includes(b)) return 1; + return 0; + }); + return languages.map(lang => `listing-${lang}`); +} + export default { name: 'ListingPage', mixins: [crispMixin], @@ -560,7 +571,9 @@ export default { try { const fetches = [ $api.$get(fetchBookstoreCMSTags({ limit: 100 })), - store.dispatch('lazyFetchBookstoreCMSProductsByTagId', 'listing'), + ...getCMSTagIdsForRecommendedBookstoreItemsByLocale(i18n.locale).map( + tagId => store.dispatch('lazyFetchBookstoreCMSProductsByTagId', tagId) + ), store.dispatch('fetchBookstoreLatestItems'), ]; if (route.query.tag) { @@ -613,9 +626,7 @@ export default { href: this.canonicalLink, }, ]; - const classIds = Array.from( - new Set(this.bookstoreItems.map(b => b.classId).flat()) - ); + const classIds = this.uniqueBookstoreItems.map(item => item.classId); classIds.forEach(classId => link.push({ rel: 'prefetch', @@ -754,6 +765,10 @@ export default { text: this.$t('listing_page_select_language_all'), value: LANGUAGE_OPTIONS.ALL, }, + { + text: this.$t('listing_page_select_language_zh'), + value: LANGUAGE_OPTIONS.ZH, + }, { text: this.$t('listing_page_select_language_en'), value: LANGUAGE_OPTIONS.EN, @@ -825,17 +840,37 @@ export default { return this.$t('listing_page_header_sort', { sort: this.$t(text) }); }, - bookstoreCMSProducts() { - return this.nftGetBookstoreCMSProductsByTagId('listing') || []; + recommendedBookstoreItems() { + // Return 100 books for each locale + return getCMSTagIdsForRecommendedBookstoreItemsByLocale(this.$i18n.locale) + .map(tagId => this.nftGetBookstoreCMSProductsByTagId(tagId)) + .flat() + .map((item, index) => ({ ...item, order: index + 1 })); }, bookstoreItems() { - const items = this.selectedTagId - ? this.nftGetBookstoreCMSProductsByTagId(this.selectedTagId) || [] - : [...this.bookstoreCMSProducts, ...this.nftBookstoreLatestItems]; + if (this.selectedTagId) { + // Return books with particular tag from CMS + return this.nftGetBookstoreCMSProductsByTagId(this.selectedTagId); + } + + if (this.selectedSorting === SORTING_OPTIONS.LATEST) { + // Return the latest 100 published books & fill up with recommended books from CMS + return this.nftBookstoreLatestItems + .map(item => ({ + ...item, + id: item.classId, // Imitate items from CMS that have id keys + })) + .concat(this.recommendedBookstoreItems); + } + + // Return recommended books from CMS by default + return this.recommendedBookstoreItems; + }, + uniqueBookstoreItems() { const uniqueIds = new Set(); const dedupedItems = []; - items.forEach(item => { + this.bookstoreItems.forEach(item => { if (!uniqueIds.has(item.classId)) { uniqueIds.add(item.classId); dedupedItems.push(item); @@ -844,7 +879,7 @@ export default { return dedupedItems; }, filteredBookstoreItems() { - return this.bookstoreItems + return this.uniqueBookstoreItems .filter(item => { if (this.isAppliedDRMFreeFilter) { return item.isDRMFree; @@ -852,11 +887,11 @@ export default { return true; }) .filter(item => { - if ( - this.selectedLanguageFilter !== LANGUAGE_OPTIONS.ALL && - item.locales - ) { - return item.locales.includes(this.selectedLanguageFilter); + if (this.selectedLanguageFilter !== LANGUAGE_OPTIONS.ALL) { + if (Array.isArray(item.locales)) { + return item.locales.includes(this.selectedLanguageFilter); + } + return this.selectedLanguageFilter.includes(item.locale); } return true; }) @@ -871,7 +906,9 @@ export default { }); }, isFilterApplied() { - return this.bookstoreItems.length !== this.filteredBookstoreItems.length; + return ( + this.uniqueBookstoreItems.length !== this.filteredBookstoreItems.length + ); }, sortedBookstoreItems() { if (this.searchQuery) { @@ -880,18 +917,6 @@ export default { const items = [...this.filteredBookstoreItems]; items.sort((a, b) => { - const locale = this.selectedLanguageFilter; - if (locale !== LANGUAGE_OPTIONS.ALL) { - const aLocales = a.locales || []; - const bLocales = b.locales || []; - if (aLocales.includes(locale) && !bLocales.includes(locale)) { - return -1; - } - if (!aLocales.includes(locale) && bLocales.includes(locale)) { - return 1; - } - } - switch (this.selectedSorting) { case SORTING_OPTIONS.RECOMMEND: if (a.order && b.order) { diff --git a/src/server/api/util/airtable.js b/src/server/api/util/airtable.js index a3de2d93c..bca1fbca9 100644 --- a/src/server/api/util/airtable.js +++ b/src/server/api/util/airtable.js @@ -11,8 +11,10 @@ function normalizeTagIdForViewName(viewName) { switch (viewName) { case 'landing': return 'Landing Page'; - case 'listing': - return 'Listing Page'; + case 'listing-zh': + return 'Listing Page (Chinese)'; + case 'listing-en': + return 'Listing Page (English)'; case 'all': return 'All'; default: @@ -39,7 +41,7 @@ async function fetchAirtableCMSProductsByTagId( } ); - const normalizedRecords = results.data.records.map(({ fields }) => { + const normalizedRecords = results.data.records.map(({ id, fields }) => { const classId = fields.ID; const classIds = fields.IDs; const title = fields.Name; @@ -52,6 +54,7 @@ async function fetchAirtableCMSProductsByTagId( const minPrice = fields['Min Price']; const isMultiple = classIds && classIds.length > 1; return { + id, classId, classIds: isMultiple ? classIds : undefined, title, diff --git a/src/store/modules/nft.js b/src/store/modules/nft.js index 243e5fe96..4b5f6423f 100644 --- a/src/store/modules/nft.js +++ b/src/store/modules/nft.js @@ -986,8 +986,9 @@ const actions = { const { data } = await this.$api.get(api.fetchBookstoreLatestItems()); commit( TYPES.NFT_SET_BOOKSTORE_LATEST_ITEMS, - data.list.map(({ hideDownload, ...item }) => ({ + data.list.map(({ hideDownload, inLanguage, ...item }) => ({ ...item, + locale: inLanguage, isDRMFree: !hideDownload, })) );