diff --git a/packages/api-client/src/api/index.ts b/packages/api-client/src/api/index.ts index cf9830eeb..4e9dd5573 100644 --- a/packages/api-client/src/api/index.ts +++ b/packages/api-client/src/api/index.ts @@ -61,3 +61,4 @@ export { default as updateCustomerEmail } from './updateCustomerEmail'; export { default as upsellProduct } from './upsellProduct'; export { default as urlResolver } from './urlResolver'; export { default as wishlist } from './wishlist'; +export { default as wishlistItemsCount } from './wishlistItemsCount'; diff --git a/packages/api-client/src/api/wishlistItemsCount/index.ts b/packages/api-client/src/api/wishlistItemsCount/index.ts new file mode 100644 index 000000000..53a3c5b82 --- /dev/null +++ b/packages/api-client/src/api/wishlistItemsCount/index.ts @@ -0,0 +1,27 @@ +import { ApolloQueryResult } from '@apollo/client/core'; +import { CustomQuery } from '@vue-storefront/core'; + +import { + WishlistQuery, +} from '../../types/GraphQL'; +import query from './wishlistItemsCount'; +import { Context } from '../../types/context'; + +export default async ( + context: Context, + customQuery: CustomQuery = { wishlistItemsCount: 'wishlistItemsCount' }, +): Promise> => { + const { wishlistItemsCount } = context.extendQuery(customQuery, { + wishlistItemsCount: { + query, + }, + }); + try { + return await context.client + .query({ + query: wishlistItemsCount.query, + }); + } catch (error) { + throw error.graphQLErrors?.[0].message || error.networkError?.result || error; + } +}; diff --git a/packages/api-client/src/api/wishlistItemsCount/wishlistItemsCount.ts b/packages/api-client/src/api/wishlistItemsCount/wishlistItemsCount.ts new file mode 100644 index 000000000..9833ba895 --- /dev/null +++ b/packages/api-client/src/api/wishlistItemsCount/wishlistItemsCount.ts @@ -0,0 +1,11 @@ +import gql from 'graphql-tag'; + +export default gql` + query wishlist { + customer { + wishlists { + items_count + } + } + } +`; diff --git a/packages/api-client/src/types/API.ts b/packages/api-client/src/types/API.ts index 95e0226bf..b30404d5a 100644 --- a/packages/api-client/src/types/API.ts +++ b/packages/api-client/src/types/API.ts @@ -444,4 +444,8 @@ export interface MagentoApiMethods { searchParams: WishlistQueryVariables, customQuery?: CustomQuery, ): Promise>; + + wishlistItemsCount( + customQuery?: CustomQuery, + ): Promise>; } diff --git a/packages/composables/src/composables/useCart/index.ts b/packages/composables/src/composables/useCart/index.ts index fa59696a2..27a7d1be8 100644 --- a/packages/composables/src/composables/useCart/index.ts +++ b/packages/composables/src/composables/useCart/index.ts @@ -335,8 +335,8 @@ const factoryParams: UseCartFactoryParams = { } }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - clear: (context: Context, _params = null) => { - context.$magento.config.state.setCartId(null); + clear: async (context: Context, _params = null) => { + context.$magento.config.state.setCartId(); return factoryParams.load(context, {}); }, @@ -388,7 +388,7 @@ const factoryParams: UseCartFactoryParams = { const apiState = context.$magento.config.state; const { data } : any = await context.$magento.api.cartTotalQty(apiState.getCartId()); - return data?.cart?.total_quantity; + return data?.cart?.total_quantity ?? 0; }, }; diff --git a/packages/composables/src/composables/useUser/index.ts b/packages/composables/src/composables/useUser/index.ts index 00b37a524..c105e7f7b 100644 --- a/packages/composables/src/composables/useUser/index.ts +++ b/packages/composables/src/composables/useUser/index.ts @@ -60,9 +60,8 @@ CustomerCreateInput await context.$magento.api.revokeCustomerToken(params); - apiState.setCustomerToken(null); - apiState.setCartId(null); - context.cart.setCart(null); + apiState.setCustomerToken(); + apiState.setCartId(); }, updateUser: async (context: Context, params) => { Logger.debug('[Magento] Update user information', { params }); diff --git a/packages/composables/src/composables/useWishlist/index.ts b/packages/composables/src/composables/useWishlist/index.ts index 9656820a9..f657abb7a 100644 --- a/packages/composables/src/composables/useWishlist/index.ts +++ b/packages/composables/src/composables/useWishlist/index.ts @@ -30,6 +30,20 @@ const factoryParams: UseWishlistFactoryParams = { return []; }, + loadItemsCount: async (context: Context, params) => { + Logger.debug('[Magento Storefront]: useWishlist.wishlistItemsCount params->', params); + const apiState = context.$magento.config.state; + + if (apiState.getCustomerToken()) { + const { data } = await context.$magento.api.wishlistItemsCount(); + + Logger.debug('[Result]:', { data }); + + return data?.customer?.wishlists ?? []; + } + + return []; + }, addItem: async (context, params) => { const { currentWishlist, diff --git a/packages/composables/src/factories/useCartFactory.ts b/packages/composables/src/factories/useCartFactory.ts index 55d7d3eeb..0e0bc62d1 100644 --- a/packages/composables/src/factories/useCartFactory.ts +++ b/packages/composables/src/factories/useCartFactory.ts @@ -159,22 +159,11 @@ export const useCartFactory = { Logger.debug('useCart.load'); - if (cart.value) { - /** - * Triggering change for hydration purpose, - * temporary issue related with cpapi plugin - */ - loading.value = false; - error.value.load = null; - cart.value = { ...cart.value }; - - return; - } try { loading.value = true; const loadedCart = await _factoryParams.load({ customQuery }); cart.value = loadedCart; - totalQuantity.value = loadedCart.total_quantity; + totalQuantity.value = loadedCart?.total_quantity ?? 0; error.value.load = null; } catch (err) { error.value.load = err; @@ -207,7 +196,7 @@ export const useCartFactory = , }>) => Promise; + loadItemsCount: (context: Context, params: ComposableFunctionArgs<{ + searchParams?: Partial<{ + currentPage: number; + pageSize: number; + }>, + }>) => Promise; addItem: ( context: Context, params: ComposableFunctionArgs<{ @@ -42,13 +48,17 @@ export interface UseWishlistFactoryParams( factoryParams: UseWishlistFactoryParams, ) => { + const calculateWishlistTotal = (wishlists) => wishlists.reduce((prev, next) => (prev?.items_count ?? 0) + (next?.items_count ?? 0), 0); + const useWishlist = (ssrKey = 'useWishlistFactory'): UseWishlist => { const loading: Ref = sharedRef(false, `useWishlist-loading-${ssrKey}`); const wishlist: Ref = sharedRef(null, `useWishlist-wishlist-${ssrKey}`); + const itemsCount: Ref = sharedRef(0, `useWishlist-itemsCount-${ssrKey}`); const error: Ref = sharedRef({ addItem: null, removeItem: null, load: null, + loadItemsCount: null, clear: null, }, `useWishlist-error-${ssrKey}`); @@ -65,6 +75,7 @@ export const useWishlistFactory = { wishlist.value = newWishlist; + itemsCount.value = newWishlist[0].items_count; Logger.debug(`useWishlistFactory/${ssrKey}/setWishlist`, newWishlist); }; @@ -80,6 +91,7 @@ export const useWishlistFactory = { + Logger.debug(`useWishlist/${ssrKey}/loadItemsCount`); + try { + const loadedWishlist = await _factoryParams.loadItemsCount(params); + itemsCount.value = calculateWishlistTotal(loadedWishlist); + error.value.loadItemsCount = null; + } catch (err) { + error.value.loadItemsCount = err; + Logger.error(`useWishlist/${ssrKey}/loadItemsCount`, err); + } + }; + const clear = async () => { Logger.debug(`useWishlist/${ssrKey}/clear`); @@ -138,6 +167,7 @@ export const useWishlistFactory = wishlist.value), + wishlist, + itemsCount, isInWishlist, addItem, load, + loadItemsCount, removeItem, clear, setWishlist, - loading: computed(() => loading.value), - error: computed(() => error.value), + loading, + error, }; }; diff --git a/packages/composables/src/getters/wishlistGetters.ts b/packages/composables/src/getters/wishlistGetters.ts index 3c0f42b85..a856a1dba 100644 --- a/packages/composables/src/getters/wishlistGetters.ts +++ b/packages/composables/src/getters/wishlistGetters.ts @@ -87,7 +87,7 @@ const getProducts = (wishlistData: Wishlist[] | Wishlist): { return []; } - const reducer = (acc, curr) => [...acc, ...curr.items_v2.items.map((item) => ({ + const reducer = (acc, curr) => [...acc, ...curr?.items_v2?.items.map((item) => ({ product: item.product, quantity: item.quantity, added_at: item.added_at, diff --git a/packages/composables/src/types/composables.ts b/packages/composables/src/types/composables.ts index 3ce8a12f3..6fe22188b 100644 --- a/packages/composables/src/types/composables.ts +++ b/packages/composables/src/types/composables.ts @@ -4,7 +4,7 @@ import { ComputedProperty, CustomQuery, } from '@vue-storefront/core'; -import { ComputedRef } from '@vue/composition-api'; +import { ComputedRef, Ref } from '@vue/composition-api'; import { PlatformApi } from '@vue-storefront/core/lib/src/types'; import { FetchPolicy } from './index'; @@ -300,6 +300,7 @@ export interface UseWishlistErrors { addItem: Error; removeItem: Error; load: Error; + loadItemsCount: Error; clear: Error; } @@ -310,7 +311,7 @@ export interface UseWishlist extends Composable { wishlist: ComputedProperty; loading: ComputedProperty; - + itemsCount: Ref; addItem(params: ComposableFunctionArgs<{ product: PRODUCT; }>): Promise; removeItem(params: ComposableFunctionArgs<{ product: WISHLIST_ITEM; }>): Promise; @@ -322,6 +323,8 @@ export interface UseWishlist, }>): Promise; + loadItemsCount(params: ComposableFunctionArgs<{}>): Promise; + clear(): Promise; setWishlist: (wishlist: WISHLIST) => void; diff --git a/packages/theme/components/AppHeader.vue b/packages/theme/components/AppHeader.vue index eff73e1c2..37db9e73a 100644 --- a/packages/theme/components/AppHeader.vue +++ b/packages/theme/components/AppHeader.vue @@ -176,7 +176,7 @@ import { useCategory, useCategorySearch, useFacet, - useUser, useWishlist, wishlistGetters, + useUser, useWishlist, } from '@vue-storefront/magento'; import { computed, @@ -220,8 +220,8 @@ export default defineComponent({ const { toggleCartSidebar, toggleWishlistSidebar, toggleLoginModal } = useUiState(); const { setTermForUrl, getFacetsFromURL, getAgnosticCatLink } = useUiHelpers(); const { isAuthenticated } = useUser(); - const { totalQuantity, loadTotalQty } = useCart(); - const { wishlist } = useWishlist('GlobalWishlist'); + const { totalQuantity: cartTotalItems, loadTotalQty: loadCartTotalQty } = useCart(); + const { itemsCount: wishlistItemsQty, loadItemsCount: loadWishlistItemsCount } = useWishlist('GlobalWishlist'); const { result: searchResult, search: productsSearch, @@ -240,8 +240,7 @@ export default defineComponent({ const searchBarRef = ref(null); const result = ref(null); - const wishlistHasProducts = computed(() => wishlistGetters.getTotalItems(wishlist.value) > 0); - const wishlistItemsQty = computed(() => wishlistGetters.getTotalItems(wishlist.value)); + const wishlistHasProducts = computed(() => wishlistItemsQty.value > 0); const accountIcon = computed(() => (isAuthenticated.value ? 'profile_fill' : 'profile')); @@ -256,7 +255,7 @@ export default defineComponent({ }; useAsync(async () => { - await Promise.all([loadTotalQty(), categoriesListSearch({ pageSize: 20 })]); + await Promise.all([loadCartTotalQty(), loadWishlistItemsCount(), categoriesListSearch({ pageSize: 20 })]); }); const showSearch = () => { @@ -323,7 +322,7 @@ export default defineComponent({ return { accountIcon, - cartTotalItems: totalQuantity, + cartTotalItems, categoryTree, closeOrFocusSearchBar, closeSearch, diff --git a/packages/theme/components/CartSidebar.vue b/packages/theme/components/CartSidebar.vue index ad3928ba8..3da1d37f3 100644 --- a/packages/theme/components/CartSidebar.vue +++ b/packages/theme/components/CartSidebar.vue @@ -302,8 +302,9 @@ export default defineComponent({ const tempProduct = ref(); onMounted(() => { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - loadCart(); + if (cart.value === null) { + loadCart(); + } }); const goToCheckout = async () => { diff --git a/packages/theme/components/LoginModal.vue b/packages/theme/components/LoginModal.vue index a21baaf75..56126e9df 100644 --- a/packages/theme/components/LoginModal.vue +++ b/packages/theme/components/LoginModal.vue @@ -298,7 +298,9 @@ import { } from '@storefront-ui/vue'; import { ValidationProvider, ValidationObserver, extend } from 'vee-validate'; import { required, email } from 'vee-validate/dist/rules'; -import { useUser, useForgotPassword, useWishlist } from '@vue-storefront/magento'; +import { + useUser, useForgotPassword, useWishlist, useCart, +} from '@vue-storefront/magento'; import { useUiState } from '~/composables'; import { customerPasswordRegExp, invalidPasswordMsg } from '~/helpers/customer/regex'; @@ -348,7 +350,9 @@ export default defineComponent({ loading, error: userError, } = useUser(); - const { load: loadWishlist } = useWishlist('GlobalWishlist'); + + const { load: loadCart } = useCart(); + const { loadItemsCount } = useWishlist('GlobalWishlist'); const { request, error: forgotPasswordError, loading: forgotPasswordLoading } = useForgotPassword(); const barTitle = computed(() => { @@ -439,7 +443,7 @@ export default defineComponent({ const handleLogin = async () => { await handleForm(login)(); - await loadWishlist('GlobalWishlist'); + await Promise.all([loadItemsCount('GlobalWishlist'), loadCart()]); }; const handleForgotten = async () => { diff --git a/packages/theme/components/WishlistSidebar.vue b/packages/theme/components/WishlistSidebar.vue index b7b2f6c6d..a59b6b5d1 100644 --- a/packages/theme/components/WishlistSidebar.vue +++ b/packages/theme/components/WishlistSidebar.vue @@ -27,10 +27,8 @@ - + +
-
+