Skip to content

Commit

Permalink
[SWM-410] Feat : timestamp in lecturenote
Browse files Browse the repository at this point in the history
  • Loading branch information
oikkoikk committed Nov 10, 2023
1 parent 47d23cf commit be78ad6
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 19 deletions.
1 change: 0 additions & 1 deletion src/api/materials/materials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export async function fetchCourseMaterials(course_video_id: number) {
headers
}).then(async (res) => {
if (res.ok) {
console.log('강의자료 api 호출! ', course_video_id)
return (await res.json()) as Promise<CourseMaterials>;
} else {
return fetchErrorHandling(res, ErrorMessage.COURSE_MATERIALS);
Expand Down
22 changes: 12 additions & 10 deletions src/components/course/CourseTaking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { showModalHandler } from '@/src/util/modal/modalHandler';
import { useQueryClient } from '@tanstack/react-query';
import { QueryKeys } from '@/src/api/queryKeys';
import { ONE_SECOND_IN_MS } from '@/src/constants/time/time';
import convertCompactFormattedTimeToSeconds from '@/src/util/time/convertCompactFormattedTimeToSeconds';
import { YouTubePlayer } from 'react-youtube';

type Props = {
courseDetail: CourseDetail;
Expand All @@ -25,15 +27,12 @@ export default function CourseTaking({

const last_view_video = findVideoById() as LastViewVideo;
const currentPlayingVideo = last_view_video;
const lastVideoInCourse =
courseDetail.sections[courseDetail.sections.length - 1].videos[
courseDetail.sections[courseDetail.sections.length - 1].videos.length - 1
];

const [prevPlayingVideo, setPrevPlayingVideo] =
useState<LastViewVideo | null>(last_view_video);
const [nextPlayingVideo, setNextPlayingVideo] =
useState<LastViewVideo | null>(last_view_video);

const viewDuration = useRef<number>(
parseInt(
sessionStorage.getItem(
Expand All @@ -42,6 +41,7 @@ export default function CourseTaking({
)
);
const currentIntervalID = useRef<NodeJS.Timer | null>(null);
const playerRef = useRef<YouTubePlayer | null>(null);

function findVideoById() {
const videos = courseDetail.sections.flatMap((section) => section.videos);
Expand Down Expand Up @@ -138,11 +138,12 @@ export default function CourseTaking({
}
}, 1 * ONE_SECOND_IN_MS);
}
}, [
courseDetail.course_id,
courseDetail.progress,
queryClient
]);
}, [courseDetail.course_id, courseDetail.progress, queryClient]);

const handleTimestampClick = (formattedTimestamp: string) => {
const timestamp = convertCompactFormattedTimeToSeconds(formattedTimestamp);
playerRef.current?.seekTo(timestamp, true);
};

return (
<div className='flex items-stretch flex-1 h-[calc(100vh-4rem)] bg-sroom-gray-200'>
Expand All @@ -166,6 +167,7 @@ export default function CourseTaking({
viewDuration={viewDuration}
is_completed={currentPlayingVideo.is_completed}
currentIntervalID={currentIntervalID}
playerRef={playerRef}
/>
<div id='controller'>
<CourseVideoController
Expand All @@ -179,7 +181,7 @@ export default function CourseTaking({
/>
</div>
</div>
<CourseMaterialDrawer courseVideoId={currentCourseVideoId} />
<CourseMaterialDrawer courseVideoId={currentCourseVideoId} handleTimestampClick={handleTimestampClick} />
<CourseReviewModal courseId={courseDetail.course_id} />
</div>
);
Expand Down
12 changes: 10 additions & 2 deletions src/components/course/YoutubePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { QueryKeys } from '@/src/api/queryKeys';
import { SessionStorageKeys } from '@/src/constants/courseTaking/courseTaking';
import { ONE_MINUTE_IN_MS, ONE_SECOND_IN_MS } from '@/src/constants/time/time';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/navigation';
import { memo, useCallback, useEffect, useRef } from 'react';
import YouTube, { YouTubeEvent } from 'react-youtube';
import PlayerStates from 'youtube-player/dist/constants/PlayerStates';
import { Options } from 'youtube-player/dist/types';
import { Options, YouTubePlayer } from 'youtube-player/dist/types';

type Props = {
width: number | string;
Expand All @@ -20,6 +21,7 @@ type Props = {
viewDuration: React.MutableRefObject<number>;
is_completed: boolean;
currentIntervalID: React.MutableRefObject<NodeJS.Timer | null>;
playerRef: React.MutableRefObject<YouTubePlayer | null>;
};

const UPDATE_INTERVAL = 10;
Expand All @@ -36,9 +38,12 @@ const YoutubePlayer = ({
end,
viewDuration,
is_completed,
currentIntervalID
currentIntervalID,
playerRef
}: Props) => {
const queryClient = useQueryClient();
const router = useRouter();

isCompleted = is_completed;
const currentRevalidateID = useRef<NodeJS.Timer | null>(null);

Expand Down Expand Up @@ -164,6 +169,9 @@ const YoutubePlayer = ({
videoId={videoId}
className='relative pb-[56.25%] pt-0 h-0 w-full'
iframeClassName='absolute top-0 left-0 w-full h-full'
onReady={(event) => {
playerRef.current = event.target;
}}
onStateChange={async (event) => {
onPlayerStateChange(event);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import LoadingSpinner from '@/src/components/ui/LoadingSpinner';
type Props = {
courseVideoId: number;
drawerHandler: () => void;
handleTimestampClick: (formattedTimestamp: string) => void;
};

const STATUS = {
Expand All @@ -28,7 +29,8 @@ const REFETCH_INTERVAL = 10 * ONE_SECOND_IN_MS;

export default function CourseMaterialContent({
courseVideoId,
drawerHandler
drawerHandler,
handleTimestampClick
}: Props) {
const [activeTab, setActiveTab] =
useState<CourseMaterialType>('lecture-notes');
Expand Down Expand Up @@ -81,6 +83,7 @@ export default function CourseMaterialContent({
<CourseMaterialLectureNotes
lectureNotes={data.summary_brief}
courseVideoId={courseVideoId}
handleTimestampClick={handleTimestampClick}
/>
)}
{activeTab === 'quizzes' && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ import { COURSE_MATERIAL_BREAKPOINT } from '@/src/constants/window/window';

type Props = {
courseVideoId: number;
handleTimestampClick: (formattedTimestamp: string) => void;
};

const BOTTOM_SHEET_MAX_HEIGHT = 700;
const BOTTOM_SHEET_RESIZE_MIN = 100;
const DRAWER_MAX_WIDTH = 580;
const DRAWER_RESIZE_MIN = 250;

export default function CourseMaterialDrawer({ courseVideoId }: Props) {
export default function CourseMaterialDrawer({
courseVideoId,
handleTimestampClick
}: Props) {
const controls = useAnimationControls();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [isDragging, setIsDragging] = useState(false);
Expand Down Expand Up @@ -214,6 +218,7 @@ export default function CourseMaterialDrawer({ courseVideoId }: Props) {
<CourseMaterialContent
courseVideoId={courseVideoId}
drawerHandler={drawerHandler}
handleTimestampClick={handleTimestampClick}
/>
</motion.div>
</AnimatePresence>
Expand Down Expand Up @@ -245,6 +250,7 @@ export default function CourseMaterialDrawer({ courseVideoId }: Props) {
<CourseMaterialContent
courseVideoId={courseVideoId}
drawerHandler={drawerHandler}
handleTimestampClick={handleTimestampClick}
/>
</motion.div>
</AnimatePresence>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ const MarkdownPreview = dynamic(
type Props = {
lectureNotes: LectureNote;
courseVideoId: number;
handleTimestampClick: (formattedTimestamp: string) => void;
};
const DEBOUNCE_TIME = ONE_SECOND_IN_MS / 2;

export default function CourseMaterialLectureNotes({
lectureNotes,
courseVideoId
courseVideoId,
handleTimestampClick
}: Props) {
const sessionStorageContentKey = `${SessionStorageKeys.LECTURE_NOTES}-${courseVideoId}`;
const sessionStorageIsEditModeKey = `${SessionStorageKeys.LECTURE_NOTES_IS_EDIT_MODE}-${courseVideoId}`;
Expand Down Expand Up @@ -109,6 +111,33 @@ export default function CourseMaterialLectureNotes({
sessionStorageIsEditModeKey
]);

useEffect(() => {
const listeners: { [key: string]: () => void } = {};

setTimeout(() => {
const timestampButtons = document.querySelectorAll('button.timestamp');
timestampButtons.forEach((timestampButton) => {
const clickHandler = () => {
handleTimestampClick(timestampButton.id);
};

listeners[timestampButton.id] = clickHandler;

timestampButton.addEventListener('click', clickHandler);
});
}, 500);

return () => {
const timestampButtons = document.querySelectorAll('button.timestamp');
timestampButtons.forEach((timestampButton) => {
const clickHandler = listeners[timestampButton.id];
if (clickHandler) {
timestampButton.removeEventListener('click', clickHandler);
}
});
};
});

useLayoutEffect(() => {
setContent(
() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default function LectureEnrollmentButton({
}, 5 * ONE_SECOND_IN_MS);
setLectureEnrollToast(lecture_code, () => {
clearTimeout(navigateToCourseTaking);
toast.remove('lecture_enrollment');
toast.remove(`lecture_enrollment_${lecture_code}`);
});
}
queryClient.invalidateQueries([QueryKeys.DETAIL, lecture_code]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default function LectureEnrollmentModal({
}, 5 * ONE_SECOND_IN_MS);
setLectureEnrollToast(lecture_code, () => {
clearTimeout(navigateToCourseTaking);
toast.remove('lecture_enrollment');
toast.remove(`lecture_enrollment_${lecture_code}`);
});
}
queryClient.invalidateQueries([QueryKeys.DETAIL, lecture_code]);
Expand Down
2 changes: 1 addition & 1 deletion src/components/lectureEnrollment/SchedulingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function SchedulingModal({
}, 5 * ONE_SECOND_IN_MS);
setLectureEnrollToast(lecture_code, () => {
clearTimeout(navigateToCourseTaking);
toast.remove('lecture_enrollment');
toast.remove(`lecture_enrollment_${lecture_code}`);
});
}
queryClient.invalidateQueries([
Expand Down
1 change: 1 addition & 0 deletions src/constants/time/time.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const ONE_SECOND_IN_MS = 1000;
//login
export const ONE_MINUTE = 60;
export const ONE_HOUR = 60 * 60;
export const ONE_DAY = 24 * ONE_HOUR;
export const SESSION_AGE = ONE_DAY * 3 - ONE_HOUR;
Expand Down
14 changes: 14 additions & 0 deletions src/util/time/convertCompactFormattedTimeToSeconds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ONE_HOUR, ONE_MINUTE } from '@/src/constants/time/time';

export default function convertCompactFormattedTimeToSeconds(
formattedTime: string
) {
const len = formattedTime.length;

const seconds = parseInt(formattedTime.substring(len - 2));
const minutes = parseInt(formattedTime.substring(len - 4, len - 2));

const hours = len > 4 ? parseInt(formattedTime.substring(0, len - 4)) : 0;

return hours * ONE_HOUR + minutes * ONE_MINUTE + seconds;
}

0 comments on commit be78ad6

Please sign in to comment.