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)
This commit is contained in:
1 parent
0c7a69ffe0
commit
13fce9d906
3 files changed
+32
-14
No files matched your search
@@ -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 { 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 { apiGet } from '@/lib/api';
|
||||||
import type { School as SchoolType } 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 KeyboardShortcutsDialog from '@/components/keyboard-shortcuts-dialog';
|
||||||
import {
|
import {
|
||||||
CommandDialog,
|
CommandDialog,
|
||||||
@@ -755,10 +755,11 @@ export default function AppLayout() {
|
|||||||
setCurrentView('classes');
|
setCurrentView('classes');
|
||||||
}, [setCurrentClass, setCurrentView]);
|
}, [setCurrentClass, setCurrentView]);
|
||||||
|
|
||||||
// Check onboarding status on mount
|
// Check onboarding status on mount - respect "don't show again" preference
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOnboardingCompleted()) {
|
if (!isOnboardingCompleted() && !isDontShowAgain()) {
|
||||||
setOnboardingOpen(true);
|
const timer = setTimeout(() => setOnboardingOpen(true), 1000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,27 @@ export function PWAInstallPrompt() {
|
|||||||
return;
|
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) => {
|
const handleBeforeInstall = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
||||||
setShowInstall(true);
|
// Delay showing by 30 seconds to avoid interrupting the user
|
||||||
|
setTimeout(() => setShowInstall(true), 30000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAppInstalled = () => {
|
const handleAppInstalled = () => {
|
||||||
@@ -155,7 +172,10 @@ export function PWAInstallPrompt() {
|
|||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setShowInstall(false)}
|
onClick={() => {
|
||||||
|
setShowInstall(false);
|
||||||
|
try { localStorage.setItem('pwa_prompt_dismissed', String(Date.now())); } catch {}
|
||||||
|
}}
|
||||||
className="h-8 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
className="h-8 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
>
|
>
|
||||||
{t('action.cancel')}
|
{t('action.cancel')}
|
||||||
|
|||||||
@@ -73,26 +73,23 @@ function connectSocket(userId: string, userName: string, schoolId: string, role:
|
|||||||
const socket = io('/?XTransformPort=3003', {
|
const socket = io('/?XTransformPort=3003', {
|
||||||
transports: ['websocket', 'polling'],
|
transports: ['websocket', 'polling'],
|
||||||
reconnection: true,
|
reconnection: true,
|
||||||
reconnectionDelay: 1000,
|
reconnectionDelay: 5000,
|
||||||
reconnectionAttempts: 10,
|
reconnectionAttempts: 3,
|
||||||
timeout: 10000,
|
timeout: 15000,
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect', () => {
|
socket.on('connect', () => {
|
||||||
console.log('[CT-WS] Connected to WebSocket service');
|
|
||||||
socketConnected = true;
|
socketConnected = true;
|
||||||
// Authenticate
|
|
||||||
socket.emit('auth', { userId, userName, schoolId, role });
|
socket.emit('auth', { userId, userName, schoolId, role });
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log('[CT-WS] Disconnected from WebSocket service');
|
|
||||||
socketConnected = false;
|
socketConnected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connect_error', (err) => {
|
socket.on('connect_error', () => {
|
||||||
console.warn('[CT-WS] Connection error:', err.message);
|
|
||||||
socketConnected = false;
|
socketConnected = false;
|
||||||
|
// Silently fail - WebSocket is optional (real-time collaboration only)
|
||||||
});
|
});
|
||||||
|
|
||||||
socketInstance = socket;
|
socketInstance = socket;
|
||||||
|
|||||||
Reference in new issue
Block a user