Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Implemented "Generate Quiz from Selected Text" Context Menu Feature #62

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions extension/public/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Create context menu on installation
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "askExtension",
title: "Generate Quiz with Selected Text",
contexts: ["selection"]
});
});

// Handle context menu clicks
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
if (info.menuItemId === "askExtension" && info.selectionText) {
try {
// Store the selected text first
await chrome.storage.local.set({
selectedText: info.selectionText
});

// Inject content script if needed
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["contentScript.js"]
});

// Send message to content script
await chrome.tabs.sendMessage(tab.id, {
selectedText: info.selectionText
});

// Open the popup
// Note: Chrome extensions can't programmatically open the popup,
// but we can show the user where to click
chrome.action.setPopup({
popup: "src/popup/popup.html"
});

// Show a badge to indicate text was captured
chrome.action.setBadgeText({
text: "!"
});
chrome.action.setBadgeBackgroundColor({
color: "#FF005C"
});

// Clear the badge after 2 seconds
setTimeout(() => {
chrome.action.setBadgeText({ text: "" });
}, 2000);

} catch (error) {
console.error("Error in context menu handler:", error);
}
}
});

// Listen for messages from content script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === "TEXT_SELECTED") {
chrome.storage.local.set({
selectedText: request.text
}, () => {
console.log("Text saved to storage:", request.text);
sendResponse({ status: "success" });
});
return true; // Required for async sendResponse
}
});

//Clear badge when popup is opened
chrome.action.onClicked.addListener(() => {
chrome.action.setBadgeText({ text: "" });
});

chrome.storage.onChanged.addListener((changes, namespace) => {
for (let [key, { oldValue, newValue }] of Object.entries(changes)) {
console.log(
`Storage key "${key}" in namespace "${namespace}" changed.`,
`Old value was "${oldValue}", new value is "${newValue}".`
);

// Store the key-value pair in local storage
chrome.storage.local.set({ [key]: newValue }, () => {
if (chrome.runtime.lastError) {
console.error("Error storing data:", chrome.runtime.lastError);
} else {
console.log(`Stored key-value pair: { ${key}: ${newValue} }`);
}
});
}
});


// Optional: Handle extension install/update
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
console.log("Extension installed");
} else if (details.reason === "update") {
console.log("Extension updated");
}
});
13 changes: 13 additions & 0 deletions extension/public/contentScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.selectedText) {
generateQuestions(request.selectedText);
}
});

function generateQuestions(text) {
console.log("Generating questions for:", text);
chrome.storage.local.set({ "selectedText": text }, () => {
console.log('Questions stored in local storage:', text);
});
}

13 changes: 11 additions & 2 deletions extension/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"name": "EduAid: AI Quiz Generator",
"version": "1.0",
"description": "Generate quizzes with AI-powered questions.",
"permissions": ["activeTab", "storage","sidePanel", "contextMenus"],
"permissions": ["activeTab", "storage", "sidePanel", "contextMenus", "scripting"],
"background": {
"service_worker": "background.js"
},
"side_panel": {
"default_path":"/src/pages/question/sidePanel.html"
},
Expand All @@ -26,5 +29,11 @@
"resources": ["static/js/*", "static/css/*", "src/assets/*"],
"matches": ["<all_urls>"]
}
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
}
]
}
}
16 changes: 14 additions & 2 deletions extension/src/pages/answer/Answer.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "../../index.css";
import logo from "../../assets/aossie_logo.webp";
Expand All @@ -15,7 +15,16 @@ const Answer = () => {

const [isToggleOn, setIsToggleOn] = useState(1);
const [mode, setMode] = useState("ask_question"); // Dropdown state


useEffect(() => {
chrome.storage.local.get(["selectedText"], (result) => {
if (result.selectedText) {
console.log("Selected Text: ", result.selectedText);
setContext(result.selectedText);
localStorage.setItem("textContent", result.selectedText);
}
});
},[])

// const toggleSwitch = () => {
// window.location.href = "/src/pages/home/home.html";
Expand Down Expand Up @@ -112,6 +121,9 @@ const Answer = () => {
console.error("Error:", error);
} finally {
setLoading(false);
chrome.storage.local.remove(["selectedText"], () => {
console.log("Chrome storage cleared");
});
}
};

