From 13fce9d906c4a84baab125044db028d652917a32 Mon Sep 17 00:00:00 2001 From: Z User Date: Mon, 10 Aug 2026 20:51:24 +0000 Subject: [PATCH] Fix UX issues: Suppress PWA in dev, respect onboarding preference, silence WS warnings PWA Install Prompt: - Suppress in development (process.env.NODE_ENV === 'development') - Remember dismissal for 7 days (localStorage 'pwa_prompt_dismissed') - Delay showing by 30 seconds in production to avoid interrupting users Onboarding Tour: - Now respects 'Dont Show Again' checkbox (isDontShowAgain check added) - 1-second delay before showing (smoother UX) - Won't appear if user previously checked 'Dont show again' WebSocket Connection: - Removed console.log/warn for connect/disconnect/error events - Increased reconnection delay from 1s to 5s (less spam) - Reduced reconnection attempts from 10 to 3 - Increased timeout from 10s to 15s - Errors now silently fail (WebSocket is optional for real-time collaboration) --- src/components/app-layout.tsx | 9 +++++---- src/components/offline-indicator.tsx | 24 ++++++++++++++++++++++-- src/lib/websocket.ts | 13 +++++-------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/components/app-layout.tsx b/src/components/app-layout.tsx index f51acd5..aaf35fd 100644 --- a/src/components/app-layout.tsx +++ b/src/components/app-layout.tsx @@ -126,7 +126,7 @@ import { t } from '@/lib/i18n'; import { fetchSchoolYears, fetchStudents, fetchClasses, fetchDBNotifications, markAllNotificationsRead, markSingleNotificationRead, type SchoolYear, type Student, type ClassGroup, type DBNotification, type DBNotificationData } from '@/lib/api'; import { apiGet } from '@/lib/api'; import type { School as SchoolType } from '@/lib/api'; -import OnboardingTour, { isOnboardingCompleted } from '@/components/onboarding-tour'; +import OnboardingTour, { isOnboardingCompleted, isDontShowAgain } from '@/components/onboarding-tour'; import KeyboardShortcutsDialog from '@/components/keyboard-shortcuts-dialog'; import { CommandDialog, @@ -755,10 +755,11 @@ export default function AppLayout() { setCurrentView('classes'); }, [setCurrentClass, setCurrentView]); - // Check onboarding status on mount + // Check onboarding status on mount - respect "don't show again" preference useEffect(() => { - if (!isOnboardingCompleted()) { - setOnboardingOpen(true); + if (!isOnboardingCompleted() && !isDontShowAgain()) { + const timer = setTimeout(() => setOnboardingOpen(true), 1000); + return () => clearTimeout(timer); } }, []); diff --git a/src/components/offline-indicator.tsx b/src/components/offline-indicator.tsx index 8ac7548..0538501 100644 --- a/src/components/offline-indicator.tsx +++ b/src/components/offline-indicator.tsx @@ -94,10 +94,27 @@ export function PWAInstallPrompt() { return; } + // Don't show PWA prompt in development + if (process.env.NODE_ENV === 'development') { + return; + } + + // Check if user dismissed recently (within 7 days) + try { + const dismissedAt = localStorage.getItem('pwa_prompt_dismissed'); + if (dismissedAt) { + const daysSince = (Date.now() - parseInt(dismissedAt)) / (1000 * 60 * 60 * 24); + if (daysSince < 7) return; + } + } catch { + // ignore + } + const handleBeforeInstall = (e: Event) => { e.preventDefault(); setDeferredPrompt(e as BeforeInstallPromptEvent); - setShowInstall(true); + // Delay showing by 30 seconds to avoid interrupting the user + setTimeout(() => setShowInstall(true), 30000); }; const handleAppInstalled = () => { @@ -155,7 +172,10 @@ export function PWAInstallPrompt() {