From dc3fa1e2b92be3980ba4ffd5ee794fa29d99b571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20M=C3=BCnzel?= Date: Wed, 29 May 2024 13:19:11 +0200 Subject: [PATCH] Add code documentation --- i18n.js | 4 + src/common/bridge.js | 227 +++++++++++++++++- src/common/log.js | 19 ++ src/common/pretty.js | 87 +++++++ src/common/utility.js | 86 ++++++- src/components/Icons/PlusSparklesIcon.jsx | 8 + src/components/Icons/ServiceIcon.jsx | 9 + src/components/Icons/Twitch.jsx | 10 + src/components/Icons/Youtube.jsx | 10 + src/components/LoginButton.jsx | 8 + src/components/Logo.jsx | 6 + src/components/Logo.module.css | 3 + src/components/Ui/Switch.jsx | 9 + src/config.js | 3 + src/enableDevHmr.js | 4 + src/entries/background/common/actions.js | 74 ++++-- src/entries/background/common/api.js | 129 ++++++++++ src/entries/background/common/controllers.js | 14 ++ src/entries/background/common/spaceship.js | 20 ++ src/entries/background/common/storage.js | 62 ++++- src/entries/background/script.js | 4 + src/entries/background/serviceWorker.js | 3 + src/entries/contentScript/App.css | 4 + src/entries/contentScript/App.jsx | 6 + .../components/AnalyticsNotice.jsx | 27 ++- .../contentScript/components/AppContainer.jsx | 12 + src/entries/contentScript/components/Card.jsx | 30 ++- .../components/CommunityNoteNotice.jsx | 5 + .../components/ContentRatingNotice.jsx | 11 + .../contentScript/components/DevTools.jsx | 6 + .../contentScript/components/Footer.jsx | 14 ++ .../contentScript/components/H2Header.jsx | 10 + .../contentScript/components/H3Header.jsx | 10 + .../components/MarkReactionForm.jsx | 8 + .../components/MarkReactionNotice.jsx | 5 + .../components/OriginalVideoNotice.jsx | 23 ++ .../contentScript/components/Overlay.jsx | 10 + .../components/PremiumCtaLabel.jsx | 6 + .../components/ReactionPolicyNotice.jsx | 47 ++++ .../components/ReactionsHistoryNotice.jsx | 12 + .../contentScript/components/StatusNotice.jsx | 12 + .../components/StreamerModeNotice.jsx | 5 + .../components/SubmitSuggestionForm.jsx | 5 + .../components/SubmitSuggestionNotice.jsx | 5 + .../contentScript/components/SubwaySurfer.jsx | 7 + .../headless/PlayerProgressListener.jsx | 6 + .../headless/WatchedVideosObserver.jsx | 16 +- .../hooks/useBackgroundEvents.js | 23 +- .../contentScript/hooks/useSubscription.js | 15 ++ src/entries/contentScript/main.jsx | 3 + src/entries/contentScript/renderContent.js | 18 ++ src/entries/contentScript/state.js | 6 + src/entries/contentScriptInternal/app.js | 21 ++ src/entries/contentScriptInternal/main.jsx | 4 + src/entries/popup/App.css | 3 + src/entries/popup/App.jsx | 7 + src/entries/popup/components/LoginView.jsx | 8 + src/entries/popup/components/SettingsView.jsx | 7 + src/entries/popup/main.jsx | 3 + src/enums.js | 3 + src/events.js | 3 + src/hooks/useAuth.js | 25 ++ src/hooks/usePage.js | 6 + src/hooks/useYouTubePlayer.js | 8 + src/manifest.js | 9 +- src/messages.js | 5 + src/shapes.js | 3 + src/styles/content-rating.module.css | 8 + src/styles/input.module.css | 7 + 69 files changed, 1245 insertions(+), 51 deletions(-) diff --git a/i18n.js b/i18n.js index 243fe7e..0743ebb 100644 --- a/i18n.js +++ b/i18n.js @@ -1,3 +1,7 @@ +/** + * This code snippet exports translations from two different locales (en and de) stored in separate JSON files. + * It also exports the defaultLocale as 'en'. + */ import en from '@/locales/en.json'; import de from '@/locales/de.json'; diff --git a/src/common/bridge.js b/src/common/bridge.js index cea7fd2..1417225 100644 --- a/src/common/bridge.js +++ b/src/common/bridge.js @@ -4,26 +4,59 @@ import { useQuery } from '@tanstack/react-query'; import * as messages from '~/messages'; import { sendMessageToBackground } from '~/entries/background/common/spaceship'; -// Auth +// ------------------------------------------------------------------------------------------------------------------------ +// Authentication +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: login + * Description: Sends a message to the background script to initiate the login process. + * + * @returns {Promise} A promise that resolves with the response from the background script. + */ export async function login() { return sendMessageToBackground(messages.LOGIN); } +/** + * Function: logout + * Description: Sends a message to the background script to initiate the logout process. + * + * @returns {Promise} A promise that resolves with the response from the background script. + */ export async function logout() { return sendMessageToBackground(messages.LOGOUT); } +// ------------------------------------------------------------------------------------------------------------------------ // User +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: toggleIncognitoMode + * Description: Toggles the incognito mode by sending a message to the background script. + * + * @param {Object} params - An object containing the length parameter. + * @param {number} params.length - The length parameter for the incognito mode. + * + * @returns {Promise} - A promise that resolves with the response from the background script. + */ export async function toggleIncognitoMode({ length }) { return sendMessageToBackground(messages.TOGGLE_INCOGNITO_MODE, { length, }); } +// ------------------------------------------------------------------------------------------------------------------------ // Player +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Sends player progress data to the background. + * + * @param {Object} data - The player progress data to send. + * @returns {Promise} A promise that resolves when the data is successfully sent. + */ export async function sendPlayerProgress(data) { await sendMessageToBackground( messages.PLAYER_PROGRESS, @@ -31,36 +64,79 @@ export async function sendPlayerProgress(data) { ); } +// ------------------------------------------------------------------------------------------------------------------------ // Status +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: getStatus + * Description: Retrieves the status by sending a message to the background script. + * + * @returns {Promise} The status data retrieved from the background script, or null if no data is returned. + */ async function getStatus() { const { data } = await sendMessageToBackground(messages.GET_STATUS); return data || null; } +// ------------------------------------------------------------------------------------------------------------------------ // Suggestions +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: searchSuggestionAccounts + * + * Description: This function sends a message to the background with a type of SUGGESTIONS_SEARCH_ACCOUNT + * and retrieves a list of suggested accounts based on the provided data. + * + * @param {Object} data - The data to be sent along with the message for searching suggestion accounts. + * + * @returns {Array} - An array of suggested accounts based on the provided data. + */ export async function searchSuggestionAccounts(data) { const { data: accounts } = await sendMessageToBackground(messages.SUGGESTIONS_SEARCH_ACCOUNT, data); return accounts; } +/** + * Submits a suggestion to the background process. + * + * @param {Object} data - The data of the suggestion to submit. + * @returns {Object} The submitted suggestion response. + */ export async function submitSuggestion(data) { const response = await sendMessageToBackground(messages.SUGGESTIONS_SUBMIT, data); return response.suggestion; } +/** + * Function: getWatchedReactions + * Description: Retrieves watched reactions data from the background using sendMessageToBackground function. + * + * @param {Object} data - The data object to be sent to the background for retrieving watched reactions. + * @returns {Promise} - A promise that resolves to the watched reactions data. + */ export async function getWatchedReactions(data) { const { data: suggestion } = await sendMessageToBackground(messages.WATCHED_REACTIONS_GET, data); return suggestion; } -// Content Ratings +// ------------------------------------------------------------------------------------------------------------------------ +// Content Rating +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function to get content ratings for a specific video by sending a message to the background. + * + * @param {Object} params - The parameters for getting content ratings. + * @param {string} params.videoUrl - The URL of the video for which content ratings are requested. + * + * @returns {Promise} A promise that resolves with the content ratings data for the specified video. + */ async function getContentRatingsForVideo({ videoUrl }) { return sendMessageToBackground( messages.CONTENT_RATINGS_GET, @@ -68,8 +144,21 @@ async function getContentRatingsForVideo({ videoUrl }) { ); } +// ------------------------------------------------------------------------------------------------------------------------ // Reactions +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: getReactionsForVideo + * + * Description: This function sends a message to the background with the type 'REACTIONS_GET_FOR_VIDEO' and the provided videoUrl and onlyFollowed parameters. + * + * @param {Object} params - An object containing the videoUrl and onlyFollowed parameters. + * @param {string} params.videoUrl - The URL of the video for which reactions are being requested. + * @param {boolean} params.onlyFollowed - A flag indicating whether to fetch reactions only from followed users. + * + * @returns {Promise} A Promise that resolves with the response from the background after sending the message. + */ async function getReactionsForVideo({ videoUrl, onlyFollowed }) { return sendMessageToBackground( messages.REACTIONS_GET_FOR_VIDEO, @@ -77,6 +166,16 @@ async function getReactionsForVideo({ videoUrl, onlyFollowed }) { ); } +/** + * Function: getReactionOriginalVideos + * + * Description: This function sends a message to the background script to get original videos for a given video URL. + * + * @param {Object} params - An object containing the videoUrl parameter. + * @param {string} params.videoUrl - The URL of the video for which original videos are requested. + * + * @returns {Promise} A promise that resolves with the response from the background script. + */ async function getReactionOriginalVideos({ videoUrl }) { return sendMessageToBackground( messages.REACTIONS_GET_ORIGINAL_VIDEOS, @@ -84,6 +183,13 @@ async function getReactionOriginalVideos({ videoUrl }) { ); } +/** + * Submits a reaction to the background using the sendMessageToBackground function. + * + * @param {Object} data - The data to be submitted for the reaction. + * @returns {Promise} - A promise that resolves to the response from the background. + * @throws {Error} - If there is an error in the response or if the response contains an error message. + */ export async function submitReaction(data) { const response = await sendMessageToBackground(messages.REACTION_SUBMIT, data); @@ -94,8 +200,19 @@ export async function submitReaction(data) { return response; } +// ------------------------------------------------------------------------------------------------------------------------ // Reaction Policy +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Retrieves the reaction policy for a specific video by sending a message to the background script. + * + * @param {Object} params - The parameters for the request. + * @param {string} params.videoUrl - The URL of the video. + * @param {string} params.channelUrl - The URL of the channel. + * @param {string} params.userId - The ID of the user. + * @returns {Promise} A promise that resolves to an object containing the reaction policy data. + */ async function getReactionPolicyForVideo({ videoUrl, channelUrl, userId }) { const data = await sendMessageToBackground( messages.REACTION_POLICY_GET, @@ -105,8 +222,21 @@ async function getReactionPolicyForVideo({ videoUrl, channelUrl, userId }) { return { data: data?.data }; } +// ------------------------------------------------------------------------------------------------------------------------ // Analytics +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: getVideoAnalytics + * + * Description: This function sends a message to the background script to retrieve video analytics data for a specific video URL and account IDs. + * + * @param {Object} params - An object containing the videoUrl and accountIds for which the video analytics data is requested. + * @param {string} params.videoUrl - The URL of the video for which analytics data is requested. + * @param {Array} params.accountIds - An array of account IDs associated with the video. + * + * @returns {Promise} A promise that resolves with the video analytics data retrieved from the background script. + */ async function getVideoAnalytics({ videoUrl, accountIds }) { return sendMessageToBackground( messages.VIDEO_ANALYTICS_GET, @@ -114,8 +244,17 @@ async function getVideoAnalytics({ videoUrl, accountIds }) { ); } +// ------------------------------------------------------------------------------------------------------------------------ // Settings +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Update the visibility settings by sending a message to the background script. + * + * @param {Object} options - The options object. + * @param {boolean} options.visible - The visibility status to be updated. + * @returns {Promise} A promise that resolves with the response from the background script. + */ export async function settingsUpdateVisible({ visible }) { return sendMessageToBackground( messages.SETTING_UPDATE_VISIBLE, @@ -123,6 +262,14 @@ export async function settingsUpdateVisible({ visible }) { ); } +/** + * Set the browser theme to either dark or light mode. + * + * @param {Object} options - The options object. + * @param {boolean} options.isDark - A boolean indicating whether to set the theme to dark mode. + * + * @returns {Promise} A promise that resolves with the response from the background script. + */ export async function setTheme({ isDark }) { return sendMessageToBackground( messages.SET_BROWSER_THEME, @@ -130,8 +277,22 @@ export async function setTheme({ isDark }) { ); } +// ------------------------------------------------------------------------------------------------------------------------ // Community Notes +// ------------------------------------------------------------------------------------------------------------------------ +/** + * Function: getCommunityNotes + * + * Description: + * This function is responsible for fetching community notes for a given video URL by sending a message to the background script. + * + * Parameters: + * - videoUrl (string): The URL of the video for which community notes are to be fetched. + * + * Returns: + * Promise: A promise that resolves with the response containing the community notes data. + */ export async function getCommunityNotes({ videoUrl }) { return sendMessageToBackground( messages.COMMUNITY_NOTES_GET, @@ -143,6 +304,15 @@ export async function getCommunityNotes({ videoUrl }) { // Hooks // ------------------------------------------------------------------------------------------------------------------------ +/** + * Custom hook to fetch and return the status data. + * Uses useQuery hook from @tanstack/react-query to make the API call. + * Query key is set to 'status'. + * Query function calls the getStatus function to fetch the status data. + * Previous data is kept and retries are disabled. + * Data is refetched every 10 seconds (10000 milliseconds). + * @returns {Object} The status data object or null if no data is available. + */ export function useStatus() { return useQuery({ queryKey: ['status'], @@ -153,6 +323,16 @@ export function useStatus() { }); } +/** + * Custom React hook to fetch reaction policy for a video based on the provided video URL, channel URL, and user ID. + * + * @param {Object} params - The parameters object containing videoUrl, channelUrl, and userId. + * @param {string} params.videoUrl - The URL of the video. + * @param {string} params.channelUrl - The URL of the channel. + * @param {string} params.userId - The ID of the user. + * + * @returns {Object} An object containing the query result with reaction policy data. + */ export function useReactionPolicyForVideo({ videoUrl, channelUrl, userId }) { const query = useQuery({ queryKey: ['reaction-policies', videoUrl, channelUrl, userId], @@ -166,6 +346,14 @@ export function useReactionPolicyForVideo({ videoUrl, channelUrl, userId }) { }; } +/** + * Custom React hook to fetch content ratings for a specific video URL. + * + * @param {Object} params - The parameters for fetching content ratings. + * @param {string} params.videoUrl - The URL of the video to fetch content ratings for. + * + * @returns {Object} An object containing the query result with the content ratings data. + */ export function useContentRatings({ videoUrl }) { const query = useQuery({ queryKey: ['content-ratings', videoUrl], @@ -179,6 +367,15 @@ export function useContentRatings({ videoUrl }) { }; } +/** + * Custom React hook to fetch reactions for a video based on the provided video URL and filter option. + * + * @param {Object} params - The parameters for fetching reactions. + * @param {string} params.videoUrl - The URL of the video to fetch reactions for. + * @param {boolean} params.onlyFollowed - A flag to indicate whether to fetch reactions only from followed users. + * + * @returns {Object} An object containing the query result with reaction data for the video. + */ export function useReactions({ videoUrl, onlyFollowed }) { const query = useQuery({ queryKey: ['reactions-to-video', videoUrl, onlyFollowed], @@ -192,6 +389,15 @@ export function useReactions({ videoUrl, onlyFollowed }) { }; } +/** + * Custom React hook to fetch original videos for a given video URL. + * Uses the useQuery hook from '@tanstack/react-query' to make the API call. + * + * @param {Object} params - The parameters for fetching original videos. + * @param {string} params.videoUrl - The URL of the video for which original videos are to be fetched. + * + * @returns {Object} An object containing the query result with original videos data. + */ export function useOriginalVideos({ videoUrl }) { const query = useQuery({ queryKey: ['original-videos-for-video', videoUrl], @@ -205,6 +411,15 @@ export function useOriginalVideos({ videoUrl }) { }; } +/** + * Custom React hook to fetch video analytics data based on the provided video URL and account IDs. + * + * @param {Object} params - Object containing videoUrl and accountIds to fetch analytics data for. + * @param {string} params.videoUrl - The URL of the video to fetch analytics data for. + * @param {Array} params.accountIds - An array of account IDs associated with the video. + * + * @returns {Object} An object containing the query result with data property for easy access to the fetched analytics data. + */ export function useVideoAnalytics({ videoUrl, accountIds }) { const query = useQuery({ queryKey: ['video-analytics', videoUrl, accountIds], @@ -218,6 +433,14 @@ export function useVideoAnalytics({ videoUrl, accountIds }) { }; } +/** + * Custom React hook to fetch community notes for a given video URL. + * + * @param {Object} params - The parameters for fetching community notes. + * @param {string} params.videoUrl - The URL of the video for which to fetch community notes. + * + * @returns {Object} An object containing the query result with the community notes data. + */ export function useCommunityNotes({ videoUrl }) { const query = useQuery({ queryKey: ['community-notes', videoUrl], diff --git a/src/common/log.js b/src/common/log.js index 677f762..6a9cfe3 100644 --- a/src/common/log.js +++ b/src/common/log.js @@ -3,6 +3,12 @@ import browser from 'webextension-polyfill'; import { storageGetUser } from '~/entries/background/common/storage'; import { getApiUrl } from '~/config'; +/** + * Returns the name of the browser based on the user agent string. + * If the browser is not recognized, it returns 'unknown'. + * + * @returns {string} The name of the browser. + */ function getBrowser() { if (!navigator?.userAgent) { return null; @@ -23,6 +29,11 @@ function getBrowser() { return 'unknown'; } +/** + * Asynchronous function that retrieves the user data. + * + * @returns {Promise} A promise that resolves with the user data if available, otherwise null. + */ async function getUser() { if (typeof window !== 'undefined' && window.streamfinityUser) { return window.streamfinityUser; @@ -172,6 +183,14 @@ const log = { }, }; +/** + * Creates a logger instance with the specified section and options. + * + * @param {string} section - The section name for the logger. + * @param {Object} options - Additional options for the logger. + * @param {Function} options.forwardCallback - A callback function to forward log messages. + * @returns {Object} A logger instance with the specified section and options. + */ export function createLogger(section, options = {}) { const logger = { ...log }; logger.section = section; diff --git a/src/common/pretty.js b/src/common/pretty.js index 11f8838..5ef3fc3 100644 --- a/src/common/pretty.js +++ b/src/common/pretty.js @@ -1,14 +1,32 @@ import moment from 'moment'; import { createLogger } from '~/common/log'; +/** + * Format a number into a pretty representation with the 'en-US' locale. + * + * @param {number} number - The number to be formatted. + * @returns {string} The formatted number as a string. + */ export function prettyNumber(number) { return new Intl.NumberFormat('en-US').format(number); } +/** + * Format a number into a compact representation with the 'en-US' locale. + * + * @param {number} number - The number to be formatted. + * @returns {string} The compact formatted number as a string. + */ export function prettyShortNumber(number) { return new Intl.NumberFormat('en-US', { notation: 'compact' }).format(number); } +/** + * Format a number into a price representation with the 'en-US' locale. + * + * @param {number} number - The number to be formatted as a price. + * @returns {string} The formatted price as a string with 2 decimal places. + */ export function prettyPrice(number) { return new Intl.NumberFormat('en-US', { maximumFractionDigits: 2, @@ -16,6 +34,13 @@ export function prettyPrice(number) { }).format(number); } +/** + * Format a number into a currency representation with the specified currency and the 'en-US' locale. + * + * @param {number} number - The number to be formatted as currency. + * @param {string} [currency='EUR'] - The currency code to be used for formatting (default is 'EUR'). + * @returns {string} The formatted currency as a string with 2 decimal places. + */ export function prettyCurrency(number, currency = 'EUR') { return new Intl.NumberFormat('en-US', { style: 'currency', @@ -25,6 +50,15 @@ export function prettyCurrency(number, currency = 'EUR') { }).format(number); } +/** + * Format a duration in seconds into a human-readable time format. + * + * If the duration is greater than or equal to 1 hour (3600 seconds), it will be formatted as 'hh:mm:ss'. + * Otherwise, it will be formatted as 'mm:ss'. + * + * @param {number} seconds - The duration in seconds to be formatted. + * @returns {string} The formatted duration as a string in the 'hh:mm:ss' or 'mm:ss' format. + */ export function prettyDuration(seconds) { if (seconds >= 3600) { return moment.unix(seconds).format('hh:mm:ss'); @@ -33,10 +67,23 @@ export function prettyDuration(seconds) { return moment.unix(seconds).format('mm:ss'); } +/** + * Capitalizes the first letter of a given string. + * + * @param {string} string - The input string to capitalize. + * @returns {string} The input string with the first letter capitalized. + */ export function ucfirst(string) { return string.charAt(0).toUpperCase() + string.slice(1); } +/** + * Limit the length of a text by a specified count. + * + * @param {string} text - The text to be limited. + * @param {number} [count=25] - The maximum number of characters to keep in the text (default is 25). + * @returns {string} The limited text with ellipsis (...) if it exceeds the count. + */ export function strLimit(text, count = 25) { const parts = [...text]; @@ -47,6 +94,13 @@ export function strLimit(text, count = 25) { return text; } +/** + * Generate a slug from the given text by converting it to lowercase, removing diacritics, trimming, replacing spaces with hyphens, + * removing non-word characters except hyphens, and collapsing multiple hyphens into a single one. + * + * @param {string} text - The text to generate a slug from. + * @returns {string} The generated slug from the text. + */ export function strSlug(text) { return text .toString() @@ -59,6 +113,15 @@ export function strSlug(text) { .replace(/--+/g, '-'); } +/** + * Convert a duration in the format 'mm:ss' or 'hh:mm:ss' to seconds. + * + * If the duration is in the format 'mm:ss', it will be converted directly to seconds. + * If the duration is in the format 'hh:mm:ss', it will be converted to total seconds. + * + * @param {string} duration - The duration string in the format 'mm:ss' or 'hh:mm:ss'. + * @returns {number} The total duration in seconds. + */ export function durationToSeconds(duration) { let modifiedDuration = duration; // is in format "00:00", moment assumes that last segment is minutes @@ -71,6 +134,13 @@ export function durationToSeconds(duration) { const log = createLogger('why'); +/** + * Function: why + * Description: This function takes an error object as input and extracts the error message from it. If the error is a string, it directly assigns it as the message. If the error is an object, it checks for various properties like 'message', 'response.data', 'response._data', 'data.errors', 'data.messages', 'data.error', and 'data.message' to determine the message. If the error is neither a string nor an object, it converts it to a string and assigns it as the message. Finally, it returns the extracted message. + * + * @param {any} e - The error object from which to extract the message + * @returns {string} - The extracted error message + */ export function why(e) { let message; @@ -103,6 +173,14 @@ export function why(e) { return message; } +/** + * Function: getVideoUrlWithTimestamp + * Description: This function generates a URL for a video with an optional timestamp parameter. If the timestamp is provided, it appends it to the video's external tracking URL as a query parameter. If no timestamp is provided, it returns the video's external tracking URL as is. + * + * @param {Object} video - The video object for which the URL is generated. + * @param {number} ts - The optional timestamp in seconds to be added as a query parameter (default is null). + * @returns {string} - The URL of the video with an optional timestamp query parameter. + */ function getVideoUrlWithTimestamp(video, ts) { if (!ts) { return video.external_tracking_url; @@ -111,6 +189,15 @@ function getVideoUrlWithTimestamp(video, ts) { return `${video.external_tracking_url}?t=${ts}`; } +/** + * Function that builds a reaction URL based on the provided reaction object. + * If the reaction is from a video, it appends the timestamp to the video URL. + * If the reaction is from a stream, it finds the corresponding video and appends the timestamp. + * If none of the above, it returns the service external URL from the reaction info. + * + * @param {Object} reaction - The reaction object containing information about the reaction source. + * @returns {string} The URL with timestamp or service external URL based on the reaction type. + */ export function buildReactionFromUrl(reaction) { if (reaction.from_video) { return getVideoUrlWithTimestamp(reaction.from_video, reaction.video_seconds_from); diff --git a/src/common/utility.js b/src/common/utility.js index db10f9f..a0ce062 100644 --- a/src/common/utility.js +++ b/src/common/utility.js @@ -4,14 +4,32 @@ import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { why } from '~/common/pretty'; +/** + * Combines multiple class names into a single string using Tailwind CSS utility classes. + * + * @param {...string} inputs - The class names to be combined. + * @returns {string} - The combined class names string. + */ export function cn(...inputs) { return twMerge(clsx(inputs)); } +/** + * Builds the frontend URL by combining the VITE_FRONTEND_URL environment variable with the provided path. + * + * @param {string} path - The path to be appended to the frontend URL. + * @returns {string} - The complete frontend URL. + */ export function buildFrontendUrl(path) { return `${import.meta.env.VITE_FRONTEND_URL}${path}`; } +/** + * Extracts the video ID from a YouTube video link. + * + * @param {string} link - The YouTube video link from which to extract the video ID. + * @returns {string|null} - The extracted video ID if found, otherwise null. + */ export function getIdFromLink(link) { const match = link.match(/watch(.*)v=(?[A-Za-z0-9\-_]+)/); @@ -23,10 +41,12 @@ export function getIdFromLink(link) { } /** - * @param callback - * @param {number} intervalMs - * @param {number} maxTries - * @returns {(Promise|function)[]} + * Retries a callback function until a condition is met or a maximum number of tries is reached, clearing the interval on success or failure. + * + * @param {Function} callback - The callback function to be executed on each interval. + * @param {number} [intervalMs=300] - The interval in milliseconds between each callback execution. + * @param {number} [maxTries=300] - The maximum number of tries before rejecting the promise. + * @returns {[Promise, Function]} - A promise that resolves when the condition is met, and a function to manually clear the interval. */ export function retryFindWithClearFn(callback, intervalMs = 300, maxTries = 300) { let intervalId; @@ -60,10 +80,12 @@ export function retryFindWithClearFn(callback, intervalMs = 300, maxTries = 300) } /** - * @param callback - * @param {number} intervalMs - * @param {number} maxTries - * @returns {Promise} + * Retry finding an element using the provided callback function until the element is found or the maximum number of tries is reached. + * + * @param {Function} callback - The callback function to be executed to find the element. + * @param {number} [intervalMs=300] - The interval in milliseconds between each try. + * @param {number} [maxTries=300] - The maximum number of tries before giving up. + * @returns {Promise} A promise that resolves with the found element or rejects if the element is not found within the maximum tries. */ export async function retryFind(callback, intervalMs = 300, maxTries = 300) { const [findFn] = retryFindWithClearFn(callback, intervalMs, maxTries); @@ -74,14 +96,29 @@ export async function retryFind(callback, intervalMs = 300, maxTries = 300) { // Get elements on page +/** + * Returns the YouTube player element on the page. + * + * @returns {Element|null} The YouTube player element if found, otherwise null. + */ export function getYouTubePlayer() { return document.querySelector('video.video-stream.html5-main-video'); } +/** + * Returns the YouTube player progress bar element on the page. + * + * @returns {Element|null} The YouTube player progress bar element if found, otherwise null. + */ export function getYouTubePlayerProgressBar() { return document.querySelector('.ytp-progress-bar-container .ytp-progress-bar .ytp-timed-markers-container'); } +/** + * Returns the publish date of the current video element on the page. + * + * @returns {moment|null} The publish date of the current video as a moment object if found, otherwise null. + */ export function getCurrentVideoPublishDate() { const metaPublish = document.querySelector('meta[itemprop="datePublished"]'); if (metaPublish) { @@ -93,6 +130,13 @@ export function getCurrentVideoPublishDate() { // Retry find elements on page +/** + * Find the YouTube player element by retrying for a specified number of times at a given interval. + * + * @param {number} interval - The interval in milliseconds between each retry (default is 300ms). + * @param {number} maxTries - The maximum number of retries before giving up (default is 300). + * @returns {Promise} - A promise that resolves with the found YouTube player element, or rejects if not found. + */ export function findYouTubePlayer(interval = 300, maxTries = 300) { return retryFind( () => getYouTubePlayer(), @@ -101,6 +145,13 @@ export function findYouTubePlayer(interval = 300, maxTries = 300) { ); } +/** + * Find the video player bar element by querying the DOM with the specified interval and maximum number of tries. + * + * @param {number} interval - The interval in milliseconds to wait between each try. + * @param {number} maxTries - The maximum number of tries before giving up. + * @returns {Array} An array containing an async function that returns a promise to find the element and a function to clear the interval. + */ export function findVideoPlayerBar(interval = 300, maxTries = 300) { return retryFindWithClearFn( () => document.querySelector('.ytp-progress-bar-container .ytp-progress-bar .ytp-progress-list'), @@ -111,12 +162,24 @@ export function findVideoPlayerBar(interval = 300, maxTries = 300) { // Toaster +/** + * Displays a success toast message using the provided message and options. + * + * @param {string} message - The message to be displayed in the toast. + * @param {object} [options] - The options for customizing the toast display (optional). + */ export function toastSuccess(message, options) { toast.success(message, { ...(options || {}), }); } +/** + * Displays a warning toast message using the provided warning message and options. + * + * @param {string} warning - The warning message to be displayed in the toast. + * @param {object} [options] - The options for customizing the toast display (optional). + */ export function toastWarn(warning, options) { toast(warning, { ...options || {}, @@ -126,6 +189,13 @@ export function toastWarn(warning, options) { }); } +/** + * Display an error toast message using react-hot-toast with a custom error message generated by the 'why' function. + * + * @param {any} error - The error object or message to display in the toast. + * @param {object} options - Additional options for customizing the toast display (optional). + * @returns {void} + */ export function toastError(error, options) { toast.error(() => why(error), { ...(options || {}), diff --git a/src/components/Icons/PlusSparklesIcon.jsx b/src/components/Icons/PlusSparklesIcon.jsx index 19cb21c..2f83caa 100644 --- a/src/components/Icons/PlusSparklesIcon.jsx +++ b/src/components/Icons/PlusSparklesIcon.jsx @@ -1,6 +1,14 @@ import React from 'react'; import PropTypes from 'prop-types'; +/** + * Function component that renders a SVG icon of PlusSparkles. + * + * @param {string} className - Additional CSS class for styling. + * @param {number} width - The width of the SVG icon. + * @param {number} height - The height of the SVG icon. + * @returns {JSX.Element} SVG icon of PlusSparkles. + */ function PlusSparkles({ className = '', width = 20, height = 20 }) { return ( ( } + * Sends a notice message to the content script. + * + * @param {string} type - The type of the notice message. + * @param {string} message - The content of the notice message. + * @returns {Promise} - A promise that resolves once the notice message is sent. */ async function sendNotice(type, message) { await sendMessageToContentScript(EVENT_NOTICE, { @@ -35,6 +25,11 @@ async function sendNotice(type, message) { }); } +/** + * Function to log out the user by clearing storage and sending a message to content script to refresh authentication. + * + * @returns {Object} Object indicating the success of the logout operation. + */ export async function logout() { await clearStorage(); @@ -43,11 +38,14 @@ export async function logout() { return { success: true }; } -/** - * @type {ExtensionStatus} - */ let extensionStatus = {}; +/** + * Function: getStatus + * Description: This function retrieves the status of the extension and performs various actions based on the status. + * + * @returns {Object} An object containing the data retrieved from the extension status. + */ export async function getStatus() { const token = await storageGetToken(); @@ -90,6 +88,12 @@ export async function getStatus() { }; } +/** + * Sends the player progress to the server. + * + * @param {Object} data - The data containing the player progress information. + * @returns {Object} An object indicating the success of sending the player progress. + */ export async function sendPlayerProgress(data) { if (!extensionStatus.user) { log.debug('no status data, dont send player progress'); @@ -119,6 +123,12 @@ export async function sendPlayerProgress(data) { }; } +/** + * Function to fetch user data using the provided access token and store the token and user data in browser storage. + * + * @param {string} accessToken - The access token to authenticate the user. + * @returns {Object} - An object containing the user data if successful, otherwise null. + */ export async function loginFetchUser(accessToken) { try { const { data } = await getAuthenticatedUser({ @@ -141,6 +151,14 @@ export async function loginFetchUser(accessToken) { return { user: null }; } +/** + * Function to handle the login process. + * + * This function initiates the login process by redirecting the user to the authorization URL, + * obtaining the access token, fetching user data, and sending a refresh authentication event. + * + * @returns {Object} An object containing the user data if successful, or an error message if login fails. + */ export async function login() { const redirectUri = browser.identity.getRedirectURL(); @@ -156,6 +174,7 @@ export async function login() { log.debug('login', url); try { + // TODO: should this be here? // https://cfiiggfhnccbchheekifachmajflgcgn.chromiumapp.org/ // #access_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ // &token_type=Bearer @@ -190,6 +209,13 @@ export async function login() { } } +/** + * Update the visibility setting in storage and send a message to content script to refresh settings. + * + * @param {Object} data - The data containing the visibility setting. + * @param {boolean} data.visible - The visibility setting to be updated. + * @returns {Object} An empty object indicating the completion of the update process. + */ export async function updateSettingUpdateVisible(data) { await storageSetSettingVisible(data.visible); @@ -198,6 +224,11 @@ export async function updateSettingUpdateVisible(data) { return {}; } +/** + * Function: openPopup + * Description: Opens the browser action popup if available. Note that browser.action.openPopup may not be available in Firefox. + * Returns: An empty object. + */ export async function openPopup() { // WARNING: browser.action is not available in Firefox for some reason if (!browser.action || !('openPopup' in browser.action)) { @@ -210,6 +241,13 @@ export async function openPopup() { return {}; } +/** + * Update the browser action icon scheme based on the dark mode setting. + * + * @param {Object} options - The options object. + * @param {boolean} options.dark - A boolean indicating whether dark mode is enabled. + * @returns {Object} An empty object. + */ export async function updateScheme({ dark }) { if (!browser.action) { log.warn('updateScheme', 'no browser action available'); diff --git a/src/entries/background/common/api.js b/src/entries/background/common/api.js index 15311da..43638df 100644 --- a/src/entries/background/common/api.js +++ b/src/entries/background/common/api.js @@ -7,6 +7,19 @@ import { getApiUrl } from '~/config'; const log = createLogger('Background-API'); +/** + * Makes an API request to the specified URL with the given options. + * + * @param {string} url - The URL to make the API request to. + * @param {Object} opts - The options for the API request. + * @param {Object} opts.query - The query parameters for the request. + * @param {Object} opts.headers - The headers for the request. + * @param {string} opts.token - The authorization token for the request. + * @param {Object} opts.json - The JSON data to send in the request body. + * + * @returns {Promise} - A promise that resolves to an object with the response status and data. + * @throws {Error} - If an error occurs during the API request. + */ export async function api(url, opts) { const options = opts; @@ -53,6 +66,11 @@ export async function api(url, opts) { } } +/** + * Retrieves the extension status by gathering information such as platform, version, and operating system. + * + * @returns {Promise} A promise that resolves with the extension status data, including platform, version, and OS. + */ export async function getExtensionStatus() { let platform = null; let version = null; @@ -84,12 +102,26 @@ export async function getExtensionStatus() { }); } +/** + * Function to retrieve the authenticated user information. + * + * @param {Object} options - The options object containing the authentication token. + * @param {string} options.token - The authentication token for the user. + * + * @returns {Promise} A promise that resolves to an object with the status and data of the API response. + */ export async function getAuthenticatedUser({ token }) { return api('users/@me', { token, }); } +/** + * Update the visibility of the extension by sending a POST request to the 'extension/visibility' API endpoint. + * + * @param {Object} data - The data to be sent in the request body. + * @returns {Promise} A promise that resolves to the suggestion data received from the API. + */ export async function updateExtensionVisibility(data) { const { data: suggestion } = await api('extension/visibility', { method: 'POST', @@ -100,6 +132,14 @@ export async function updateExtensionVisibility(data) { return suggestion; } +/** + * Function to search for suggestion accounts based on a query. + * + * @param {Object} options - The options object containing the query to search for. + * @param {string} options.query - The query string to search for suggestion accounts. + * + * @returns {Promise} - A promise that resolves with an array of suggestion accounts matching the query. + */ export async function searchSuggestionAccounts({ query }) { const { data } = await api('suggestions/search-account', { token: await storageGetToken(), @@ -111,6 +151,12 @@ export async function searchSuggestionAccounts({ query }) { return data; } +/** + * Submits a suggestion by making a POST request to the 'suggestions' API endpoint. + * + * @param {Object} data - The data of the suggestion to be submitted. + * @returns {Object} The submitted suggestion data. + */ export async function submitSuggestion(data) { const { data: suggestion } = await api('suggestions', { method: 'POST', @@ -121,6 +167,13 @@ export async function submitSuggestion(data) { return suggestion; } +/** + * Retrieves the watched reactions for a specific user. + * + * @param {Object} params - The parameters for the function. + * @param {string} params.userId - The ID of the user for whom to retrieve watched reactions. + * @returns {Array} An array of watched reactions for the specified user. + */ export async function getWatchedReactions({ userId }) { const { data: watchedReactions } = await api(`users/${userId}/watched-reactions`, { token: await storageGetToken(), @@ -129,6 +182,12 @@ export async function getWatchedReactions({ userId }) { return watchedReactions; } +/** + * Submits a reaction by making a POST request to the 'reactions' API endpoint. + * + * @param {Object} data - The data object containing the information for the reaction. + * @returns {Promise} - A promise that resolves to the response data from the API. + */ export async function submitReaction(data) { const { data: response } = await api('reactions', { method: 'POST', @@ -139,6 +198,15 @@ export async function submitReaction(data) { return response; } +/** + * Retrieves reactions for a specific video based on the provided video URL and filter criteria. + * + * @param {Object} params - The parameters for fetching reactions. + * @param {string} params.videoUrl - The URL of the video to fetch reactions for. + * @param {boolean} params.onlyFollowed - Flag to indicate whether to fetch reactions only from followed users. + * + * @returns {Promise} A promise that resolves to the reactions policy object for the specified video. + */ export async function getReactionsForVideo({ videoUrl, onlyFollowed }) { const { data: policy } = await api('reactions/to-video', { token: await storageGetToken(), @@ -151,6 +219,16 @@ export async function getReactionsForVideo({ videoUrl, onlyFollowed }) { return policy; } +/** + * Retrieves the reaction policy for a specific video based on the provided parameters. + * + * @param {Object} params - The parameters object containing the videoUrl, channelUrl, and userId. + * @param {string} params.videoUrl - The URL of the video for which the reaction policy is being retrieved. + * @param {string} params.channelUrl - The URL of the channel associated with the video. + * @param {string} params.userId - The ID of the user for whom the reaction policy is being fetched. + * + * @returns {Promise} A promise that resolves to the reaction policy data object. + */ export async function getReactionPolicy({ videoUrl, channelUrl, userId }) { const { data: policy } = await api('reaction-policies/for-video', { query: { @@ -163,6 +241,14 @@ export async function getReactionPolicy({ videoUrl, channelUrl, userId }) { return policy; } +/** + * Retrieves content ratings for a specific video URL. + * + * @param {Object} params - The parameters for fetching content ratings. + * @param {string} params.videoUrl - The URL of the video for which ratings are to be fetched. + * + * @returns {Promise} - A promise that resolves with an array of content ratings for the specified video. + */ export async function getContentRatings({ videoUrl }) { const { data: ratings } = await api('content-ratings/by-video', { query: { @@ -173,6 +259,14 @@ export async function getContentRatings({ videoUrl }) { return ratings; } +/** + * Retrieves the original videos associated with a given video URL. + * + * @param {Object} options - The options object containing the video URL. + * @param {string} options.videoUrl - The URL of the video for which original videos are to be retrieved. + * + * @returns {Array} An array of original videos associated with the provided video URL. + */ export async function getOriginalVideosForVideo({ videoUrl }) { const { data: videos } = await api('reactions/original-videos', { query: { @@ -183,6 +277,17 @@ export async function getOriginalVideosForVideo({ videoUrl }) { return videos; } +/** + * Creates playback progress for a given live stream. + * + * @param {Object} options - The options object containing data and liveStreamId. + * @param {Object} options.data - The data object containing timestamp, date, and url. + * @param {number} options.data.timestamp - The timestamp of the playback progress. + * @param {string} options.data.date - The date when the playback progress was created. + * @param {string} options.data.url - The original URL of the playback progress. + * @param {string} liveStreamId - The ID of the live stream for which playback progress is being created. + * @returns {Promise} - A promise that resolves once the playback progress is created. + */ export async function createPlaybackProgress({ data, liveStreamId }) { const items = [{ timestamp: Math.round(data.timestamp), @@ -198,6 +303,15 @@ export async function createPlaybackProgress({ data, liveStreamId }) { }); } +/** + * Function to retrieve video analytics data for a given video URL and account IDs. + * + * @param {Object} params - Object containing videoUrl and accountIds. + * @param {string} params.videoUrl - The URL of the video for which analytics data is to be retrieved. + * @param {Array} params.accountIds - An array of account IDs for which analytics data is to be retrieved. + * + * @returns {Promise} - A promise that resolves to the analytics data for the specified video and account IDs. + */ export async function getVideoAnalytics({ videoUrl, accountIds }) { const { data: analytics } = await api('analytics/videos', { token: await storageGetToken(), @@ -210,6 +324,14 @@ export async function getVideoAnalytics({ videoUrl, accountIds }) { return analytics; } +/** + * Retrieves community notes for a specific video URL. + * + * @param {Object} params - The parameters for fetching community notes. + * @param {string} params.videoUrl - The URL of the video for which to fetch community notes. + * + * @returns {Promise} - A promise that resolves to an array of community notes for the specified video URL. + */ export async function getCommunityNotes({ videoUrl }) { const { data: notes } = await api('community/notes/for-video', { token: await storageGetToken(), @@ -221,6 +343,13 @@ export async function getCommunityNotes({ videoUrl }) { return notes; } +/** + * Updates the user's incognito mode by sending a POST request to the 'extension/visibility' API endpoint. + * + * @param {Object} params - The parameters for updating the incognito mode. + * @param {number} params.length - The length of the incognito mode. + * @returns {Promise} - A promise that resolves to the data returned from the API after updating the incognito mode. + */ export async function updateUserIncognitoMode({ length }) { const { data } = await api('extension/visibility', { method: 'POST', diff --git a/src/entries/background/common/controllers.js b/src/entries/background/common/controllers.js index c31eb06..d956811 100644 --- a/src/entries/background/common/controllers.js +++ b/src/entries/background/common/controllers.js @@ -6,6 +6,13 @@ import { createLogger } from '~/common/log'; const log = createLogger('Background'); +/** + * Retrieves the appropriate action or API function based on the provided type and executes it with the given data. + * + * @param {string} type - The type of action or API function to be executed. + * @param {object} data - The data to be passed to the action or API function. + * @returns {Promise} A Promise that resolves with the result of the executed action or API function, or null if the type is not recognized. + */ async function getResponse(type, data) { const callback = { // Actions @@ -42,6 +49,13 @@ async function getResponse(type, data) { return null; } +/** + * Handle messages received from content scripts. + * + * @param {string} type - The type of message being received. + * @param {any} data - The data associated with the message. + * @returns {Promise} A promise that resolves with the response to the message. + */ export async function onContentScriptMessage(type, data) { const response = await getResponse(type, data); diff --git a/src/entries/background/common/spaceship.js b/src/entries/background/common/spaceship.js index e477c38..dc5b6f0 100644 --- a/src/entries/background/common/spaceship.js +++ b/src/entries/background/common/spaceship.js @@ -3,6 +3,12 @@ import { createLogger } from '~/common/log'; const log = createLogger('Spaceship 👽'); +/** + * Registers a listener for incoming messages from the browser runtime. + * + * @param {function} listener - The callback function to be executed when a message is received. + * @returns {void} + */ export function registerListener(listener) { browser.runtime.onMessage.addListener((req, sender) => { const { type, data } = req; @@ -13,10 +19,24 @@ export function registerListener(listener) { }); } +/** + * Unregisters a listener for incoming messages from the browser runtime. + * + * @param {function} listener - The callback function to be unregistered. + * @returns {void} + */ export function unregisterListener(listener) { browser.runtime.onMessage.removeListener(listener); } +/** + * Sends a message to a specific tab with the provided tabId, type, and data. + * + * @param {number} tabId - The ID of the tab to send the message to. + * @param {string} type - The type of the message being sent. + * @param {Object} data - The data to be sent along with the message. + * @returns {Promise} A Promise that resolves when the message is sent successfully. + */ export async function sendMessageToTab(tabId, type, data) { log.debug('SEND ➡️', type, data); diff --git a/src/entries/background/common/storage.js b/src/entries/background/common/storage.js index 048f260..5d02afc 100644 --- a/src/entries/background/common/storage.js +++ b/src/entries/background/common/storage.js @@ -8,8 +8,16 @@ export const STORAGE_USER = 'user'; export const STORAGE_COMPACT = 'compact'; export const STORAGE_SETTING_SCRIPT_VISIBLE = 'invis'; -// Setter - +/** + * Sets the user data in browser storage after minimalizing the user object. + * + * @param {Object} user - The user object containing user data. + * @param {string} user.display_name - The display name of the user. + * @param {string} user.id - The unique identifier of the user. + * @param {string} user.extension_log - The extension log of the user. + * @param {string} user.locale_frontend - The locale of the user for frontend. + * @returns {Promise} A Promise that resolves once the user data is stored in browser storage. + */ export async function storageSetUser(user) { const minimalUser = { display_name: user.display_name, @@ -21,20 +29,41 @@ export async function storageSetUser(user) { await browser.storage.sync.set({ [STORAGE_USER]: minimalUser }); } +/** + * Set the token in browser storage. + * + * @param {string} token - The token to be stored. + * @returns {Promise} A Promise that resolves once the token is successfully stored. + */ export async function storageSetToken(token) { await browser.storage.sync.set({ [STORAGE_TOKEN]: token }); } +/** + * Set the value of the 'compact' key in the browser's synchronized storage. + * + * @param {any} compact - The value to be set for the 'compact' key. + * @returns {Promise} A Promise that resolves when the value is successfully set in the storage. + */ export async function storageSetCompact(compact) { await browser.storage.sync.set({ [STORAGE_COMPACT]: compact }); } +/** + * Set the visibility setting for a script in browser storage. + * + * @param {boolean} visible - The visibility status to be set for the script. + * @returns {Promise} A Promise that resolves once the visibility setting is stored in browser storage. + */ export async function storageSetSettingVisible(visible) { await browser.storage.sync.set({ [STORAGE_SETTING_SCRIPT_VISIBLE]: visible }); } -// Getter - +/** + * Retrieves the visibility setting from browser storage. + * + * @returns {Promise} A promise that resolves to a boolean value indicating the visibility setting. + */ export async function storageGetSettingVisible() { const value = (await browser.storage.sync.get(STORAGE_SETTING_SCRIPT_VISIBLE))[STORAGE_SETTING_SCRIPT_VISIBLE]; @@ -50,22 +79,47 @@ export async function storageGetSettingVisible() { return value !== false; } +/** + * Retrieves the value of the 'compact' key from browser's sync storage. + * + * @returns {Promise} A promise that resolves with the value associated with the 'compact' key in the sync storage. + */ export async function storageGetCompact() { return (await browser.storage.sync.get(STORAGE_COMPACT))[STORAGE_COMPACT]; } +/** + * Asynchronous function to retrieve the user data from browser storage. + * + * @returns {Promise} A promise that resolves with the user data stored in the browser storage. + */ export async function storageGetUser() { return (await browser.storage.sync.get(STORAGE_USER))[STORAGE_USER]; } +/** + * Retrieves the token from browser storage. + * + * @returns {Promise} A promise that resolves with the token value stored in browser storage. + */ export async function storageGetToken() { return (await browser.storage.sync.get(STORAGE_TOKEN))[STORAGE_TOKEN]; } +/** + * Retrieves all data stored in the browser's synchronized storage. + * + * @returns {Promise} A promise that resolves with an object containing all data stored in the synchronized storage. + */ export function storageGetAll() { return browser.storage.sync.get(); } +/** + * Clears the specified keys from the browser's synchronized storage. + * + * @returns {Promise} A Promise that resolves once the specified keys are removed from storage. + */ export async function clearStorage() { return browser.storage.sync.remove([ STORAGE_TOKEN, diff --git a/src/entries/background/script.js b/src/entries/background/script.js index 8623c51..a1f0a88 100644 --- a/src/entries/background/script.js +++ b/src/entries/background/script.js @@ -1,3 +1,7 @@ +/** + * Initializes the background script by creating a logger, getting API and frontend URLs, + * and registering a listener for content script messages. + */ import { createLogger } from '~/common/log'; import { onContentScriptMessage } from '~/entries/background/common/controllers'; import { registerListener } from '~/entries/background/common/spaceship'; diff --git a/src/entries/background/serviceWorker.js b/src/entries/background/serviceWorker.js index f4d8f41..3539b30 100644 --- a/src/entries/background/serviceWorker.js +++ b/src/entries/background/serviceWorker.js @@ -1,3 +1,6 @@ +/** + * Initializes a ServiceWorker with logging, message handling, and listener registration. + */ import { createLogger } from '~/common/log'; import { onContentScriptMessage } from '~/entries/background/common/controllers'; import { registerListener } from '~/entries/background/common/spaceship'; diff --git a/src/entries/contentScript/App.css b/src/entries/contentScript/App.css index 5388e05..3895fa5 100644 --- a/src/entries/contentScript/App.css +++ b/src/entries/contentScript/App.css @@ -1,3 +1,7 @@ +/** + * Set up Tailwind CSS with a custom font variable for Inter font family. + * This code snippet includes the base, components, and utilities layers. + */ @tailwind base; @tailwind components; @tailwind utilities; diff --git a/src/entries/contentScript/App.jsx b/src/entries/contentScript/App.jsx index 0e2c419..26b2861 100644 --- a/src/entries/contentScript/App.jsx +++ b/src/entries/contentScript/App.jsx @@ -1,3 +1,9 @@ +/** + * React functional component representing the main application. + * Handles user authentication, state management, theme settings, and various UI components. + * Utilizes hooks like useTranslation, useAuth, useAppStore, usePage, useBackgroundEvents. + * Renders different components based on the user's state and actions. + */ import './App.css'; import '@streamfinity/streamfinity-branding/dist/index.css'; import React, { useEffect, useState } from 'react'; diff --git a/src/entries/contentScript/components/AnalyticsNotice.jsx b/src/entries/contentScript/components/AnalyticsNotice.jsx index ae5616c..9c5ef41 100644 --- a/src/entries/contentScript/components/AnalyticsNotice.jsx +++ b/src/entries/contentScript/components/AnalyticsNotice.jsx @@ -13,10 +13,15 @@ import { hasSubscription } from '~/entries/contentScript/hooks/useSubscription'; import { subscriptionIds, subscriptionFeatures } from '~/enums'; import PremiumCtaLabel from '~/entries/contentScript/components/PremiumCtaLabel'; -// ------------------------------------------------------------------------------------------------------- -// Components -// ------------------------------------------------------------------------------------------------------- - +/** + * Function component for displaying a statistic with a title, value, and optional children. + * + * @param {string} title - The title of the statistic. + * @param {number} value - The numerical value of the statistic. + * @param {bool} blur - Whether to apply blur effect to the statistic. + * @param {PropTypes.node|PropTypes.element|PropTypes.arrayOf(PropTypes.element)} children - Optional children components. + * @returns {JSX.Element} A div element displaying the statistic with the provided information. + */ function Statistic({ title, value, children, blur = false, }) { @@ -49,6 +54,14 @@ Statistic.defaultProps = { children: null, }; +/** + * Function component for displaying a statistic change with an arrow icon. + * + * @param {Object} props - The props object containing the children to be displayed. + * @param {node|element|arrayOf(element)} props.children - The content to be displayed within the component. + * + * @returns {JSX.Element} A div element displaying the statistic change with an arrow icon and the provided children. + */ function StatisticChange({ children }) { return (
@@ -70,6 +83,12 @@ StatisticChange.propTypes = { // Mount // ------------------------------------------------------------------------------------------------------- +/** + * Function that renders an analytics notice component. + * It fetches video analytics data, checks if the user has a subscription, and displays relevant information. + * + * @returns {JSX.Element} The rendered AnalyticsNotice component. + */ function AnalyticsNotice() { const { t } = useTranslation(); const currentUrl = useAppStore((state) => state.currentUrl); diff --git a/src/entries/contentScript/components/AppContainer.jsx b/src/entries/contentScript/components/AppContainer.jsx index 7734852..0ec6edf 100644 --- a/src/entries/contentScript/components/AppContainer.jsx +++ b/src/entries/contentScript/components/AppContainer.jsx @@ -11,6 +11,18 @@ import SubwaySurfer from '~/entries/contentScript/components/SubwaySurfer'; const dev = import.meta.env.DEV; +/** + * Function that renders the main AppContainer component. + * + * @param {Object} props - The props for the AppContainer component. + * @param {childrenShape} props.children - The children elements to be rendered. + * @param {childrenShape} [props.user=null] - The user element to be rendered. + * @param {boolean} [props.dark=false] - Flag to determine if dark mode is enabled. + * @param {string} [props.state='default'] - The state of the component (default, live, own-video). + * @param {boolean} [props.isTrackingVideos] - Flag to determine if video tracking is enabled. + * + * @returns {JSX.Element} JSX element representing the AppContainer component. + */ function AppContainer({ children, dark, diff --git a/src/entries/contentScript/components/Card.jsx b/src/entries/contentScript/components/Card.jsx index 7e46d2e..ce94125 100644 --- a/src/entries/contentScript/components/Card.jsx +++ b/src/entries/contentScript/components/Card.jsx @@ -4,10 +4,14 @@ import classNames from 'classnames'; import { ChevronDownIcon } from '@heroicons/react/16/solid'; import { childrenShape } from '~/shapes'; -// ------------------------------------------------------------------------------------------------------- -// Title -// ------------------------------------------------------------------------------------------------------- - +/** + * Function that renders a card title with the provided children. + * + * @param {Object} props - The props object containing the children to be rendered. + * @param {Node | Element | Array} props.children - The children elements to be displayed in the card title. + * + * @returns {JSX.Element} A div element representing the card title with the provided children. + */ function CardTitle({ children }) { return (
@@ -20,10 +24,20 @@ CardTitle.propTypes = { children: childrenShape.isRequired, }; -// ------------------------------------------------------------------------------------------------------- -// Card -// ------------------------------------------------------------------------------------------------------- - +/** + * Function that defines a Card component in React. + * + * @param {Object} props - The props for the Card component. + * @param {string} props.className - The class name for styling the Card. + * @param {string} props.color - The color theme for the Card. + * @param {node} props.children - The content to be displayed inside the Card. + * @param {string} props.title - The title of the Card. + * @param {string} props.titleCompact - The compact title of the Card. + * @param {string} props.preview - The preview content of the Card. + * @param {boolean} props.compact - Flag to determine if the Card should be displayed in compact mode. + * + * @returns {JSX.Element} A React component representing a Card. + */ function Card({ children, title, diff --git a/src/entries/contentScript/components/CommunityNoteNotice.jsx b/src/entries/contentScript/components/CommunityNoteNotice.jsx index 0d7f12e..231f018 100644 --- a/src/entries/contentScript/components/CommunityNoteNotice.jsx +++ b/src/entries/contentScript/components/CommunityNoteNotice.jsx @@ -8,6 +8,11 @@ import { usePage } from '~/hooks/usePage'; import { buildFrontendUrl } from '~/common/utility'; import { strLimit } from '~/common/pretty'; +/** + * Function component for rendering a community note notice. + * Fetches notes based on the current video URL and displays them in a card format. + * Allows expanding notes and provides options to report or add new notes. + */ function CommunityNoteNotice() { const { t } = useTranslation(); const compact = useAppStore((state) => state.isCompact); diff --git a/src/entries/contentScript/components/ContentRatingNotice.jsx b/src/entries/contentScript/components/ContentRatingNotice.jsx index 3bcdf82..4b6ba3f 100644 --- a/src/entries/contentScript/components/ContentRatingNotice.jsx +++ b/src/entries/contentScript/components/ContentRatingNotice.jsx @@ -13,6 +13,17 @@ import { useAppStore } from '~/entries/contentScript/state'; const log = createLogger('Content-Rating'); +/** + * Function component for displaying content rating notice. + * - Fetches current URL and compact state from app store. + * - Retrieves player progress using useYouTubePlayer hook. + * - Displays content rating segments in the player based on received segments. + * - Uses useContentRatings hook to fetch content ratings for the current video. + * - Computes content ratings based on player progress and received segments. + * - Finds and sets the player progress bar element. + * - Listens for content rating results and mounts them in the player. + * - Renders a Card component with content rating information. + */ function ContentRatingNotice() { const currentUrl = useAppStore((state) => state.currentUrl); const compact = useAppStore((state) => state.isCompact); diff --git a/src/entries/contentScript/components/DevTools.jsx b/src/entries/contentScript/components/DevTools.jsx index d9c8e83..2e8846d 100644 --- a/src/entries/contentScript/components/DevTools.jsx +++ b/src/entries/contentScript/components/DevTools.jsx @@ -2,6 +2,12 @@ import React from 'react'; import useAuth from '~/hooks/useAuth'; import { getApiUrl } from '~/config'; +/** + * Function component for displaying Streamfinity Dev Tools. + * Retrieves user authentication information and displays user details, accounts, live streams, and current state. + * Utilizes useAuth hook to fetch user authentication data. + * Renders UI elements with user information, accounts for reaction tracking, live stream status, and API endpoint. + */ function DevTools() { const { state, user, liveStreams, accounts, isLive, diff --git a/src/entries/contentScript/components/Footer.jsx b/src/entries/contentScript/components/Footer.jsx index 6ea11f2..294b795 100644 --- a/src/entries/contentScript/components/Footer.jsx +++ b/src/entries/contentScript/components/Footer.jsx @@ -6,6 +6,20 @@ import { buildFrontendUrl } from '~/common/utility'; import useAuth, { STATE_LIVE, STATE_DEFAULT, STATE_OWN_VIDEO } from '~/hooks/useAuth'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * Renders a footer component with buttons and links based on user and state information. + * + * @param {Object} props - The props object containing state and liveStream information. + * @param {string} props.state - The state of the footer, can be one of STATE_DEFAULT, STATE_LIVE, STATE_OWN_VIDEO. + * @param {Object} [props.liveStream] - The live stream object with title, account, and service information. + * @param {string} props.liveStream.title - The title of the live stream. + * @param {Object} props.liveStream.account - The account object with display_name. + * @param {string} props.liveStream.account.display_name - The display name of the account. + * @param {Object} props.liveStream.service - The service object with title. + * @param {string} props.liveStream.service.title - The title of the service. + * + * @returns {JSX.Element} A JSX element representing the footer component with buttons and links. + */ function Footer({ state, liveStream = null, diff --git a/src/entries/contentScript/components/H2Header.jsx b/src/entries/contentScript/components/H2Header.jsx index 723fa09..8af82b6 100644 --- a/src/entries/contentScript/components/H2Header.jsx +++ b/src/entries/contentScript/components/H2Header.jsx @@ -3,6 +3,16 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { childrenShape } from '~/shapes'; +/** + * Function component for rendering an H2 header with specified margin top and margin bottom. + * + * @param {Object} props - The props for the H2Header component. + * @param {node} props.children - The children elements to be rendered within the H2 header. + * @param {string|bool} props.mt - The margin top class name or boolean value. + * @param {string|bool} props.mb - The margin bottom class name or boolean value. + * + * @returns {JSX.Element} JSX element representing the H2 header with specified styles and children. + */ function H2Header({ children, mt, mb }) { return (

diff --git a/src/entries/contentScript/components/H3Header.jsx b/src/entries/contentScript/components/H3Header.jsx index 0419ea8..70acac1 100644 --- a/src/entries/contentScript/components/H3Header.jsx +++ b/src/entries/contentScript/components/H3Header.jsx @@ -3,6 +3,16 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { childrenShape } from '~/shapes'; +/** + * Function component for rendering an H3 header with optional step indicator. + * + * @param {Object} props - The props object containing children, mt, mb, and step. + * @param {node} props.children - The content to be displayed within the H3 header. + * @param {string|bool} props.mt - The margin top class name or boolean value. + * @param {string|bool} props.mb - The margin bottom class name or boolean value. + * @param {number} props.step - The step indicator number (optional). + * @returns {JSX.Element} An H3 header component with optional step indicator. + */ function H3Header({ children, mt, mb, step, }) { diff --git a/src/entries/contentScript/components/MarkReactionForm.jsx b/src/entries/contentScript/components/MarkReactionForm.jsx index 5953111..5b3ceab 100644 --- a/src/entries/contentScript/components/MarkReactionForm.jsx +++ b/src/entries/contentScript/components/MarkReactionForm.jsx @@ -9,6 +9,14 @@ import { submitReaction } from '~/common/bridge'; import { useYouTubePlayer } from '~/hooks/useYouTubePlayer'; import { toastError, toastSuccess } from '~/common/utility'; +/** + * Function component for a MarkReactionForm. + * + * @param {Object} props - The props for the MarkReactionForm component. + * @param {Function} props.onSubmitted - Function to be called when the form is submitted. + * + * @returns {JSX.Element} A React component representing the MarkReactionForm. + */ function MarkReactionForm({ onSubmitted }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); diff --git a/src/entries/contentScript/components/MarkReactionNotice.jsx b/src/entries/contentScript/components/MarkReactionNotice.jsx index a7c2711..79d431d 100644 --- a/src/entries/contentScript/components/MarkReactionNotice.jsx +++ b/src/entries/contentScript/components/MarkReactionNotice.jsx @@ -5,6 +5,11 @@ import Card from '~/entries/contentScript/components/Card'; import MarkReactionForm from '~/entries/contentScript/components/MarkReactionForm'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * Function component for displaying a Mark Reaction Notice. + * It renders a Card component with a title and conditional content based on the showForm state. + * The showForm state toggles the display of MarkReactionForm component. + */ function MarkReactionNotice() { const { t } = useTranslation(); const [showForm, setShowForm] = useState(false); diff --git a/src/entries/contentScript/components/OriginalVideoNotice.jsx b/src/entries/contentScript/components/OriginalVideoNotice.jsx index 985e0ec..edeb2cc 100644 --- a/src/entries/contentScript/components/OriginalVideoNotice.jsx +++ b/src/entries/contentScript/components/OriginalVideoNotice.jsx @@ -6,6 +6,23 @@ import { reactionShape } from '~/shapes'; import { prettyDuration } from '~/common/pretty'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * React component for displaying a preview of a reaction. + * + * @param {Object} props - The props for the ReactionPreview component. + * @param {Object} props.reaction - The reaction object containing information to display. + * @param {Object} props.reaction.to_video - The video object to display. + * @param {string} props.reaction.to_video.external_tracking_url - The external tracking URL of the video. + * @param {string} props.reaction.to_video.thumbnail_url - The thumbnail URL of the video. + * @param {string} props.reaction.to_video.title - The title of the video. + * @param {Object} props.reaction.to_video.channel - The channel object of the video. + * @param {string} props.reaction.to_video.channel.title - The title of the channel. + * @param {number} props.reaction.video_seconds_from - The starting time of the reaction in seconds. + * @param {number} props.reaction.video_seconds_to - The ending time of the reaction in seconds. + * @param {number} props.reaction.interval_duration - The duration of the reaction interval in seconds. + * + * @returns {JSX.Element} A React element representing the ReactionPreview component. + */ function ReactionPreview({ reaction }) { return ( state.currentUrl); diff --git a/src/entries/contentScript/components/Overlay.jsx b/src/entries/contentScript/components/Overlay.jsx index efa9f6d..b26d960 100644 --- a/src/entries/contentScript/components/Overlay.jsx +++ b/src/entries/contentScript/components/Overlay.jsx @@ -3,6 +3,16 @@ import PropTypes from 'prop-types'; import { childrenShape } from '~/shapes'; import H2Header from '~/entries/contentScript/components/H2Header'; +/** + * Function component for rendering an overlay with a title, children, and a close button. + * + * @param {Object} props - The props object containing children, onHide function, and title string. + * @param {ReactNode} props.children - The content to be displayed within the overlay. + * @param {Function} props.onHide - The function to be called when the close button is clicked. + * @param {string} props.title - The title to be displayed in the overlay. + * + * @returns {JSX.Element} A JSX element representing the overlay component. + */ function Overlay({ children, onHide, title }) { return (
diff --git a/src/entries/contentScript/components/PremiumCtaLabel.jsx b/src/entries/contentScript/components/PremiumCtaLabel.jsx index 295d406..f635401 100644 --- a/src/entries/contentScript/components/PremiumCtaLabel.jsx +++ b/src/entries/contentScript/components/PremiumCtaLabel.jsx @@ -6,6 +6,12 @@ import { buildFrontendUrl } from '~/common/utility'; import { childrenShape } from '~/shapes'; import PlusSparklesIcon from '~/components/Icons/PlusSparklesIcon'; +/** + * Function that renders a premium call-to-action label component. + * + * @param {Object} props - The props object containing children, campaign, plan, feature, and className. + * @returns {JSX.Element} A JSX element representing the premium call-to-action label. + */ function PremiumCtaLabel({ children, campaign, diff --git a/src/entries/contentScript/components/ReactionPolicyNotice.jsx b/src/entries/contentScript/components/ReactionPolicyNotice.jsx index 1af0b40..81a86c7 100644 --- a/src/entries/contentScript/components/ReactionPolicyNotice.jsx +++ b/src/entries/contentScript/components/ReactionPolicyNotice.jsx @@ -19,6 +19,12 @@ const STATUS_CONDITION = 2; const STATUS_ON_COUNTDOWN = 3; const STATUS_OTHER_CONDITION = 4; +/** + * Formats the given time difference into a human-readable countdown format. + * + * @param {number} diff - The time difference in milliseconds to be formatted. + * @returns {string} The formatted countdown string in the format "d days hh:mm:ss" or "hh:mm:ss" if less than a day. + */ function prettyFormatCountdown(diff) { const duration = moment.duration(diff); @@ -34,6 +40,17 @@ function prettyFormatCountdown(diff) { return `${days}d ${hours}:${minutes}:${seconds}`; } +/** + * Functional component for rendering a notice card. + * + * @param {string} title - The title of the notice. + * @param {PropTypes.node} description - The description content of the notice. + * @param {string} preview - The preview content of the notice. + * @param {string} cardColor - The color theme of the notice card. + * @param {string} className - Additional CSS classes for styling. + * + * @returns {JSX.Element} A Card component displaying the notice content. + */ function Notice({ title, description, @@ -100,6 +117,28 @@ Notice.defaultProps = { className: '', }; +/** + * React component for rendering a notice line with different statuses and options. + * + * @param {string} title - The title of the notice line. + * @param {number} status - The status code of the notice line. + * @param {object} countdown - The countdown object for displaying time remaining. + * @param {number} maxPercentage - The maximum percentage value. + * @param {string} comment - Additional comment for the notice line. + * @param {array} options - Array of options with value and title. + * + * @returns {JSX.Element} A div element containing the notice line with appropriate styling based on the status. + * + * @example + * + */ function NoticeLine({ title, status, countdown, maxPercentage, comment, options, }) { @@ -205,6 +244,14 @@ NoticeLine.defaultProps = { options: null, }; +/** + * Renders a notice component based on the reaction policy for a video. + * The notice component displays information about the reaction policy status, including live reactions and video reactions. + * It fetches the reaction policy for the current video and channel, and calculates countdown durations for live and video reactions. + * The component dynamically renders different content based on the reaction policy status, such as allowed reactions, denied reactions, or conditional reactions. + * + * @returns {JSX.Element} The React component that displays the notice based on the reaction policy. + */ function ReactionPolicyNotice() { const { t } = useTranslation(); diff --git a/src/entries/contentScript/components/ReactionsHistoryNotice.jsx b/src/entries/contentScript/components/ReactionsHistoryNotice.jsx index ec7b1e3..1331911 100644 --- a/src/entries/contentScript/components/ReactionsHistoryNotice.jsx +++ b/src/entries/contentScript/components/ReactionsHistoryNotice.jsx @@ -13,6 +13,13 @@ import { subscriptionIds, subscriptionFeatures } from '~/enums'; import PremiumCtaLabel from '~/entries/contentScript/components/PremiumCtaLabel'; import { buildReactionFromUrl } from '~/common/pretty'; +/** + * React component for displaying a preview of a reaction. + * + * @param {Object} props - The props for the component. + * @param {Object} props.reaction - The reaction object to display. + * @returns {JSX.Element} A React element representing the reaction preview. + */ function ReactionPreview({ reaction }) { const { t } = useTranslation(); @@ -85,6 +92,11 @@ ReactionPreview.propTypes = { reaction: reactionShape.isRequired, }; +/** + * React component for displaying reactions history notice. + * Fetches reactions data based on the current video URL and user's subscription status. + * Renders different content based on the presence of reactions and user's subscription. + */ function ReactionsHistoryNotice() { const { t } = useTranslation(); const currentUrl = useAppStore((state) => state.currentUrl); diff --git a/src/entries/contentScript/components/StatusNotice.jsx b/src/entries/contentScript/components/StatusNotice.jsx index 2de807e..93e2ce6 100644 --- a/src/entries/contentScript/components/StatusNotice.jsx +++ b/src/entries/contentScript/components/StatusNotice.jsx @@ -6,6 +6,18 @@ import classNames from 'classnames'; import { buildFrontendUrl } from '~/common/utility'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * Functional component for displaying a status notice based on the liveStream prop. + * + * @param {Object} liveStream - The live stream object containing title, account, and service information. + * @param {string} liveStream.title - The title of the live stream. + * @param {Object} liveStream.account - The account object with display_name. + * @param {string} liveStream.account.display_name - The display name of the account. + * @param {Object} liveStream.service - The service object with title. + * @param {string} liveStream.service.title - The title of the service. + * + * @returns {JSX.Element} JSX element representing the status notice component. + */ function StatusNotice({ liveStream, }) { diff --git a/src/entries/contentScript/components/StreamerModeNotice.jsx b/src/entries/contentScript/components/StreamerModeNotice.jsx index 20773ff..2016f8f 100644 --- a/src/entries/contentScript/components/StreamerModeNotice.jsx +++ b/src/entries/contentScript/components/StreamerModeNotice.jsx @@ -5,6 +5,11 @@ import Card from '~/entries/contentScript/components/Card'; import useAuth, { STATE_DEFAULT } from '~/hooks/useAuth'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * Function component for displaying a notice in streamer mode. + * Uses translation and authentication hooks to render a Card component with a title and description. + * Includes a Button component to allow users to show all content. + */ function StreamerModeNotice() { const { t } = useTranslation(); const { setOverrideState } = useAuth(); diff --git a/src/entries/contentScript/components/SubmitSuggestionForm.jsx b/src/entries/contentScript/components/SubmitSuggestionForm.jsx index 3982487..12ecc6d 100644 --- a/src/entries/contentScript/components/SubmitSuggestionForm.jsx +++ b/src/entries/contentScript/components/SubmitSuggestionForm.jsx @@ -12,6 +12,11 @@ const RESULTS_LOADING = 1; const RESULTS_EMPTY = 2; const RESULTS_OK = 3; +/** + * Function component for a form to submit suggestions. + * + * @param {Function} onSubmit - Function to handle form submission. + */ function SubmitSuggestionForm({ onSubmit }) { const { t } = useTranslation(); const [hasLoadedSuggestedAccounts, setHasLoadedSuggestedAccounts] = useState(false); diff --git a/src/entries/contentScript/components/SubmitSuggestionNotice.jsx b/src/entries/contentScript/components/SubmitSuggestionNotice.jsx index a9234c7..a7880c0 100644 --- a/src/entries/contentScript/components/SubmitSuggestionNotice.jsx +++ b/src/entries/contentScript/components/SubmitSuggestionNotice.jsx @@ -5,6 +5,11 @@ import Card from '~/entries/contentScript/components/Card'; import SubmitSuggestionForm from '~/entries/contentScript/components/SubmitSuggestionForm'; import { useAppStore } from '~/entries/contentScript/state'; +/** + * Function component for rendering a Submit Suggestion Notice. + * It displays a Card component with a title and a Button to suggest a video. + * Toggles between showing a form for submitting a suggestion and hiding it. + */ function SubmitSuggestionNotice() { const { t } = useTranslation(); const [showForm, setShowForm] = useState(false); diff --git a/src/entries/contentScript/components/SubwaySurfer.jsx b/src/entries/contentScript/components/SubwaySurfer.jsx index 6bf634e..120ae51 100644 --- a/src/entries/contentScript/components/SubwaySurfer.jsx +++ b/src/entries/contentScript/components/SubwaySurfer.jsx @@ -20,6 +20,13 @@ const availableVideoItems = [{ url: 'https://cdn.streamfinity.tv/extension-assets/subway-surfer-mina.mp4', }]; +/** + * Function component for rendering a 🤫 video player. + * + * @param {Function} onClose - Function to handle closing the video player. + * + * @returns {JSX.Element} A div containing the video player with controls and information. + */ function SubwaySurfer({ onClose }) { const { t } = useTranslation(); const [videoItem, setVideoItem] = useState(null); diff --git a/src/entries/contentScript/components/headless/PlayerProgressListener.jsx b/src/entries/contentScript/components/headless/PlayerProgressListener.jsx index 05af902..c884627 100644 --- a/src/entries/contentScript/components/headless/PlayerProgressListener.jsx +++ b/src/entries/contentScript/components/headless/PlayerProgressListener.jsx @@ -9,6 +9,12 @@ import { why } from '~/common/pretty'; const log = createLogger('PlayerProgress'); +/** + * Function component for handling player progress updates. + * + * @param {Object} props - The props object containing the 'active' boolean property. + * @returns {null} - This component does not render any UI elements. + */ function PlayerProgressListener({ active }) { const { element: playerElement, progress: playerProgress } = useYouTubePlayer(); const currentUrl = useAppStore((state) => state.currentUrl); diff --git a/src/entries/contentScript/components/headless/WatchedVideosObserver.jsx b/src/entries/contentScript/components/headless/WatchedVideosObserver.jsx index 51a8ab8..2e66374 100644 --- a/src/entries/contentScript/components/headless/WatchedVideosObserver.jsx +++ b/src/entries/contentScript/components/headless/WatchedVideosObserver.jsx @@ -12,6 +12,13 @@ import { MARK_WATCHED_REACTIONS_INTERVAL } from '~/config'; const log = createLogger('WatchedReactions'); +/** + * Marks elements as watched based on the provided list of watched videos. + * + * @param {Array} watchedVideos - List of watched videos to compare against. + * @param {Function} t - Translation function for i18n support. + * @returns {void} + */ function markElements(watchedVideos, t) { if (!watchedVideos) { return; @@ -95,9 +102,16 @@ function markElements(watchedVideos, t) { countFound += 1; }); - // log.debug('mark watched reactions. found:', countFound); } +/** + * Function component for observing watched videos. + * + * This component sets up an observer to mark watched videos based on user interactions. + * It fetches watched videos data, listens for resize events, and periodically updates the watched status. + * + * @returns {null} Returns null as the component does not render any UI elements. + */ function WatchedVideosObserver() { const { t } = useTranslation(); const currentUrl = useAppStore((state) => state.currentUrl); diff --git a/src/entries/contentScript/hooks/useBackgroundEvents.js b/src/entries/contentScript/hooks/useBackgroundEvents.js index 75481af..5e98525 100644 --- a/src/entries/contentScript/hooks/useBackgroundEvents.js +++ b/src/entries/contentScript/hooks/useBackgroundEvents.js @@ -9,6 +9,15 @@ import { toastSuccess, toastError, toastWarn } from '~/common/utility'; const log = createLogger('Background-Events'); +/** + * Asynchronously refreshes settings by updating the visibility state using the provided setIsVisible function. + * This function logs a debug message before updating the visibility state. + * + * @param {Object} options - An object containing the setIsVisible function. + * @param {Function} options.setIsVisible - The function used to set the visibility state. + * + * @returns {Promise} - A promise that resolves once the visibility state is updated. + */ async function refreshSettings({ setIsVisible }) { log.debug('refreshing settings...'); @@ -18,8 +27,11 @@ async function refreshSettings({ setIsVisible }) { } /** - * @param {('success','warning','error')} type - * @param {string} message + * Sends a notice message based on the provided type. + * + * @param {string} type - The type of notice message ('success', 'error', 'warning'). + * @param {string} message - The message content to be displayed in the notice. + * @returns {void} */ function sendNotice(type, message) { // eslint-disable-next-line default-case @@ -36,6 +48,13 @@ function sendNotice(type, message) { } } +/** + * Custom hook that handles background events and performs necessary actions based on the event type. + * Registers a listener for background messages and triggers corresponding functions for different event types. + * Also initializes the background events by calling the refreshSettings function and sets the initial state. + * + * @returns {Object} An empty object as this hook does not return any values. + */ export function useBackgroundEvents() { const setIsVisible = useAppStore((state) => state.setIsVisible); const { refreshStatusData } = useAuth(); diff --git a/src/entries/contentScript/hooks/useSubscription.js b/src/entries/contentScript/hooks/useSubscription.js index c47fc67..e6c3d2b 100644 --- a/src/entries/contentScript/hooks/useSubscription.js +++ b/src/entries/contentScript/hooks/useSubscription.js @@ -2,6 +2,13 @@ import { useMemo } from 'react'; +/** + * Check if a user has a specific subscription based on the subscription ID. + * + * @param {Object} user - The user object containing accounts and subscriptions. + * @param {string} subscriptionId - The ID of the subscription to check for. + * @returns {boolean} - Returns true if the user has the subscription, false otherwise. + */ export function hasSubscription(user, subscriptionId) { return useMemo( () => user @@ -11,6 +18,14 @@ export function hasSubscription(user, subscriptionId) { ); } +/** + * Check if a user has a specific feature available based on their subscriptions. + * + * @param {string} feature - The feature to check availability for. + * @param {Object} account - The user's account object containing subscriptions. + * @param {Object} user - The user object containing multiple accounts and subscriptions. + * @returns {boolean} - Returns true if the feature is available to the user, false otherwise. + */ export function hasSubscriptionFeature(feature, account, user) { return useMemo(() => { const accountSubscriptions = account?.subscriptions || []; diff --git a/src/entries/contentScript/main.jsx b/src/entries/contentScript/main.jsx index 24fff47..a88f1d9 100644 --- a/src/entries/contentScript/main.jsx +++ b/src/entries/contentScript/main.jsx @@ -1,3 +1,6 @@ +/** + * Initializes i18n, creates a logger, sets up a QueryClient, and renders the main content of the content script. + */ import '~/enableDevHmr'; import React from 'react'; import { createRoot } from 'react-dom/client'; diff --git a/src/entries/contentScript/renderContent.js b/src/entries/contentScript/renderContent.js index d8d5252..94d942e 100644 --- a/src/entries/contentScript/renderContent.js +++ b/src/entries/contentScript/renderContent.js @@ -8,6 +8,11 @@ import { const log = createLogger('Content-Script'); +/** + * Binds an event listener to detect changes in the window location and dispatches a custom event when a navigation occurs. + * + * @returns {void} + */ function bindWindowEvents() { let currentUrl = window.location.href; @@ -26,6 +31,12 @@ function bindWindowEvents() { }, URL_CHANGE_INTERVAL_SECONDS * 1000); } +/** + * Append the shadow root container to the DOM under a specific parent element after retrying to find the parent element. + * + * @param {Element} appContainer - The shadow root container element to be appended to the DOM. + * @returns {Promise} - A Promise that resolves once the shadow root container is successfully appended to the DOM. + */ async function appendShadowRootToDom(appContainer) { const parent = await retryFind( () => { @@ -54,6 +65,13 @@ async function appendShadowRootToDom(appContainer) { // //
+/** + * Renders content on the current page by injecting a shadow DOM container with specified CSS paths. + * + * @param {Array} cssPaths - An array of CSS paths to be loaded into the shadow DOM container. + * @param {Function} render - A function to render content inside the shadow DOM container. + * @returns {void} + */ export async function renderContent( cssPaths, render = () => {}, diff --git a/src/entries/contentScript/state.js b/src/entries/contentScript/state.js index 9075467..1bac00b 100644 --- a/src/entries/contentScript/state.js +++ b/src/entries/contentScript/state.js @@ -1,3 +1,9 @@ +/** + * Creates a custom hook using Zustand library to manage application state. + * The hook provides access to various state variables such as currentUrl, currentChannel, reactionPolicy, etc. + * It also exposes setter functions to update these state variables. + * @returns {Object} The custom hook object with state variables and setter functions. + */ import { create } from 'zustand'; export const useAppStore = create((set) => ({ diff --git a/src/entries/contentScriptInternal/app.js b/src/entries/contentScriptInternal/app.js index d60bd72..8f01d4c 100644 --- a/src/entries/contentScriptInternal/app.js +++ b/src/entries/contentScriptInternal/app.js @@ -4,6 +4,21 @@ import { createLogger } from '~/common/log'; const log = createLogger('Content-Script'); +/** + * Function: inspectDomElements + * + * Description: This function inspects DOM elements with a specific attribute and performs actions based on the attribute value. + * + * Steps: + * 1. Select all elements with the attribute '[data-streamfinity-extension]'. + * 2. Iterate over each element and get the value of the 'data-streamfinity-extension' attribute. + * 3. Based on the attribute value, perform the following actions: + * - If the value is 'hide', hide the element by setting its display style to 'none'. + * - If the value is 'show', show the element by setting its display style to 'block'. + * - For any other value, log a warning message indicating an unknown action. + * + * @returns {void} + */ async function inspectDomElements() { const elements = document.querySelectorAll('[data-streamfinity-extension]'); @@ -27,6 +42,12 @@ async function inspectDomElements() { }); } +/** + * Asynchronous function that runs at regular intervals to inspect DOM elements with specific attributes. + * Uses setInterval to call the inspectDomElements function every 2000 milliseconds. + * + * @returns {Promise} A Promise that resolves once the function is executed. + */ export default async function app() { setInterval(async () => { await inspectDomElements(); diff --git a/src/entries/contentScriptInternal/main.jsx b/src/entries/contentScriptInternal/main.jsx index 5961563..9328517 100644 --- a/src/entries/contentScriptInternal/main.jsx +++ b/src/entries/contentScriptInternal/main.jsx @@ -1,3 +1,7 @@ +/** + * Imports necessary modules and functions, creates a logger for the 'Content-Script' section, + * logs a debug message, and executes the 'app' function. + */ import '~/enableDevHmr'; import app from './app'; import { createLogger } from '~/common/log'; diff --git a/src/entries/popup/App.css b/src/entries/popup/App.css index 5388e05..ef48853 100644 --- a/src/entries/popup/App.css +++ b/src/entries/popup/App.css @@ -1,3 +1,6 @@ +/** + * Set up Tailwind CSS with a custom font variable for Inter font family. + */ @tailwind base; @tailwind components; @tailwind utilities; diff --git a/src/entries/popup/App.jsx b/src/entries/popup/App.jsx index ee32ca9..d7ccc54 100644 --- a/src/entries/popup/App.jsx +++ b/src/entries/popup/App.jsx @@ -14,6 +14,13 @@ const availableViews = [ }, ]; +/** + * Function representing the main component of the application. + * Renders different views based on user authentication status. + * Uses state, memoization, and custom hooks like useAuth and useBackgroundEvents. + * + * @returns {JSX.Element} The main component of the application. + */ function App() { const [activeView] = useState(availableViews[0]); const ActiveViewComponent = useMemo(() => activeView.component, [activeView]); diff --git a/src/entries/popup/components/LoginView.jsx b/src/entries/popup/components/LoginView.jsx index d58b6a7..ac9c1e3 100644 --- a/src/entries/popup/components/LoginView.jsx +++ b/src/entries/popup/components/LoginView.jsx @@ -3,6 +3,14 @@ import useAuth from '~/hooks/useAuth'; import LoginButton from '~/components/LoginButton'; import { getApiUrl } from '~/config'; +/** + * Renders a login view component. + * + * This component displays a login button that triggers the login process when clicked. + * It also shows the current environment's hostname fetched from the API URL. + * + * @returns {JSX.Element} The rendered login view component. + */ function LoginView() { const { login, loadingLogin } = useAuth(); diff --git a/src/entries/popup/components/SettingsView.jsx b/src/entries/popup/components/SettingsView.jsx index 20fd3bf..78a759c 100644 --- a/src/entries/popup/components/SettingsView.jsx +++ b/src/entries/popup/components/SettingsView.jsx @@ -12,6 +12,13 @@ import { Switch } from '~/components/Ui/Switch'; import Card from '~/entries/contentScript/components/Card'; import { getApiUrl } from '~/config'; +/** + * Function component for rendering the settings view. + * Handles toggling visibility, incognito mode, and logout functionality. + * Uses various hooks and state management for user interaction. + * + * @param {object} user - The user object containing display_name. + */ function SettingsView() { const { t } = useTranslation(); const { diff --git a/src/entries/popup/main.jsx b/src/entries/popup/main.jsx index c9ed4a1..36a76e1 100644 --- a/src/entries/popup/main.jsx +++ b/src/entries/popup/main.jsx @@ -1,3 +1,6 @@ +/** + * Initializes i18n configuration, creates a logger, sets up a QueryClient, and renders the main App component. + */ import '~/enableDevHmr'; import React from 'react'; import { createRoot } from 'react-dom/client'; diff --git a/src/enums.js b/src/enums.js index 2fd1512..edda78d 100644 --- a/src/enums.js +++ b/src/enums.js @@ -1,3 +1,6 @@ +/** + * Constants for account services, reaction policy, subscription IDs, account types, and subscription features. + */ export const accountServices = { TWITCH: 0, YOUTUBE: 1, diff --git a/src/events.js b/src/events.js index a4f23a1..b0ede3b 100644 --- a/src/events.js +++ b/src/events.js @@ -1 +1,4 @@ +/** + * Constant representing a custom event type for window navigation. + */ export const WINDOW_NAVIGATE = 'custom_pushtate'; diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js index d4d9b57..c7a0ea2 100644 --- a/src/hooks/useAuth.js +++ b/src/hooks/useAuth.js @@ -13,6 +13,31 @@ export const STATE_DEFAULT = 'default'; export const STATE_LIVE = 'live'; export const STATE_OWN_VIDEO = 'own-video'; +/** + * Custom React hook for handling user authentication logic. + * + * This hook manages user authentication state, including login, logout, user status, and user actions. + * It provides functions to perform login and logout operations, as well as access to user data and status. + * + * @returns {{ +* user: object, +* accounts: array, +* liveStreams: array, +* liveStream: object, +* isLive: boolean, +* isOwnVideo: boolean, +* isIncognito: boolean, +* isTrackingVideos: boolean, +* loadingAuth: boolean, +* state: string, +* setOverrideState: function, +* refreshStatusData: function, +* loadingLogin: boolean, +* login: function, +* loadingLogout: boolean, +* logout: function +* }} +*/ export default function useAuth() { const [ overrideState, setOverrideState, currentChannel, diff --git a/src/hooks/usePage.js b/src/hooks/usePage.js index 41316d0..d08983c 100644 --- a/src/hooks/usePage.js +++ b/src/hooks/usePage.js @@ -5,6 +5,12 @@ import { createLogger } from '~/common/log'; const log = createLogger('usePage'); +/** + * Custom React hook that scrapes information from the current page and updates the state accordingly. + * It listens for page navigation events and periodically scrapes for new URL, channel information, and dark mode status. + * + * @returns {Object} An object containing the current URL. + */ export function usePage() { const [ currentUrl, setCurrentUrl, diff --git a/src/hooks/useYouTubePlayer.js b/src/hooks/useYouTubePlayer.js index 1a16096..146f487 100644 --- a/src/hooks/useYouTubePlayer.js +++ b/src/hooks/useYouTubePlayer.js @@ -1,6 +1,14 @@ import { useState, useEffect } from 'react'; import { retryFind, getYouTubePlayer } from '~/common/utility'; +/** + * Custom React hook to track and retrieve information about the YouTube player element. + * + * @param {Object} options - An optional object containing configuration options. + * @param {number} options.pollInterval - The interval (in milliseconds) at which to poll for player progress. Default is 2000ms. + * + * @returns {Object} An object containing the player element and current progress. + */ export function useYouTubePlayer({ pollInterval } = {}) { const [progress, setProgress] = useState(null); const [playerElement, setPlayerElement] = useState(null); diff --git a/src/manifest.js b/src/manifest.js index 297b615..ee6f18b 100644 --- a/src/manifest.js +++ b/src/manifest.js @@ -16,11 +16,12 @@ const browserAction = { }; /** + * Function to generate a manifest object based on the manifest version, browser target, and environment variables. * - * @param {number} manifestVersion - * @param {string} browserTarget - * @param {Record} env - * @returns {object} + * @param {number} manifestVersion - The version of the manifest (2 or 3). + * @param {string} browserTarget - The target browser ('chrome' or 'firefox'). + * @param {Record} env - Environment variables object. + * @returns {object} - The generated manifest object. */ export function getManifest(manifestVersion, browserTarget, env) { if (!Object.values(browserTargets).includes(browserTarget)) { diff --git a/src/messages.js b/src/messages.js index bdb9b2c..c24a175 100644 --- a/src/messages.js +++ b/src/messages.js @@ -1,3 +1,8 @@ +/** + * Constants for communication between Content-Script and Background in a browser extension. + * Includes actions like getting status, player progress, login/logout, submitting suggestions, etc. + */ + // Content-Script -> Background export const GET_STATUS = 'GET_STATUS'; diff --git a/src/shapes.js b/src/shapes.js index a677db7..13c0c69 100644 --- a/src/shapes.js +++ b/src/shapes.js @@ -1,3 +1,6 @@ +/** + * PropTypes for defining shapes of various data structures used in the application. + */ import PropTypes from 'prop-types'; export const userShape = PropTypes.shape({ diff --git a/src/styles/content-rating.module.css b/src/styles/content-rating.module.css index 127838e..cf9480f 100644 --- a/src/styles/content-rating.module.css +++ b/src/styles/content-rating.module.css @@ -1,3 +1,11 @@ +/** + * Keyframes animation for a content rating alert. + * + * Animates the background and border opacity of an element along with scaling effect. + * Applies red background and border colors. + * Animation duration is 900ms with infinite iteration. + * Uses cubic-bezier timing function for smooth animation. + */ @keyframes contentRatingAlert { 0%,100% { --tw-bg-opacity: 0.2; diff --git a/src/styles/input.module.css b/src/styles/input.module.css index 2e4a6b6..4e5778f 100644 --- a/src/styles/input.module.css +++ b/src/styles/input.module.css @@ -1,3 +1,10 @@ +/** + * Styles for a label and an input field. + * - Label has base text styling with gray color and margin bottom. + * - Input field has full width, padding, border, rounded corners, and no outline. + * - Input field has white background with 70% opacity and light gray border with 30% opacity. + * - Input field changes border color on focus, with different styles for dark mode. + */ .label { @apply block text-base text-gray-200 mb-2; }