Template
140 lines
4.5 KiB
TypeScript
140 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { useCallback, useEffect, useState } from 'react';
|
|
|
|
import type { LinkJson } from '@/lib/links';
|
|
|
|
const semProtocolo = (url: string) => url.replace(/^https?:\/\//, '');
|
|
|
|
export default function Encurtador({ linksIniciais }: { linksIniciais: LinkJson[] }) {
|
|
const [url, setUrl] = useState('');
|
|
const [enviando, setEnviando] = useState(false);
|
|
const [erro, setErro] = useState<string | null>(null);
|
|
const [criado, setCriado] = useState<LinkJson | null>(null);
|
|
const [copiado, setCopiado] = useState(false);
|
|
const [links, setLinks] = useState(linksIniciais);
|
|
|
|
const carregarLinks = useCallback(async () => {
|
|
try {
|
|
const resposta = await fetch('/api/links', { cache: 'no-store' });
|
|
if (resposta.ok) setLinks(((await resposta.json()) as { links: LinkJson[] }).links);
|
|
} catch {
|
|
// Sem rede: a lista fica como estava.
|
|
}
|
|
}, []);
|
|
|
|
// Voltou para a aba depois de testar um link? Atualiza a contagem de cliques.
|
|
useEffect(() => {
|
|
const aoVoltar = () => {
|
|
if (document.visibilityState === 'visible') void carregarLinks();
|
|
};
|
|
document.addEventListener('visibilitychange', aoVoltar);
|
|
return () => document.removeEventListener('visibilitychange', aoVoltar);
|
|
}, [carregarLinks]);
|
|
|
|
async function encurtar(evento: React.FormEvent<HTMLFormElement>) {
|
|
evento.preventDefault();
|
|
setErro(null);
|
|
let alvo = url.trim();
|
|
if (!alvo) return setErro('Cole um link para encurtar.');
|
|
// Quem digita "exemplo.com.br" quer dizer https://exemplo.com.br.
|
|
if (!/^[a-z][a-z0-9+.-]*:/i.test(alvo)) alvo = `https://${alvo}`;
|
|
|
|
setEnviando(true);
|
|
try {
|
|
const resposta = await fetch('/api/links', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ url: alvo }),
|
|
});
|
|
const dados = await resposta.json().catch(() => ({}));
|
|
if (!resposta.ok) return setErro(dados.erro || 'Não deu para encurtar esse link. Tente de novo.');
|
|
setCriado(dados as LinkJson);
|
|
setCopiado(false);
|
|
setUrl('');
|
|
await carregarLinks();
|
|
} catch {
|
|
setErro('Sem conexão com o servidor. Tente de novo.');
|
|
} finally {
|
|
setEnviando(false);
|
|
}
|
|
}
|
|
|
|
async function copiar() {
|
|
if (!criado) return;
|
|
try {
|
|
await navigator.clipboard.writeText(criado.curto);
|
|
setCopiado(true);
|
|
} catch {
|
|
// Navegador sem permissão de área de transferência: seleciona o link.
|
|
const alvo = document.getElementById('curto');
|
|
if (alvo) getSelection()?.selectAllChildren(alvo);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<section className="cartao">
|
|
<form className="formulario" onSubmit={encurtar} noValidate>
|
|
<label htmlFor="url" className="so-leitor">
|
|
Link para encurtar
|
|
</label>
|
|
<input
|
|
id="url"
|
|
name="url"
|
|
type="url"
|
|
inputMode="url"
|
|
maxLength={2048}
|
|
placeholder="Cole seu link aqui"
|
|
autoComplete="off"
|
|
required
|
|
value={url}
|
|
onChange={(e) => setUrl(e.target.value)}
|
|
/>
|
|
<button type="submit" disabled={enviando}>
|
|
Encurtar
|
|
</button>
|
|
</form>
|
|
{erro && (
|
|
<p className="erro" role="alert">
|
|
{erro}
|
|
</p>
|
|
)}
|
|
{criado && (
|
|
<div className="resultado">
|
|
<a id="curto" href={criado.curto} target="_blank" rel="noopener">
|
|
{semProtocolo(criado.curto)}
|
|
</a>
|
|
<button type="button" className="secundario" onClick={copiar}>
|
|
{copiado ? 'Copiado!' : 'Copiar'}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
<h2>Últimos links</h2>
|
|
<section className="cartao">
|
|
{links.length === 0 ? (
|
|
<p className="vazio">Nenhum link ainda. Encurte o primeiro aí em cima.</p>
|
|
) : (
|
|
<ul className="lista">
|
|
{links.map((link) => (
|
|
<li key={link.codigo}>
|
|
<div className="link-info">
|
|
<a href={link.curto} target="_blank" rel="noopener">
|
|
{semProtocolo(link.curto)}
|
|
</a>
|
|
<span className="destino" title={link.url}>
|
|
{link.url}
|
|
</span>
|
|
</div>
|
|
<span className="cliques">{link.cliques === 1 ? '1 clique' : `${link.cliques} cliques`}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</section>
|
|
</>
|
|
);
|
|
}
|