@wf-financing/ui 2.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/global.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { IWayflyerUiCtaSdkConstructor, IHeadlessWayflyerSdkConstructor } from '@wf-financing/embedded-types';
1
+ import { IWayflyerUiSdkConstructor, IWayflyerHeadlessSdkConstructor } from '@wf-financing/embedded-types';
2
2
 
3
3
  declare global {
4
4
  interface Window {
5
- WayflyerUiCtaSdk: IWayflyerUiCtaSdkConstructor;
6
- WayflyerHeadlessSdk: IHeadlessWayflyerSdkConstructor;
5
+ WayflyerUiSdk: IWayflyerUiSdkConstructor;
6
+ WayflyerHeadlessSdk: IWayflyerHeadlessSdkConstructor;
7
7
  }
8
8
  }
package/index.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { WayflyerUiCtaSdk } from './sdk';
1
+ import { WayflyerUiSdk } from './sdk';
2
2
 
3
3
  const addHeadlessSdkToWindow = () => {
4
- window.WayflyerUiCtaSdk = WayflyerUiCtaSdk;
4
+ window.WayflyerUiSdk = WayflyerUiSdk;
5
5
  };
6
6
 
7
7
  addHeadlessSdkToWindow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wf-financing/ui",
3
- "version": "2.0.1",
3
+ "version": "3.1.0",
4
4
  "exports": {
5
5
  ".": {
6
6
  "import": "./dist/index.es.js",
@@ -37,7 +37,7 @@
37
37
  "react-aria": "^3.41.1",
38
38
  "react-intl": "^6.2.5",
39
39
  "styled-components": "^6.1.19",
40
- "@wf-financing/embedded-types": "0.4.0"
40
+ "@wf-financing/embedded-types": "0.4.1"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@wayflyer/flyui": "204.4.1"
package/sdk/index.ts CHANGED
@@ -4,12 +4,10 @@ import { PartnerTheme } from '../src/config';
4
4
  import { mountToTarget } from '../src/main';
5
5
  import { getPartnerIdFromToken, getPartnerThemeById } from '../src/utils';
6
6
 
7
- export class WayflyerUiCtaSdk {
7
+ export class WayflyerUiSdk {
8
8
  private readonly partnerTheme: PartnerTheme;
9
9
 
10
10
  constructor(
11
- private readonly targetId: string,
12
- private readonly partnerCallback: PartnerCallbackType,
13
11
  private readonly companyToken: string,
14
12
  private readonly options?: SdkOptionsType,
15
13
  ) {
@@ -17,13 +15,13 @@ export class WayflyerUiCtaSdk {
17
15
  this.partnerTheme = getPartnerThemeById(partnerId);
18
16
  }
19
17
 
20
- mountCta() {
18
+ mountCta(targetId: string, partnerCallback: PartnerCallbackType) {
21
19
  if (document.readyState === 'loading') {
22
20
  document.addEventListener('DOMContentLoaded', () =>
23
- mountToTarget(this.targetId, this.partnerTheme, this.partnerCallback, this.companyToken, this.options),
21
+ mountToTarget(targetId, this.partnerTheme, partnerCallback, this.companyToken, this.options),
24
22
  );
25
23
  } else {
26
- mountToTarget(this.targetId, this.partnerTheme, this.partnerCallback, this.companyToken, this.options);
24
+ mountToTarget(targetId, this.partnerTheme, partnerCallback, this.companyToken, this.options);
27
25
  }
28
26
  }
29
27
  }
@@ -1,9 +1,9 @@
1
- import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { IWayflyerHeadlessSdk, SdkOptionsType } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { HEADLESS_SDK_URL, WAYFLYER_HEADLESS_SDK_ID } from '../config';
4
4
  import { initializeHeadlessSdk, loadScriptAndInitializeSdk } from '../utils';
5
5
 
6
- let cachedSdkInstance: IHeadlessWayflyerSdk | null = null;
6
+ let cachedSdkInstance: IWayflyerHeadlessSdk | null = null;
7
7
 
8
8
  export const getHeadlessSdkInstance = async (companyToken: string, options?: SdkOptionsType) => {
9
9
  try {
@@ -30,7 +30,7 @@ export const getHeadlessSdkInstance = async (companyToken: string, options?: Sdk
30
30
  script.async = true;
31
31
 
32
32
  document.head.appendChild(script);
33
- const headlessSdk: IHeadlessWayflyerSdk = await loadScriptAndInitializeSdk(script, companyToken, options);
33
+ const headlessSdk: IWayflyerHeadlessSdk = await loadScriptAndInitializeSdk(script, companyToken, options);
34
34
  cachedSdkInstance = headlessSdk;
35
35
 
36
36
  return headlessSdk;
@@ -4,6 +4,8 @@ import { styled } from 'styled-components';
4
4
  import { CtaBannerContent } from './CtaBannerContent';
5
5
  import { FooterActions } from './FooterActions';
6
6
  import { HeaderActions } from './HeaderActions';
7
+ import { usePreloadImage } from '../../hooks';
8
+ import { MODAL_LOGO_IMAGE_URL, STATIC_BASE_URL } from '../../config';
7
9
 
8
10
  type BannerContainerPropTypes = {
9
11
  theme: Theme;
@@ -23,6 +25,8 @@ const BannerContainer = styled.aside<BannerContainerPropTypes>`
23
25
  export const CtaBanner = () => {
24
26
  const { isMobile, isTablet } = useDetectDeviceSize();
25
27
  const theme = useTheme();
28
+ const logoImageUrl = `${STATIC_BASE_URL}${MODAL_LOGO_IMAGE_URL}`;
29
+ usePreloadImage(logoImageUrl);
26
30
 
27
31
  return (
28
32
  <BannerContainer $isMobile={isMobile} theme={theme}>
@@ -8,7 +8,7 @@ import {
8
8
  } from '@wf-financing/embedded-types';
9
9
  import { useState } from 'react';
10
10
 
11
- import { useCtaBanner, useContinueHostedApplication } from '../../hooks';
11
+ import { useCtaBanner, useContinueHostedApplication, useInvalidateCta } from '../../hooks';
12
12
  import { ConsentModal } from '../modal/ConsentModal';
13
13
 
14
14
  type CtaResponseType = CtaGenericOfferType | CtaIndicativeOfferType | CtaContinueFundingType;
@@ -18,6 +18,7 @@ export const ProceedFundingButton = () => {
18
18
  const sdkResponse = useCtaBanner();
19
19
  const sdk = sdkResponse.data as CtaResponseType;
20
20
  const continueHostedApplicationMutation = useContinueHostedApplication();
21
+ const invalidateCta = useInvalidateCta();
21
22
 
22
23
  if (!sdk) return null;
23
24
 
@@ -33,6 +34,7 @@ export const ProceedFundingButton = () => {
33
34
  onSuccess: (nextUrl: ContinueHostedApplicationResponseType) => {
34
35
  const { next } = nextUrl;
35
36
  window.open(next);
37
+ invalidateCta();
36
38
  },
37
39
  onError: (error) => {
38
40
  console.error('Failed to continue application', error);
@@ -6,6 +6,7 @@ import { useDetectSmallScreen } from '../../hooks';
6
6
  import { FundingSteps } from './FundingSteps';
7
7
  import { Modal } from './Modal';
8
8
  import { ModalFooter } from './ModalFooter';
9
+ import { STATIC_BASE_URL, MODAL_LOGO_IMAGE_URL } from '../../config';
9
10
 
10
11
  type ConsentModalProps = {
11
12
  isModalOpen: boolean;
@@ -26,7 +27,7 @@ export const ConsentModal = ({ isModalOpen, setIsModalOpen }: ConsentModalProps)
26
27
 
27
28
  return (
28
29
  <Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen}>
29
- {!isSmallScreen && <ImageContainer src="https://static.wayflyer.com/flyui-assets/logos/wayflyer-ef.png" />}
30
+ {!isSmallScreen && <ImageContainer src={`${STATIC_BASE_URL}${MODAL_LOGO_IMAGE_URL}`} />}
30
31
  <Flex direction="column" gap="8" padding={isReducedSpacing ? '4' : '6'}>
31
32
  <Flex direction="column" gap="6">
32
33
  <Text fontStyle="regular" fontWeight="medium" lineHeight="tight" size="2xl">
@@ -3,7 +3,7 @@ import { IconArrowOnSquareUpRight16Line } from '@wayflyer/flyui-icons/16/line';
3
3
  import { StartHostedApplicationResponseType } from '@wf-financing/embedded-types';
4
4
  import { FormattedMessage } from 'react-intl';
5
5
 
6
- import { useDetectSmallScreen, useStartHostedApplication } from '../../hooks';
6
+ import { useDetectSmallScreen, useStartHostedApplication, useInvalidateCta } from '../../hooks';
7
7
 
8
8
  type ModalFooterType = {
9
9
  setOpen: (isOpen: boolean) => void;
@@ -13,6 +13,7 @@ export const ModalFooter = ({ setOpen }: ModalFooterType) => {
13
13
  const { isMobile } = useDetectDeviceSize();
14
14
  const startHostedAppMutation = useStartHostedApplication();
15
15
  const isSmallScreen = useDetectSmallScreen();
16
+ const invalidateCta = useInvalidateCta();
16
17
 
17
18
  const handleStartApplication = () => {
18
19
  startHostedAppMutation.mutate(undefined, {
@@ -20,6 +21,7 @@ export const ModalFooter = ({ setOpen }: ModalFooterType) => {
20
21
  const { next } = nextUrl;
21
22
  setOpen(false);
22
23
  window.open(next);
24
+ invalidateCta();
23
25
  },
24
26
  onError: (error) => {
25
27
  console.error('Failed to start application', error);
@@ -1,3 +1,5 @@
1
+ import { STATIC_BASE_URL, DM_SANS_URL, MERRION_SANS_URL } from './staticUrls';
2
+
1
3
  export type FontParamsType = {
2
4
  fontFamily: string;
3
5
  fontUrl: string;
@@ -6,13 +8,13 @@ export type FontParamsType = {
6
8
 
7
9
  const dmSansParams = {
8
10
  fontFamily: 'DM Sans',
9
- fontUrl: 'https://static.wayflyer.com/flyui-assets/fonts/dm-sans/DMSans-VariableFont_opsz,wght.ttf',
11
+ fontUrl: `${STATIC_BASE_URL}${DM_SANS_URL}`,
10
12
  fallbackFontUrl: 'https://app.wayflyer.com/flyui-assets/fonts/dm-sans/DMSans-VariableFont_opsz,wght.ttf',
11
13
  };
12
14
 
13
15
  const merrionSansParams = {
14
16
  fontFamily: 'Merrion Sans',
15
- fontUrl: 'https://static.wayflyer.com/flyui-assets/fonts/merrion-sans/Merrion_Sans-Medium.woff2',
17
+ fontUrl: `${STATIC_BASE_URL}${MERRION_SANS_URL}`,
16
18
  fallbackFontUrl: 'https://app.wayflyer.com/flyui-assets/fonts/merrion-sans/Merrion_Sans-Medium.woff2',
17
19
  };
18
20
 
@@ -5,3 +5,4 @@ export { WAYFLYER_HEADLESS_SDK_ID } from './scriptId';
5
5
  export { HEADLESS_SDK_URL } from './url';
6
6
  export { WHITELISTED_PARTNER_IDS } from './whitelistedPartnerIds';
7
7
  export type { PartnerId, PartnerTheme } from './whitelistedPartnerIds';
8
+ export { STATIC_BASE_URL, DM_SANS_URL, MERRION_SANS_URL, MODAL_LOGO_IMAGE_URL } from './staticUrls';
@@ -0,0 +1,4 @@
1
+ export const STATIC_BASE_URL = 'https://static.wayflyer.com/flyui-assets';
2
+ export const DM_SANS_URL = '/fonts/dm-sans/DMSans-VariableFont_opsz,wght.ttf';
3
+ export const MERRION_SANS_URL = 'fonts/dm-sans/DMSans-VariableFont_opsz,wght.ttf';
4
+ export const MODAL_LOGO_IMAGE_URL = '/logos/wayflyer-ef.png';
@@ -5,3 +5,5 @@ export { useDetectSmallScreen } from './useDetectSmallScreen';
5
5
  export { useContinueHostedApplication } from './useContinueHostedApplication';
6
6
  export { useDismissCta } from './useDismissCta';
7
7
  export { useRemoveInerted } from './useRemoveInerted';
8
+ export { usePreloadImage } from './usePreloadImage';
9
+ export { useInvalidateCta } from './useInvalidateCta';
@@ -1,4 +1,4 @@
1
- import { useQuery } from '@tanstack/react-query';
1
+ import { useQuery, keepPreviousData } from '@tanstack/react-query';
2
2
 
3
3
  import { fetchCtaBanner } from '../api';
4
4
  import { usePartnerContext } from './usePartnerContext';
@@ -7,8 +7,11 @@ export const useCtaBanner = () => {
7
7
  const { companyToken, options } = usePartnerContext();
8
8
 
9
9
  return useQuery({
10
- queryKey: ['cta', companyToken, options],
10
+ queryKey: ['cta', companyToken],
11
11
  queryFn: () => fetchCtaBanner(companyToken, options),
12
12
  staleTime: Infinity,
13
+ refetchOnWindowFocus: false,
14
+ placeholderData: keepPreviousData,
15
+ enabled: !!companyToken,
13
16
  });
14
17
  };
@@ -0,0 +1,13 @@
1
+ import { useQueryClient } from '@tanstack/react-query';
2
+
3
+ import { usePartnerContext } from './index';
4
+
5
+ export const useInvalidateCta = () => {
6
+ const queryClient = useQueryClient();
7
+ const { companyToken } = usePartnerContext();
8
+
9
+ return () =>
10
+ queryClient.invalidateQueries({
11
+ queryKey: ['cta', companyToken],
12
+ });
13
+ };
@@ -0,0 +1,14 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export function usePreloadImage(src: string) {
4
+ useEffect(() => {
5
+ if (!src) return;
6
+
7
+ const img = new Image();
8
+ img.decoding = 'async';
9
+ img.fetchPriority = 'high';
10
+ img.src = src;
11
+
12
+ img.decode();
13
+ }, [src]);
14
+ }
@@ -1,6 +1,6 @@
1
- import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { IWayflyerHeadlessSdk, SdkOptionsType } from '@wf-financing/embedded-types';
2
2
 
3
- export const initializeHeadlessSdk = (companyToken: string, options?: SdkOptionsType): IHeadlessWayflyerSdk => {
3
+ export const initializeHeadlessSdk = (companyToken: string, options?: SdkOptionsType): IWayflyerHeadlessSdk => {
4
4
  if (!window.WayflyerHeadlessSdk) {
5
5
  throw new Error('Failed to load WayflyerHeadlessSdk from the script.');
6
6
  }
@@ -8,9 +8,9 @@ export const initializeHeadlessSdk = (companyToken: string, options?: SdkOptions
8
8
  const WayflyerHeadlessSdk = window.WayflyerHeadlessSdk;
9
9
 
10
10
  if (options) {
11
- const wayflyerCtaSdk = new WayflyerHeadlessSdk(companyToken, options);
11
+ const wayflyerSdk = new WayflyerHeadlessSdk(companyToken, options);
12
12
 
13
- return wayflyerCtaSdk;
13
+ return wayflyerSdk;
14
14
  }
15
15
 
16
16
  return new WayflyerHeadlessSdk(companyToken);
@@ -1,4 +1,4 @@
1
- import { IHeadlessWayflyerSdk, SdkOptionsType } from '@wf-financing/embedded-types';
1
+ import { IWayflyerHeadlessSdk, SdkOptionsType } from '@wf-financing/embedded-types';
2
2
 
3
3
  import { initializeHeadlessSdk } from './initializeHeadlessSdk';
4
4
 
@@ -6,8 +6,8 @@ export const loadScriptAndInitializeSdk = (
6
6
  script: HTMLScriptElement,
7
7
  companyToken: string,
8
8
  options?: SdkOptionsType,
9
- ): Promise<IHeadlessWayflyerSdk> => {
10
- return new Promise<IHeadlessWayflyerSdk>((resolve, reject) => {
9
+ ): Promise<IWayflyerHeadlessSdk> => {
10
+ return new Promise<IWayflyerHeadlessSdk>((resolve, reject) => {
11
11
  script.onload = () => {
12
12
  try {
13
13
  resolve(initializeHeadlessSdk(companyToken, options));
@@ -1,4 +0,0 @@
1
- declare module 'https://unpkg.com/@wf-financing/headless/dist/index.es.js' {
2
- const HeadlessWayflyerCtaSdk: any;
3
- export = HeadlessWayflyerCtaSdk;
4
- }