didi-lot3-frontend/extension/popup.js
Top Clossers cec967f953 Livrare Lot 3 (Frontend): aplicație web, aplicație mobilă Android, extensie browser
- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3)
- Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor
- Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS
- Configurare adresă platformă externalizată (build args / .env / config.js)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 12:40:40 +03:00

327 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// DIDI Analysis Tool - Popup Script (OIDC auth + URL/text analysis)
console.log('[POPUP] Script loaded');
document.addEventListener('DOMContentLoaded', async () => {
console.log('[POPUP] DOM loaded');
const loginSection = document.getElementById('loginSection');
const mainSection = document.getElementById('mainSection');
const authStatusEl = document.getElementById('authStatus');
const loginBtn = document.getElementById('loginBtn');
const loginStatus = document.getElementById('loginStatus');
const activateSnippingBtn = document.getElementById('activateSnipping');
const activateVideoSnippingBtn = document.getElementById('activateVideoSnipping');
const urlInput = document.getElementById('urlInput');
const analyzeUrlBtn = document.getElementById('analyzeUrlBtn');
const analyzeCurrentTabBtn = document.getElementById('analyzeCurrentTab');
const textInput = document.getElementById('textInput');
const analyzeTextBtn = document.getElementById('analyzeText');
const charCount = document.getElementById('charCount');
const statusDiv = document.getElementById('status');
const viewHistoryBtn = document.getElementById('viewHistory');
const disconnectBtn = document.getElementById('disconnectBtn');
// === AUTH UI ===
function showLogin() {
loginSection.style.display = 'block';
mainSection.style.display = 'none';
const dot = authStatusEl.querySelector('.auth-dot');
const text = authStatusEl.querySelector('.auth-text');
dot.className = 'auth-dot disconnected';
text.textContent = 'Neconectat';
}
function showMain(email) {
loginSection.style.display = 'none';
mainSection.style.display = 'block';
const dot = authStatusEl.querySelector('.auth-dot');
const text = authStatusEl.querySelector('.auth-text');
dot.className = 'auth-dot connected';
text.textContent = email || 'Conectat';
}
// Check auth on load
chrome.runtime.sendMessage({ action: 'getAuthStatus' }, (response) => {
if (response?.authenticated) {
showMain(response.email);
prefillFromActiveTab();
} else {
showLogin();
}
});
// === LOGIN (OIDC/PKCE prin Keycloak) ===
loginBtn.addEventListener('click', () => {
loginBtn.disabled = true;
loginBtn.textContent = 'Se deschide autentificarea…';
loginStatus.textContent = '';
chrome.runtime.sendMessage({ action: 'login' }, (response) => {
if (response?.success) {
loginStatus.className = 'login-status success';
loginStatus.textContent = 'Autentificat!';
setTimeout(() => {
showMain(response.email);
prefillFromActiveTab();
}, 400);
} else {
loginStatus.className = 'login-status error';
loginStatus.textContent = 'Autentificare eșuată: ' + (response?.error || 'eroare necunoscută');
}
loginBtn.disabled = false;
loginBtn.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M11 7L9.6 8.4l2.6 2.6H2v2h10.2l-2.6 2.6L11 17l5-5-5-5zm9 12h-8v2h8c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-8v2h8v14z" fill="currentColor"/>
</svg>
Autentificare`;
});
});
// === DISCONNECT ===
disconnectBtn.addEventListener('click', () => {
chrome.runtime.sendMessage({ action: 'logout' }, () => {
showLogin();
});
});
// === PREFILL: URL-ul tabului activ + textul selectat în pagină ===
async function prefillFromActiveTab() {
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) return;
if (tab.url && /^https?:/i.test(tab.url)) {
urlInput.value = tab.url;
analyzeUrlBtn.disabled = false;
}
// Selected text from the page → prefill the textarea (cerință caiet 2a)
try {
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => window.getSelection().toString(),
});
const selected = (results?.[0]?.result || '').trim();
if (selected && !textInput.value) {
textInput.value = selected.substring(0, 5000);
textInput.dispatchEvent(new Event('input'));
showStatus('Textul selectat în pagină a fost preluat.', 'info');
}
} catch { /* restricted page (chrome://, PDF) — nothing to prefill */ }
} catch (e) {
console.warn('[POPUP] prefill failed:', e.message);
}
}
// === SCREENSHOT ===
activateSnippingBtn.addEventListener('click', async () => {
try {
showStatus('Se activează modul captură…', 'info');
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) { showStatus('Nu există tab activ', 'error'); return; }
try {
// Inject render.js BEFORE content.js so window.DidiRender exists when
// content.js's 'showAnalysis' handler tries to render the modal. Required
// for tabs opened before the extension was last reloaded — manifest's
// content_scripts only inject on fresh page loads.
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['render.js', 'content.js'] });
} catch (e) { /* already injected */ }
await new Promise(r => setTimeout(r, 100));
chrome.tabs.sendMessage(tab.id, { action: 'activateSnipping' }, (response) => {
if (chrome.runtime.lastError) {
showStatus('Activare eșuată. Reîncarcă pagina.', 'error');
return;
}
if (response?.success) {
showStatus('Mod captură activ!', 'success');
setTimeout(() => window.close(), 800);
} else {
showStatus('Captura necesită consimțământul pentru trimiterea imaginii.', 'error');
}
});
} catch (error) {
showStatus('Eroare: ' + error.message, 'error');
}
});
// === VIDEO REGION RECORDING ===
activateVideoSnippingBtn.addEventListener('click', async () => {
try {
showStatus('Se activează modul video…', 'info');
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab) { showStatus('Nu există tab activ', 'error'); return; }
try {
// Same injection ordering rationale as the screenshot flow above.
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['render.js', 'content.js'] });
} catch (e) { /* already injected */ }
await new Promise(r => setTimeout(r, 100));
chrome.tabs.sendMessage(tab.id, { action: 'activateVideoSnipping' }, (response) => {
if (chrome.runtime.lastError) {
showStatus('Activare eșuată. Reîncarcă pagina.', 'error');
return;
}
if (response?.success) {
showStatus('Mod video activ!', 'success');
setTimeout(() => window.close(), 800);
} else {
showStatus('Înregistrarea necesită consimțământul pentru trimiterea capturii.', 'error');
}
});
} catch (error) {
showStatus('Eroare: ' + error.message, 'error');
}
});
// === SOCIAL POST SELECTION (extractoare Facebook / Twitter-X) ===
const selectSocialPostBtn = document.getElementById('selectSocialPost');
function socialPlatformFor(url) {
if (/https?:\/\/([a-z0-9-]+\.)?facebook\.com\//i.test(url)) return 'facebook';
if (/https?:\/\/([a-z0-9-]+\.)?(twitter|x)\.com\//i.test(url)) return 'twitter';
return null;
}
(async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab?.url && socialPlatformFor(tab.url)) {
selectSocialPostBtn.style.display = '';
}
})();
selectSocialPostBtn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const platform = tab?.url ? socialPlatformFor(tab.url) : null;
if (!platform) { showStatus('Deschide o pagină Facebook sau X/Twitter.', 'error'); return; }
try {
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['render.js', 'content.js'] });
} catch (e) { /* already injected */ }
await new Promise(r => setTimeout(r, 100));
chrome.tabs.sendMessage(tab.id, { action: 'activateSelection', platform }, (response) => {
if (chrome.runtime.lastError) {
showStatus('Activare eșuată. Reîncarcă pagina.', 'error');
return;
}
if (response?.success) {
showStatus('Mod selecție activ — dă click pe o postare.', 'success');
setTimeout(() => window.close(), 800);
}
});
});
// === URL ANALYSIS (cerință caiet 1a/1b) ===
function isValidHttpUrl(value) {
try {
const u = new URL(/^www\./i.test(value) ? `https://${value}` : value);
return u.protocol === 'http:' || u.protocol === 'https:';
} catch {
return false;
}
}
urlInput.addEventListener('input', () => {
analyzeUrlBtn.disabled = !isValidHttpUrl(urlInput.value.trim());
});
analyzeCurrentTabBtn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab?.url || !/^https?:/i.test(tab.url)) {
showStatus('Pagina curentă nu are un URL analizabil.', 'error');
return;
}
urlInput.value = tab.url;
analyzeUrlBtn.disabled = false;
submitUrl(tab.url);
});
analyzeUrlBtn.addEventListener('click', () => {
const url = urlInput.value.trim();
if (!isValidHttpUrl(url)) {
showStatus('Introdu un URL valid (http/https).', 'error');
return;
}
submitUrl(url);
});
function submitUrl(url) {
analyzeUrlBtn.disabled = true;
showStatus('Trimis către DiDi… rezultatul se deschide într-un tab nou în 3090s.', 'info');
chrome.runtime.sendMessage({ action: 'analyzeUrl', url }, (response) => {
analyzeUrlBtn.disabled = false;
if (response?.success) {
showStatus('Trimis! Rezultatul se va deschide când e gata.', 'success');
setTimeout(() => window.close(), 1500);
} else {
showStatus('Eroare: ' + (response?.error || 'necunoscută'), 'error');
}
});
}
// === TEXT ANALYSIS ===
textInput.addEventListener('input', () => {
const len = textInput.value.trim().length;
charCount.textContent = len;
analyzeTextBtn.disabled = len === 0 || len > 5000;
});
analyzeTextBtn.addEventListener('click', () => {
const text = textInput.value.trim();
if (!text) return;
analyzeTextBtn.disabled = true;
analyzeTextBtn.textContent = 'Se trimite…';
showStatus('Trimis către DiDi… rezultatul se deschide într-un tab nou în 3090s.', 'info');
// Background acks immediately, then runs the polling in the service worker
// and opens a new tab with the rendered standalone result page when done.
chrome.runtime.sendMessage({ action: 'analyzeText', text }, (response) => {
analyzeTextBtn.innerHTML = `
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path d="M8 2L10 6L14 7L10 8L8 14L6 8L2 7L6 6L8 2Z" fill="currentColor"/>
</svg>
Analizează text`;
analyzeTextBtn.disabled = false;
if (response?.success) {
showStatus('Trimis! Rezultatul se va deschide când e gata.', 'success');
textInput.value = '';
charCount.textContent = '0';
setTimeout(() => window.close(), 1500);
} else {
showStatus('Eroare: ' + (response?.error || 'necunoscută'), 'error');
}
});
});
// === HISTORY ===
viewHistoryBtn.addEventListener('click', () => {
chrome.tabs.create({ url: chrome.runtime.getURL('history.html') });
});
// === STATUS ===
function showStatus(message, type) {
statusDiv.textContent = message;
statusDiv.className = `status ${type}`;
statusDiv.style.display = 'block';
setTimeout(() => { statusDiv.style.display = 'none'; }, 3000);
}
});