feat: implement error handling and logging in server hooks to capture and notify on 404 and 500 errors, enhancing server reliability and monitoring
This commit is contained in:
@@ -52,6 +52,12 @@
|
||||
let dragThreshold = $state(5); // Distância mínima em pixels para considerar arrastar
|
||||
let hasMoved = $state(false); // Flag para verificar se houve movimento durante o arrastar
|
||||
let shouldPreventClick = $state(false); // Flag para prevenir clique após arrastar
|
||||
|
||||
// Suporte a gestos touch (swipe)
|
||||
let touchStart = $state<{ x: number; y: number; time: number } | null>(null);
|
||||
let touchCurrent = $state<{ x: number; y: number } | null>(null);
|
||||
let isTouching = $state(false);
|
||||
let swipeVelocity = $state(0); // Velocidade do swipe para animação
|
||||
|
||||
// Tamanho da janela (redimensionável)
|
||||
const MIN_WIDTH = 300;
|
||||
@@ -613,6 +619,134 @@
|
||||
// Não prevenir default para permitir clique funcionar se não houver movimento
|
||||
}
|
||||
|
||||
// Handlers para gestos touch (swipe)
|
||||
function handleTouchStart(e: TouchEvent) {
|
||||
if (!position || e.touches.length !== 1) return;
|
||||
const touch = e.touches[0];
|
||||
touchStart = {
|
||||
x: touch.clientX,
|
||||
y: touch.clientY,
|
||||
time: Date.now()
|
||||
};
|
||||
touchCurrent = { x: touch.clientX, y: touch.clientY };
|
||||
isTouching = true;
|
||||
isDragging = true;
|
||||
dragStart = {
|
||||
x: touch.clientX - position.x,
|
||||
y: touch.clientY - position.y
|
||||
};
|
||||
hasMoved = false;
|
||||
shouldPreventClick = false;
|
||||
document.body.classList.add('dragging');
|
||||
}
|
||||
|
||||
function handleTouchMove(e: TouchEvent) {
|
||||
if (!isTouching || !touchStart || !position || e.touches.length !== 1) return;
|
||||
const touch = e.touches[0];
|
||||
touchCurrent = { x: touch.clientX, y: touch.clientY };
|
||||
|
||||
// Calcular velocidade do swipe
|
||||
const deltaTime = Date.now() - touchStart.time;
|
||||
const deltaX = touch.clientX - touchStart.x;
|
||||
const deltaY = touch.clientY - touchStart.y;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
if (deltaTime > 0) {
|
||||
swipeVelocity = distance / deltaTime; // pixels por ms
|
||||
}
|
||||
|
||||
// Calcular nova posição
|
||||
const newX = touch.clientX - dragStart.x;
|
||||
const newY = touch.clientY - dragStart.y;
|
||||
|
||||
// Verificar se houve movimento significativo
|
||||
const deltaXAbs = Math.abs(newX - position.x);
|
||||
const deltaYAbs = Math.abs(newY - position.y);
|
||||
|
||||
if (deltaXAbs > dragThreshold || deltaYAbs > dragThreshold) {
|
||||
hasMoved = true;
|
||||
shouldPreventClick = true;
|
||||
}
|
||||
|
||||
// Dimensões do widget
|
||||
const widgetWidth = isOpen && !isMinimized ? windowSize.width : 72;
|
||||
const widgetHeight = isOpen && !isMinimized ? windowSize.height : 72;
|
||||
|
||||
const winWidth =
|
||||
windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0);
|
||||
const winHeight =
|
||||
windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0);
|
||||
|
||||
const minX = -(widgetWidth - 100);
|
||||
const maxX = Math.max(0, winWidth - 100);
|
||||
const minY = -(widgetHeight - 100);
|
||||
const maxY = Math.max(0, winHeight - 100);
|
||||
|
||||
position = {
|
||||
x: Math.max(minX, Math.min(newX, maxX)),
|
||||
y: Math.max(minY, Math.min(newY, maxY))
|
||||
};
|
||||
}
|
||||
|
||||
function handleTouchEnd(e: TouchEvent) {
|
||||
if (!isTouching || !touchStart || !position) return;
|
||||
|
||||
const hadMoved = hasMoved;
|
||||
|
||||
// Aplicar momentum se houver velocidade suficiente
|
||||
if (swipeVelocity > 0.5 && hadMoved) {
|
||||
const deltaX = touchCurrent ? touchCurrent.x - touchStart.x : 0;
|
||||
const deltaY = touchCurrent ? touchCurrent.y - touchStart.y : 0;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
if (distance > 10) {
|
||||
// Aplicar momentum suave
|
||||
const momentum = Math.min(swipeVelocity * 50, 200); // Limitar momentum
|
||||
const angle = Math.atan2(deltaY, deltaX);
|
||||
|
||||
let momentumX = position.x + Math.cos(angle) * momentum;
|
||||
let momentumY = position.y + Math.sin(angle) * momentum;
|
||||
|
||||
// Limitar dentro dos bounds
|
||||
const widgetWidth = isOpen && !isMinimized ? windowSize.width : 72;
|
||||
const widgetHeight = isOpen && !isMinimized ? windowSize.height : 72;
|
||||
const winWidth = windowDimensions.width || (typeof window !== 'undefined' ? window.innerWidth : 0);
|
||||
const winHeight = windowDimensions.height || (typeof window !== 'undefined' ? window.innerHeight : 0);
|
||||
const minX = -(widgetWidth - 100);
|
||||
const maxX = Math.max(0, winWidth - 100);
|
||||
const minY = -(widgetHeight - 100);
|
||||
const maxY = Math.max(0, winHeight - 100);
|
||||
|
||||
momentumX = Math.max(minX, Math.min(momentumX, maxX));
|
||||
momentumY = Math.max(minY, Math.min(momentumY, maxY));
|
||||
|
||||
position = { x: momentumX, y: momentumY };
|
||||
isAnimating = true;
|
||||
|
||||
setTimeout(() => {
|
||||
isAnimating = false;
|
||||
ajustarPosicao();
|
||||
}, 300);
|
||||
}
|
||||
} else {
|
||||
ajustarPosicao();
|
||||
}
|
||||
|
||||
isDragging = false;
|
||||
isTouching = false;
|
||||
touchStart = null;
|
||||
touchCurrent = null;
|
||||
swipeVelocity = 0;
|
||||
document.body.classList.remove('dragging');
|
||||
|
||||
setTimeout(() => {
|
||||
hasMoved = false;
|
||||
shouldPreventClick = false;
|
||||
}, 100);
|
||||
|
||||
savePosition();
|
||||
}
|
||||
|
||||
function handleMouseMove(e: MouseEvent) {
|
||||
if (isResizing) {
|
||||
handleResizeMove(e);
|
||||
@@ -747,10 +881,14 @@
|
||||
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
window.addEventListener('mouseup', handleMouseUp);
|
||||
window.addEventListener('touchmove', handleTouchMove, { passive: false });
|
||||
window.addEventListener('touchend', handleTouchEnd);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
window.removeEventListener('mouseup', handleMouseUp);
|
||||
window.removeEventListener('touchmove', handleTouchMove);
|
||||
window.removeEventListener('touchend', handleTouchEnd);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
@@ -789,9 +927,10 @@
|
||||
onmouseup={(e) => {
|
||||
handleMouseUp(e);
|
||||
}}
|
||||
ontouchstart={handleTouchStart}
|
||||
onclick={(e) => {
|
||||
// Só executar toggle se não houve movimento durante o arrastar
|
||||
if (!shouldPreventClick && !hasMoved) {
|
||||
if (!shouldPreventClick && !hasMoved && !isTouching) {
|
||||
handleToggle();
|
||||
} else {
|
||||
// Prevenir clique se houve movimento
|
||||
@@ -802,11 +941,23 @@
|
||||
}}
|
||||
aria-label="Abrir chat"
|
||||
>
|
||||
<!-- Anel de brilho rotativo -->
|
||||
<!-- Anel de brilho rotativo melhorado com múltiplas camadas -->
|
||||
<div
|
||||
class="absolute inset-0 rounded-full opacity-0 transition-opacity duration-500 group-hover:opacity-100"
|
||||
style="background: conic-gradient(from 0deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%); animation: rotate 3s linear infinite;"
|
||||
style="background: conic-gradient(from 0deg, transparent 0%, rgba(255,255,255,0.4) 25%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.4) 75%, transparent 100%); animation: rotate 3s linear infinite; transform-origin: center;"
|
||||
></div>
|
||||
<!-- Segunda camada para efeito de profundidade -->
|
||||
<div
|
||||
class="absolute inset-0 rounded-full opacity-0 transition-opacity duration-700 group-hover:opacity-60"
|
||||
style="background: conic-gradient(from 180deg, transparent 0%, rgba(255,255,255,0.2) 30%, transparent 60%); animation: rotate 4s linear infinite reverse; transform-origin: center;"
|
||||
></div>
|
||||
<!-- Efeito de brilho pulsante durante arrasto -->
|
||||
{#if isDragging || isTouching}
|
||||
<div
|
||||
class="absolute inset-0 rounded-full opacity-30 animate-pulse"
|
||||
style="background: radial-gradient(circle at center, rgba(255,255,255,0.4) 0%, transparent 70%); animation: pulse-glow 1.5s ease-in-out infinite;"
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
<!-- Ícone de chat moderno com efeito 3D -->
|
||||
<MessageCircle
|
||||
@@ -1182,7 +1333,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Rotação para anel de brilho */
|
||||
/* Rotação para anel de brilho - suavizada */
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
@@ -1192,6 +1343,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Efeito de pulso de brilho durante arrasto */
|
||||
@keyframes pulse-glow {
|
||||
0%, 100% {
|
||||
opacity: 0.2;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
/* Efeito shimmer para o header */
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
|
||||
Reference in New Issue
Block a user