diff --git a/CHANGELOG.md b/CHANGELOG.md index b70b2f7f25..fd8fe32d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/core/modules/catalog/store/category/actions.ts b/core/modules/catalog/store/category/actions.ts index 1001698730..d3a3e4a486 100644 --- a/core/modules/catalog/store/category/actions.ts +++ b/core/modules/catalog/store/category/actions.ts @@ -330,7 +330,10 @@ const actions: ActionTree = { 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')() diff --git a/core/pages/Category.js b/core/pages/Category.js index 3f60418266..5ef3e16ba3 100644 --- a/core/pages/Category.js +++ b/core/pages/Category.js @@ -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' @@ -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) @@ -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, diff --git a/package.json b/package.json index 6413ae9b40..bf5aaa006b 100755 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/themes/default/pages/Home.vue b/src/themes/default/pages/Home.vue index 8aa2a5482a..0ba6fb3ec1 100755 --- a/src/themes/default/pages/Home.vue +++ b/src/themes/default/pages/Home.vue @@ -30,6 +30,7 @@