Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix/offline products caching #2611

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.8.4] - UNRELEASED

### Added

### Fixed
- Problem with incomplete category products load for offline use - @patzick (#2543)
- Category products view crash on scrolling down in offline mode - @patzick (#2569)

### Changed / Improved
- Category and Homepage products are now cached for offline use on SSR entry - @patzick (@1698)

## [1.8.3] - 2019.03.03

### Added
Expand Down
5 changes: 4 additions & 1 deletion core/modules/catalog/store/category/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,10 @@ const actions: ActionTree<CategoryState, RootState> = {
size: perPage,
excludeFields: null,
includeFields: null,
updateState: false // not update the product listing - this request is only for caching
configuration: configuration,
sort: sort,
updateState: false, // not update the product listing - this request is only for caching
prefetchGroupProducts: prefetchGroupProducts
}).catch((err) => {
Logger.info("Problem with second stage caching - couldn't store the data", 'category')()
Logger.info(err, 'category')()
Expand Down
92 changes: 43 additions & 49 deletions core/pages/Category.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import toString from 'lodash-es/toString'
import i18n from '@vue-storefront/i18n'
import store from '@vue-storefront/core/store'
import EventBus from '@vue-storefront/core/compatibility/plugins/event-bus'
import { baseFilterProductsQuery, buildFilterProductsQuery } from '@vue-storefront/core/helpers'
import { baseFilterProductsQuery, buildFilterProductsQuery, isServer } from '@vue-storefront/core/helpers'
import { htmlDecode } from '@vue-storefront/core/filters/html-decode'
import { currentStoreView, localizedRoute } from '@vue-storefront/core/lib/multistore'
import Composite from '@vue-storefront/core/mixins/composite'
Expand Down Expand Up @@ -81,58 +81,51 @@ export default {
append: false
})
},
asyncData ({ store, route, context }) { // this is for SSR purposes to prefetch data
return new Promise((resolve, reject) => {
Logger.info('Entering asyncData in Category Page (core)')()
async asyncData ({ store, route, context }) { // this is for SSR purposes to prefetch data
Logger.info('Entering asyncData in Category Page (core)')()
try {
if (context) context.output.cacheTags.add(`category`)
const defaultFilters = store.state.config.products.defaultFilters
store.dispatch('category/list', { level: store.state.config.entities.category.categoriesDynamicPrefetch && store.state.config.entities.category.categoriesDynamicPrefetchLevel ? store.state.config.entities.category.categoriesDynamicPrefetchLevel : null, includeFields: store.state.config.entities.optimize && Vue.prototype.$isServer ? store.state.config.entities.category.includeFields : null }).then((categories) => {
store.dispatch('attribute/list', { // load filter attributes for this specific category
filterValues: defaultFilters, // TODO: assign specific filters/ attribute codes dynamicaly to specific categories
includeFields: store.state.config.entities.optimize && Vue.prototype.$isServer ? store.state.config.entities.attribute.includeFields : null
}).catch(err => {
Logger.error(err)()
reject(err)
}).then((attrs) => {
store.dispatch('category/single', { key: store.state.config.products.useMagentoUrlKeys ? 'url_key' : 'slug', value: route.params.slug }).then((parentCategory) => {
let query = store.getters['category/getCurrentCategoryProductQuery']
if (!query.searchProductQuery) {
store.dispatch('category/mergeSearchOptions', {
searchProductQuery: baseFilterProductsQuery(parentCategory, defaultFilters)
})
}
store.dispatch('category/products', query).then((subloaders) => {
if (subloaders) {
Promise.all(subloaders).then((results) => {
EventBus.$emitFilter('category-after-load', { store: store, route: route }).then((results) => {
return resolve()
}).catch((err) => {
Logger.error(err)()
return resolve()
})
}).catch(err => {
Logger.error(err)()
reject(err)
})
} else {
const err = new Error('Category query returned empty result')
Logger.error(err)()
reject(err)
}
}).catch(err => {
Logger.error(err)()
reject(err)
})
}).catch(err => {
Logger.error(err)()
reject(err)
})
await store.dispatch('category/list', { level: store.state.config.entities.category.categoriesDynamicPrefetch && store.state.config.entities.category.categoriesDynamicPrefetchLevel ? store.state.config.entities.category.categoriesDynamicPrefetchLevel : null, includeFields: store.state.config.entities.optimize && Vue.prototype.$isServer ? store.state.config.entities.category.includeFields : null })
await store.dispatch('attribute/list', { // load filter attributes for this specific category
filterValues: defaultFilters, // TODO: assign specific filters/ attribute codes dynamicaly to specific categories
includeFields: store.state.config.entities.optimize && Vue.prototype.$isServer ? store.state.config.entities.attribute.includeFields : null
})
const parentCategory = await store.dispatch('category/single', { key: store.state.config.products.useMagentoUrlKeys ? 'url_key' : 'slug', value: route.params.slug })
let query = store.getters['category/getCurrentCategoryProductQuery']
if (!query.searchProductQuery) {
store.dispatch('category/mergeSearchOptions', {
searchProductQuery: baseFilterProductsQuery(parentCategory, defaultFilters)
})
}).catch(err => {
Logger.error(err)()
reject(err)
}
const subloaders = await store.dispatch('category/products', query)
if (subloaders) {
await Promise.all(subloaders)
await EventBus.$emitFilter('category-after-load', { store: store, route: route })
} else {
throw new Error('Category query returned empty result')
}
} catch (err) {
Logger.error(err)()
throw err
}
},
async beforeRouteEnter (to, from, next) {
if (!isServer && !from.name) { // Loading category products to cache on SSR render
next(vm => {
const defaultFilters = store.state.config.products.defaultFilters
let parentCategory = store.getters['category/getCurrentCategory']
let query = store.getters['category/getCurrentCategoryProductQuery']
if (!query.searchProductQuery) {
store.dispatch('category/mergeSearchOptions', {
searchProductQuery: baseFilterProductsQuery(parentCategory, defaultFilters)
})
}
store.dispatch('category/products', query)
})
})
} else {
next()
}
},
beforeMount () {
this.$bus.$on('filter-changed-category', this.onFilterChanged)
Expand Down Expand Up @@ -165,6 +158,7 @@ export default {
return bottomOfPage || pageHeight < visible
},
pullMoreProducts () {
if (typeof navigator !== 'undefined' && !navigator.onLine) return
let current = this.getCurrentCategoryProductQuery.current + this.getCurrentCategoryProductQuery.perPage
this.mergeSearchOptions({
append: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"all": "cross-env NODE_ENV=development node ./core/scripts/all",
"cache": "node ./core/scripts/cache",
"dev": "cross-env TS_NODE_PROJECT=\"tsconfig-build.json\" ts-node ./core/scripts/entry.ts",
"dev:sw": "cross-env TS_NODE_PROJECT=\"tsconfig-build.json\" yarn build:sw && ts-node ./core/scripts/entry",
"dev:sw": "cross-env TS_NODE_PROJECT=\"tsconfig-build.json\" yarn build:sw && yarn dev",
"dev:inspect": "cross-env TS_NODE_PROJECT=\"tsconfig-build.json\" node --inspect -r ts-node/register ./core/scripts/entry",
"build:sw": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-build.json\" webpack --config ./core/build/webpack.prod.sw.config.ts --mode production --progress --hide-modules",
"build:client": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-build.json\" webpack --config ./core/build/webpack.prod.client.config.ts --mode production --progress --hide-modules",
Expand Down
18 changes: 16 additions & 2 deletions src/themes/default/pages/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<script>
// query constructor
import { prepareQuery } from '@vue-storefront/core/modules/catalog/queries/common'
import { isServer } from '@vue-storefront/core/helpers'

// Core pages
import Home from '@vue-storefront/core/pages/Home'
Expand Down Expand Up @@ -87,8 +88,7 @@ export default {
const newProductsResult = await store.dispatch('product/list', {
query: newProductsQuery,
size: 8,
sort: 'created_at:desc',
includeFields: config.entities.optimize ? (config.products.setFirstVarianAsDefaultInURL ? config.entities.productListWithChildren.includeFields : config.entities.productList.includeFields) : []
sort: 'created_at:desc'
})
if (newProductsResult) {
store.state.homepage.new_collection = newProductsResult.items
Expand All @@ -106,6 +106,20 @@ export default {

await store.dispatch('promoted/updateHeadImage')
await store.dispatch('promoted/updatePromotedOffers')
},
beforeRouteEnter (to, from, next) {
if (!isServer && !from.name) { // Loading products to cache on SSR render
next(vm => {
let newProductsQuery = prepareQuery({ queryConfig: 'newProducts' })
vm.$store.dispatch('product/list', {
query: newProductsQuery,
size: 8,
sort: 'created_at:desc'
})
})
} else {
next()
}
}
}
</script>
Expand Down