Expand Down
61 changes: 37 additions & 24 deletions extension/src/pages/text_input/TextInput.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef } from "react";
import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import "../../index.css";
import logo from "../../assets/aossie_logo.webp";
Expand All @@ -18,8 +18,19 @@ function Second() {
const [docUrl, setDocUrl] = useState('');
const [isToggleOn, setIsToggleOn] = useState(0);

useEffect(() => {
chrome.storage.local.get(["selectedText"], (result) => {
if (result.selectedText) {
console.log("Selected Text: ", result.selectedText);
setText(result.selectedText);
localStorage.setItem("textContent", result.selectedText);
}
});
}, [])


const toggleSwitch = () => {
setIsToggleOn((isToggleOn+1)%2);
setIsToggleOn((isToggleOn + 1) % 2);
};

const handleFileUpload = async (event) => {
Expand Down Expand Up @@ -52,7 +63,7 @@ function Second() {

const handleSaveToLocalStorage = async () => {
setLoading(true);

// Check if a Google Doc URL is provided
if (docUrl) {
try {
Expand All @@ -63,7 +74,7 @@ function Second() {
},
body: JSON.stringify({ document_url: docUrl })
});

if (response.ok) {
const data = await response.json();
setDocUrl("")
Expand All @@ -77,13 +88,16 @@ function Second() {
setText('Error retrieving Google Doc content');
} finally {
setLoading(false);
chrome.storage.local.remove(["selectedText"], () => {
console.log("Chrome storage cleared");
});
}
} else if (text) {
// Proceed with existing functionality for local storage
localStorage.setItem("textContent", text);
localStorage.setItem("difficulty", difficulty);
localStorage.setItem("numQuestions", numQuestions);

await sendToBackend(
text,
difficulty,
Expand Down Expand Up @@ -121,7 +135,7 @@ function Second() {
const formData = JSON.stringify({
input_text: data,
max_questions: numQuestions,
use_mediawiki : isToggleOn
use_mediawiki: isToggleOn
});
const response = await fetch(`http://localhost:5000/${endpoint}`, {
method: "POST",
Expand All @@ -130,26 +144,26 @@ function Second() {
"Content-Type": "application/json",
},
});

if (response.ok) {
const responseData = await response.json();
localStorage.setItem("qaPairs", JSON.stringify(responseData));

// Save quiz details to local storage
const quizDetails = {
difficulty,
numQuestions,
date: new Date().toLocaleDateString(),
qaPair:responseData
qaPair: responseData
};

let last5Quizzes = JSON.parse(localStorage.getItem('last5Quizzes')) || [];
last5Quizzes.push(quizDetails);
if (last5Quizzes.length > 5) {
last5Quizzes.shift(); // Keep only the last 5 quizzes
}
localStorage.setItem('last5Quizzes', JSON.stringify(last5Quizzes));

window.location.href = "/src/pages/question/question.html";
} else {
console.error("Backend request failed.");
Expand All @@ -160,7 +174,7 @@ function Second() {
setLoading(false);
}
};


return (
<div className="popup w-42rem h-35rem bg-[#02000F] flex justify-center items-center">
Expand All @@ -170,9 +184,8 @@ function Second() {
</div>
)}
<div
className={`w-full h-full bg-cust bg-opacity-50 bg-custom-gradient ${
loading ? "pointer-events-none" : ""
}`}
className={`w-full h-full bg-cust bg-opacity-50 bg-custom-gradient ${loading ? "pointer-events-none" : ""
}`}
>
<div className="flex items-end gap-[2px]">
<img src={logo} alt="logo" className="w-16 my-4 ml-4 block" />
Expand Down Expand Up @@ -285,15 +298,15 @@ function Second() {
</button>
</div>
<div className="items-center bg-[#202838] text-white rounded-xl px-2 py-2">
<Switch
checked={isToggleOn}
onChange={toggleSwitch}
offColor="#FF005C"
onColor="#00CBE7"
height={24}
width={44}
/>
</div>
<Switch
checked={isToggleOn}
onChange={toggleSwitch}
offColor="#FF005C"
onColor="#00CBE7"
height={24}
width={44}
/>
</div>
</div>
<div className="flex my-2 justify-center gap-6 items-start">
<div className="">
Expand Down
Loading