diff --git a/src/App.tsx b/src/App.tsx index d2bc8a20..822f4e0b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; -import Start from "./pages/start"; -import Home from "./pages/home"; -import Loading from "./pages/loading"; +import Start from "./popup/pages/start"; +import Home from "./popup/pages/home"; +import Loading from "./popup/pages/loading"; import { useAuth } from "./hooks/useAuth"; import { goTo } from "react-chrome-extension-router"; diff --git a/src/content-scripts/components/AICodeRefactor/ChangeSuggestorButton.ts b/src/content-scripts/components/AICodeRefactor/ChangeSuggestorButton.ts index 047e3160..fa6f1c58 100644 --- a/src/content-scripts/components/AICodeRefactor/ChangeSuggestorButton.ts +++ b/src/content-scripts/components/AICodeRefactor/ChangeSuggestorButton.ts @@ -38,9 +38,6 @@ const handleSubmit = async (commentNode: HTMLElement) => { if (!descriptionConfig) { return; } - if (!descriptionConfig.enabled) { - return alert("AI PR description is disabled!"); - } logo.classList.toggle("animate-spin"); button.classList.toggle("pointer-events-none"); diff --git a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts index 7a44ea59..d10f5000 100644 --- a/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts +++ b/src/content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton.ts @@ -16,7 +16,6 @@ export const DescriptionGeneratorButton = () => { Generate PR description`, onclick: handleSubmit, - }); return descriptionGeneratorButton; @@ -34,6 +33,13 @@ const handleSubmit = async () => { if (!logo || !button) { return; } + + const descriptionConfig = await getAIDescriptionConfig(); + + if (!descriptionConfig) { + return; + } + logo.classList.toggle("animate-spin"); button.classList.toggle("pointer-events-none"); diff --git a/src/content-scripts/github.ts b/src/content-scripts/github.ts index a1e4f9e3..917bfcf1 100644 --- a/src/content-scripts/github.ts +++ b/src/content-scripts/github.ts @@ -26,7 +26,7 @@ const processGithubPage = async () => { } else if (isPullRequestFilesChangedPage(window.location.href)) { prReviewWatch(injectChangeSuggestorButton, 500); } else if (isGithubPullRequestPage(window.location.href)) { - prEditWatch(injectDescriptionGeneratorButton); + prEditWatch(injectDescriptionGeneratorButton, 500); void injectAddPRToHighlightsButton(); } else if (isGithubProfilePage(window.location.href)) { const username = getGithubUsername(window.location.href); diff --git a/src/popup/components/ToggleSwitch.tsx b/src/popup/components/ToggleSwitch.tsx new file mode 100644 index 00000000..8ffa270a --- /dev/null +++ b/src/popup/components/ToggleSwitch.tsx @@ -0,0 +1,55 @@ +import { useState } from "react"; + +const Toggle = ({ settingName, settingLabel, enabledSetting }: { settingName: string; enabledSetting: boolean; settingLabel: string }) => { + const [enabled, setEnabled] = useState(enabledSetting); + + const changeSetting = async (value: boolean) => { + const settingsConfig = await chrome.storage.sync.get("osSettingsConfig"); + + if (settingsConfig.osSettingsConfig === undefined) { + const defaultSettings = { aiPrDescription: false }; + + await chrome.storage.sync.set({ osSettingsConfig: defaultSettings }); + } + + const newSettingsConfig = { ...settingsConfig.osSettingsConfig, [settingName]: value }; + + await chrome.storage.sync.set({ osSettingsConfig: newSettingsConfig }); + + setEnabled(value); + }; + + return ( +
+ + {settingLabel} + + +
- -
@@ -103,7 +79,7 @@ const AIPRDescription = () => { ref={setRefFromKey("form")} onSubmit={handleFormSubmit} > -
+

OpenSauced AI

diff --git a/src/pages/help.tsx b/src/popup/pages/help.tsx similarity index 94% rename from src/pages/help.tsx rename to src/popup/pages/help.tsx index bf51907f..0aa99025 100644 --- a/src/pages/help.tsx +++ b/src/popup/pages/help.tsx @@ -1,13 +1,13 @@ import { FaChevronLeft } from "react-icons/fa"; -import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { EXTERNAL_RESOURCES } from "../constants"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { EXTERNAL_RESOURCES } from "../../constants"; import { HiOutlineBookOpen, HiOutlineChatBubbleLeftRight, } from "react-icons/hi2"; import { goBack } from "react-chrome-extension-router"; import { VscIssues } from "react-icons/vsc"; -import { version } from "../../package.json"; +import { version } from "../../../package.json"; const Help = () => (
diff --git a/src/pages/home.tsx b/src/popup/pages/home.tsx similarity index 90% rename from src/pages/home.tsx rename to src/popup/pages/home.tsx index 76c13d80..f9ac0b60 100644 --- a/src/pages/home.tsx +++ b/src/popup/pages/home.tsx @@ -6,20 +6,22 @@ import { HiPencil, HiUserCircle, } from "react-icons/hi2"; -import { useEffect, useState } from "react"; -import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { useAuth } from "../hooks/useAuth"; -import { useOpensaucedUserCheck } from "../hooks/useOpensaucedUserCheck"; +import { FiSettings } from "react-icons/fi"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { useAuth } from "../../hooks/useAuth"; +import { useOpensaucedUserCheck } from "../../hooks/useOpensaucedUserCheck"; import { Profile } from "./profile"; import { goTo } from "react-chrome-extension-router"; import AIPRDescription from "./aiprdescription"; import PostOnHighlight from "./posthighlight"; -import { getHighlights } from "../utils/fetchOpenSaucedApiData"; +import { getHighlights } from "../../utils/fetchOpenSaucedApiData"; import Help from "./help"; -import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../constants"; -import type { Highlight } from "../ts/types"; -import { useIsGithubPRPageCheck } from "../hooks/useGithubPRPageCheck"; +import { useEffect, useState } from "react"; +import Settings from "./settings"; +import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../../constants"; +import type { Highlight } from "../../ts/types"; +import { useIsGithubPRPageCheck } from "../../hooks/useGithubPRPageCheck"; const Home = () => { const { user } = useAuth(); @@ -215,6 +217,16 @@ const Home = () => { Help + +
diff --git a/src/pages/loading.tsx b/src/popup/pages/loading.tsx similarity index 100% rename from src/pages/loading.tsx rename to src/popup/pages/loading.tsx diff --git a/src/pages/posthighlight.tsx b/src/popup/pages/posthighlight.tsx similarity index 93% rename from src/pages/posthighlight.tsx rename to src/popup/pages/posthighlight.tsx index 4d63a5f2..20a71037 100644 --- a/src/pages/posthighlight.tsx +++ b/src/popup/pages/posthighlight.tsx @@ -1,12 +1,12 @@ import { useEffect, useState } from "react"; import { FaChevronLeft } from "react-icons/fa"; -import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { useAuth } from "../hooks/useAuth"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { useAuth } from "../../hooks/useAuth"; import toast, { Toaster } from "react-hot-toast"; +import { createHighlight } from "../../utils/fetchOpenSaucedApiData"; import { goBack } from "react-chrome-extension-router"; -import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../constants"; -import { getAiDescription } from "../content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton"; -import { createHighlight } from "../utils/fetchOpenSaucedApiData"; +import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../../constants"; +import { getAiDescription } from "../../content-scripts/components/GenerateAIDescription/DescriptionGeneratorButton"; const PostOnHighlight = ({ prUrl, prTitle }: { prUrl: string, prTitle: string }) => { const { authToken, user } = useAuth(); diff --git a/src/pages/profile.tsx b/src/popup/pages/profile.tsx similarity index 96% rename from src/pages/profile.tsx rename to src/popup/pages/profile.tsx index 25018a64..dc8b8dfc 100644 --- a/src/pages/profile.tsx +++ b/src/popup/pages/profile.tsx @@ -3,16 +3,16 @@ import { FaBrain, FaChevronLeft, FaRobot } from "react-icons/fa"; import { RiLinkedinFill, RiLinkM, RiTwitterFill } from "react-icons/ri"; import { SiC, SiCplusplus, SiCsharp, SiGoland, SiJavascript, SiPhp, SiPython, SiReact, SiRuby, SiRust, SiTypescript } from "react-icons/si"; import { DiJava } from "react-icons/di"; -import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { getUserData, getUserPRData, getUserHighlightsData } from "../utils/fetchOpenSaucedApiData"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { getUserData, getUserPRData, getUserHighlightsData } from "../../utils/fetchOpenSaucedApiData"; import { emojify } from "node-emoji"; import { goBack, goTo } from "react-chrome-extension-router"; -import { getRelativeDays } from "../utils/dateUtils"; -import { getUserPRVelocity } from "../utils/getUserPRVelocity"; +import { getRelativeDays } from "../../utils/dateUtils"; +import { getUserPRVelocity } from "../../utils/getUserPRVelocity"; import { BiExit } from "react-icons/bi"; import Start from "./start"; -import { optLogOut } from "../utils/checkAuthentication"; -import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../constants"; +import { optLogOut } from "../../utils/checkAuthentication"; +import { OPEN_SAUCED_INSIGHTS_DOMAIN } from "../../constants"; const interestIcon = { python: , diff --git a/src/popup/pages/settings.tsx b/src/popup/pages/settings.tsx new file mode 100644 index 00000000..db17b366 --- /dev/null +++ b/src/popup/pages/settings.tsx @@ -0,0 +1,78 @@ +import { FaChevronLeft } from "react-icons/fa"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { goBack } from "react-chrome-extension-router"; +import Toggle from "../components/ToggleSwitch"; +import { useEffect, useState } from "react"; + +export type SettingsConfig = Record; + +const settingLabels: Record = { + aiPrDescription: "AI PR Description", + codeRefactor: "Code Refactor", +}; + +const Settings = () => { + const [settingsConfig, setSettingsConfig] = useState({}); + + useEffect(() => { + const getSettingsDataFromStorage = async () => { + const settingsConfig = await chrome.storage.sync.get("osSettingsConfig"); + + if (settingsConfig.osSettingsConfig === undefined) { + const defaultSettings = { aiPrDescription: true, codeRefactor: true }; + + await chrome.storage.sync.set({ osSettingsConfig: defaultSettings }); + setSettingsConfig(defaultSettings); + } else { + setSettingsConfig(settingsConfig.osSettingsConfig); + } + }; + + void getSettingsDataFromStorage(); + }, []); + + return ( +
+
+
+
+ + + OpenSauced logo +
+
+ +
+

Settings:

+ + { + Object.keys(settingsConfig).map(settingName => ( + + )) + + } + +
+
+
+
+ ); +}; + +export default Settings; diff --git a/src/pages/start.tsx b/src/popup/pages/start.tsx similarity index 86% rename from src/pages/start.tsx rename to src/popup/pages/start.tsx index c16ab4ba..16cac954 100644 --- a/src/pages/start.tsx +++ b/src/popup/pages/start.tsx @@ -1,5 +1,5 @@ -import OpenSaucedLogo from "../assets/opensauced-logo.svg"; -import { optLogIn } from "../utils/checkAuthentication"; +import OpenSaucedLogo from "../../assets/opensauced-logo.svg"; +import { optLogIn } from "../../utils/checkAuthentication"; const Start = () => (
diff --git a/src/utils/aiprdescription/configurationReducer.ts b/src/utils/aiprdescription/configurationReducer.ts index 9ec1361f..872b4a3c 100644 --- a/src/utils/aiprdescription/configurationReducer.ts +++ b/src/utils/aiprdescription/configurationReducer.ts @@ -25,9 +25,6 @@ export const configurationReducer = (state: DescriptionConfig, action: { type: s case "SET_TONE": newState.config.tone = action.value; break; - case "TOGGLE_ENABLED": - newState.enabled = !newState.enabled; - break; case "CLEAR": newState = getDefaultDescriptionConfig(); break; diff --git a/src/utils/aiprdescription/descriptionconfig.ts b/src/utils/aiprdescription/descriptionconfig.ts index 09dd9436..861ec07c 100644 --- a/src/utils/aiprdescription/descriptionconfig.ts +++ b/src/utils/aiprdescription/descriptionconfig.ts @@ -15,7 +15,6 @@ export type DescriptionLanguage = | "korean"; export interface DescriptionConfig { - enabled: boolean; config: { length: number; maxInputLength: number; @@ -42,7 +41,6 @@ export const setAIDescriptionConfig = async (data: DescriptionConfig): Promise ({ - enabled: true, config: { length: 500, maxInputLength: 3900, @@ -58,13 +56,3 @@ export const setDefaultDescriptionConfig = () => { void setAIDescriptionConfig(defaultConfig); }; - -export const toggleAIPRDescriptionEnabled = async () => { - const config = await getAIDescriptionConfig(); - - if (typeof config?.enabled === "undefined") { - return; - } - config.enabled = !config.enabled; - await setAIDescriptionConfig(config); -}; diff --git a/src/utils/dom-utils/addDescriptionGenerator.ts b/src/utils/dom-utils/addDescriptionGenerator.ts index 0154468c..1c75f2b0 100644 --- a/src/utils/dom-utils/addDescriptionGenerator.ts +++ b/src/utils/dom-utils/addDescriptionGenerator.ts @@ -2,12 +2,27 @@ import { DescriptionGeneratorButton } from "../../content-scripts/components/Gen import { GITHUB_NEW_PR_COMMENT_EDITOR_SELECTOR, GITHUB_PR_COMMENT_EDITOR_SELECTOR } from "../../constants"; import { isGithubPullRequestPage } from "../urlMatchers"; import { isPublicRepository } from "../fetchGithubAPIData"; +import { SettingsConfig } from "../../popup/pages/settings"; const injectDescriptionGeneratorButton = async () => { if (document.getElementById("ai-description-button") || !(await isPublicRepository(window.location.href))) { return; } + const settingsConfig = await new Promise(resolve => { + chrome.storage.sync.get("osSettingsConfig", result => { + resolve(result.osSettingsConfig); + }); + }); + + if (settingsConfig) { + const { aiPrDescription } = settingsConfig as SettingsConfig; + + if (!aiPrDescription) { + return; + } + } + const selector = isGithubPullRequestPage(window.location.href) ? GITHUB_PR_COMMENT_EDITOR_SELECTOR : GITHUB_NEW_PR_COMMENT_EDITOR_SELECTOR; const commentFormatRow = document.getElementsByClassName(selector)[0]; const addGeneratorButton = DescriptionGeneratorButton(); diff --git a/src/utils/dom-utils/changeSuggestorButton.ts b/src/utils/dom-utils/changeSuggestorButton.ts index 36148ed0..47d79ede 100644 --- a/src/utils/dom-utils/changeSuggestorButton.ts +++ b/src/utils/dom-utils/changeSuggestorButton.ts @@ -1,12 +1,27 @@ import { ChangeSuggestorButton } from "../../content-scripts/components/AICodeRefactor/ChangeSuggestorButton"; import { GITHUB_REVIEW_SUGGESTION_SELECTOR } from "../../constants"; import { isPublicRepository } from "../fetchGithubAPIData"; +import { SettingsConfig } from "../../popup/pages/settings"; const injectChangeSuggestorButton = async (commentNode: HTMLElement) => { if (!(await isPublicRepository(window.location.href))) { return; } + const settingsConfig = await new Promise(resolve => { + chrome.storage.sync.get("osSettingsConfig", result => { + resolve(result.osSettingsConfig); + }); + }); + + if (settingsConfig) { + const { codeRefactor } = settingsConfig as SettingsConfig; + + if (!codeRefactor) { + return; + } + } + const suggestChangesIcon = commentNode.getElementsByClassName(GITHUB_REVIEW_SUGGESTION_SELECTOR)[0]; const changeSuggestorButton = ChangeSuggestorButton(commentNode);