adicionado suporte a pwa para compartilhamento rapido

This commit is contained in:
Renan Bernordi 2025-01-10 11:02:01 -03:00
parent c5c3b4c784
commit 592c5d0a2e
11 changed files with 166 additions and 6 deletions

View file

@ -1,8 +1,18 @@
/**
* JavaScript functions for form validation and error handling
* Funções JavaScript para validação de formulário e manipulação de erros
*/
/**
* Validates the form before submission
*
* Checks:
* - If URL is not empty
* - If URL starts with http:// or https://
* - If URL has a valid format
*
* @returns {boolean} True if URL is valid, False otherwise
*
* Valida o formulário antes do envio
*
* Verifica:
@ -17,18 +27,21 @@ function validateForm() {
const submitButton = document.querySelector('button[type="submit"]');
const url = urlInput.value.trim();
// Check if URL is not empty
// Verifica se a URL não está vazia
if (!url) {
showError('Por favor, insira uma URL');
return false;
}
// Check if URL starts with http:// or https://
// Verifica se a URL começa com http:// ou https://
if (!/^https?:\/\//i.test(url)) {
showError('A URL deve começar com http:// ou https://');
return false;
}
// Try to create a URL object to validate format
// Tenta criar um objeto URL para validar o formato
try {
new URL(url);
@ -37,16 +50,19 @@ function validateForm() {
return false;
}
// Disable input and button
// Desabilita o input e o botão
urlInput.readonly = true;
submitButton.disabled = true;
// Add Tailwind disabled classes
// Adiciona classes de disabled do Tailwind
submitButton.classList.add('cursor-wait', 'disabled:bg-blue-400');
submitButton.classList.remove('hover:bg-blue-700');
urlInput.classList.add('cursor-wait', 'disabled:bg-gray-50', 'focus:outline-none');
// Add loading state to button
// Adiciona estado de loading ao botão
submitButton.innerHTML = `
<img src="assets/svg/search.svg" class="w-6 h-6 mr-3" alt="Search">
@ -57,6 +73,13 @@ function validateForm() {
}
/**
* Displays an error message below the form
*
* Removes any existing error message before displaying the new one.
* The message is displayed with an error icon and red formatting.
*
* @param {string} message Error message to be displayed
*
* Exibe uma mensagem de erro abaixo do formulário
*
* Remove qualquer mensagem de erro existente antes de exibir a nova.
@ -68,11 +91,13 @@ function showError(message) {
const form = document.getElementById('urlForm');
const existingError = form.querySelector('.error-message');
// Remove previous error message if it exists
// Remove mensagem de erro anterior se existir
if (existingError) {
existingError.remove();
}
// Create and add new error message
// Cria e adiciona nova mensagem de erro
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message mt-4 text-base text-red-600';
@ -82,3 +107,24 @@ function showError(message) {
form.appendChild(errorDiv);
}
/**
* Service Worker registration for PWA functionality
* Registers a service worker to enable offline capabilities and PWA features
*
* Registro do Service Worker para funcionalidade PWA
* Registra um service worker para habilitar recursos offline e funcionalidades PWA
*/
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(() => {
// Service Worker registered successfully
// Service Worker registrado com sucesso
})
.catch(() => {
// Service Worker registration failed
// Falha no registro do Service Worker
});
});
}

BIN
app/assets/pwa/192x192.png Normal file

Binary file not shown.

After

(image error) Size: 5.6 KiB

BIN
app/assets/pwa/512x512.png Normal file

Binary file not shown.

After

(image error) Size: 19 KiB

View file

@ -62,7 +62,13 @@ $cache_folder = $cache->getCacheFileCount();
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo SITE_NAME; ?></title>
<link rel="icon" href="assets/svg/marreta.svg" type="image/svg+xml">
<link rel="icon" href="<?php echo SITE_URL; ?>/assets/svg/marreta.svg" type="image/svg+xml">
<link rel="manifest" href="<?php echo SITE_URL; ?>/manifest.json">
<meta name="theme-color" content="#2563eb">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="<?php echo SITE_NAME; ?>">
<link rel="apple-touch-icon" href="<?php echo SITE_URL; ?>/assets/pwa/192x192.png">
<meta property="og:type" content="website" />
<meta property="og:url" content="<?php echo SITE_URL; ?>" />
<meta property="og:title" content="<?php echo SITE_NAME; ?>" />
@ -148,6 +154,26 @@ $cache_folder = $cache->getCacheFileCount();
<pre class="bg-gray-100 p-3 rounded-lg text-sm overflow-x-auto"><?php echo SITE_URL; ?>/p/https://exemplo.com</pre>
</p>
</div>
<!-- Adicionar como aplicativo / Add as app -->
<div class="bg-white rounded-xl shadow-lg p-8 mt-8">
<h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
<img src="assets/svg/marreta.svg" class="w-6 h-6 mr-3" alt="App">
<?php echo Language::get('add_as_app'); ?>
</h2>
<div class="space-y-4">
<p class="text-gray-600">
<?php echo str_replace('{site_name}', SITE_NAME, Language::get('add_as_app_description')); ?>
</p>
<div class="bg-gray-50 rounded-lg p-4">
<ol class="list-decimal list-inside space-y-2 text-gray-700">
<li><?php echo Language::get('add_as_app_step1'); ?></li>
<li><?php echo Language::get('add_as_app_step2'); ?></li>
<li><?php echo str_replace('{site_name}', SITE_NAME, Language::get('add_as_app_step3')); ?></li>
</ol>
</div>
</div>
</div>
<!-- Seção de Bookmarklet / Bookmarklet section -->
<div class="bg-white rounded-xl shadow-lg p-8 mt-8 mb-8">

