diff --git a/api/composables.api.md b/api/composables.api.md index 40c9c792c..72994e47f 100644 --- a/api/composables.api.md +++ b/api/composables.api.md @@ -140,6 +140,12 @@ export function getDefaultApiParams(): { [composableName: string]: ShopwareSearchParams; }; +// @beta +export interface IInterceptorCallbackFunction { + // (undocumented) + (payload: any, rootContext?: ApplicationVueContext_2): void; +} + // @beta export const INTERCEPTOR_KEYS: { ADD_TO_CART: string; @@ -230,8 +236,8 @@ export interface IUseCheckout { // @beta export interface IUseIntercept { broadcast: (broadcastKey: string, value?: any) => void; - disconnect: (broadcastKey: string, method: Function) => void; - intercept: (broadcastKey: string, method: Function) => void; + disconnect: (broadcastKey: string, method: IInterceptorCallbackFunction) => void; + intercept: (broadcastKey: string, method: IInterceptorCallbackFunction) => void; } // @beta diff --git a/docs/landing/resources/api/composables.iinterceptorcallbackfunction.md b/docs/landing/resources/api/composables.iinterceptorcallbackfunction.md new file mode 100644 index 000000000..ee2ce0a56 --- /dev/null +++ b/docs/landing/resources/api/composables.iinterceptorcallbackfunction.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@shopware-pwa/composables](./composables.md) > [IInterceptorCallbackFunction](./composables.iinterceptorcallbackfunction.md) + +## IInterceptorCallbackFunction interface + +> This API is provided as a preview for developers and may change based on feedback that we receive. Do not use this API in a production environment. +> + +interface for the callback function of interceptors + +Signature: + +```typescript +export interface IInterceptorCallbackFunction +``` diff --git a/docs/landing/resources/api/composables.iuseintercept.disconnect.md b/docs/landing/resources/api/composables.iuseintercept.disconnect.md index ee2c10654..91a994913 100644 --- a/docs/landing/resources/api/composables.iuseintercept.disconnect.md +++ b/docs/landing/resources/api/composables.iuseintercept.disconnect.md @@ -12,5 +12,5 @@ Stop listening on event Signature: ```typescript -disconnect: (broadcastKey: string, method: Function) => void; +disconnect: (broadcastKey: string, method: IInterceptorCallbackFunction) => void; ``` diff --git a/docs/landing/resources/api/composables.iuseintercept.intercept.md b/docs/landing/resources/api/composables.iuseintercept.intercept.md index 0d873c82b..3ace913ee 100644 --- a/docs/landing/resources/api/composables.iuseintercept.intercept.md +++ b/docs/landing/resources/api/composables.iuseintercept.intercept.md @@ -12,5 +12,5 @@ Intercept broadcasted event Signature: ```typescript -intercept: (broadcastKey: string, method: Function) => void; +intercept: (broadcastKey: string, method: IInterceptorCallbackFunction) => void; ``` diff --git a/docs/landing/resources/api/composables.iuseintercept.md b/docs/landing/resources/api/composables.iuseintercept.md index a1b45d3e9..d5988313d 100644 --- a/docs/landing/resources/api/composables.iuseintercept.md +++ b/docs/landing/resources/api/composables.iuseintercept.md @@ -20,6 +20,6 @@ export interface IUseIntercept | Property | Type | Description | | --- | --- | --- | | [broadcast](./composables.iuseintercept.broadcast.md) | (broadcastKey: string, value?: any) => void | (BETA) Broadcast new event | -| [disconnect](./composables.iuseintercept.disconnect.md) | (broadcastKey: string, method: Function) => void | (BETA) Stop listening on event | -| [intercept](./composables.iuseintercept.intercept.md) | (broadcastKey: string, method: Function) => void | (BETA) Intercept broadcasted event | +| [disconnect](./composables.iuseintercept.disconnect.md) | (broadcastKey: string, method: [IInterceptorCallbackFunction](./composables.iinterceptorcallbackfunction.md)) => void | (BETA) Stop listening on event | +| [intercept](./composables.iuseintercept.intercept.md) | (broadcastKey: string, method: [IInterceptorCallbackFunction](./composables.iinterceptorcallbackfunction.md)) => void | (BETA) Intercept broadcasted event | diff --git a/docs/landing/resources/api/composables.md b/docs/landing/resources/api/composables.md index ae0e3fb92..60a61796c 100644 --- a/docs/landing/resources/api/composables.md +++ b/docs/landing/resources/api/composables.md @@ -18,6 +18,7 @@ | --- | --- | | [ApplicationVueContext](./composables.applicationvuecontext.md) | (BETA) Applicatoin Context for Shopware PWA. It's an extended Vue instance. | | [CurrentPagination](./composables.currentpagination.md) | (BETA) | +| [IInterceptorCallbackFunction](./composables.iinterceptorcallbackfunction.md) | (BETA) interface for the callback function of interceptors | | [IUseAddToCart](./composables.iuseaddtocart.md) | (BETA) interface for [useAddToCart](./composables.useaddtocart.md) composable | | [IUseCart](./composables.iusecart.md) | (BETA) interface for [useCart](./composables.usecart.md) composable | | [IUseCheckout](./composables.iusecheckout.md) | (BETA) interface for [useCheckout](./composables.usecheckout.md) composable | diff --git a/packages/composables/__tests__/useIntercept.spec.ts b/packages/composables/__tests__/useIntercept.spec.ts index 65484c953..37785f363 100644 --- a/packages/composables/__tests__/useIntercept.spec.ts +++ b/packages/composables/__tests__/useIntercept.spec.ts @@ -32,7 +32,10 @@ describe("Composables - useIntercept", () => { const interceptedMethod = jest.fn(); intercept("my-event", interceptedMethod); broadcast("my-event", { someParam: 123 }); - expect(interceptedMethod).toHaveBeenCalledWith({ someParam: 123 }); + expect(interceptedMethod).toHaveBeenCalledWith( + { someParam: 123 }, + rootContextMock + ); }); it("should not intercept disconnected event interceptor", () => { @@ -58,8 +61,14 @@ describe("Composables - useIntercept", () => { intercept("my-event", interceptedMethod); intercept("my-event", secondInterceptedMethod); broadcast("my-event", { someParam: 123 }); - expect(interceptedMethod).toHaveBeenCalledWith({ someParam: 123 }); - expect(secondInterceptedMethod).toHaveBeenCalledWith({ someParam: 123 }); + expect(interceptedMethod).toHaveBeenCalledWith( + { someParam: 123 }, + rootContextMock + ); + expect(secondInterceptedMethod).toHaveBeenCalledWith( + { someParam: 123 }, + rootContextMock + ); }); it("should not invoke any interceptor if there are no registered methods", () => { diff --git a/packages/composables/src/hooks/useUser.ts b/packages/composables/src/hooks/useUser.ts index 257df99f1..d734a1c6e 100644 --- a/packages/composables/src/hooks/useUser.ts +++ b/packages/composables/src/hooks/useUser.ts @@ -32,7 +32,11 @@ import { CustomerRegistrationParams } from "@shopware-pwa/commons/interfaces/req import { ClientApiError } from "@shopware-pwa/commons/interfaces/errors/ApiError"; import { Country } from "@shopware-pwa/commons/interfaces/models/system/country/Country"; import { Salutation } from "@shopware-pwa/commons/interfaces/models/system/salutation/Salutation"; -import { INTERCEPTOR_KEYS, useIntercept } from "@shopware-pwa/composables"; +import { + IInterceptorCallbackFunction, + INTERCEPTOR_KEYS, + useIntercept, +} from "@shopware-pwa/composables"; import { ApplicationVueContext, getApplicationContext } from "../appContext"; /** @@ -162,7 +166,7 @@ export const useUser = (rootContext: ApplicationVueContext): IUseUser => { await refreshUser(); } }; - const onLogout = (fn: Function) => + const onLogout = (fn: IInterceptorCallbackFunction) => intercept(INTERCEPTOR_KEYS.USER_LOGOUT, fn); const refreshUser = async (): Promise => { diff --git a/packages/composables/src/logic/useAddToCart.ts b/packages/composables/src/logic/useAddToCart.ts index d39c5b126..7ddd8602a 100644 --- a/packages/composables/src/logic/useAddToCart.ts +++ b/packages/composables/src/logic/useAddToCart.ts @@ -4,6 +4,7 @@ import { useCart, INTERCEPTOR_KEYS, useIntercept, + IInterceptorCallbackFunction, } from "@shopware-pwa/composables"; import { ClientApiError } from "@shopware-pwa/commons/interfaces/errors/ApiError"; import { ApplicationVueContext, getApplicationContext } from "../appContext"; @@ -103,7 +104,7 @@ export const useAddToCart = ( } }; - const onAddToCart = (fn: Function) => + const onAddToCart = (fn: IInterceptorCallbackFunction) => intercept(INTERCEPTOR_KEYS.ADD_TO_CART, fn); const getStock = computed(() => product && product.stock); diff --git a/packages/composables/src/logic/useIntercept.ts b/packages/composables/src/logic/useIntercept.ts index a0d26b631..533928a1e 100644 --- a/packages/composables/src/logic/useIntercept.ts +++ b/packages/composables/src/logic/useIntercept.ts @@ -36,6 +36,14 @@ export const INTERCEPTOR_KEYS = { USER_LOGOUT: "onUserLogout", }; +/** + * interface for the callback function of interceptors + * @beta + */ +export interface IInterceptorCallbackFunction { + (payload: any, rootContext?: ApplicationVueContext): void; +} + /** * interface for {@link useIntercept} composable * @beta @@ -48,11 +56,17 @@ export interface IUseIntercept { /** * Intercept broadcasted event */ - intercept: (broadcastKey: string, method: Function) => void; + intercept: ( + broadcastKey: string, + method: IInterceptorCallbackFunction + ) => void; /** * Stop listening on event */ - disconnect: (broadcastKey: string, method: Function) => void; + disconnect: ( + broadcastKey: string, + method: IInterceptorCallbackFunction + ) => void; } /** @@ -65,34 +79,43 @@ export const useIntercept = ( ): IUseIntercept => { const { interceptors } = getApplicationContext(rootContext, "useIntercept"); - const localSunscribers: any[] = []; + const localSubscribers: any[] = []; const isVueInstance: boolean = !!getCurrentInstance(); const broadcast = (broadcastKey: string, value?: any) => { if (interceptors[broadcastKey]?.length) { - interceptors[broadcastKey].forEach((broadcastMethod: Function) => - broadcastMethod(value) + interceptors[ + broadcastKey + ].forEach((broadcastMethod: IInterceptorCallbackFunction) => + broadcastMethod(value, rootContext) ); } }; - const intercept = (broadcastKey: string, method: Function) => { + const intercept = ( + broadcastKey: string, + method: IInterceptorCallbackFunction + ) => { if (!interceptors[broadcastKey]) interceptors[broadcastKey] = []; interceptors[broadcastKey].push(method); - isVueInstance && localSunscribers.push({ broadcastKey, method }); + isVueInstance && localSubscribers.push({ broadcastKey, method }); }; - const disconnect = (broadcastKey: string, method: Function) => { + const disconnect = ( + broadcastKey: string, + method: IInterceptorCallbackFunction + ) => { interceptors[broadcastKey] = interceptors[broadcastKey]?.filter( - (subscribedMethod: Function) => subscribedMethod !== method + (subscribedMethod: IInterceptorCallbackFunction) => + subscribedMethod !== method ) || []; }; // Automatically clean listener if it was used in Vue component isVueInstance && onUnmounted(() => { - localSunscribers.forEach(({ broadcastKey, method }) => { + localSubscribers.forEach(({ broadcastKey, method }) => { disconnect(broadcastKey, method); }); }); diff --git a/packages/default-theme/src/locales/de-DE.json b/packages/default-theme/src/locales/de-DE.json index 548742728..4c0adcf8c 100644 --- a/packages/default-theme/src/locales/de-DE.json +++ b/packages/default-theme/src/locales/de-DE.json @@ -177,5 +177,6 @@ "Enter promo code": "Rabattcode eingeben", "Applied promo codes:": "Angewandte Rabattcodes:", "Promotion code added successfully": "Rabattcode wurde erfolgreich angewendet", - "Promotion code does not exist": "Rabattcode existiert nicht" + "Promotion code does not exist": "Rabattcode existiert nicht", + "{productName} has been added to cart.": "{productName} wurde dem Warenkorb hinzugefügt." } diff --git a/packages/default-theme/src/locales/en-GB.json b/packages/default-theme/src/locales/en-GB.json index bd068cc59..142e65807 100644 --- a/packages/default-theme/src/locales/en-GB.json +++ b/packages/default-theme/src/locales/en-GB.json @@ -182,5 +182,6 @@ "Tap any heart next to a product to favorite.": "Tap any heart next to a product to favorite.", "We’ll save them for you here!": "We’ll save them for you here!", "No favourites yet": "No favourites yet", - "Wishlist": "Wishlist" + "Wishlist": "Wishlist", + "{productName} has been added to cart.": "{productName} has been added to cart." } diff --git a/packages/default-theme/src/logic/index.js b/packages/default-theme/src/logic/index.js index e444b7b1d..10401968a 100644 --- a/packages/default-theme/src/logic/index.js +++ b/packages/default-theme/src/logic/index.js @@ -1 +1,2 @@ export * from "@theme/logic/useLocales" +export * from "@/logic/notifications" diff --git a/packages/default-theme/src/logic/notifications/index.js b/packages/default-theme/src/logic/notifications/index.js new file mode 100644 index 000000000..aa74acea8 --- /dev/null +++ b/packages/default-theme/src/logic/notifications/index.js @@ -0,0 +1,28 @@ +import { useNotifications } from "@shopware-pwa/composables" + +export const addToCartNotification = (product, rootContext) => { + const { pushSuccess } = useNotifications(rootContext) + pushSuccess( + rootContext.i18n.t("{productName} has been added to cart.", { + productName: product?.translated?.name || product?.name, + }) + ) +} + +export const addPromotionCodeNotification = (result, rootContext) => { + const { pushSuccess, pushError } = useNotifications(rootContext) + // It's strange that success also ends up as an error in the API response + const err = Object.values(result.errors)[0] + if (err) { + switch (err.messageKey) { + case "promotion-discount-added": + pushSuccess(rootContext.i18n.t("Promotion code added successfully")) + break + case "promotion-not-found": + pushError(rootContext.i18n.t("Promotion code does not exist")) + break + default: + pushError(err.message.toString()) + } + } +} diff --git a/packages/default-theme/src/plugins/notifications.js b/packages/default-theme/src/plugins/notifications.js index a29890580..dbea09e34 100644 --- a/packages/default-theme/src/plugins/notifications.js +++ b/packages/default-theme/src/plugins/notifications.js @@ -1,32 +1,11 @@ +import { useIntercept, INTERCEPTOR_KEYS } from "@shopware-pwa/composables" import { - useIntercept, - INTERCEPTOR_KEYS, - useNotifications, -} from "@shopware-pwa/composables" + addPromotionCodeNotification, + addToCartNotification, +} from "@/logic/notifications" -export default async ({ app }) => { +export default ({ app }) => { const { intercept } = useIntercept(app) - const { pushSuccess, pushError } = useNotifications(app) - intercept(INTERCEPTOR_KEYS.ADD_TO_CART, function ({ product }) { - pushSuccess( - `${product?.translated?.name || product?.name} has been added to cart.` - ) - }) - - intercept(INTERCEPTOR_KEYS.ADD_PROMOTION_CODE, function ({ result }) { - // It's strange that success also ends up as an error in the API response - const err = Object.values(result.errors)[0] - if (err) { - switch (err.messageKey) { - case "promotion-discount-added": - pushSuccess(app.i18n.t("Promotion code added successfully")) - break - case "promotion-not-found": - pushError(app.i18n.t("Promotion code does not exist")) - break - default: - pushError(err.message.toString()) - } - } - }) + intercept(INTERCEPTOR_KEYS.ADD_TO_CART, addToCartNotification) + intercept(INTERCEPTOR_KEYS.ADD_PROMOTION_CODE, addPromotionCodeNotification) }