From 33787accdbc1b30e6bbe29e1ec76b4451819c31d Mon Sep 17 00:00:00 2001 From: duchm Date: Mon, 7 Aug 2023 17:21:46 +0700 Subject: [PATCH] refactor: blog --- package.json | 2 +- src/renderer/nuxt.config.js | 1 + src/renderer/pages/blog/_slug.vue | 233 +++++++++++----------- src/renderer/pages/blog/index.vue | 169 ++++++---------- src/renderer/pages/index.vue | 105 +++++----- src/renderer/plugins/vue-context/index.js | 10 +- yarn.lock | 30 +-- 7 files changed, 245 insertions(+), 305 deletions(-) diff --git a/package.json b/package.json index 5396f7d4..e7899e5f 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.2.0", - "@notionhq/client": "^1.0.4", + "@notionhq/client": "^2.2.10", "@nuxtjs/axios": "^5.13.6", "@nuxtjs/feed": "^2.0.0", "@nuxtjs/gtm": "^2.4.0", diff --git a/src/renderer/nuxt.config.js b/src/renderer/nuxt.config.js index 3831a22a..b0e499a3 100644 --- a/src/renderer/nuxt.config.js +++ b/src/renderer/nuxt.config.js @@ -276,6 +276,7 @@ module.exports = { } ], serverMiddleware: [ + // Notion - Whitepaper { path: '/api', handler: '~/server-middleware/rest.js' }, { path: '/api/content', handler: '~/server-middleware/notion.js' }, { path: '/api/top-banner', handler: '~/server-middleware/topBanner.js' }, diff --git a/src/renderer/pages/blog/_slug.vue b/src/renderer/pages/blog/_slug.vue index 7f3eeb4f..e6648082 100644 --- a/src/renderer/pages/blog/_slug.vue +++ b/src/renderer/pages/blog/_slug.vue @@ -193,136 +193,141 @@ import Post from '~/components/landing/blog/Post' export default { components: { Post }, + layout: 'landing', + scrollToTop: true, + async asyncData ({ store, params, error }) { - const slug = encodeURIComponent(params.slug) - const language = store.state.i18n.locale - const { data } = await axios.get( - `${process.env.blogUrl}/posts?slug=${slug}&categories=8,13,18,54,25` - ) - if (data.length === 0) { - error({ statusCode: 404 }) - return - } - const categoriesId = data[0].categories - const categories = await Promise.all( - categoriesId.map(async id => { - const res = await axios.get(`${process.env.blogUrl}/categories/${id}`) - return res.data - }) - ) - const languageTag = await axios.get( - `${process.env.blogUrl}/tags?slug=${language}` - ) - const relateResult = await axios.get( - `${process.env.blogUrl}/posts?exclude=${ - data[0].id - }&categories=${categoriesId.toString()}&tags=${ - languageTag.data[0].id - }&per_page=2` - ) - const users = await axios.get(`${process.env.blogUrl}/users`) - const userArray = users.data - const relatedPosts = [] - relateResult.data.forEach(async element => { - let featuredImage = '' + try { + const slug = encodeURIComponent(params.slug) + const language = store.state.i18n.locale + + // Get post by slug + get users + language tag + const [postRes, usersRes, langRes] = await Promise.all([ + axios.get( + `${process.env.blogUrl}/posts?slug=${slug}&categories=8,13,18,54,25` + ), + axios.get(`${process.env.blogUrl}/users`), + axios.get( + `${process.env.blogUrl}/tags?slug=${language}` + ) + ]) + + const data = postRes.data + if (data.length === 0) { + error({ statusCode: 404 }) + return + } + + const userArray = usersRes.data + const languageTagId = langRes.data[0].id + const relatedPosts = [] + const categoryIds = data[0].categories + let categories = [] + try { - featuredImage = element._embedded['wp:featuredmedia']['0'].source_url - } catch (error) {} - if (!featuredImage) { - const $ = cheerio.load(element.content.rendered) - const images = $('img').attr('src') - if (images) { - featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') - } + // Get categories + related posts + const [relateResult, ...categoriesRes] = await Promise.all([ + axios.get( + `${process.env.blogUrl}/posts?exclude=${ + data[0].id + }&categories=${categoryIds.toString()}&tags=${ + languageTagId + }&per_page=2` + ), + ...categoryIds.map(async id => { + const res = await axios.get(`${process.env.blogUrl}/categories/${id}`) + return res.data + }) + ]) + + categories = categoriesRes + + // Process related results + relateResult.data.forEach(async element => { + let featuredImage = '' + try { + featuredImage = element._embedded['wp:featuredmedia']['0'].source_url + } catch (error) {} + if (!featuredImage) { + const $ = cheerio.load(element.content.rendered) + const images = $('img').attr('src') + if (images) { + featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') + } + } + const $_ = cheerio.load(element.excerpt.rendered) + const desc = $_('p').text() + + relatedPosts.push({ + ...element, + featured_image: featuredImage, + user: userArray.find(user => user.id === element.author), + desc + }) + }) + } catch (error) { + console.log(error) } - const $_ = cheerio.load(element.excerpt.rendered) - const desc = $_('p').text() - // const authorResult = await axios.get(`${process.env.blogUrl}/users/${element.author}`) - relatedPosts.push({ - ...element, - // author: authorResult.data.name, - featured_image: featuredImage, - user: userArray.find(user => user.id === element.author), - desc - }) - }) - // const authorResult = await axios.get(`${process.env.blogUrl}/users/${data[0].author}`) - return { - post: data.map(post => { - const $ = cheerio.load(post.content.rendered) - let featuredImage = '' - try { - featuredImage = post._embedded['wp:featuredmedia']['0'].source_url - } catch (error) {} - if (!featuredImage) { + // Process post + return { + post: data.map(post => { const $ = cheerio.load(post.content.rendered) - const images = $('img').attr('src') - if (images) { - featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') - } - } - const $_ = cheerio.load(post.excerpt.rendered) - const desc = $_('p').text() - const tableOfContents = [] - $('h2, h3').each(function (i, e) { - const title = $(e).text() - const slug = slugify(title, { locale: 'vi', lower: true }) - tableOfContents[i] = { - text: $(e).text(), - link: slug, - heading: e.name + let featuredImage = '' + try { + featuredImage = post._embedded['wp:featuredmedia']['0'].source_url + } catch (error) {} + if (!featuredImage) { + const $ = cheerio.load(post.content.rendered) + const images = $('img').attr('src') + if (images) { + featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') + } } - $(this).attr('id', slug) - }) - if (!tableOfContents.length) { - $('h1').each(function (i, e) { + const $_ = cheerio.load(post.excerpt.rendered) + const desc = $_('p').text() + const tableOfContents = [] + $('h2, h3').each(function (i, e) { const title = $(e).text() const slug = slugify(title, { locale: 'vi', lower: true }) tableOfContents[i] = { - text: title, - link: slug + text: $(e).text(), + link: slug, + heading: e.name } $(this).attr('id', slug) }) - } - return { - ...post, - new_content_rendered: $.html(), - desc, - featured_image: featuredImage, - user: userArray.find(user => user.id === post.author), - table_of_contents: tableOfContents - // author: authorResult.data.name - } - })[0], - categories: categories.map(item => item.name), - relatedPosts - // relatedPosts: relateResult.data.map(post => { - // let featuredImage = '' - // try { - // featuredImage = post._embedded['wp:featuredmedia']['0'].source_url - // } catch (error) { - // } - // if (!featuredImage) { - // const $ = cheerio.load(post.content.rendered) - // const images = $('img').attr('src') - // if (images) { - // featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') - // } - // } - // const $_ = cheerio.load(post.excerpt.rendered) - // const desc = $_('p').text() - // return { - // ...post, - // featured_image: featuredImage, - // desc - // } - // }) + if (!tableOfContents.length) { + $('h1').each(function (i, e) { + const title = $(e).text() + const slug = slugify(title, { locale: 'vi', lower: true }) + tableOfContents[i] = { + text: title, + link: slug + } + $(this).attr('id', slug) + }) + } + return { + ...post, + new_content_rendered: $.html(), + desc, + featured_image: featuredImage, + user: userArray.find(user => user.id === post.author), + table_of_contents: tableOfContents + } + })[0], + categories: categories.map(item => item.name), + relatedPosts + } + } catch (error) { + console.log(error) + error({ statusCode: 404 }) } }, + data () { return { form: { diff --git a/src/renderer/pages/blog/index.vue b/src/renderer/pages/blog/index.vue index afa16103..53e47cb3 100644 --- a/src/renderer/pages/blog/index.vue +++ b/src/renderer/pages/blog/index.vue @@ -70,17 +70,6 @@