View file

@ -17,6 +17,11 @@ return [
'open_source_title' => 'Open Source Projekt',
'open_source_description' => 'Das ist ein <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">Open Source</a> Projekt das mit ❤️ erstellt wurde!<br />Sie können einen Beitrag leisten, Probleme melden oder Vorschläge machen über <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">GitHub</a>.',
'adblocker_warning' => 'Bei Konflikten zwischen {site_name} und Werbeblockern kann ein weißer Bildschirm angezeigt werden. Verwenden Sie den Inkognito-Modus oder deaktivieren Sie die Erweiterung.',
'add_as_app' => 'Als App hinzufügen',
'add_as_app_description' => 'Installieren Sie {site_name} als App für schnelles Link-Sharing:',
'add_as_app_step1' => 'Klicken Sie in Ihrem Browser auf das Menüsymbol (drei Punkte)',
'add_as_app_step2' => 'Wählen Sie "App installieren" oder "Zum Startbildschirm hinzufügen"',
'add_as_app_step3' => 'Klicken Sie auf "Installieren" für schnellen Zugriff auf {site_name}',
'messages' => [
'BLOCKED_DOMAIN' => [
@ -52,4 +57,4 @@ return [
'type' => 'warning'
]
]
];
];

View file

@ -17,6 +17,11 @@ return [
'open_source_title' => 'Open Source Project',
'open_source_description' => 'This is an <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">open source</a> project made with ❤️!<br />You can contribute, report issues, or make suggestions through <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">GitHub</a>.',
'adblocker_warning' => 'Conflicts between {site_name} and ad blockers may cause a white screen. Use incognito mode or disable the extension.',
'add_as_app' => 'Add as App',
'add_as_app_description' => 'Install {site_name} as an app for quick link sharing:',
'add_as_app_step1' => 'In your browser, click the menu icon (three dots)',
'add_as_app_step2' => 'Select "Install app" or "Add to home screen"',
'add_as_app_step3' => 'Click "Install" for quick access to {site_name}',
'messages' => [
'BLOCKED_DOMAIN' => [
@ -52,4 +57,4 @@ return [
'type' => 'warning'
]
]
];
];

View file

@ -17,6 +17,11 @@ return [
'open_source_title' => 'Proyecto de Código Abierto',
'open_source_description' => '¡Este es un proyecto de <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">código abierto</a> hecho con ❤️!<br />Puedes contribuir, reportar problemas o hacer sugerencias a través de <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">GitHub</a>.',
'adblocker_warning' => 'Los conflictos entre {site_name} y los bloqueadores de anuncios pueden causar una pantalla en blanco. Use el modo incógnito o desactive la extensión.',
'add_as_app' => 'Agregar como Aplicación',
'add_as_app_description' => 'Instale {site_name} como una aplicación para compartir enlaces rápidamente:',
'add_as_app_step1' => 'En su navegador, haga clic en el icono de menú (tres puntos)',
'add_as_app_step2' => 'Seleccione "Instalar aplicación" o "Agregar a la pantalla de inicio"',
'add_as_app_step3' => 'Haga clic en "Instalar" para tener acceso rápido a {site_name}',
'messages' => [
'BLOCKED_DOMAIN' => [
@ -52,4 +57,4 @@ return [
'type' => 'warning'
]
]
];
];

View file

