From c5fe77f05c6c586e9d5a55139a694354f266ec25 Mon Sep 17 00:00:00 2001 From: Anna Hughes Date: Tue, 17 Dec 2024 20:12:58 +0000 Subject: [PATCH] feat: resources conversations styles (#1237) --- components/cards/AccountActionsCard.tsx | 2 - components/course/CourseHeader.tsx | 16 +- components/layout/Footer.tsx | 2 +- components/layout/TopBar.tsx | 2 +- .../resources/ResourceCompleteButton.tsx | 5 +- .../resources/ResourceConversationAudio.tsx | 144 ++++++++++++++++++ components/resources/ResourceShortVideo.tsx | 62 +++----- components/session/SessionHeader.tsx | 20 +-- .../StoryblokResourceConversationPage.tsx | 140 ++++++++++++----- .../storyblok/StoryblokResourceShortPage.tsx | 37 ++--- components/video/Audio.tsx | 85 +++++++++++ components/video/Video.tsx | 16 +- constants/events.ts | 19 ++- messages/resources/en.json | 3 +- messages/shared/en.json | 1 + styles/theme.ts | 2 +- 16 files changed, 415 insertions(+), 141 deletions(-) create mode 100644 components/resources/ResourceConversationAudio.tsx create mode 100644 components/video/Audio.tsx diff --git a/components/cards/AccountActionsCard.tsx b/components/cards/AccountActionsCard.tsx index 975e91391..7515df41f 100644 --- a/components/cards/AccountActionsCard.tsx +++ b/components/cards/AccountActionsCard.tsx @@ -83,10 +83,8 @@ const AccountActionsCard = () => { id="delete-account-button" variant="outlined" sx={{ - border: `solid 2px ${theme.palette.primary.dark}`, color: theme.palette.primary.dark, '&:hover': { - border: `solid 2px ${theme.palette.primary.dark}`, background: theme.palette.primary.dark, color: theme.palette.common.white, }, diff --git a/components/course/CourseHeader.tsx b/components/course/CourseHeader.tsx index 6fe3b4c27..489fb0ce1 100644 --- a/components/course/CourseHeader.tsx +++ b/components/course/CourseHeader.tsx @@ -7,6 +7,14 @@ import { EventUserData } from '../../utils/logEvent'; import Link from '../common/Link'; import Header from '../layout/Header'; +const buttonStyle = { + background: theme.palette.background.default, + boxShadow: 'none !important', + ':hover': { + background: 'white', + }, +} as const; + export interface CourseHeaderProps { name: string; description: ISbRichtext; @@ -29,13 +37,7 @@ const CourseHeader = (props: CourseHeaderProps) => { return (
-
diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx index ec873bb73..575f18c07 100644 --- a/components/layout/Footer.tsx +++ b/components/layout/Footer.tsx @@ -190,7 +190,7 @@ const Footer = () => { alt={tS(partner.logoAlt)} src={partner.logo} fill - sizes="100vw" + sizes="300px" style={{ objectFit: 'contain', objectPosition: 'left', diff --git a/components/layout/TopBar.tsx b/components/layout/TopBar.tsx index ae4f2d4cd..d412d2876 100644 --- a/components/layout/TopBar.tsx +++ b/components/layout/TopBar.tsx @@ -82,7 +82,7 @@ const TopBar = () => { alt={tS('alt.bloomLogo')} src={bloomLogo} fill - sizes="100vw" + sizes="250px" style={{ objectFit: 'contain', }} diff --git a/components/resources/ResourceCompleteButton.tsx b/components/resources/ResourceCompleteButton.tsx index 4c7950c7c..308ddfc9a 100644 --- a/components/resources/ResourceCompleteButton.tsx +++ b/components/resources/ResourceCompleteButton.tsx @@ -22,13 +22,14 @@ const errorStyle = { } as const; interface ResourceCompleteButtonProps { + resourceName: string; category: RESOURCE_CATEGORIES; storyId: number; eventData: EventUserData; } export const ResourceCompleteButton = (props: ResourceCompleteButtonProps) => { - const { category, storyId, eventData } = props; + const { resourceName, category, storyId, eventData } = props; const t = useTranslations('Resources'); @@ -79,7 +80,7 @@ export const ResourceCompleteButton = (props: ResourceCompleteButtonProps) => { - - {resourceProgress !== PROGRESS_STATUS.COMPLETED && ( - - )} - - - Related content - + {resourceId && ( )} + + + {tS('relatedContent.title')} + + + + {!isLoggedIn && } ); diff --git a/components/storyblok/StoryblokResourceShortPage.tsx b/components/storyblok/StoryblokResourceShortPage.tsx index c81e820e2..7c10a8fa8 100644 --- a/components/storyblok/StoryblokResourceShortPage.tsx +++ b/components/storyblok/StoryblokResourceShortPage.tsx @@ -5,8 +5,6 @@ import Head from 'next/head'; import { useEffect, useMemo, useState } from 'react'; import { PROGRESS_STATUS, RESOURCE_CATEGORIES } from '../../constants/enums'; import { - RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED, - RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED, RESOURCE_SHORT_VIDEO_VIEWED, RESOURCE_SHORT_VIDEO_VISIT_SESSION, } from '../../constants/events'; @@ -83,6 +81,7 @@ const StoryblokResourceShortPage = (props: StoryblokResourceShortPageProps) => { related_exercises, } = props; const t = useTranslations('Resources'); + const tS = useTranslations('Shared'); const userCreatedAt = useTypedSelector((state) => state.user.createdAt); const partnerAccesses = useTypedSelector((state) => state.partnerAccesses); const partnerAdmin = useTypedSelector((state) => state.partnerAdmin); @@ -92,12 +91,12 @@ const StoryblokResourceShortPage = (props: StoryblokResourceShortPageProps) => { PROGRESS_STATUS.NOT_STARTED, ); const [resourceId, setResourceId] = useState(); - const [openTranscriptModal, setOpenTranscriptModal] = useState(null); const eventUserData = getEventUserData(userCreatedAt, partnerAccesses, partnerAdmin); const eventData = useMemo(() => { return { ...eventUserData, + resource_category: RESOURCE_CATEGORIES.SHORT_VIDEO, resource_name: name, resource_storyblok_id: storyId, resource_progress: resourceProgress, @@ -121,22 +120,6 @@ const StoryblokResourceShortPage = (props: StoryblokResourceShortPageProps) => { logEvent(RESOURCE_SHORT_VIDEO_VIEWED, eventData); }, []); - useEffect(() => { - if (openTranscriptModal === null) { - return; - } - - logEvent( - openTranscriptModal - ? RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED - : RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED, - { - ...eventData, - name, - }, - ); - }, [openTranscriptModal, name, eventData]); - const redirectToSession = () => { logEvent(RESOURCE_SHORT_VIDEO_VISIT_SESSION, { ...eventData, @@ -171,8 +154,11 @@ const StoryblokResourceShortPage = (props: StoryblokResourceShortPageProps) => { )} + - {name} + + {name} + { video={video} video_transcript={video_transcript} /> - {isLoggedIn && ( + {resourceId && ( {resourceProgress && } {resourceProgress !== PROGRESS_STATUS.COMPLETED && ( { )} + {/* Description field is not currently displayed */} {/* {render(description, RichTextOptions)} */} @@ -220,6 +208,7 @@ const StoryblokResourceShortPage = (props: StoryblokResourceShortPageProps) => { + {resourceId && ( { /> )} - - Related content + + + {tS('relatedContent.title')} + import('react-player/lazy'), { ssr: false }); + +interface AudioProps { + url: string; + eventData: EventUserData; + eventPrefix: string; + setAudioStarted?: Dispatch> | void; + setAudioFinished?: Dispatch> | void; +} + +const Audio = (props: AudioProps) => { + const { url, eventData, eventPrefix, setAudioStarted, setAudioFinished } = props; + const [audioDuration, setAudioDuration] = useState(0); + const [audioCompleted, setAudioCompleted] = useState(false); + const [audioTimePlayed, setAudioTimePlayed] = useState(0); + + const player = useRef(null); + const audioStarted = () => { + setAudioStarted && setAudioStarted(true); + if (player.current) { + logEvent(`${eventPrefix}_AUDIO_STARTED`, { ...eventData, audio_duration: audioDuration }); + } + }; + + const audioEnded = () => { + if (!!audioCompleted) return; + + setAudioFinished && setAudioFinished(true); + + if (player.current) { + logEvent(`${eventPrefix}_AUDIO_FINISHED`, { + ...eventData, + audio_duration: audioDuration, + }); + } + setAudioCompleted(true); + }; + + const audioPausedOrPlayed = (played: boolean) => { + if (player.current) { + const playedPercentage = Math.round((audioTimePlayed / audioDuration) * 100); + + logEvent(played ? `${eventPrefix}_AUDIO_PLAYED` : `${eventPrefix}_AUDIO_PAUSED`, { + ...eventData, + audio_duration: audioDuration, + audio_current_time: audioTimePlayed, + audio_current_percentage: playedPercentage, + }); + + if (!played && playedPercentage > 95) { + audioEnded(); + } + } + }; + const handleProgress: ((state: OnProgressProps) => void) | undefined = debounce( + (state: OnProgressProps) => { + setAudioTimePlayed(state.playedSeconds); + }, + 300, + ); + + return ( + setAudioDuration(duration)} + onStart={audioStarted} + onEnded={audioEnded} + onPause={() => audioPausedOrPlayed(false)} + onPlay={() => audioPausedOrPlayed(true)} + onProgress={handleProgress} + width="100%" + height="50px" + url={url} + controls + /> + ); +}; + +export default Audio; diff --git a/components/video/Video.tsx b/components/video/Video.tsx index b3b1b78e5..87794792c 100644 --- a/components/video/Video.tsx +++ b/components/video/Video.tsx @@ -39,6 +39,7 @@ const Video = (props: VideoProps) => { setVideoFinished, } = props; const [videoDuration, setVideoDuration] = useState(0); + const [videoCompleted, setVideoCompleted] = useState(false); const [videoTimePlayed, setVideoTimePlayed] = useState(0); const player = useRef(null); @@ -50,6 +51,8 @@ const Video = (props: VideoProps) => { }; const videoEnded = () => { + if (!!videoCompleted) return; + setVideoFinished && setVideoFinished(true); if (player.current) { @@ -58,6 +61,7 @@ const Video = (props: VideoProps) => { video_duration: videoDuration, }); } + setVideoCompleted(true); }; const videoPausedOrPlayed = (played: boolean) => { @@ -71,11 +75,8 @@ const Video = (props: VideoProps) => { video_current_percentage: playedPercentage, }); - if (played) { - } else { - if (playedPercentage > 90) { - videoEnded(); - } + if (!played && playedPercentage > 95) { + videoEnded(); } } }; @@ -106,8 +107,6 @@ const Video = (props: VideoProps) => { : {}; }; - console.log(videoConfig({ url })); - return ( @@ -116,11 +115,10 @@ const Video = (props: VideoProps) => { light={true} onDuration={(duration) => setVideoDuration(duration)} onStart={videoStarted} + onEnded={videoEnded} onPause={() => videoPausedOrPlayed(false)} onPlay={() => videoPausedOrPlayed(true)} - onEnded={videoEnded} onProgress={handleProgress} - playing={true} style={videoStyle} width="100%" height="100%" diff --git a/constants/events.ts b/constants/events.ts index e3de3c8ef..498d7ba7c 100644 --- a/constants/events.ts +++ b/constants/events.ts @@ -84,27 +84,30 @@ export const COURSE_STARTED = 'COURSE_STARTED'; export const COURSE_COMPLETE = 'COURSE_COMPLETE'; export const RESOURCE_CONVERSATION_VIEWED = 'RESOURCE_CONVERSATION_VIEWED'; +export const RESOURCE_SHORT_VIDEO_VIEWED = 'RESOURCE_SHORT_VIDEO_VIEWED'; export const RESOURCE_CONVERSATION_STARTED = 'RESOURCE_CONVERSATION_STARTED'; export const RESOURCE_CONVERSATION_PLAYED = 'RESOURCE_CONVERSATION_PLAYED'; export const RESOURCE_CONVERSATION_PAUSED = 'RESOURCE_CONVERSATION_PAUSED'; export const RESOURCE_CONVERSATION_FINISHED = 'RESOURCE_CONVERSATION_FINISHED'; -export const RESOURCE_CONVERSATION_TRANSCRIPT_OPENED = 'RESOURCE_CONVERSATION_TRANSCRIPT_OPENED'; -export const RESOURCE_CONVERSATION_TRANSCRIPT_CLOSED = 'RESOURCE_CONVERSATION_TRANSCRIPT_CLOSED'; -export const RESOURCE_SHORT_VIDEO_VIEWED = 'RESOURCE_SHORT_VIDEO_VIEWED'; -export const RESOURCE_SHORT_VIDEO_STARTED_REQUEST = 'RESOURCE_SHORT_VIDEO_STARTED_REQUEST'; -export const RESOURCE_SHORT_VIDEO_STARTED_SUCCESS = 'RESOURCE_SHORT_VIDEO_STARTED_SUCCESS'; -export const RESOURCE_SHORT_VIDEO_STARTED_ERROR = 'RESOURCE_SHORT_VIDEO_STARTED_ERROR'; export const RESOURCE_SHORT_VIDEO_PLAYED = 'RESOURCE_SHORT_VIDEO_PLAYED'; export const RESOURCE_SHORT_VIDEO_PAUSED = 'RESOURCE_SHORT_VIDEO_PAUSED'; export const RESOURCE_SHORT_VIDEO_FINISHED = 'RESOURCE_SHORT_VIDEO_FINISHED'; -export const RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED = 'RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED'; -export const RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED = 'RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED'; +export const RESOURCE_SHORT_VIDEO_STARTED_REQUEST = 'RESOURCE_SHORT_VIDEO_STARTED_REQUEST'; +export const RESOURCE_SHORT_VIDEO_STARTED_SUCCESS = 'RESOURCE_SHORT_VIDEO_STARTED_SUCCESS'; +export const RESOURCE_SHORT_VIDEO_STARTED_ERROR = 'RESOURCE_SHORT_VIDEO_STARTED_ERROR'; export const RESOURCE_SHORT_VIDEO_COMPLETE_REQUEST = 'RESOURCE_SHORT_VIDEO_COMPLETE_REQUEST'; export const RESOURCE_SHORT_VIDEO_COMPLETE_SUCCESS = 'RESOURCE_SHORT_VIDEO_COMPLETE_SUCCESS'; export const RESOURCE_SHORT_VIDEO_COMPLETE_ERROR = 'RESOURCE_SHORT_VIDEO_COMPLETE_ERROR'; +export const RESOURCE_CONVERSATION_STARTED_REQUEST = 'RESOURCE_CONVERSATION_STARTED_REQUEST'; +export const RESOURCE_CONVERSATION_STARTED_SUCCESS = 'RESOURCE_CONVERSATION_STARTED_SUCCESS'; +export const RESOURCE_CONVERSATION_STARTED_ERROR = 'RESOURCE_CONVERSATION_STARTED_ERROR'; export const RESOURCE_CONVERSATION_COMPLETE_REQUEST = 'RESOURCE_CONVERSATION_COMPLETE_REQUEST'; export const RESOURCE_CONVERSATION_COMPLETE_SUCCESS = 'RESOURCE_CONVERSATION_COMPLETE_SUCCESS'; export const RESOURCE_CONVERSATION_COMPLETE_ERROR = 'RESOURCE_CONVERSATION_COMPLETE_ERROR'; +export const RESOURCE_CONVERSATION_TRANSCRIPT_OPENED = 'RESOURCE_CONVERSATION_TRANSCRIPT_OPENED'; +export const RESOURCE_CONVERSATION_TRANSCRIPT_CLOSED = 'RESOURCE_CONVERSATION_TRANSCRIPT_CLOSED'; +export const RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED = 'RESOURCE_SHORT_VIDEO_TRANSCRIPT_OPENED'; +export const RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED = 'RESOURCE_SHORT_VIDEO_TRANSCRIPT_CLOSED'; export const RESOURCE_SHORT_VIDEO_VISIT_SESSION = 'RESOURCE_SHORT_VIDEO_VISIT_SESSION'; export const SESSION_VIEWED = 'SESSION_VIEWED'; diff --git a/messages/resources/en.json b/messages/resources/en.json index b4d644012..9acc7e971 100644 --- a/messages/resources/en.json +++ b/messages/resources/en.json @@ -5,7 +5,8 @@ "completed": "Completed", "sessionDetail": "This clip is from Session {sessionNumber}, {sessionName}, from our {courseName} course.", "sessionButtonLabel": "Watch full session", - "transcriptLink": "Watch the video or read the transcript.", + "videoTranscriptLink": "Watch the video or read the transcript.", + "conversationTranscriptLink": "Listen to the conversation or read the transcript.", "resourceFeedback": { "title": "How was this learning?", "subtitle": "We’d love to hear how you found this learning. Your feedback helps us to improve our resources.", diff --git a/messages/shared/en.json b/messages/shared/en.json index 33867fce9..336f029dc 100644 --- a/messages/shared/en.json +++ b/messages/shared/en.json @@ -102,6 +102,7 @@ "button": "Get started" }, "relatedContent": { + "title": "Related content", "short_video": "Short", "conversation": "Conversation", "course": "Course", diff --git a/styles/theme.ts b/styles/theme.ts index 7ed359ecd..3ffa864ae 100644 --- a/styles/theme.ts +++ b/styles/theme.ts @@ -180,7 +180,7 @@ theme = createTheme(theme, { }, outlinedPrimary: { color: '#000000', - borderColor: theme.palette.primary.main, + borderColor: theme.palette.primary.dark, }, containedPrimary: { borderColor: 'transparent',