diff --git a/src/features/videoHistory/index.ts b/src/features/videoHistory/index.ts index 5874b3cf..7706e086 100644 --- a/src/features/videoHistory/index.ts +++ b/src/features/videoHistory/index.ts @@ -2,7 +2,7 @@ import { YouTubePlayerDiv } from "@/src/types"; import eventManager from "@/utils/EventManager"; import { browserColorLog, isShortsPage, isWatchPage, sendContentMessage, waitForSpecificMessage } from "@/utils/utilities"; -export default async function setupVideoHistory() { +export async function setupVideoHistory() { // Wait for the "options" message from the content script const optionsData = await waitForSpecificMessage("options", "request_data", "content"); if (!optionsData) return; @@ -19,15 +19,6 @@ export default async function setupVideoHistory() { if (!videoId) return; const videoElement = document.querySelector("video.video-stream.html5-main-video") as HTMLVideoElement | null; if (!videoElement) return; - const videoHistoryOneData = await waitForSpecificMessage("videoHistoryOne", "request_data", "content", { id: videoId }); - if (!videoHistoryOneData) return; - const { - data: { video_history_entry } - } = videoHistoryOneData; - // TODO: get this to only run when the video first loads - if (video_history_entry && video_history_entry.status === "watching" && video_history_entry.timestamp > 0) { - promptUserToResumeVideo(video_history_entry.timestamp); - } const videoPlayerTimeUpdateListener = async () => { const currentTime = await playerContainer.getCurrentTime(); @@ -42,7 +33,7 @@ export default async function setupVideoHistory() { }; eventManager.addEventListener(videoElement, "timeupdate", videoPlayerTimeUpdateListener, "videoHistory"); } -async function promptUserToResumeVideo(timestamp: number) { +export async function promptUserToResumeVideo() { // Get the player container element const playerContainer = isWatchPage() ? (document.querySelector("div#movie_player") as YouTubePlayerDiv | null) : isShortsPage() ? null : null; @@ -51,82 +42,88 @@ async function promptUserToResumeVideo(timestamp: number) { const { video_id: videoId } = await playerContainer.getVideoData(); if (!videoId) return; + const videoHistoryOneData = await waitForSpecificMessage("videoHistoryOne", "request_data", "content", { id: videoId }); + if (!videoHistoryOneData) return; + const { + data: { video_history_entry } + } = videoHistoryOneData; + if (video_history_entry && video_history_entry.status === "watching" && video_history_entry.timestamp > 0) { + // Check if the prompt element already exists + const prompt = document.getElementById("resume-prompt") ?? document.createElement("div"); + // Check if the prompt progress bar already exists + const progressBar = document.getElementById("resume-prompt-progress-bar") ?? document.createElement("div"); + const progressBarDuration = 15; + // Create a countdown timer + let countdown = progressBarDuration; // Countdown in seconds + const countdownInterval = setInterval(() => { + countdown--; + progressBar.style.width = `${(countdown / progressBarDuration) * 100}%`; // Update the progress bar - // Check if the prompt element already exists - const prompt = document.getElementById("resume-prompt") ?? document.createElement("div"); - // Check if the prompt progress bar already exists - const progressBar = document.getElementById("resume-prompt-progress-bar") ?? document.createElement("div"); - const progressBarDuration = 15; - // Create a countdown timer - let countdown = progressBarDuration; // Countdown in seconds - const countdownInterval = setInterval(() => { - countdown--; - progressBar.style.width = `${(countdown / progressBarDuration) * 100}%`; // Update the progress bar - - if (countdown <= 0) { - // Automatically hide the prompt when the countdown reaches 0 + if (countdown <= 0) { + // Automatically hide the prompt when the countdown reaches 0 + clearInterval(countdownInterval); + prompt.style.display = "none"; + } + }, 1000); + if (!document.getElementById("resume-prompt-progress-bar")) { + progressBar.id = "resume-prompt-progress-bar"; + progressBar.style.width = "100%"; + progressBar.style.height = "10px"; // Height of the progress bar + progressBar.style.backgroundColor = "#007acc"; // Progress bar color + progressBar.style.position = "absolute"; + progressBar.style.zIndex = "1000"; // Adjust as needed + progressBar.style.left = "0"; // Place at the left of the prompt + progressBar.style.bottom = "0"; // Place at the bottom of the prompt + progressBar.style.transition = "all 0.5s ease-in-out"; + progressBar.style.borderBottomRightRadius = "5px"; + progressBar.style.borderBottomLeftRadius = "5px"; + prompt.appendChild(progressBar); + } + const resumeButtonClickListener = () => { + // Hide the prompt and clear the countdown timer clearInterval(countdownInterval); prompt.style.display = "none"; + browserColorLog(`Resuming video`, "FgGreen"); + playerContainer.seekTo(video_history_entry.timestamp, true); + }; + const resumeButton = document.createElement("button") ?? document.getElementById("resume-prompt-button"); + // Create the prompt element if it doesn't exist + if (!document.getElementById("resume-prompt")) { + prompt.id = "resume-prompt"; + prompt.style.position = "fixed"; + prompt.style.bottom = "10px"; // Adjust as needed + prompt.style.right = "10px"; // Adjust as needed + prompt.style.backgroundColor = "#181a1b"; + prompt.style.padding = "10px"; + prompt.style.paddingBottom = "20px"; + prompt.style.transition = "all 0.5s ease-in-out"; + prompt.style.borderRadius = "5px"; + prompt.style.boxShadow = "0px 0px 10px rgba(0, 0, 0, 0.2)"; + prompt.style.zIndex = "25000"; + document.body.appendChild(prompt); + resumeButton.id = "resume-prompt-button"; + resumeButton.textContent = "Resume"; + resumeButton.style.backgroundColor = "hsl(213, 80%, 50%)"; + resumeButton.style.border = "transparent"; + resumeButton.style.color = "white"; + resumeButton.style.padding = "10px 20px"; + resumeButton.style.borderRadius = "5px"; + resumeButton.style.boxShadow = "0px 0px 10px rgba(0, 0, 0, 0.2)"; + resumeButton.style.cursor = "pointer"; + resumeButton.style.fontSize = "1.5em"; + resumeButton.style.fontWeight = "bold"; + resumeButton.style.textAlign = "center"; + resumeButton.style.verticalAlign = "middle"; + resumeButton.style.transition = "all 0.5s ease-in-out"; + + prompt.appendChild(resumeButton); } - }, 1000); - if (!document.getElementById("resume-prompt-progress-bar")) { - progressBar.id = "resume-prompt-progress-bar"; - progressBar.style.width = "100%"; - progressBar.style.height = "10px"; // Height of the progress bar - progressBar.style.backgroundColor = "#007acc"; // Progress bar color - progressBar.style.position = "absolute"; - progressBar.style.zIndex = "1000"; // Adjust as needed - progressBar.style.left = "0"; // Place at the left of the prompt - progressBar.style.bottom = "0"; // Place at the bottom of the prompt - progressBar.style.transition = "all 0.5s ease-in-out"; - progressBar.style.borderBottomRightRadius = "5px"; - progressBar.style.borderBottomLeftRadius = "5px"; - prompt.appendChild(progressBar); - } - const resumeButtonClickListener = () => { - // Hide the prompt and clear the countdown timer - clearInterval(countdownInterval); - prompt.style.display = "none"; - browserColorLog(`Resuming video`, "FgGreen"); - playerContainer.seekTo(timestamp, true); - }; - const resumeButton = document.createElement("button") ?? document.getElementById("resume-prompt-button"); - // Create the prompt element if it doesn't exist - if (!document.getElementById("resume-prompt")) { - prompt.id = "resume-prompt"; - prompt.style.position = "fixed"; - prompt.style.bottom = "10px"; // Adjust as needed - prompt.style.right = "10px"; // Adjust as needed - prompt.style.backgroundColor = "#181a1b"; - prompt.style.padding = "10px"; - prompt.style.paddingBottom = "20px"; - prompt.style.transition = "all 0.5s ease-in-out"; - prompt.style.borderRadius = "5px"; - prompt.style.boxShadow = "0px 0px 10px rgba(0, 0, 0, 0.2)"; - prompt.style.zIndex = "25000"; - document.body.appendChild(prompt); - resumeButton.id = "resume-prompt-button"; - resumeButton.textContent = "Resume"; - resumeButton.style.backgroundColor = "hsl(213, 80%, 50%)"; - resumeButton.style.border = "transparent"; - resumeButton.style.color = "white"; - resumeButton.style.padding = "10px 20px"; - resumeButton.style.borderRadius = "5px"; - resumeButton.style.boxShadow = "0px 0px 10px rgba(0, 0, 0, 0.2)"; - resumeButton.style.cursor = "pointer"; - resumeButton.style.fontSize = "1.5em"; - resumeButton.style.fontWeight = "bold"; - resumeButton.style.textAlign = "center"; - resumeButton.style.verticalAlign = "middle"; - resumeButton.style.transition = "all 0.5s ease-in-out"; + if (document.getElementById("resume-prompt-button")) { + eventManager.removeEventListener(resumeButton, "click", "videoHistory"); + } + eventManager.addEventListener(resumeButton, "click", resumeButtonClickListener, "videoHistory"); - prompt.appendChild(resumeButton); + // Display the prompt + prompt.style.display = "block"; } - if (document.getElementById("resume-prompt-button")) { - eventManager.removeEventListener(resumeButton, "click", "videoHistory"); - } - eventManager.addEventListener(resumeButton, "click", resumeButtonClickListener, "videoHistory"); - - // Display the prompt - prompt.style.display = "block"; } diff --git a/src/pages/content/index.tsx b/src/pages/content/index.tsx index 7e6e7260..062af692 100644 --- a/src/pages/content/index.tsx +++ b/src/pages/content/index.tsx @@ -8,11 +8,12 @@ import setPlayerSpeed from "@/src/features/playerSpeed"; import setRememberedVolume from "@/src/features/rememberVolume"; import { addScreenshotButton, removeScreenshotButton } from "@/src/features/screenshotButton"; import adjustVolumeOnScrollWheel from "@/src/features/scrollWheelVolumeControl"; -import setupVideoHistory from "@/src/features/videoHistory"; +import { setupVideoHistory, promptUserToResumeVideo } from "@/src/features/videoHistory"; import volumeBoost from "@/src/features/volumeBoost"; // TODO: Add remaining time feature // TODO: Add always show progressbar feature +// eslint-disable-next-line @typescript-eslint/no-unused-vars const alwaysShowProgressBar = async function () { const player = document.querySelector("#movie_player") as YouTubePlayerDiv | null; if (!player) return; @@ -72,6 +73,7 @@ window.onload = function () { volumeBoost(); adjustVolumeOnScrollWheel(); setupVideoHistory(); + promptUserToResumeVideo(); }; document.addEventListener("yt-player-updated", enableFeatures); /** diff --git a/src/pages/inject/index.tsx b/src/pages/inject/index.tsx index f007efac..232294ec 100644 --- a/src/pages/inject/index.tsx +++ b/src/pages/inject/index.tsx @@ -1,6 +1,6 @@ import { getVideoHistory, setVideoHistory } from "@/src/features/videoHistory/utils"; import { ContentSendOnlyMessageMappings, Messages, StorageChanges, configuration } from "@/src/types"; -import { parseReviver, sendExtensionOnlyMessage, sendExtensionMessage } from "@/src/utils/utilities"; +import { parseReviver, sendExtensionOnlyMessage, sendExtensionMessage, parseStoredValue } from "@/src/utils/utilities"; /** * Adds a script element to the document's root element, which loads a JavaScript file from the extension's runtime URL. @@ -182,8 +182,13 @@ const storageChangeHandler = async (changes: StorageChanges, areaName: string) = Object.entries( changes as { [K in keyof configuration]?: { oldValue?: configuration[K] | undefined; newValue?: configuration[K] | undefined } } ).forEach(([key, change]) => { - if (Object.prototype.hasOwnProperty.call(keyActions, key) && change?.newValue !== undefined) { - if (!change) return; + if ( + change && + Object.prototype.hasOwnProperty.call(keyActions, key) && + change.newValue !== undefined && + change.oldValue !== undefined && + parseStoredValue(change.oldValue as string) !== parseStoredValue(change.newValue as string) + ) { keyActions[key]?.(); } });