@ -17,6 +17,11 @@ return [
'open_source_title' => 'Projeto Open Source',
'open_source_description' => 'Este é um projeto de <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">código aberto</a> feito com ❤️!<br />Você pode contribuir, reportar problemas ou fazer sugestões através do <a href="https://github.com/manualdousuario/marreta/" class="underline" target="_blank">GitHub</a>.',
'adblocker_warning' => 'Conflitos entre o {site_name} e bloqueadores de anúncios podem causar tela branca. Use o modo anônimo ou desative a extensão.',
'add_as_app' => 'Adicionar como Aplicativo',
'add_as_app_description' => 'Instale o {site_name} como um aplicativo para compartilhar links rapidamente:',
'add_as_app_step1' => 'No seu navegador, clique no ícone de menu (três pontos)',
'add_as_app_step2' => 'Selecione "Instalar aplicativo" ou "Adicionar à tela inicial"',
'add_as_app_step3' => 'Clique em "Instalar" para ter acesso rápido ao {site_name}',
'messages' => [
'BLOCKED_DOMAIN' => [
@ -52,4 +57,4 @@ return [
'type' => 'warning'
]
]
];
];

37
app/manifest.php Normal file
View file

@ -0,0 +1,37 @@
<?php
require_once 'config.php';
header('Content-Type: application/json');
$manifest = [
'name' => SITE_NAME,
'short_name' => SITE_NAME,
'description' => SITE_DESCRIPTION,
'start_url' => SITE_URL,
'display' => 'standalone',
'background_color' => '#ffffff',
'theme_color' => '#2563eb',
'icons' => [
[
'src' => 'assets/pwa/192x192.png',
'sizes' => '192x192',
'type' => 'image/png',
'purpose' => 'any maskable'
],
[
'src' => 'assets/pwa/512x512.png',
'sizes' => '512x512',
'type' => 'image/png',
'purpose' => 'any maskable'
]
],
'share_target' => [
'action' => '/p/',
'method' => 'GET',
'params' => [
'url' => 'text'
]
]
];
echo json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

12
app/service-worker.js Normal file
View file

@ -0,0 +1,12 @@
self.addEventListener('fetch', (event) => {
event.respondWith(fetch(event.request));
});
self.addEventListener('share_target', (event) => {
event.respondWith((async () => {
const formData = await event.request.formData();
const url = formData.get('url') || '';
const redirectUrl = `/p/${encodeURIComponent(url)}`;
return Response.redirect(redirectUrl, 303);
})());
});

View file

@ -7,32 +7,41 @@ server {
server_name _;
# Hide NGINX version to reduce exposed information
# Oculta a versão do NGINX para reduzir informações expostas
server_tokens off;
# Cabeçalhos de Segurança
# Security Headers / Cabeçalhos de Segurança
# Enable HSTS (HTTP Strict Transport Security) to force HTTPS connections
# Habilita HSTS (HTTP Strict Transport Security) para forçar conexões HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Prevent clickjacking attacks by allowing the site to be displayed only in its own domain
# Previne ataques de clickjacking, permitindo que o site seja exibido apenas em seu próprio domínio
add_header X-Frame-Options "SAMEORIGIN" always;
# Enable protection against Cross-Site Scripting (XSS) attacks
# Ativa proteção contra ataques de Cross-Site Scripting (XSS)
add_header X-XSS-Protection "1; mode=block" always;
# Prevent browsers from MIME-type sniffing
# Impede que navegadores tentem adivinhar (sniff) o tipo MIME dos arquivos
add_header X-Content-Type-Options "nosniff" always;
# Control how referrer headers are sent
# Controla como os cabeçalhos de referência são enviados
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Limit upload size to prevent denial of service attacks
# Limita o tamanho de uploads para prevenir ataques de negação de serviço
client_max_body_size 10M;
client_body_buffer_size 128k;
# Disable directory listing to prevent structure exposure
# Desativa listagem de diretórios para evitar exposição de estrutura
autoindex off;
# Block access to sensitive directories
# Bloqueia acesso a diretórios sensíveis
location ~ ^/(logs|cache|inc|data|cli)/ {
return 301 /;
@ -46,26 +55,36 @@ server {
try_files $uri $uri/ /$1.php;
}
# Serve manifest.json from manifest.php
# Serve manifest.json a partir do manifest.php
location = /manifest.json {
rewrite ^ /manifest.php last;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass 127.0.0.1:9000;
# Hide header that reveals PHP version
# Oculta cabeçalho que revela a versão do PHP
fastcgi_hide_header X-Powered-By;
}
# Block access to hidden files and directories
# Bloqueia acesso a arquivos e diretórios ocultos
location ~ /\. {
deny all;
return 404;
}
# Block access to configuration and database files
# Bloqueia acesso a arquivos de configuração e banco de dados
location ~ \.(sql|conf|ini)$ {
deny all;
return 404;
}
# Minimize logs to reduce information exposure
# Minimiza logs para reduzir exposição de informações
access_log /dev/null;
error_log /dev/stderr warn;