Skip to content

Commit 02eef23

Browse files
authored
Mpi 151 - merge issues (#51)
* feat: Integrate WidgetConfigProvider - Added WidgetConfigProvider to App component for centralized configuration management. - Updated Logo component to load a logo from a CDN if available, with fallback to a local asset. - Enhanced NavBar component to conditionally display donation and help buttons based on configuration settings. - Refactored sound configuration to utilize WidgetConfig for audio sources, ensuring flexibility in audio management. - Set default logo source in Logo component based on language selection. - Adjusted NavBar component to conditionally handle URLs, avoiding 'no' values for donation and homepage links. - Removed unnecessary state management for instructions in useAudio hook, optimizing audio playback logic. * feat: Enhance WidgetConfigProvider with i18n support for URL configuration - Integrated useTranslation from react-i18next to localize default URLs in WidgetConfigProvider. - Updated default URL values to utilize translation keys for improved internationalization. - Removed unused parameters from test functions that don't require page access - All linting issues now resolved - Added logoSrc to WidgetConfigContext for dynamic logo handling based on configuration. - Updated WidgetConfigProvider to manage logo and audio URL parameters. - Refactored Logo component to utilize logoSrc from context, improving logo management. - Introduced new tests for widget configuration, verifying logo and audio functionality. - Enhanced Playwright tests for better coverage of widget configuration scenarios.
1 parent 8fc04ab commit 02eef23

21 files changed

Lines changed: 926 additions & 89 deletions

src/components/GA4.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ const GA4 = () => {
2020
expires={150}
2121
onAccept={(acceptedByScrolling) => {
2222
if (acceptedByScrolling) {
23-
// triggered if user scrolls past threshold, dna for our case but this didnt work if i didnt write it
23+
// User accepted by scrolling - no action needed
2424
} else {
25-
//initialize GA4 and anonimize IP
25+
// User clicked accept button - initialize GA4 with anonymized IP
2626
ReactGA.initialize(gtag, {
2727
gaOptions: {
2828
anonymizeIp: true,

src/components/Instructions.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export default function BreathingInstructions({
7373
useEffect(() => {
7474
setBackgroundMusic(isSoundEnabled && shouldPlayMusic);
7575
setGuidedVoice(isSoundEnabled && showIntro);
76-
}, [isSoundEnabled, shouldPlayMusic, setBackgroundMusic]);
76+
}, [isSoundEnabled, shouldPlayMusic, setBackgroundMusic, setGuidedVoice, showIntro]);
7777

7878
useEffect(() => {
7979
if (timeLeft === 0 && !showIntro && !exerciseCompleted) {
@@ -89,7 +89,7 @@ export default function BreathingInstructions({
8989
navigate("/thank-you");
9090
resetAnimation();
9191
}
92-
}, [timeLeft, showIntro, exerciseCompleted, navigate]);
92+
}, [timeLeft, showIntro, exerciseCompleted, navigate, resetAnimation]);
9393

9494
useEffect(() => {
9595
if (hasResetRef.current) return;
@@ -118,7 +118,7 @@ export default function BreathingInstructions({
118118
);
119119
hasResetRef.current = false;
120120
};
121-
}, []);
121+
}, [resetAnimation, resetExercise]);
122122

123123
useEffect(() => {
124124
if (!animationSet.waitSet) {

src/components/Logo.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ import React from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { useWidgetConfig } from '../context/WidgetConfigContext';
44

5-
import TrevorLogoEn from '../assets/TrevorLogo-en.svg';
6-
import TrevorLogoEs from '../assets/TrevorLogo-es.svg';
7-
85

96
interface LogoProps {
107
className?: string;
@@ -21,12 +18,7 @@ const LOGO_STYLE: React.CSSProperties = {
2118

2219
const Logo: React.FC<LogoProps> = ({ className }) => {
2320
const { t } = useTranslation();
24-
const { config, language } = useWidgetConfig();
25-
26-
const trevorLogoSrc = language === 'es' ? TrevorLogoEs : TrevorLogoEn;
27-
28-
// Use CDN logo if provided, otherwise use Trevor logo
29-
const logoSrc = config.logoUrl || trevorLogoSrc;
21+
const { config, logoSrc } = useWidgetConfig();
3022

3123
return (
3224
<img

src/components/MainAnimation.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { motion, useAnimation } from "framer-motion";
2-
import { MainAnimationObject } from "../context/animationObjects";
2+
import { MainAnimationObject, CircleProps } from "../context/animationObjects";
33
import { BreathingExerciseFactory } from "../utils/breathingExerciseFactory";
44
import { useBreathingPhases } from "../hooks/useBreathingPhases";
55
import { useEffect } from "react";
@@ -10,12 +10,6 @@ interface MainAnimationProps {
1010
}
1111

1212
export const MainAnimation = ({ animation, isPaused }: MainAnimationProps) => {
13-
const positionTimes = {
14-
top: { duration: 3 },
15-
left: { duration: 3 },
16-
right: { duration: 3 },
17-
bottom: { duration: 3 },
18-
};
1913
const exercise = BreathingExerciseFactory.getExercise("4-7-8");
2014
const isCycle = animation.firstCircle.duration === exercise.cycleDuration;
2115
const inhaleTime = isCycle ? exercise.instructions[0].duration : 0;
@@ -58,7 +52,13 @@ export const MainAnimation = ({ animation, isPaused }: MainAnimationProps) => {
5852

5953
useEffect(() => {
6054
if (isCycle) return;
61-
const start = (controls: any, props: any) => {
55+
const positionTimes = {
56+
top: { duration: 3 },
57+
left: { duration: 3 },
58+
right: { duration: 3 },
59+
bottom: { duration: 3 },
60+
};
61+
const start = (controls: ReturnType<typeof useAnimation>, props: CircleProps) => {
6262
controls.start({
6363
scale: props.scale,
6464
...props.position,
@@ -89,7 +89,7 @@ export const MainAnimation = ({ animation, isPaused }: MainAnimationProps) => {
8989
thirdControls.stop();
9090
fourthControls.stop();
9191
};
92-
}, [animation, isPaused, isCycle]);
92+
}, [animation, isPaused, isCycle, firstControls, secondControls, thirdControls, fourthControls]);
9393

9494

9595

src/components/NavBar.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const NavBar = () => {
88
const { t } = useTranslation();
99
const { config } = useWidgetConfig();
1010

11-
// URLs are now provided with defaults from context (string | null)
1211
const donateUrl = config.donationUrl;
1312
const homepageUrl = config.homeUrl;
1413
const helpUrl = config.helpUrl;

src/components/QuickEscape.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect, useRef } from "react";
1+
import { useState, useEffect, useRef, useCallback } from "react";
22
import { X } from "lucide-react";
33
import { useTranslation } from "react-i18next";
44

@@ -13,7 +13,7 @@ export default function QuickEscape({ showQuickEscape }: QuickEscapeProps) {
1313
const resetTimeoutRef = useRef<number | null>(null);
1414
const isMobile = "ontouchstart" in window;
1515

16-
const incrementCounter = () => {
16+
const incrementCounter = useCallback(() => {
1717
setCounter((prevCounter) => prevCounter + 1);
1818
if (resetTimeoutRef.current !== null) {
1919
clearTimeout(resetTimeoutRef.current);
@@ -24,7 +24,7 @@ export default function QuickEscape({ showQuickEscape }: QuickEscapeProps) {
2424
},
2525
isMobile ? 500 : 1000
2626
);
27-
};
27+
}, [isMobile]);
2828

2929
useEffect(() => {
3030
if (counter >= 3) {
@@ -62,7 +62,7 @@ export default function QuickEscape({ showQuickEscape }: QuickEscapeProps) {
6262
if (resetTimeoutRef.current !== null)
6363
clearTimeout(resetTimeoutRef.current);
6464
};
65-
}, [isMobile]);
65+
}, [isMobile, incrementCounter]);
6666

6767
if (!isOpen) return null;
6868

@@ -100,4 +100,4 @@ export default function QuickEscape({ showQuickEscape }: QuickEscapeProps) {
100100
</div>
101101
)
102102
);
103-
}
103+
}

src/components/WelcomePage.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { MainAnimationContext } from "../context/MainAnimationContext";
55

66
const WelcomePage = () => {
77
const { t } = useTranslation();
8-
const animation = useContext(MainAnimationContext);
8+
const { changeAnimation } = useContext(MainAnimationContext);
99
const [isInfoVisible, setIsInfoVisible] = useState(false);
1010

1111
useEffect(() => {
12-
animation.changeAnimation("main");
13-
}, []);
12+
changeAnimation("main");
13+
}, [changeAnimation]);
1414

1515
const toggleInfo = () => {
1616
setIsInfoVisible(!isInfoVisible);
@@ -46,13 +46,13 @@ const WelcomePage = () => {
4646
onClick={(cycles) => {
4747
switch (cycles) {
4848
case 1:
49-
animation.changeAnimation("wait");
49+
changeAnimation("wait");
5050
break;
5151
case 3:
52-
animation.changeAnimation("wait");
52+
changeAnimation("wait");
5353
break;
5454
case 5:
55-
animation.changeAnimation("wait");
55+
changeAnimation("wait");
5656
break;
5757
}
5858
}}

src/config/soundConfig.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export const getSoundConfig = (config?: WidgetConfig): Record<string, HowlOption
2424
};
2525
};
2626

27-
// Keep the original export for backward compatibility
2827
export const soundConfig: Record<string, HowlOptions> = {
2928
"4-7-8": {
3029
src: [backgroundSound],

src/context/WidgetConfigContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface WidgetConfig {
55
backgroundUrl: string | null;
66
instructionsUrl: string | null;
77
guidedVoiceUrl: string | null;
8+
audioUrl: string | null;
89
donationUrl: string | null;
910
helpUrl: string | null;
1011
homeUrl: string | null;
@@ -15,6 +16,7 @@ export interface WidgetConfigContextType {
1516
isLogoOverridden: boolean;
1617
isAudioOverridden: boolean;
1718
language: 'es' | 'en';
19+
logoSrc: string;
1820
}
1921

2022
export const WidgetConfigContext = createContext<WidgetConfigContextType | undefined>(undefined);

src/context/WidgetConfigProvider.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { useMemo } from "react";
22
import { useTranslation } from "react-i18next";
33
import { WidgetConfigContext, WidgetConfig } from "./WidgetConfigContext";
4+
import TrevorLogoEn from '../assets/TrevorLogo-en.svg';
5+
import TrevorLogoEs from '../assets/TrevorLogo-es.svg';
46

57
interface WidgetConfigProviderProps {
68
children: React.ReactNode;
@@ -14,6 +16,8 @@ const parseQueryParameters = (): WidgetConfig => {
1416
backgroundUrl: urlParams.get('backgroundUrl'),
1517
instructionsUrl: urlParams.get('instructionsUrl'),
1618
guidedVoiceUrl: urlParams.get('guidedVoiceUrl'),
19+
audioUrl: urlParams.get('audioUrl'),
20+
1721
donationUrl: urlParams.get('donationUrl'),
1822
helpUrl: urlParams.get('helpUrl'),
1923
homeUrl: urlParams.get('homeUrl'),
@@ -30,29 +34,35 @@ export const WidgetConfigProvider: React.FC<WidgetConfigProviderProps> = ({ chil
3034
homeUrl: t("homepage-url"),
3135
}), [t]);
3236

33-
const language = useMemo((): 'es' | 'en' => {
34-
return i18n.language.startsWith('es') ? 'es' : 'en';
35-
}, [i18n.language]);
36-
3737
const config = useMemo(() => ({
3838
logoUrl: rawConfig.logoUrl,
3939
backgroundUrl: rawConfig.backgroundUrl,
4040
instructionsUrl: rawConfig.instructionsUrl,
4141
guidedVoiceUrl: rawConfig.guidedVoiceUrl,
42+
audioUrl: rawConfig.audioUrl,
4243
donationUrl: rawConfig.donationUrl === 'no' ? null : (rawConfig.donationUrl || defaults.donationUrl),
4344
helpUrl: rawConfig.helpUrl === 'no' ? null : (rawConfig.helpUrl || defaults.helpUrl),
4445
homeUrl: rawConfig.homeUrl === 'no' ? null : (rawConfig.homeUrl || defaults.homeUrl),
4546
}), [rawConfig, defaults]);
4647

4748
const isLogoOverridden = Boolean(config.logoUrl);
48-
const isAudioOverridden = Boolean(config.backgroundUrl || config.instructionsUrl || config.guidedVoiceUrl);
49+
const isAudioOverridden = Boolean(config.audioUrl);
50+
51+
const defaultLogoSrc = useMemo(() => {
52+
return i18n.language === 'es' ? TrevorLogoEs : TrevorLogoEn;
53+
}, [i18n.language]);
54+
55+
const logoSrc = useMemo(() => {
56+
return config.logoUrl || defaultLogoSrc;
57+
}, [config.logoUrl, defaultLogoSrc]);
4958

5059
const contextValue = useMemo(() => ({
5160
config,
5261
isLogoOverridden,
5362
isAudioOverridden,
54-
language,
55-
}), [config, isLogoOverridden, isAudioOverridden, language]);
63+
language: i18n.language as 'es' | 'en',
64+
logoSrc,
65+
}), [config, isLogoOverridden, isAudioOverridden, i18n.language, logoSrc]);
5666

5767
return (
5868
<WidgetConfigContext.Provider value={contextValue}>

0 commit comments

Comments
 (0)