{{ $t('blog.most_recent') }}

-
@@ -96,27 +85,6 @@
- -
{ let featuredImage = '' try { @@ -180,7 +157,6 @@ export default { } const $_ = cheerio.load(post.excerpt.rendered) const desc = $_('p').text() - // const author = await axios.get(`${process.env.blogUrl}/users/${post.author}`) posts.push({ ...post, featured_image: featuredImage, @@ -188,35 +164,17 @@ export default { desc }) }) + return { - // posts: data.map(post => { - // let featuredImage = '' - // try { - // featuredImage = post._embedded['wp:featuredmedia']['0'].source_url - // } catch (error) { - // } - // if (!featuredImage) { - // const $ = cheerio.load(post.content.rendered) - // const images = $('img').attr('src') - // if (images) { - // featuredImage = images.replace(/-[0-9]+x[0-9]+/g, '') - // } - // } - // const $_ = cheerio.load(post.excerpt.rendered) - // const desc = $_('p').text() - // // const categories = this.categories.filter(item => post.categories.includes(item.id)) - // return { - // ...post, - // featured_image: featuredImage, - // desc - // } - // }) posts, - users: userArray + users: userArray, + localeTagId: tagId } } catch (error) { + console.log(error) } }, + data () { return { form: { @@ -234,12 +192,15 @@ export default { loadingMore: false, loadMoreDisabled: false, postLoading: false, - posts: [] + posts: [], + localeTagId: null } }, + computed: { locale () { return this.$store.state.i18n.locale } }, + watch: { '$route.query.category' (newValue) { console.log(newValue) @@ -254,6 +215,7 @@ export default { this.changeCategory(newValue) } }, + mounted () { if (this.$route.query.category) { const category = this.blog_categories.find(cate => cate.slug === this.$route.query.category) @@ -262,23 +224,30 @@ export default { } } }, + methods: { showPre () { const slider = document.querySelector('.catalog-list') - const scrollLeft = slider.scrollLeft - slider.scroll({ - left: scrollLeft - 150, - behavior: 'smooth' - }) + if (slider) { + const scrollLeft = slider.scrollLeft || 0 + slider.scroll({ + left: scrollLeft - 150, + behavior: 'smooth' + }) + } }, + showNext () { const slider = document.querySelector('.catalog-list') - const scrollLeft = slider.scrollLeft - slider.scroll({ - left: scrollLeft + 150, - behavior: 'smooth' - }) + if (slider) { + const scrollLeft = slider.scrollLeft || 0 + slider.scroll({ + left: scrollLeft + 150, + behavior: 'smooth' + }) + } }, + async subscribeBlog () { if (this.$refs.form) { const isValid = await this.$refs.form.validate() @@ -290,10 +259,6 @@ export default { } try { await this.subscribe(this.form.email) - // this.$message({ - // message: this.$t('landing_contact.messages.request_has_been_sent'), - // type: 'success' - // }) this.$refs.subscribeSuccessful.openDialog() } catch (error) { this.$message({ @@ -302,17 +267,20 @@ export default { }) } }, + async getTagBySlug (tagSlug) { try { + if (tagSlug === this.locale && this.localeTagId) { + return this.localeTagId + } const result = await axios.get(`${process.env.blogUrl}/tags?slug=${tagSlug}`) return result.data[0] } catch (error) { - return {} + return null } }, + async changeCategory (id) { - // const category = this.blog_categories.find(cate => cate.id === id) - // this.$router.push(`/blog?category=${category.slug}`) window.scroll({ top: 650, left: 0, @@ -324,13 +292,8 @@ export default { this.page = 1 this.query = '' try { - const localeTag = await this.getTagBySlug(this.locale) - const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&tags=${localeTag.id}&categories=${this.category}` - // if (this.category !== 0) { - // url += `&categories=${parseInt(this.category)}` - // } else { - // url += `&categories=${this.categories_id.toString()}` - // } + const localeTagId = await this.getTagBySlug(this.locale) + const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&tags=${localeTagId}&categories=${this.category}` const { data } = await axios.get(url) if (data.length < 9) { this.loadMoreDisabled = true @@ -339,21 +302,18 @@ export default { } this.posts = data.map(this.mapFeatures) } catch (error) { - this.errorMessage() + console.log(error) + this.notify(this.$t('errors.something_went_wrong'), 'warning') } this.postLoading = false }, + async loadMore () { this.loadingMore = true this.page += 1 try { - const localeTag = await this.getTagBySlug(this.locale) - const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&page=${this.page}&tags=${localeTag.id}&search=${this.query}&categories=${this.category}` - // if (this.category !== 0) { - // url += `&categories=${this.category}` - // } else { - // url += `&categories=${this.categories_id.toString()}` - // } + const localeTagId = await this.getTagBySlug(this.locale) + const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&page=${this.page}&tags=${localeTagId}&search=${this.query}&categories=${this.category}` const newData = await axios.get(url) const newPosts = newData.data.map(this.mapFeatures) if (newPosts.length < 9) { @@ -361,17 +321,17 @@ export default { } else { this.loadMoreDisabled = false } - // console.log(newPosts) this.posts = this.posts.concat(newPosts) } catch (error) { - // this.errorMessage() this.loadMoreDisabled = true } this.loadingMore = false }, + dateToFormattedString (date) { return moment(date).format('DD MMM YYYY') }, + mapFeatures (post) { let featuredImage = '' try { @@ -394,6 +354,7 @@ export default { user: this.users.find(user => user.id === post.author) } }, + searchPosts: debounce(async function () { window.scroll({ top: 650, @@ -404,13 +365,8 @@ export default { this.loadMoreDisabled = false this.page = 1 try { - const localeTag = await this.getTagBySlug(this.locale) - const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&tags=${localeTag.id}&search=${this.query}&categories=${this.category}` - // if (this.category !== 0) { - // url += `&categories=${parseInt(this.category)}` - // } else { - // url += `&categories=${this.categories_id.toString()}` - // } + const localeTagId = await this.getTagBySlug(this.locale) + const url = `${process.env.blogUrl}/posts?_embed=1&per_page=9&tags=${localeTagId}&search=${this.query}&categories=${this.category}` const { data } = await axios.get(url) if (data.length < 9) { this.loadMoreDisabled = true @@ -419,7 +375,8 @@ export default { } this.posts = data.map(this.mapFeatures) } catch (error) { - this.errorMessage() + console.log(error) + this.notify(this.$t('errors.something_went_wrong'), 'warning') } this.postLoading = false }, 800) diff --git a/src/renderer/pages/index.vue b/src/renderer/pages/index.vue index c2fce934..97c26726 100644 --- a/src/renderer/pages/index.vue +++ b/src/renderer/pages/index.vue @@ -653,10 +653,35 @@ export default { layout: 'landing', - async asyncData ({ store, $axios }) { + data () { + return { + videoId: 'kAutqE2ATfU', + dialogVisible: false, + posts: [], + testimonials: [] + } + }, + + head () { + return { + script: [ + { + src: '//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js', + async: true + } + ] + } + }, + + mounted () { + this.getBlogs() + this.getTestimonials() + }, + + methods: { // Get blogs - const _getBlogs = async () => { - const language = store.state.i18n.locale + async getBlogs () { + const language = this.locale let tagId try { const posts = [] @@ -694,73 +719,39 @@ export default { desc }) }) - return { - posts - } + this.posts = posts } catch (error) { - // console.log(error) + console.log(error) } - } + }, // Get testimonials - const _getTestimonials = async () => { + async getTestimonials () { try { - const res = await $axios.get(`${process.env.baseUrl}/api/testimonials`) + const res = await this.$axios.get(`${process.env.baseUrl}/api/testimonials`) if (!res.data?.data) { return { testimonials: [] } } - return { - testimonials: res.data.data - .filter(t => t.Status === 'Active') - .map(t => { - let rating = 5 - try { - rating = parseInt(t.Rating) - } catch (error) { - // - } - return { - ...t, - Rating: rating - } - }) - } + this.testimonials = res.data.data + .filter(t => t.Status === 'Active') + .map(t => { + let rating = 5 + try { + rating = parseInt(t.Rating) + } catch (error) { + // + } + return { + ...t, + Rating: rating + } + }) } catch (error) { - // console.log(error) + console.log(error) } } - - const [blogRes, testimonialRes] = await Promise.all([ - _getBlogs(), - _getTestimonials() - ]) - - return { - ...blogRes, - ...testimonialRes - } - }, - - data () { - return { - videoId: 'kAutqE2ATfU', - dialogVisible: false, - posts: [], - testimonials: [] - } - }, - - head () { - return { - script: [ - { - src: '//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js', - async: true - } - ] - } } } diff --git a/src/renderer/plugins/vue-context/index.js b/src/renderer/plugins/vue-context/index.js index 0231a44d..d166bf28 100644 --- a/src/renderer/plugins/vue-context/index.js +++ b/src/renderer/plugins/vue-context/index.js @@ -82,7 +82,7 @@ export default { }, focusItem (index, items) { - const el = items.find((el, idx) => idx === index) + const el = items.find((_, idx) => idx === index) if (el && el.focus) { el.focus() } @@ -168,8 +168,8 @@ export default { }, positionMenu (top, left) { - const largestHeight = window.innerHeight - this.$el.offsetHeight - 25 - const largestWidth = window.innerWidth - this.$el.offsetWidth - 25 + const largestHeight = window.innerHeight - (this.$el?.offsetHeight || 0) - 25 + const largestWidth = window.innerWidth - (this.$el?.offsetWidth || 0) - 25 if (top > largestHeight) { top = largestHeight @@ -197,7 +197,7 @@ export default { setItemRoles () { // Add role="menuitem" and tabindex="-1" to all items selectAll(this.localItemSelector, this.$el) - .forEach((el) => { + .forEach(el => { setAttr(el, 'role', 'menuitem') setAttr(el, 'tabindex', '-1') }) @@ -232,7 +232,7 @@ export default { // Only register the events we need const on = { // `!` modifier for capture - '!contextmenu': (e) => { + '!contextmenu': e => { e.preventDefault() }, keydown: this.onKeydown // up, down, esc diff --git a/yarn.lock b/yarn.lock index 92bc3cb1..36383f02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1763,10 +1763,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@notionhq/client@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@notionhq/client/-/client-1.0.4.tgz#405e9468576baf81019db4d791f4b52d091f4a57" - integrity sha512-m7zZ5l3RUktayf1lRBV1XMb8HSKsmWTv/LZPqP7UGC1NMzOlc+bbTOPNQ4CP/c1P4cP61VWLb/zBq7a3c0nMaw== +"@notionhq/client@^2.2.10": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@notionhq/client/-/client-2.2.10.tgz#736332e6308a61e3d842ca2093037f758c14eac0" + integrity sha512-MggdzjB320UI2fUFR5XmUBbRBaqNr3QCBHQyK2CqbiO2T29p8RzzQBm5c7RJqvFhi7P0MaLjuY1CI8ypGEYssg== dependencies: "@types/node-fetch" "^2.5.10" node-fetch "^2.6.1" @@ -2659,9 +2659,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node-fetch@^2.5.10": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + version "2.6.4" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" + integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== dependencies: "@types/node" "*" form-data "^3.0.0" @@ -11872,21 +11872,7 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^7.3.7, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" - -semver@^7.5.3, semver@^7.5.4: +semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==