diff --git a/app/src/modules/background/index.ts b/app/src/background/index.ts similarity index 87% rename from app/src/modules/background/index.ts rename to app/src/background/index.ts index 9eba992..a7c2bcf 100644 --- a/app/src/modules/background/index.ts +++ b/app/src/background/index.ts @@ -1,4 +1,5 @@ -import { getConfig, DEFAULT_CONFIG } from "../../store/"; +import DEFAULT_CONFIG from "../extension.config"; +import getExtensionConfig from "../lib/getExtensionConfig"; import { type WebAppContext, getWebAppContext, @@ -26,7 +27,7 @@ chrome.runtime.onInstalled.addListener( chrome.webNavigation.onCompleted.addListener(async (details) => { const tabId = details.tabId; const webAppContext: WebAppContext = await getWebAppContext(details.url); - const config = await getConfig(); + const config = await getExtensionConfig(); const isExtensionEnabled = config.globalSettings.isExtensionEnabled; if (!webAppContext.isSupported || !isExtensionEnabled) return; @@ -36,7 +37,7 @@ chrome.webNavigation.onCompleted.addListener(async (details) => { chrome.webNavigation.onHistoryStateUpdated.addListener(async (details) => { const tabId = details.tabId; const webAppContext: WebAppContext = await getWebAppContext(details.url); - const config = await getConfig(); + const config = await getExtensionConfig(); const isExtensionEnabled = config.globalSettings.isExtensionEnabled; if (!webAppContext.isSupported || !isExtensionEnabled) return; diff --git a/app/src/modules/background/webAppContextParsers/index.ts b/app/src/background/parsers/index.ts similarity index 100% rename from app/src/modules/background/webAppContextParsers/index.ts rename to app/src/background/parsers/index.ts diff --git a/app/src/background/parsers/parseUdemyContext.ts b/app/src/background/parsers/parseUdemyContext.ts new file mode 100644 index 0000000..91c5d32 --- /dev/null +++ b/app/src/background/parsers/parseUdemyContext.ts @@ -0,0 +1,60 @@ +import { type UdemyConfig } from "../../content_scripts/webApps/udemy/webApp.config"; +import getWebAppConfig from "../../lib/getWebAppConfig"; +import { type SupportedWebAppContext } from "../webAppContext"; + +export interface UdemyLearnContext extends SupportedWebAppContext { + contextData: { + webAppURL: URL; + activeRoute: "learn"; + payload: UdemyLecture; + }; +} + +export interface UdemyLecture { + lectureID: string; + courseName: string; +} + +export type UdemyContext = UdemyLearnContext | SupportedWebAppContext; + +export default async function parseUdemyContext( + webAppURL: URL, +): Promise { + const udemyConfig = (await getWebAppConfig("udemy")) as UdemyConfig; + + if (!udemyConfig) { + throw new Error("Udemy configuration not found."); + } + + const lectureRouteRegex = + /https:\/\/www\.udemy\.com\/course\/([a-zA-Z0-9-]+)\/learn\/lecture\/(\d+)/; + const match = lectureRouteRegex.exec(webAppURL.href); + + if (match !== null) { + const lectureID = match[2]; + const courseName = match[1]; + const context: UdemyContext = { + isSupported: true, + appName: "udemy", + webAppConfig: udemyConfig, + contextData: { + webAppURL, + activeRoute: "learn", + payload: { + lectureID, + courseName, + }, + }, + }; + return context; + } + + return { + isSupported: true, + appName: "udemy", + webAppConfig: udemyConfig, + contextData: { + webAppURL, + }, + }; +} diff --git a/app/src/modules/background/webAppContextParsers/parseYouTubeContext.ts b/app/src/background/parsers/parseYouTubeContext.ts similarity index 83% rename from app/src/modules/background/webAppContextParsers/parseYouTubeContext.ts rename to app/src/background/parsers/parseYouTubeContext.ts index 90c5441..ab24322 100644 --- a/app/src/modules/background/webAppContextParsers/parseYouTubeContext.ts +++ b/app/src/background/parsers/parseYouTubeContext.ts @@ -1,5 +1,6 @@ +import { YouTubeConfig } from "../../content_scripts/webApps/youtube/webApp.config"; +import getWebAppConfig from "../../lib/getWebAppConfig"; import { type SupportedWebAppContext } from "../webAppContext"; -import { getWorkerConfig, type YouTubeWorkerConfig } from "../../../store/"; export interface YouTubeWatchContext extends SupportedWebAppContext { contextData: { @@ -46,12 +47,10 @@ export type YouTubeContext = export default async function parseYoutubeContext( webAppURL: URL, ): Promise { - const youtubeWorkerConfig = (await getWorkerConfig( - "youtube", - )) as YouTubeWorkerConfig; + const youtubeConfig = (await getWebAppConfig("youtube")) as YouTubeConfig; - if (!youtubeWorkerConfig) { - throw new Error("YouTube worker configuration not found."); + if (!youtubeConfig) { + throw new Error("YouTube configuration not found."); } switch (webAppURL.pathname) { @@ -61,7 +60,7 @@ export default async function parseYoutubeContext( return { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL, activeRoute: "playlist", @@ -80,7 +79,7 @@ export default async function parseYoutubeContext( return { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL, activeRoute: "watch", @@ -99,7 +98,7 @@ export default async function parseYoutubeContext( return { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL, activeRoute: "shorts", @@ -115,7 +114,7 @@ export default async function parseYoutubeContext( return { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL, }, diff --git a/app/src/background/webAppContext.ts b/app/src/background/webAppContext.ts new file mode 100644 index 0000000..c349157 --- /dev/null +++ b/app/src/background/webAppContext.ts @@ -0,0 +1,55 @@ +import { WebAppConfig } from "../lib/getWebAppConfig"; +import webAppContextParser from "./parsers"; + +export interface UnSupportedWebAppContext { + isSupported: false; + appName: null; +} + +export interface SupportedWebAppContext { + isSupported: true; + appName: string; + webAppConfig: WebAppConfig; + contextData: { + webAppURL: URL; + activeRoute?: string; + payload?: ContextPayload | ContextPayload[]; + [key: string]: any; + }; +} + +export interface ContextPayload { + [key: string]: any; +} + +export type WebAppContext = UnSupportedWebAppContext | SupportedWebAppContext; + +export const getWebAppContext = async ( + href: string, +): Promise => { + const webAppURL = new URL(href); + const origin = webAppURL.origin; + + switch (origin) { + case "https://www.youtube.com": { + return await webAppContextParser.YouTube(webAppURL); + } + case "https://www.udemy.com": { + return await webAppContextParser.Udemy(webAppURL); + } + default: { + const webAppContext: WebAppContext = { + appName: null, + isSupported: false, + }; + return webAppContext; + } + } +}; + +export const dispatchWebAppContext = async ( + tabId: number, + webAppContext: SupportedWebAppContext, +): Promise => { + return await chrome.tabs.sendMessage(tabId, webAppContext); +}; diff --git a/app/src/ui/Card.tsx b/app/src/components/Card.tsx similarity index 100% rename from app/src/ui/Card.tsx rename to app/src/components/Card.tsx diff --git a/app/src/ui/Switch.tsx b/app/src/components/Switch.tsx similarity index 100% rename from app/src/ui/Switch.tsx rename to app/src/components/Switch.tsx diff --git a/app/src/ui/Tooltip.tsx b/app/src/components/Tooltip.tsx similarity index 100% rename from app/src/ui/Tooltip.tsx rename to app/src/components/Tooltip.tsx diff --git a/app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlButton.tsx b/app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlButton.tsx similarity index 100% rename from app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlButton.tsx rename to app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlButton.tsx diff --git a/app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlMenu.tsx b/app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlMenu.tsx similarity index 100% rename from app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlMenu.tsx rename to app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlMenu.tsx diff --git a/app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlMenuItem.tsx b/app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlMenuItem.tsx similarity index 100% rename from app/src/modules/content_scripts/components/PlaybackControl/PlaybackControlMenuItem.tsx rename to app/src/content_scripts/a-la-carte/PlaybackControl/PlaybackControlMenuItem.tsx diff --git a/app/src/modules/content_scripts/components/PlaybackControl/index.tsx b/app/src/content_scripts/a-la-carte/PlaybackControl/index.tsx similarity index 100% rename from app/src/modules/content_scripts/components/PlaybackControl/index.tsx rename to app/src/content_scripts/a-la-carte/PlaybackControl/index.tsx diff --git a/app/src/modules/content_scripts/components/ScrollToTop/index.tsx b/app/src/content_scripts/a-la-carte/ScrollToTop/index.tsx similarity index 100% rename from app/src/modules/content_scripts/components/ScrollToTop/index.tsx rename to app/src/content_scripts/a-la-carte/ScrollToTop/index.tsx diff --git a/app/src/modules/content_scripts/components/index.css b/app/src/content_scripts/a-la-carte/index.css similarity index 100% rename from app/src/modules/content_scripts/components/index.css rename to app/src/content_scripts/a-la-carte/index.css diff --git a/app/src/modules/content_scripts/index.ts b/app/src/content_scripts/index.ts similarity index 52% rename from app/src/modules/content_scripts/index.ts rename to app/src/content_scripts/index.ts index 525107d..e4793a9 100644 --- a/app/src/modules/content_scripts/index.ts +++ b/app/src/content_scripts/index.ts @@ -1,34 +1,31 @@ import { type SupportedWebAppContext } from "../background/webAppContext"; -import { - type YouTubeContext, - type UdemyContext, -} from "../background/webAppContextParsers"; +import { type YouTubeContext, type UdemyContext } from "../background/parsers"; async function runApp( webAppContext: SupportedWebAppContext, ): Promise { const { appName, - workerConfig: { - generalSettings: { isEnabled: isWorkerEnabled }, + webAppConfig: { + generalSettings: { isEnabled: isWebAppEnabled }, }, } = webAppContext; - if (isWorkerEnabled) { + if (isWebAppEnabled) { switch (appName) { case "youtube": { const youtubeContext = webAppContext as YouTubeContext; - const { default: runYouTubeWorker } = await import( - /* webpackIgnore: true */ chrome.runtime.getURL("workers/youtube.js") + const { default: runYouTube } = await import( + /* webpackIgnore: true */ chrome.runtime.getURL("webApps/youtube.js") ); - void runYouTubeWorker(youtubeContext); + void runYouTube(youtubeContext); break; } case "udemy": { const udemyContext = webAppContext as UdemyContext; - const { default: runUdemyWorker } = await import( - /* webpackIgnore: true */ chrome.runtime.getURL("workers/udemy.js") + const { default: runUdemy } = await import( + /* webpackIgnore: true */ chrome.runtime.getURL("webApps/udemy.js") ); - void runUdemyWorker(udemyContext); + void runUdemy(udemyContext); break; } } diff --git a/app/src/modules/content_scripts/workers/udemy/forgeDOM.ts b/app/src/content_scripts/webApps/udemy/forgeDOM.ts similarity index 100% rename from app/src/modules/content_scripts/workers/udemy/forgeDOM.ts rename to app/src/content_scripts/webApps/udemy/forgeDOM.ts diff --git a/app/src/content_scripts/webApps/udemy/index.ts b/app/src/content_scripts/webApps/udemy/index.ts new file mode 100644 index 0000000..d22787c --- /dev/null +++ b/app/src/content_scripts/webApps/udemy/index.ts @@ -0,0 +1,21 @@ +import { type UdemyContext } from "../../../background/parsers"; +import { type UdemyLearnContext } from "../../../background/parsers/parseUdemyContext"; +import runLearn from "./routes/learn"; +import { UdemyConfig } from "./webApp.config"; + +const runUdemy = async (context: UdemyContext): Promise => { + const { activeRoute } = context.contextData; + const webAppConfig = context.webAppConfig as UdemyConfig; + + switch (activeRoute) { + case "learn": { + const isLearnEnabled = webAppConfig.routes.learn.isEnabled; + isLearnEnabled && (await runLearn(context as UdemyLearnContext)); + break; + } + default: + break; + } +}; + +export default runUdemy; diff --git a/app/src/modules/content_scripts/workers/udemy/routes/learn/index.ts b/app/src/content_scripts/webApps/udemy/routes/learn/index.ts similarity index 97% rename from app/src/modules/content_scripts/workers/udemy/routes/learn/index.ts rename to app/src/content_scripts/webApps/udemy/routes/learn/index.ts index a32158d..e6c7086 100644 --- a/app/src/modules/content_scripts/workers/udemy/routes/learn/index.ts +++ b/app/src/content_scripts/webApps/udemy/routes/learn/index.ts @@ -1,11 +1,11 @@ -import loadElement from "../../../../lib/loadElement"; import ForgeDOM from "../../forgeDOM"; import { type UdemyPlayer } from "./interfaces"; import { type UdemyLearnContext, type UdemyLecture, -} from "../../../../../background/webAppContextParsers/parseUdemyContext"; -import { type UdemyWorkerConfig } from "../../config"; +} from "../../../../../background/parsers//parseUdemyContext"; +import { UdemyConfig } from "../../webApp.config"; +import loadElement from "../../../../../lib/loadElement"; let toggleSpeedShortcut: string; let seekBackwardShortcut: string; @@ -30,7 +30,7 @@ let customSpeedButton: HTMLLIElement; const addLearnToppings = async (context: UdemyLearnContext): Promise => { const { lectureID, courseName } = context.contextData.payload; const { isEnabled, keybindings, preferences } = ( - context.workerConfig as UdemyWorkerConfig + context.webAppConfig as UdemyConfig ).routes.learn; toggleSpeedShortcut = keybindings.toggleSpeedShortcut; seekBackwardShortcut = keybindings.seekBackwardShortcut; diff --git a/app/src/modules/content_scripts/workers/udemy/routes/learn/interfaces.ts b/app/src/content_scripts/webApps/udemy/routes/learn/interfaces.ts similarity index 61% rename from app/src/modules/content_scripts/workers/udemy/routes/learn/interfaces.ts rename to app/src/content_scripts/webApps/udemy/routes/learn/interfaces.ts index 9c71369..b7d7c44 100644 --- a/app/src/modules/content_scripts/workers/udemy/routes/learn/interfaces.ts +++ b/app/src/content_scripts/webApps/udemy/routes/learn/interfaces.ts @@ -1,4 +1,4 @@ -import { UdemyLecture } from "../../../../../background/webAppContextParsers/parseUdemyContext"; +import { UdemyLecture } from "../../../../../background/parsers//parseUdemyContext"; export type UdemyPlayer = { videoElement: HTMLVideoElement; playbackRate: string | number; diff --git a/app/src/modules/content_scripts/workers/udemy/config.ts b/app/src/content_scripts/webApps/udemy/webApp.config.ts similarity index 81% rename from app/src/modules/content_scripts/workers/udemy/config.ts rename to app/src/content_scripts/webApps/udemy/webApp.config.ts index a1ab157..aa46091 100644 --- a/app/src/modules/content_scripts/workers/udemy/config.ts +++ b/app/src/content_scripts/webApps/udemy/webApp.config.ts @@ -1,6 +1,6 @@ -import { type WorkerConfig } from "../../../../store/"; +import { WebAppConfig } from "../../../lib/getWebAppConfig"; -const udemyWorkerConfig = { +const udemyConfig = { generalSettings: { isEnabled: true as boolean, }, @@ -34,7 +34,7 @@ const udemyWorkerConfig = { }, }, }, -} satisfies WorkerConfig; +} satisfies WebAppConfig; -export type UdemyWorkerConfig = typeof udemyWorkerConfig; -export default udemyWorkerConfig; +export type UdemyConfig = typeof udemyConfig; +export default udemyConfig; diff --git a/app/src/modules/content_scripts/workers/youtube/common/VideoPlayer.ts b/app/src/content_scripts/webApps/youtube/common/VideoPlayer.ts similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/common/VideoPlayer.ts rename to app/src/content_scripts/webApps/youtube/common/VideoPlayer.ts diff --git a/app/src/modules/content_scripts/workers/youtube/common/dom.ts b/app/src/content_scripts/webApps/youtube/common/dom.ts similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/common/dom.ts rename to app/src/content_scripts/webApps/youtube/common/dom.ts diff --git a/app/src/modules/content_scripts/workers/youtube/common/interfaces.ts b/app/src/content_scripts/webApps/youtube/common/interfaces.ts similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/common/interfaces.ts rename to app/src/content_scripts/webApps/youtube/common/interfaces.ts diff --git a/app/src/modules/content_scripts/workers/youtube/global.css b/app/src/content_scripts/webApps/youtube/global.css similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/global.css rename to app/src/content_scripts/webApps/youtube/global.css diff --git a/app/src/content_scripts/webApps/youtube/index.ts b/app/src/content_scripts/webApps/youtube/index.ts new file mode 100644 index 0000000..dec2699 --- /dev/null +++ b/app/src/content_scripts/webApps/youtube/index.ts @@ -0,0 +1,39 @@ +import type { + YouTubeContext, + YouTubePlaylistContext, + YouTubeWatchContext, + YouTubeShortsContext, +} from "../../../background/parsers//parseYouTubeContext"; +import runWatch from "./routes/watch"; +import runPlaylist from "./routes/playlist"; +import runShorts from "./routes/shorts"; +import { YouTubeConfig } from "./webApp.config"; +import "./global.css"; + +const runYouTube = async (context: YouTubeContext): Promise => { + const { activeRoute } = context.contextData; + const webAppConfig = context.webAppConfig as YouTubeConfig; + + switch (activeRoute) { + case "watch": { + const isWatchEnabled = webAppConfig.routes.watch.isEnabled; + isWatchEnabled && (await runWatch(context as YouTubeWatchContext)); + break; + } + case "playlist": { + const isPlaylistEnabled = webAppConfig.routes.playlist.isEnabled; + isPlaylistEnabled && + (await runPlaylist(context as YouTubePlaylistContext)); + break; + } + case "shorts": { + const isShortsEnabled = webAppConfig.routes.shorts.isEnabled; + isShortsEnabled && (await runShorts(context as YouTubeShortsContext)); + break; + } + default: + break; + } +}; + +export default runYouTube; diff --git a/app/src/modules/content_scripts/workers/youtube/routes/playlist/addMetadataToppings.ts b/app/src/content_scripts/webApps/youtube/routes/playlist/addMetadataToppings.ts similarity index 98% rename from app/src/modules/content_scripts/workers/youtube/routes/playlist/addMetadataToppings.ts rename to app/src/content_scripts/webApps/youtube/routes/playlist/addMetadataToppings.ts index 2c1954a..c6b9d6b 100644 --- a/app/src/modules/content_scripts/workers/youtube/routes/playlist/addMetadataToppings.ts +++ b/app/src/content_scripts/webApps/youtube/routes/playlist/addMetadataToppings.ts @@ -1,7 +1,7 @@ import { createMetadataSection, createSectionItem } from "../../common/dom"; import fetchYouTubeToppings from "../../utils/fetchYouTubeToppings"; -import { formatRuntime } from "../../../../lib/formatRuntime"; import { type YouTubePlaylistMetadata } from "../../common/interfaces"; +import { formatRuntime } from "../../../../../lib/formatRuntime"; const addMetadataToppings = async (playlistID: string): Promise => { const metadataActionBar = document.querySelector( diff --git a/app/src/modules/content_scripts/workers/youtube/routes/playlist/index.css b/app/src/content_scripts/webApps/youtube/routes/playlist/index.css similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/routes/playlist/index.css rename to app/src/content_scripts/webApps/youtube/routes/playlist/index.css diff --git a/app/src/modules/content_scripts/workers/youtube/routes/playlist/index.ts b/app/src/content_scripts/webApps/youtube/routes/playlist/index.ts similarity index 63% rename from app/src/modules/content_scripts/workers/youtube/routes/playlist/index.ts rename to app/src/content_scripts/webApps/youtube/routes/playlist/index.ts index 07d0c39..fca3a96 100644 --- a/app/src/modules/content_scripts/workers/youtube/routes/playlist/index.ts +++ b/app/src/content_scripts/webApps/youtube/routes/playlist/index.ts @@ -1,11 +1,9 @@ -import { YouTubePlaylistContext } from "../../../../../background/webAppContextParsers/parseYouTubeContext"; -import loadElement from "../../../../lib/loadElement"; +import { YouTubePlaylistContext } from "../../../../../background/parsers//parseYouTubeContext"; +import loadElement from "../../../../../lib/loadElement"; import addMetadataToppings from "./addMetadataToppings"; import "./index.css"; -const runPlaylistWorker = async ( - context: YouTubePlaylistContext, -): Promise => { +const runPlaylist = async (context: YouTubePlaylistContext): Promise => { const { playlistID } = context.contextData.payload; const metadataActionBar = await loadElement( ".metadata-action-bar", @@ -17,4 +15,4 @@ const runPlaylistWorker = async ( } }; -export default runPlaylistWorker; +export default runPlaylist; diff --git a/app/src/modules/content_scripts/workers/youtube/routes/shorts/components/AutoScrollButton.tsx b/app/src/content_scripts/webApps/youtube/routes/shorts/components/AutoScrollButton.tsx similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/routes/shorts/components/AutoScrollButton.tsx rename to app/src/content_scripts/webApps/youtube/routes/shorts/components/AutoScrollButton.tsx diff --git a/app/src/modules/content_scripts/workers/youtube/routes/shorts/components/TogglePlaybackRateButton.tsx b/app/src/content_scripts/webApps/youtube/routes/shorts/components/TogglePlaybackRateButton.tsx similarity index 100% rename from app/src/modules/content_scripts/workers/youtube/routes/shorts/components/TogglePlaybackRateButton.tsx rename to app/src/content_scripts/webApps/youtube/routes/shorts/components/TogglePlaybackRateButton.tsx diff --git a/app/src/modules/content_scripts/workers/youtube/routes/shorts/index.ts b/app/src/content_scripts/webApps/youtube/routes/shorts/index.ts similarity index 89% rename from app/src/modules/content_scripts/workers/youtube/routes/shorts/index.ts rename to app/src/content_scripts/webApps/youtube/routes/shorts/index.ts index 955eb98..89cf9bb 100644 --- a/app/src/modules/content_scripts/workers/youtube/routes/shorts/index.ts +++ b/app/src/content_scripts/webApps/youtube/routes/shorts/index.ts @@ -1,6 +1,6 @@ -import { YouTubeShortsContext } from "../../../../../background/webAppContextParsers/parseYouTubeContext"; -import loadElement from "../../../../lib/loadElement"; -import { YouTubeWorkerConfig } from "../../config"; +import { YouTubeShortsContext } from "../../../../../background/parsers//parseYouTubeContext"; +import loadElement from "../../../../../lib/loadElement"; +import { YouTubeConfig } from "../../webApp.config"; import AutoScrollButton from "./components/AutoScrollButton"; import ToggleShortsSpeedButton, { togglePlaybackRate, @@ -9,12 +9,9 @@ import ToggleShortsSpeedButton, { let togglePlaybackRateKeybinding: string; let isAutoScrollEnabled: boolean; -const runShortsWorker = async ( - context: YouTubeShortsContext, -): Promise => { - const { keybindings, preferences } = ( - context.workerConfig as YouTubeWorkerConfig - ).routes.shorts; +const runShorts = async (context: YouTubeShortsContext): Promise => { + const { keybindings, preferences } = (context.webAppConfig as YouTubeConfig) + .routes.shorts; togglePlaybackRateKeybinding = keybindings.toggleSpeedShortcut; isAutoScrollEnabled = preferences.reelAutoScroll; @@ -60,7 +57,7 @@ const runShortsWorker = async ( } }; -export default runShortsWorker; +export default runShorts; function addShortsKeybindings(event: KeyboardEvent) { if ( diff --git a/app/src/modules/content_scripts/workers/youtube/routes/watch/index.ts b/app/src/content_scripts/webApps/youtube/routes/watch/index.ts similarity index 97% rename from app/src/modules/content_scripts/workers/youtube/routes/watch/index.ts rename to app/src/content_scripts/webApps/youtube/routes/watch/index.ts index 9c52030..116a559 100644 --- a/app/src/modules/content_scripts/workers/youtube/routes/watch/index.ts +++ b/app/src/content_scripts/webApps/youtube/routes/watch/index.ts @@ -1,8 +1,8 @@ import { createMenuItem } from "../../common/dom"; import YTPlayer from "../../common/VideoPlayer"; -import loadElement from "../../../../lib/loadElement"; -import { type YouTubeWatchContext } from "../../../../../background/webAppContextParsers/parseYouTubeContext"; -import { YouTubeWorkerConfig } from "../../config"; +import { type YouTubeWatchContext } from "../../../../../background/parsers//parseYouTubeContext"; +import { YouTubeConfig } from "../../webApp.config"; +import loadElement from "../../../../../lib/loadElement"; let toggleSpeedShortcut: string; let seekBackwardShortcut: string; @@ -26,10 +26,10 @@ let customSpeed: string; let customSpeedButton: HTMLElement; let doubleTapSeekTimeout: ReturnType; -const runWatchWorker = async (context: YouTubeWatchContext): Promise => { +const runWatch = async (context: YouTubeWatchContext): Promise => { const { videoID } = context.contextData.payload; const { isEnabled, keybindings, preferences } = ( - context.workerConfig as YouTubeWorkerConfig + context.webAppConfig as YouTubeConfig ).routes.watch; toggleSpeedShortcut = keybindings.toggleSpeedShortcut; seekBackwardShortcut = keybindings.seekBackwardShortcut; @@ -446,4 +446,4 @@ const changePlaybackSpeed = (speed: number): void => { } }; -export default runWatchWorker; +export default runWatch; diff --git a/app/src/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.ts b/app/src/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.ts new file mode 100644 index 0000000..35eaa5b --- /dev/null +++ b/app/src/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.ts @@ -0,0 +1,40 @@ +import { + type YouTubeToppingsRequest, + type YouTubeToppingsResponse, +} from "../common/interfaces"; + +export const SERVER_BASE_URI = + process.env.NODE_ENV === "development" + ? "http://localhost:8080" + : "https://toppings.onrender.com"; + +/** + * Fetches data from the Toppings API for YouTube. + * + * @param {YouTubeToppingsRequest} request - The request object for the data to fetch. + * @returns {Promise} - A Promise that resolves to the fetched data. + */ +const fetchYouTubeToppings = async ( + request: YouTubeToppingsRequest, +): Promise => { + let endpoint, contentId; + + if (request.body.routeType === "playlist") { + endpoint = "youtube/playlist"; + contentId = request.body.contentId; + } + + const apiURI = `${SERVER_BASE_URI}/${endpoint}/${contentId}`; + + const httpResponse = await fetch(apiURI, { + method: "GET", + headers: { + Accept: "application/json", + }, + }); + + const response = await httpResponse.json(); + return response; +}; + +export default fetchYouTubeToppings; diff --git a/app/src/modules/content_scripts/workers/youtube/config.ts b/app/src/content_scripts/webApps/youtube/webApp.config.ts similarity index 86% rename from app/src/modules/content_scripts/workers/youtube/config.ts rename to app/src/content_scripts/webApps/youtube/webApp.config.ts index e7129a9..3ffb08b 100644 --- a/app/src/modules/content_scripts/workers/youtube/config.ts +++ b/app/src/content_scripts/webApps/youtube/webApp.config.ts @@ -1,6 +1,6 @@ -import { type WorkerConfig } from "../../../../store/"; +import { WebAppConfig } from "../../../lib/getWebAppConfig"; -const youtubeWorkerConfig = { +const youtubeConfig = { generalSettings: { isEnabled: true as boolean, }, @@ -50,7 +50,7 @@ const youtubeWorkerConfig = { }, }, }, -} satisfies WorkerConfig; +} satisfies WebAppConfig; -export type YouTubeWorkerConfig = typeof youtubeWorkerConfig; -export default youtubeWorkerConfig; +export type YouTubeConfig = typeof youtubeConfig; +export default youtubeConfig; diff --git a/app/src/extension.config.ts b/app/src/extension.config.ts new file mode 100644 index 0000000..4a2493a --- /dev/null +++ b/app/src/extension.config.ts @@ -0,0 +1,17 @@ +import udemyConfig from "./content_scripts/webApps/udemy/webApp.config"; +import youtubeConfig from "./content_scripts/webApps/youtube/webApp.config"; + +const DEFAULT_CONFIG = { + globalSettings: { + isExtensionEnabled: true as boolean, + }, + webApps: { + youtube: youtubeConfig, + udemy: udemyConfig, + }, +}; + +export type ExtensionConfig = typeof DEFAULT_CONFIG; +export type WebApps = keyof typeof DEFAULT_CONFIG.webApps; + +export default DEFAULT_CONFIG; diff --git a/app/src/modules/content_scripts/lib/formatRuntime.ts b/app/src/lib/formatRuntime.ts similarity index 100% rename from app/src/modules/content_scripts/lib/formatRuntime.ts rename to app/src/lib/formatRuntime.ts diff --git a/app/src/lib/getAllWebAppConfig.ts b/app/src/lib/getAllWebAppConfig.ts new file mode 100644 index 0000000..e507d2c --- /dev/null +++ b/app/src/lib/getAllWebAppConfig.ts @@ -0,0 +1,11 @@ +import { WebAppConfig } from "./getWebAppConfig"; + +const getAllWebAppConfig = async (): Promise> => { + return new Promise((resolve) => { + chrome.storage.sync.get("webApps", (storage) => { + resolve(storage.webApps); + }); + }); +}; + +export default getAllWebAppConfig; diff --git a/app/src/lib/getExtensionConfig.ts b/app/src/lib/getExtensionConfig.ts new file mode 100644 index 0000000..9753d8b --- /dev/null +++ b/app/src/lib/getExtensionConfig.ts @@ -0,0 +1,11 @@ +import { ExtensionConfig } from "../extension.config"; + +const getExtensionConfig = async (): Promise => { + return new Promise((resolve) => { + chrome.storage.sync.get(undefined, (storage) => { + resolve(storage as ExtensionConfig); + }); + }); +}; + +export default getExtensionConfig; diff --git a/app/src/lib/getWebAppConfig.ts b/app/src/lib/getWebAppConfig.ts new file mode 100644 index 0000000..3d9be43 --- /dev/null +++ b/app/src/lib/getWebAppConfig.ts @@ -0,0 +1,24 @@ +export interface WebAppConfig { + generalSettings: WebAppGeneralSettings; + routes?: Record; +} + +export interface WebAppGeneralSettings { + isEnabled: boolean; +} + +export interface WebAppRouteConfig { + isEnabled: boolean; + keybindings?: Record; + preferences?: Record; +} + +const getWebAppConfig = async (webAppName: string): Promise => { + return new Promise((resolve) => { + chrome.storage.sync.get("webApps", (storage) => { + resolve(storage.webApps[webAppName]); + }); + }); +}; + +export default getWebAppConfig; diff --git a/app/src/modules/content_scripts/lib/loadElement.ts b/app/src/lib/loadElement.ts similarity index 100% rename from app/src/modules/content_scripts/lib/loadElement.ts rename to app/src/lib/loadElement.ts diff --git a/app/src/manifest.json b/app/src/manifest.json index f80b28a..011aead 100644 --- a/app/src/manifest.json +++ b/app/src/manifest.json @@ -38,7 +38,7 @@ { "matches": [""], "resources": [ - "workers/*.js", + "webApps/*.js", "assets/**/*.png", "assets/**/*.svg", "options/assets/*.png" diff --git a/app/src/modules/background/webAppContext.ts b/app/src/modules/background/webAppContext.ts deleted file mode 100644 index 1ab9b01..0000000 --- a/app/src/modules/background/webAppContext.ts +++ /dev/null @@ -1,55 +0,0 @@ -import webAppContextParser from './webAppContextParsers'; -import { type WorkerConfig } from '../../store/'; - -export interface UnSupportedWebAppContext { - isSupported: false; - appName: null; -} - -export interface SupportedWebAppContext { - isSupported: true; - appName: string; - workerConfig: WorkerConfig; - contextData: { - webAppURL: URL; - activeRoute?: string; - payload?: ContextPayload | ContextPayload[]; - [key: string]: any; - }; -} - -export interface ContextPayload { - [key: string]: any; -} - -export type WebAppContext = UnSupportedWebAppContext | SupportedWebAppContext; - -export const getWebAppContext = async ( - href: string -): Promise => { - const webAppURL = new URL(href); - const origin = webAppURL.origin; - - switch (origin) { - case 'https://www.youtube.com': { - return await webAppContextParser.YouTube(webAppURL); - } - case 'https://www.udemy.com': { - return await webAppContextParser.Udemy(webAppURL); - } - default: { - const webAppContext: WebAppContext = { - appName: null, - isSupported: false, - }; - return webAppContext; - } - } -}; - -export const dispatchWebAppContext = async ( - tabId: number, - webAppContext: SupportedWebAppContext -): Promise => { - return await chrome.tabs.sendMessage(tabId, webAppContext); -}; diff --git a/app/src/modules/background/webAppContextParsers/parseUdemyContext.ts b/app/src/modules/background/webAppContextParsers/parseUdemyContext.ts deleted file mode 100644 index 585afe3..0000000 --- a/app/src/modules/background/webAppContextParsers/parseUdemyContext.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { type SupportedWebAppContext } from '../webAppContext'; -import { getWorkerConfig, type UdemyWorkerConfig } from '../../../store/'; - -export interface UdemyLearnContext extends SupportedWebAppContext { - contextData: { - webAppURL: URL; - activeRoute: 'learn'; - payload: UdemyLecture; - }; -} - -export interface UdemyLecture { - lectureID: string; - courseName: string; -} - -export type UdemyContext = UdemyLearnContext | SupportedWebAppContext; - -export default async function parseUdemyContext( - webAppURL: URL -): Promise { - const udemyWorkerConfig = (await getWorkerConfig( - 'udemy' - )) as UdemyWorkerConfig; - - if (!udemyWorkerConfig) { - throw new Error('Udemy worker configuration not found.'); - } - - const lectureRouteRegex = - /https:\/\/www\.udemy\.com\/course\/([a-zA-Z0-9-]+)\/learn\/lecture\/(\d+)/; - const match = lectureRouteRegex.exec(webAppURL.href); - - if (match !== null) { - const lectureID = match[2]; - const courseName = match[1]; - const context: UdemyContext = { - isSupported: true, - appName: 'udemy', - workerConfig: udemyWorkerConfig, - contextData: { - webAppURL, - activeRoute: 'learn', - payload: { - lectureID, - courseName, - }, - }, - }; - return context; - } - - return { - isSupported: true, - appName: 'udemy', - workerConfig: udemyWorkerConfig, - contextData: { - webAppURL, - }, - }; -} diff --git a/app/src/modules/content_scripts/workers/udemy/index.ts b/app/src/modules/content_scripts/workers/udemy/index.ts deleted file mode 100644 index 733ec08..0000000 --- a/app/src/modules/content_scripts/workers/udemy/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type UdemyContext } from "../../../background/webAppContextParsers"; -import { type UdemyLearnContext } from "../../../background/webAppContextParsers/parseUdemyContext"; -import { type UdemyWorkerConfig } from "./config"; -import runLearnWorker from "./routes/learn"; - -const runUdemyWorker = async (context: UdemyContext): Promise => { - const { activeRoute } = context.contextData; - const workerConfig = context.workerConfig as UdemyWorkerConfig; - - switch (activeRoute) { - case "learn": { - const isLearnWorkerEnabled = workerConfig.routes.learn.isEnabled; - isLearnWorkerEnabled && - (await runLearnWorker(context as UdemyLearnContext)); - break; - } - default: - break; - } -}; - -export default runUdemyWorker; diff --git a/app/src/modules/content_scripts/workers/youtube/index.ts b/app/src/modules/content_scripts/workers/youtube/index.ts deleted file mode 100644 index b1dcd4c..0000000 --- a/app/src/modules/content_scripts/workers/youtube/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { - YouTubeContext, - YouTubePlaylistContext, - YouTubeWatchContext, - YouTubeShortsContext, -} from "../../../background/webAppContextParsers/parseYouTubeContext"; -import { type YouTubeWorkerConfig } from "./config"; -import runWatchWorker from "./routes/watch"; -import runPlaylistWorker from "./routes/playlist"; -import runShortsWorker from "./routes/shorts"; -import "./global.css"; - -const runYouTubeWorker = async (context: YouTubeContext): Promise => { - const { activeRoute } = context.contextData; - const workerConfig = context.workerConfig as YouTubeWorkerConfig; - - switch (activeRoute) { - case "watch": { - const isWatchWorkerEnabled = workerConfig.routes.watch.isEnabled; - isWatchWorkerEnabled && - (await runWatchWorker(context as YouTubeWatchContext)); - break; - } - case "playlist": { - const isPlaylistWorkerEnabled = workerConfig.routes.playlist.isEnabled; - isPlaylistWorkerEnabled && - (await runPlaylistWorker(context as YouTubePlaylistContext)); - break; - } - case "shorts": { - const isShortsWorkerEnabled = workerConfig.routes.shorts.isEnabled; - isShortsWorkerEnabled && - (await runShortsWorker(context as YouTubeShortsContext)); - break; - } - default: - break; - } -}; - -export default runYouTubeWorker; diff --git a/app/src/modules/content_scripts/workers/youtube/utils/fetchYouTubeToppings.ts b/app/src/modules/content_scripts/workers/youtube/utils/fetchYouTubeToppings.ts deleted file mode 100644 index 10b96d3..0000000 --- a/app/src/modules/content_scripts/workers/youtube/utils/fetchYouTubeToppings.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { SERVER_BASE_URI } from '../../../../../store'; -import { - type YouTubeToppingsRequest, - type YouTubeToppingsResponse, -} from '../common/interfaces'; - -/** - * Fetches data from the Toppings API for YouTube. - * - * @param {YouTubeToppingsRequest} request - The request object for the data to fetch. - * @returns {Promise} - A Promise that resolves to the fetched data. - */ -const fetchYouTubeToppings = async ( - request: YouTubeToppingsRequest -): Promise => { - let endpoint, contentId; - - if (request.body.routeType === 'playlist') { - endpoint = 'youtube/playlist'; - contentId = request.body.contentId; - } - - const apiURI = `${SERVER_BASE_URI}/${endpoint}/${contentId}`; - - const httpResponse = await fetch(apiURI, { - method: 'GET', - headers: { - Accept: 'application/json', - }, - }); - - const response = await httpResponse.json(); - return response; -}; - -export default fetchYouTubeToppings; diff --git a/app/src/modules/options/src/components/Apps/index.tsx b/app/src/modules/options/src/components/Apps/index.tsx deleted file mode 100644 index af980ef..0000000 --- a/app/src/modules/options/src/components/Apps/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import { useContext } from "react"; -import ConfigContext from "../../store"; -import AppWorker from "./AppWorker"; - -export default function AppsConfig() { - const { config } = useContext(ConfigContext)!; - const workers = config.workers; - const appWorkers = Object.keys(workers) as Array; - return ( -
- {appWorkers.map((appWorker, idx) => { - return ; - })} -
- ); -} diff --git a/app/src/modules/options/index.html b/app/src/options/index.html similarity index 100% rename from app/src/modules/options/index.html rename to app/src/options/index.html diff --git a/app/src/modules/options/src/App.tsx b/app/src/options/src/App.tsx similarity index 73% rename from app/src/modules/options/src/App.tsx rename to app/src/options/src/App.tsx index aa758c8..3c619b3 100644 --- a/app/src/modules/options/src/App.tsx +++ b/app/src/options/src/App.tsx @@ -1,12 +1,12 @@ import React, { useState } from "react"; import { Outlet, useLoaderData } from "react-router-dom"; import Sidebar from "./components/Sidebar"; -import { Config } from "../../../store"; import ConfigContext from "./store"; +import { ExtensionConfig } from "../../extension.config"; function App() { - const loadedConfig = useLoaderData() as Config; - const [config, setConfig] = useState(loadedConfig); + const loadedConfig = useLoaderData() as ExtensionConfig; + const [config, setConfig] = useState(loadedConfig); return ( diff --git a/app/src/modules/options/src/components/Apps/AppRoute.tsx b/app/src/options/src/components/Apps/AppRoute.tsx similarity index 83% rename from app/src/modules/options/src/components/Apps/AppRoute.tsx rename to app/src/options/src/components/Apps/AppRoute.tsx index 1c1e19e..305b41d 100644 --- a/app/src/modules/options/src/components/Apps/AppRoute.tsx +++ b/app/src/options/src/components/Apps/AppRoute.tsx @@ -1,25 +1,26 @@ import React from "react"; import { useContext } from "react"; import { produce } from "immer"; -import { WorkerConfigRouteConfig, WorkerName } from "../../../../../store"; import ConfigContext from "../../store"; import Switch from "../Switch"; import Keybinding from "../Keybinding"; import Preferences from "./Preferences"; +import { WebApps } from "../../../../extension.config"; +import { WebAppRouteConfig } from "../../../../lib/getWebAppConfig"; const AppRoute = ({ appName, routeName, }: { - appName: WorkerName; + appName: WebApps; routeName: string; }) => { const { config, setConfig } = useContext(ConfigContext)!; const routeTitle = routeName.at(0)?.toUpperCase() + routeName.slice(1); - const routes = config.workers[appName].routes as Record< + const routes = config.webApps[appName].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig >; const appRoute = routes[routeName]; @@ -29,9 +30,9 @@ const AppRoute = ({ const toggleAppRouteEnabled = (isEnabled: boolean) => { const newConfig = produce(config, (draft) => { - ( - draft.workers[appName].routes as Record - )[routeName].isEnabled = isEnabled; + (draft.webApps[appName].routes as Record)[ + routeName + ].isEnabled = isEnabled; }); setConfig(newConfig); chrome.storage.sync.set(newConfig); diff --git a/app/src/modules/options/src/components/Apps/GeneralSettings.tsx b/app/src/options/src/components/Apps/GeneralSettings.tsx similarity index 58% rename from app/src/modules/options/src/components/Apps/GeneralSettings.tsx rename to app/src/options/src/components/Apps/GeneralSettings.tsx index 402c981..24967df 100644 --- a/app/src/modules/options/src/components/Apps/GeneralSettings.tsx +++ b/app/src/options/src/components/Apps/GeneralSettings.tsx @@ -1,20 +1,20 @@ import React from "react"; import { useContext } from "react"; import { produce } from "immer"; -import { WorkerConfigGeneralSettings, WorkerName } from "../../../../../store"; import Switch from "../Switch"; import ConfigContext from "../../store"; +import { WebApps } from "../../../../extension.config"; +import { WebAppGeneralSettings } from "../../../../lib/getWebAppConfig"; -const GeneralSettings = ({ name }: { name: WorkerName }) => { +const GeneralSettings = ({ name }: { name: WebApps }) => { const { config, setConfig } = useContext(ConfigContext)!; const title = name.at(0)?.toUpperCase() + name.slice(1); - const appWorker = config.workers[name]; - const generalSettings = - appWorker.generalSettings as WorkerConfigGeneralSettings; + const webApp = config.webApps[name]; + const generalSettings = webApp.generalSettings as WebAppGeneralSettings; - const toggleWorkerEnabled = (isEnabled: boolean) => { + const toggleWebAppEnabled = (isEnabled: boolean) => { const newConfig = produce(config, (draft) => { - draft.workers[name].generalSettings.isEnabled = isEnabled; + draft.webApps[name].generalSettings.isEnabled = isEnabled; }); setConfig(newConfig); chrome.storage.sync.set(newConfig); @@ -25,9 +25,9 @@ const GeneralSettings = ({ name }: { name: WorkerName }) => {

General Settings

diff --git a/app/src/modules/options/src/components/Apps/Preferences.tsx b/app/src/options/src/components/Apps/Preferences.tsx similarity index 92% rename from app/src/modules/options/src/components/Apps/Preferences.tsx rename to app/src/options/src/components/Apps/Preferences.tsx index eacdbb9..2ad0ba2 100644 --- a/app/src/modules/options/src/components/Apps/Preferences.tsx +++ b/app/src/options/src/components/Apps/Preferences.tsx @@ -1,23 +1,24 @@ import React from "react"; import { ChangeEvent, useContext } from "react"; import ConfigContext from "../../store"; -import { WorkerConfigRouteConfig, WorkerName } from "../../../../../store"; import PreferenceInput from "../PreferenceInput"; import Switch from "../Switch"; import { produce } from "immer"; +import { WebApps } from "../../../../extension.config"; +import { WebAppRouteConfig } from "../../../../lib/getWebAppConfig"; const Preferences = ({ appName, routeName, }: { - appName: WorkerName; + appName: WebApps; routeName: string; }) => { const { config, setConfig } = useContext(ConfigContext)!; - const routes = config.workers[appName].routes as Record< + const routes = config.webApps[appName].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig >; const appRoute = routes[routeName]; const preferences = appRoute.preferences!; @@ -132,9 +133,9 @@ const Preferences = ({ onToggle={(isEnabled: boolean) => { const newConfig = produce(config, (draft) => { ( - draft.workers[appName].routes as Record< + draft.webApps[appName].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig > )[routeName].preferences!.reelAutoScroll = isEnabled; }); diff --git a/app/src/modules/options/src/components/Apps/Routes.tsx b/app/src/options/src/components/Apps/Routes.tsx similarity index 67% rename from app/src/modules/options/src/components/Apps/Routes.tsx rename to app/src/options/src/components/Apps/Routes.tsx index 7cab074..9b75f8d 100644 --- a/app/src/modules/options/src/components/Apps/Routes.tsx +++ b/app/src/options/src/components/Apps/Routes.tsx @@ -1,15 +1,16 @@ import React from "react"; import { useContext } from "react"; -import { WorkerConfigRouteConfig, WorkerName } from "../../../../../store"; import ConfigContext from "../../store"; import AppRoute from "./AppRoute"; +import { WebApps } from "../../../../extension.config"; +import { WebAppRouteConfig } from "../../../../lib/getWebAppConfig"; -const Routes = ({ name }: { name: WorkerName }) => { +const Routes = ({ name }: { name: WebApps }) => { const { config } = useContext(ConfigContext)!; - const routes = config.workers[name].routes as Record< + const routes = config.webApps[name].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig >; const appRoutes = Object.keys(routes); diff --git a/app/src/modules/options/src/components/Apps/AppWorker.tsx b/app/src/options/src/components/Apps/WebAppsSettings.tsx similarity index 59% rename from app/src/modules/options/src/components/Apps/AppWorker.tsx rename to app/src/options/src/components/Apps/WebAppsSettings.tsx index 22901ee..2313d95 100644 --- a/app/src/modules/options/src/components/Apps/AppWorker.tsx +++ b/app/src/options/src/components/Apps/WebAppsSettings.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { WorkerName } from "../../../../../store"; -import Card from "../../../../../ui/Card"; import GeneralSettings from "./GeneralSettings"; import Routes from "./Routes"; +import Card from "../../../../components/Card"; +import { WebApps } from "../../../../extension.config"; -const AppWorker = ({ name }: { name: WorkerName }) => { +const WebAppsSettings = ({ name }: { name: WebApps }) => { const title = name.at(0)?.toUpperCase() + name.slice(1); return ( @@ -14,4 +14,4 @@ const AppWorker = ({ name }: { name: WorkerName }) => { ); }; -export default AppWorker; +export default WebAppsSettings; diff --git a/app/src/options/src/components/Apps/index.tsx b/app/src/options/src/components/Apps/index.tsx new file mode 100644 index 0000000..d759fb6 --- /dev/null +++ b/app/src/options/src/components/Apps/index.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { useContext } from "react"; +import ConfigContext from "../../store"; +import WebAppsSettings from "./WebAppsSettings"; + +export default function AppsConfig() { + const { config } = useContext(ConfigContext)!; + const webApps = config.webApps; + const webAppsName = Object.keys(webApps) as Array; + return ( +
+ {webAppsName.map((webAppName, idx) => { + return ; + })} +
+ ); +} diff --git a/app/src/modules/options/src/components/Keybinding.tsx b/app/src/options/src/components/Keybinding.tsx similarity index 86% rename from app/src/modules/options/src/components/Keybinding.tsx rename to app/src/options/src/components/Keybinding.tsx index 6f504b5..2afaed6 100644 --- a/app/src/modules/options/src/components/Keybinding.tsx +++ b/app/src/options/src/components/Keybinding.tsx @@ -1,10 +1,11 @@ import React from "react"; import { KeyboardEvent, useContext, useState } from "react"; import { produce } from "immer"; -import { WorkerConfigRouteConfig, WorkerName } from "../../../../store"; -import Tooltip from "../../../../ui/Tooltip"; +import Tooltip from "../../../components/Tooltip"; import ConfigContext from "../store"; import camelCaseToTitleCase from "../lib/camelCaseToTitleCase"; +import { WebApps } from "../../../extension.config"; +import { WebAppRouteConfig } from "../../../lib/getWebAppConfig"; export default function KeybindingBlock({ appName, @@ -12,15 +13,15 @@ export default function KeybindingBlock({ keybinding, description, }: { - appName: WorkerName; + appName: WebApps; routeName: string; keybinding: string; description?: string; }) { const { config, setConfig } = useContext(ConfigContext)!; - const routes = config.workers[appName].routes as Record< + const routes = config.webApps[appName].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig >; const appRoute = routes[routeName]; const [key, setKey] = useState(appRoute.keybindings![keybinding]); @@ -37,12 +38,9 @@ export default function KeybindingBlock({ const key = String.fromCharCode(e.keyCode); setKey(key); const newConfig = produce(config, (draft) => { - ( - draft.workers[appName].routes as Record< - string, - WorkerConfigRouteConfig - > - )[routeName].keybindings![keybinding] = key; + (draft.webApps[appName].routes as Record)[ + routeName + ].keybindings![keybinding] = key; }); setConfig(newConfig); chrome.storage.sync.set(newConfig); diff --git a/app/src/modules/options/src/components/PreferenceInput.tsx b/app/src/options/src/components/PreferenceInput.tsx similarity index 92% rename from app/src/modules/options/src/components/PreferenceInput.tsx rename to app/src/options/src/components/PreferenceInput.tsx index 71067ea..56e9b7d 100644 --- a/app/src/modules/options/src/components/PreferenceInput.tsx +++ b/app/src/options/src/components/PreferenceInput.tsx @@ -1,10 +1,11 @@ import React from "react"; import { ChangeEvent, useContext, useState, useEffect, useRef } from "react"; import { produce } from "immer"; -import { WorkerConfigRouteConfig, WorkerName } from "../../../../store"; -import Tooltip from "../../../../ui/Tooltip"; +import Tooltip from "../../../components/Tooltip"; import ConfigContext from "../store"; import camelCaseToTitleCase from "../lib/camelCaseToTitleCase"; +import { WebApps } from "../../../extension.config"; +import { WebAppRouteConfig } from "../../../lib/getWebAppConfig"; export default function PreferenceInput({ appName, @@ -14,7 +15,7 @@ export default function PreferenceInput({ validator, description, }: { - appName: WorkerName; + appName: WebApps; routeName: string; preferenceName: string; type?: "plain" | "list"; @@ -22,9 +23,9 @@ export default function PreferenceInput({ description?: string; }) { const { config, setConfig } = useContext(ConfigContext)!; - const routes = config.workers[appName].routes as Record< + const routes = config.webApps[appName].routes as Record< string, - WorkerConfigRouteConfig + WebAppRouteConfig >; const appRoute = routes[routeName]; const [preference, setPreference] = useState( @@ -62,12 +63,9 @@ export default function PreferenceInput({ if (isValid) { const newConfig = produce(config, (draft) => { - ( - draft.workers[appName].routes as Record< - string, - WorkerConfigRouteConfig - > - )[routeName].preferences![preferenceName] = newPreference; + (draft.webApps[appName].routes as Record)[ + routeName + ].preferences![preferenceName] = newPreference; }); setConfig(newConfig); chrome.storage.sync.set(newConfig); diff --git a/app/src/modules/options/src/components/Sidebar.tsx b/app/src/options/src/components/Sidebar.tsx similarity index 100% rename from app/src/modules/options/src/components/Sidebar.tsx rename to app/src/options/src/components/Sidebar.tsx diff --git a/app/src/modules/options/src/components/Switch.tsx b/app/src/options/src/components/Switch.tsx similarity index 92% rename from app/src/modules/options/src/components/Switch.tsx rename to app/src/options/src/components/Switch.tsx index 0cf9f7b..2d04009 100644 --- a/app/src/modules/options/src/components/Switch.tsx +++ b/app/src/options/src/components/Switch.tsx @@ -1,6 +1,6 @@ import React from "react"; -import Switch from "../../../../ui/Switch"; -import Tooltip from "../../../../ui/Tooltip"; +import Switch from "../../../components/Switch"; +import Tooltip from "../../../components/Tooltip"; export default function SwitchBlock({ title, diff --git a/app/src/modules/options/src/index.css b/app/src/options/src/index.css similarity index 100% rename from app/src/modules/options/src/index.css rename to app/src/options/src/index.css diff --git a/app/src/modules/options/src/index.tsx b/app/src/options/src/index.tsx similarity index 76% rename from app/src/modules/options/src/index.tsx rename to app/src/options/src/index.tsx index 23f30e7..2c4fde6 100644 --- a/app/src/modules/options/src/index.tsx +++ b/app/src/options/src/index.tsx @@ -6,22 +6,14 @@ import General from "./pages/General"; import Apps from "./pages/Apps"; import Advanced from "./pages/Advanced"; import ErrorPage from "./pages/error-page"; -import { Config } from "../../../store"; import "./index.css"; - -const configLoader = async (): Promise => { - return new Promise((resolve) => { - chrome.storage.sync.get(undefined, (storage) => { - resolve(storage as Config); - }); - }); -}; +import getExtensionConfig from "../../lib/getExtensionConfig"; const router = createMemoryRouter([ { path: "/", element: , - loader: configLoader, + loader: getExtensionConfig, errorElement: , children: [ { diff --git a/app/src/modules/options/src/lib/camelCaseToTitleCase.tsx b/app/src/options/src/lib/camelCaseToTitleCase.tsx similarity index 100% rename from app/src/modules/options/src/lib/camelCaseToTitleCase.tsx rename to app/src/options/src/lib/camelCaseToTitleCase.tsx diff --git a/app/src/modules/options/src/pages/Advanced.tsx b/app/src/options/src/pages/Advanced.tsx similarity index 100% rename from app/src/modules/options/src/pages/Advanced.tsx rename to app/src/options/src/pages/Advanced.tsx diff --git a/app/src/modules/options/src/pages/Apps.tsx b/app/src/options/src/pages/Apps.tsx similarity index 100% rename from app/src/modules/options/src/pages/Apps.tsx rename to app/src/options/src/pages/Apps.tsx diff --git a/app/src/modules/options/src/pages/General.tsx b/app/src/options/src/pages/General.tsx similarity index 97% rename from app/src/modules/options/src/pages/General.tsx rename to app/src/options/src/pages/General.tsx index 24d29c6..e584e1b 100644 --- a/app/src/modules/options/src/pages/General.tsx +++ b/app/src/options/src/pages/General.tsx @@ -1,7 +1,7 @@ import React from "react"; import { useContext } from "react"; import ConfigContext from "../store"; -import Card from "../../../../ui/Card"; +import Card from "../../../components/Card"; import Switch from "../components/Switch"; import { produce } from "immer"; diff --git a/app/src/modules/options/src/pages/error-page.tsx b/app/src/options/src/pages/error-page.tsx similarity index 100% rename from app/src/modules/options/src/pages/error-page.tsx rename to app/src/options/src/pages/error-page.tsx diff --git a/app/src/modules/options/src/store.tsx b/app/src/options/src/store.tsx similarity index 54% rename from app/src/modules/options/src/store.tsx rename to app/src/options/src/store.tsx index 707e539..f2c337d 100644 --- a/app/src/modules/options/src/store.tsx +++ b/app/src/options/src/store.tsx @@ -1,10 +1,10 @@ import React from "react"; import { Dispatch, SetStateAction } from "react"; -import { Config } from "../../../store"; +import { ExtensionConfig } from "../../extension.config"; const ConfigContext = React.createContext<{ - config: Config; - setConfig: Dispatch>; + config: ExtensionConfig; + setConfig: Dispatch>; } | null>(null); export default ConfigContext; diff --git a/app/src/modules/popup/index.html b/app/src/popup/index.html similarity index 100% rename from app/src/modules/popup/index.html rename to app/src/popup/index.html diff --git a/app/src/modules/popup/src/App.tsx b/app/src/popup/src/App.tsx similarity index 100% rename from app/src/modules/popup/src/App.tsx rename to app/src/popup/src/App.tsx diff --git a/app/src/modules/popup/src/components/Navbar.tsx b/app/src/popup/src/components/Navbar.tsx similarity index 100% rename from app/src/modules/popup/src/components/Navbar.tsx rename to app/src/popup/src/components/Navbar.tsx diff --git a/app/src/modules/popup/src/index.css b/app/src/popup/src/index.css similarity index 100% rename from app/src/modules/popup/src/index.css rename to app/src/popup/src/index.css diff --git a/app/src/modules/popup/src/index.tsx b/app/src/popup/src/index.tsx similarity index 100% rename from app/src/modules/popup/src/index.tsx rename to app/src/popup/src/index.tsx diff --git a/app/src/modules/popup/src/pages/Dashboard.tsx b/app/src/popup/src/pages/Dashboard.tsx similarity index 100% rename from app/src/modules/popup/src/pages/Dashboard.tsx rename to app/src/popup/src/pages/Dashboard.tsx diff --git a/app/src/modules/popup/src/pages/error-page.tsx b/app/src/popup/src/pages/error-page.tsx similarity index 100% rename from app/src/modules/popup/src/pages/error-page.tsx rename to app/src/popup/src/pages/error-page.tsx diff --git a/app/src/store/config/index.ts b/app/src/store/config/index.ts deleted file mode 100644 index 381d66b..0000000 --- a/app/src/store/config/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import workersConfig from "./worker"; - -export const DEFAULT_CONFIG = { - globalSettings: { - isExtensionEnabled: true as boolean, - }, - workers: workersConfig, -}; - -export type Config = typeof DEFAULT_CONFIG; - -export default DEFAULT_CONFIG; diff --git a/app/src/store/config/lib/getConfig.ts b/app/src/store/config/lib/getConfig.ts deleted file mode 100644 index ad70e5b..0000000 --- a/app/src/store/config/lib/getConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type Config } from '../'; - -const getConfig = async (): Promise => { - return new Promise((resolve) => { - chrome.storage.sync.get(undefined, (storage) => { - resolve(storage as Config); - }); - }); -}; - -export default getConfig; diff --git a/app/src/store/config/worker/index.ts b/app/src/store/config/worker/index.ts deleted file mode 100644 index 1d2d6d7..0000000 --- a/app/src/store/config/worker/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import youtubeWorkerConfig, { - type YouTubeWorkerConfig, -} from "../../../modules/content_scripts/workers/youtube/config"; -import udemyWorkerConfig, { - UdemyWorkerConfig, -} from "../../../modules/content_scripts/workers/udemy/config"; - -export type { YouTubeWorkerConfig, UdemyWorkerConfig }; - -const workersConfig = { - youtube: youtubeWorkerConfig, - udemy: udemyWorkerConfig, -}; - -export type WorkerName = keyof typeof workersConfig; - -export default workersConfig; diff --git a/app/src/store/config/worker/interfaces.ts b/app/src/store/config/worker/interfaces.ts deleted file mode 100644 index eaaca6d..0000000 --- a/app/src/store/config/worker/interfaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface WorkerConfig { - generalSettings: WorkerConfigGeneralSettings; - routes?: Record; -} - -export interface WorkerConfigGeneralSettings { - isEnabled: boolean; -} - -export interface WorkerConfigRouteConfig { - isEnabled: boolean; - keybindings?: Record; - preferences?: Record; -} diff --git a/app/src/store/config/worker/lib/getAllWorkerConfig.ts b/app/src/store/config/worker/lib/getAllWorkerConfig.ts deleted file mode 100644 index c6e6799..0000000 --- a/app/src/store/config/worker/lib/getAllWorkerConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type WorkerConfig } from '../interfaces'; - -const getAllWorkerConfig = async (): Promise> => { - return new Promise((resolve) => { - chrome.storage.sync.get('workers', (storage) => { - resolve(storage.workers); - }); - }); -}; - -export default getAllWorkerConfig; diff --git a/app/src/store/config/worker/lib/getWorkerConfig.ts b/app/src/store/config/worker/lib/getWorkerConfig.ts deleted file mode 100644 index 9c188be..0000000 --- a/app/src/store/config/worker/lib/getWorkerConfig.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { type WorkerConfig } from '../interfaces'; - -const getWorkerConfig = async (worker: string): Promise => { - return new Promise((resolve) => { - chrome.storage.sync.get('workers', (storage) => { - resolve(storage.workers[worker]); - }); - }); -}; - -export default getWorkerConfig; diff --git a/app/src/store/index.ts b/app/src/store/index.ts deleted file mode 100644 index 74a1cfb..0000000 --- a/app/src/store/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import SERVER_BASE_URI from "./server"; -import DEFAULT_CONFIG, { type Config } from "./config"; -import getConfig from "./config/lib/getConfig"; -import getAllWorkerConfig from "./config/worker/lib/getAllWorkerConfig"; -import getWorkerConfig from "./config/worker/lib/getWorkerConfig"; -import type { - YouTubeWorkerConfig, - UdemyWorkerConfig, - WorkerName, -} from "./config/worker"; -import type { - WorkerConfig, - WorkerConfigGeneralSettings, - WorkerConfigRouteConfig, -} from "./config/worker/interfaces"; - -export { getConfig, getAllWorkerConfig, getWorkerConfig }; -export type { - Config, - WorkerName, - WorkerConfig, - WorkerConfigGeneralSettings, - WorkerConfigRouteConfig, - YouTubeWorkerConfig, - UdemyWorkerConfig, -}; - -export { SERVER_BASE_URI, DEFAULT_CONFIG }; diff --git a/app/src/store/server/index.ts b/app/src/store/server/index.ts deleted file mode 100644 index faee23f..0000000 --- a/app/src/store/server/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -const SERVER_BASE_URI = - process.env.NODE_ENV === "development" - ? "http://localhost:8080" - : "https://toppings.onrender.com"; - -export default SERVER_BASE_URI; diff --git a/app/test/unit/README.md b/app/test/unit/README.md index e944e73..3327895 100644 --- a/app/test/unit/README.md +++ b/app/test/unit/README.md @@ -19,5 +19,5 @@ When adding unit tests, place them in this directory and mirror the structure of To run a specific test file, use the following command: ```bash -bunx mocha "test/unit/modules/content_scripts/lib/formatRuntime.spec.ts" +bunx mocha "test/unit/content_scripts/lib/formatRuntime.spec.ts" ``` diff --git a/app/test/unit/modules/background/webAppContext.spec.ts b/app/test/unit/background/webAppContext.spec.ts similarity index 82% rename from app/test/unit/modules/background/webAppContext.spec.ts rename to app/test/unit/background/webAppContext.spec.ts index 546fec8..1c9be2b 100644 --- a/app/test/unit/modules/background/webAppContext.spec.ts +++ b/app/test/unit/background/webAppContext.spec.ts @@ -3,17 +3,15 @@ import sinon from "sinon"; import { getWebAppContext, WebAppContext, -} from "../../../../src/modules/background/webAppContext"; -import youtubeWorkerConfig from "../../../../src/modules/content_scripts/workers/youtube/config"; +} from "../../../src/background/webAppContext"; +import youtubeConfig from "../../../src/content_scripts/webApps/youtube/webApp.config"; describe("getWebAppContext", () => { describe("YouTube Parser", () => { const chromeMock = { storage: { sync: { - get: sinon - .stub() - .yields({ workers: { youtube: youtubeWorkerConfig } }), + get: sinon.stub().yields({ webApps: { youtube: youtubeConfig } }), }, }, }; @@ -23,7 +21,7 @@ describe("getWebAppContext", () => { const expectedResult1: WebAppContext = { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL: new URL(testCase1), activeRoute: "playlist", @@ -38,7 +36,7 @@ describe("getWebAppContext", () => { const expectedResult2: WebAppContext = { isSupported: true, appName: "youtube", - workerConfig: youtubeWorkerConfig, + webAppConfig: youtubeConfig, contextData: { webAppURL: new URL(testCase2), activeRoute: "watch", diff --git a/app/test/unit/store/server/index.spec.ts b/app/test/unit/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.spec.ts similarity index 62% rename from app/test/unit/store/server/index.spec.ts rename to app/test/unit/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.spec.ts index b02e6b4..4b3f95b 100644 --- a/app/test/unit/store/server/index.spec.ts +++ b/app/test/unit/content_scripts/webApps/youtube/utils/fetchYouTubeToppings.spec.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { SERVER_BASE_URI } from "../../../../src/store"; +import { SERVER_BASE_URI } from "../../../../../../src/content_scripts/webApps/youtube/utils/fetchYouTubeToppings"; describe("SERVER_BASE_URI", () => { it("should use correct server URI", () => { diff --git a/app/test/unit/modules/content_scripts/lib/formatRuntime.spec.ts b/app/test/unit/lib/formatRuntime.spec.ts similarity index 95% rename from app/test/unit/modules/content_scripts/lib/formatRuntime.spec.ts rename to app/test/unit/lib/formatRuntime.spec.ts index f522bef..1258999 100644 --- a/app/test/unit/modules/content_scripts/lib/formatRuntime.spec.ts +++ b/app/test/unit/lib/formatRuntime.spec.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { formatRuntime } from "../../../../../src/modules/content_scripts/lib/formatRuntime"; +import { formatRuntime } from "../../../src/lib/formatRuntime"; describe("formatRuntime", () => { it("should format runtime with days, hours, minutes, and seconds", () => { diff --git a/app/webpack.common.cjs b/app/webpack.common.cjs index 6ee59c6..bc12ab8 100644 --- a/app/webpack.common.cjs +++ b/app/webpack.common.cjs @@ -6,17 +6,17 @@ const CopyWebpackPlugin = require("copy-webpack-plugin"); module.exports = { target: "web", entry: { - background: "./src/modules/background/index.ts", - content: ["./src/modules/content_scripts/index.ts"], + background: "./src/background/index.ts", + content: ["./src/content_scripts/index.ts"], popup: { filename: "./popup/index.js", - import: "./src/modules/popup/src/index.tsx", + import: "./src/popup/src/index.tsx", }, options: { filename: "./options/index.js", - import: "./src/modules/options/src/index.tsx", + import: "./src/options/src/index.tsx", }, - ...getWorkers(), + ...getWebAppsEntry(), }, output: { @@ -61,8 +61,8 @@ module.exports = { new CopyWebpackPlugin({ patterns: [ { from: "src/assets", to: "assets" }, - { from: "src/modules/options/index.html", to: "options" }, - { from: "src/modules/popup/index.html", to: "popup" }, + { from: "src/options/index.html", to: "options" }, + { from: "src/popup/index.html", to: "popup" }, "src/manifest.json", ], }), @@ -77,22 +77,19 @@ module.exports = { }, }; -function getWorkers() { - const workersPath = path.resolve( - __dirname, - "src/modules/content_scripts/workers", - ); - const workers = getDirectories(workersPath); +function getWebAppsEntry() { + const webAppsPath = path.resolve(__dirname, "src/content_scripts/webApps"); + const webApps = getDirectories(webAppsPath); const entryPoints = {}; - workers.forEach((dir) => { - const indexPath = fs.existsSync(path.resolve(workersPath, dir, "index.tsx")) - ? path.resolve(workersPath, dir, "index.tsx") - : path.resolve(workersPath, dir, "index.ts"); + webApps.forEach((dir) => { + const indexPath = fs.existsSync(path.resolve(webAppsPath, dir, "index.tsx")) + ? path.resolve(webAppsPath, dir, "index.tsx") + : path.resolve(webAppsPath, dir, "index.ts"); entryPoints[dir] = { import: indexPath, - filename: "./workers/[name].js", + filename: "./webApps/[name].js", library: { type: "module", },