Compare commits

..

No commits in common. "90949a94519233adcb1df9e95de6a54305c6b7e9" and "61978a72a14011056f70d426b400eed71790cef9" have entirely different histories.

10 changed files with 58 additions and 253 deletions

View File

@ -5,7 +5,7 @@ function createSaveButton() {
// Adicionar ícone do Launchr // Adicionar ícone do Launchr
const icon = document.createElement('img'); const icon = document.createElement('img');
icon.src = chrome.runtime.getURL('src/assets/icon16.png'); icon.src = chrome.runtime.getURL('images/logo.png');
icon.alt = 'Launchr'; icon.alt = 'Launchr';
icon.className = 'launchr-icon'; icon.className = 'launchr-icon';

View File

@ -1,33 +1,31 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Launchr Extension", "name": "Sua Extensão",
"version": "1.0.0", "version": "1.0",
"description": "Extensão para salvar posts do LinkedIn e gerar mensagens personalizadas.", "description": "Extensão para salvar posts do LinkedIn e gerar mensagens personalizadas.",
"permissions": [ "permissions": [
"activeTab", "activeTab",
"storage", "storage",
"contextMenus", "contextMenus",
"scripting" "scripting",
"windows"
], ],
"host_permissions": [ "host_permissions": [
"https://launchr.com.br/*", "https://launchr.com.br/*",
"https://api.openai.com/*", "https://api.openai.com/*",
"*://www.linkedin.com/*" "*://www.linkedin.com/*"
], ],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
"background": { "background": {
"service_worker": "background.js", "service_worker": "background.js",
"type": "module" "type": "module"
}, },
"action": { "action": {
"default_popup": "popup.html", "default_popup": "popup.html",
"default_title": "Launchr", "default_title": "Minha Extensão",
"default_icon": { "default_icon": {
"16": "src/assets/icon16.png", "16": "icon16.png",
"48": "src/assets/icon48.png", "48": "icon48.png",
"128": "src/assets/icon128.png" "128": "icon128.png"
} }
}, },
"content_scripts": [ "content_scripts": [
@ -38,12 +36,15 @@
} }
], ],
"icons": { "icons": {
"16": "src/assets/icon16.png", "16": "icon16.png",
"48": "src/assets/icon48.png", "48": "icon48.png",
"128": "src/assets/icon128.png" "128": "icon128.png"
}, },
"web_accessible_resources": [{ "web_accessible_resources": [{
"resources": ["src/assets/*", "src/styles/*"], "resources": [
"images/*",
"lib/*"
],
"matches": ["<all_urls>"] "matches": ["<all_urls>"]
}] }]
} }

View File

@ -52,7 +52,7 @@
<div id="loginSection" class="container p-4"> <div id="loginSection" class="container p-4">
<!-- Logo --> <!-- Logo -->
<div class="d-flex align-items-center mb-4"> <div class="d-flex align-items-center mb-4">
<img src=".src/assets/logo.png" alt="Launchr" height="32" class="me-2"> <img src="./images/logo.png" alt="Launchr" height="32" class="me-2">
<small class="text-muted">Versão 1.3.6</small> <small class="text-muted">Versão 1.3.6</small>
</div> </div>
@ -99,7 +99,7 @@
<div class="container-fluid px-0"> <div class="container-fluid px-0">
<!-- Logo --> <!-- Logo -->
<a class="navbar-brand" href="#"> <a class="navbar-brand" href="#">
<img src="./src/assets/logo.png" alt="Launchr" height="32"> <img src="./images/logo.png" alt="Launchr" height="32">
</a> </a>
<!-- Links centralizados --> <!-- Links centralizados -->

View File

@ -1,36 +0,0 @@
import sass from 'sass';
import { writeFileSync, mkdirSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
// Compilar SCSS para CSS
function compileSass(inputFile, outputFile) {
try {
const result = sass.compile(inputFile, {
style: 'compressed',
loadPaths: [resolve(__dirname, '../src/styles')]
});
// Criar diretório de saída se não existir
mkdirSync(dirname(outputFile), { recursive: true });
// Escrever arquivo CSS
writeFileSync(outputFile, result.css);
console.log(`SCSS compilado: ${inputFile} -> ${outputFile}`);
} catch (error) {
console.error(`Erro ao compilar SCSS ${inputFile}:`, error);
}
}
// Compilar arquivos
compileSass(
resolve(__dirname, '../src/styles/content.scss'),
resolve(__dirname, '../dist/styles/content.css')
);
compileSass(
resolve(__dirname, '../src/styles/custom.scss'),
resolve(__dirname, '../dist/styles/custom.css')
);

View File

@ -1,46 +1,8 @@
import { MessagePayload, MessageTypes, AuthState, SavePostData } from '../types/messages'; let currentPostData = {};
let authToken: string | null = null;
const initialAuthState: AuthState = { let negocios_id: string | null = null;
isAuthenticated: false, let negocios_nome: string | null = null;
token: null, let user_id: string | null = null;
businessId: null,
businessName: null,
userId: null
};
let authState: AuthState = { ...initialAuthState };
// Handler de mensagens tipado
function handleMessage(
message: MessagePayload,
sender: chrome.runtime.MessageSender,
sendResponse: (response: { success: boolean; error?: string; data?: unknown }) => void
): boolean {
switch (message.type) {
case MessageTypes.SAVE_POST:
handleSavePost(message.data as SavePostData, sendResponse);
break;
case MessageTypes.CHECK_AUTH:
sendResponse({ success: true, data: authState.isAuthenticated });
break;
case MessageTypes.LOGIN:
handleLogin(message.data as { email: string; password: string }, sendResponse);
break;
case MessageTypes.LOGOUT:
handleLogout(sendResponse);
break;
default:
sendResponse({ success: false, error: 'Tipo de mensagem não suportado' });
}
return true; // Indica que a resposta será assíncrona
}
chrome.runtime.onMessage.addListener(handleMessage);
// URL base da API do Bubble no Launchr // URL base da API do Bubble no Launchr
const API_URL = 'https://launchr.com.br/api/1.1/wf'; const API_URL = 'https://launchr.com.br/api/1.1/wf';
@ -52,8 +14,8 @@ let loginWindowId: number | null = null;
const checkAuth = (): Promise<boolean> => { const checkAuth = (): Promise<boolean> => {
return new Promise((resolve) => { return new Promise((resolve) => {
chrome.storage.local.get(['authToken'], (result) => { chrome.storage.local.get(['authToken'], (result) => {
authState.token = result.authToken; authToken = result.authToken;
resolve(!!authState.token); resolve(!!authToken);
}); });
}); });
}; };
@ -86,6 +48,29 @@ chrome.windows.onRemoved.addListener((windowId) => {
} }
}); });
// Listener para mensagens do content script e popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'SAVE_POST' || request.type === 'SAVE_IDEA') {
checkAuth().then(isAuthenticated => {
if (!isAuthenticated) {
openLoginPopup();
sendResponse({ success: false, error: 'AUTH_REQUIRED' });
} else {
// Implementar lógica de salvamento
handleSaveRequest(request, sendResponse);
}
});
return true; // Indica que a resposta será assíncrona
}
if (request.type === 'CHECK_AUTH') {
checkAuth().then(isAuthenticated => {
sendResponse({ isAuthenticated });
});
return true;
}
});
// Listener para o clique no ícone da extensão // Listener para o clique no ícone da extensão
chrome.action.onClicked.addListener(async () => { chrome.action.onClicked.addListener(async () => {
const isAuthenticated = await checkAuth(); const isAuthenticated = await checkAuth();
@ -112,7 +97,7 @@ const handleSaveRequest = async (request: any, sendResponse: (response: any) =>
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': authState.token || '' 'Authorization': authToken || ''
}, },
body: JSON.stringify(request.data) body: JSON.stringify(request.data)
}); });
@ -121,7 +106,7 @@ const handleSaveRequest = async (request: any, sendResponse: (response: any) =>
if (response.status === 401) { if (response.status === 401) {
// Token inválido ou expirado // Token inválido ou expirado
chrome.storage.local.remove(['authToken']); chrome.storage.local.remove(['authToken']);
authState.token = null; authToken = null;
openLoginPopup(); openLoginPopup();
sendResponse({ success: false, error: 'AUTH_REQUIRED' }); sendResponse({ success: false, error: 'AUTH_REQUIRED' });
return; return;

View File

@ -1,16 +1,14 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { store } from '../store'; import { store } from '../store';
import { PopupApp } from './components/PopupApp'; import Router from './Router';
import '../index.css';
import '../styles/custom.scss'; ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
function Popup() {
return (
<Provider store={store}> <Provider store={store}>
<PopupApp /> <Router />
</Provider> </Provider>
); </React.StrictMode>
} );
export default Popup;

View File

@ -1,56 +0,0 @@
// Variáveis do tema
@import "variables";
// Estilos do botão
.launchr {
&-save-button {
background: none;
border: none;
padding: $spacer * 0.5;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: $transition-base;
margin-right: $spacer;
border-radius: $border-radius;
&:hover {
background-color: rgba($black, 0.05);
}
&.saving {
opacity: 0.7;
cursor: wait;
}
&.saved {
background-color: rgba($primary, 0.1);
}
}
&-icon {
width: 20px;
height: 20px;
border-radius: $border-radius-sm;
}
&-save-button-container {
display: inline-flex;
align-items: center;
margin-left: $spacer;
}
}
// Ajustes para o LinkedIn
.feed-shared-control-menu,
.feed-shared-update-v2__control-menu {
.launchr-save-button {
position: relative;
z-index: 2;
}
.display-flex {
align-items: center;
}
}

View File

@ -1,36 +0,0 @@
@import "variables";
@import "bootstrap/scss/bootstrap";
// Estilos personalizados da extensão
.launchr {
&-container {
width: 400px;
min-height: 500px;
max-height: 600px;
overflow-y: auto;
}
&-header {
padding: $spacer;
border-bottom: 1px solid $border-color;
background-color: $white;
}
&-content {
padding: $spacer;
}
&-save-button {
@extend .btn;
@extend .btn-primary;
&--saving {
opacity: 0.7;
cursor: wait;
}
&--saved {
@extend .btn-success;
}
}
}

View File

@ -1,21 +0,0 @@
// Sobrescrita de variáveis Bootstrap
$primary: #4F46E5;
$primary-hover: #4338CA;
// Cores personalizadas
$launchr-colors: (
"primary": $primary,
"primary-hover": $primary-hover,
"error": #EF4444,
"success": #10B981
);
// Configurações do tema
$enable-shadows: true;
$enable-gradients: false;
$enable-responsive-font-sizes: true;
// Configurações de componentes
$border-radius: 0.375rem;
$btn-border-radius: $border-radius;
$input-border-radius: $border-radius;

View File

@ -1,30 +0,0 @@
export interface MessagePayload {
type: MessageTypes;
data: unknown;
}
export const MessageTypes = {
SAVE_POST: 'SAVE_POST',
SAVE_IDEA: 'SAVE_IDEA',
CHECK_AUTH: 'CHECK_AUTH',
LOGIN: 'LOGIN',
LOGOUT: 'LOGOUT'
} as const;
export type MessageTypes = typeof MessageTypes[keyof typeof MessageTypes];
export interface AuthState {
isAuthenticated: boolean;
token: string | null;
businessId: string | null;
businessName: string | null;
userId: string | null;
}
export interface SavePostData {
title: string;
content: string;
author: string;
authorProfileLink?: string;
postLink?: string;
}