feat: PWA install prompt, brand-colored channel icons, image load fix, semantic connection indicator
- Fix image display bug: remove loading='lazy' + use opacity instead of hidden (#66) - PWA: add id/scope to manifest, fix SW cache name, add install prompt hook + button (#64) - Colorize channel icons with brand colors (Discord blurple, Telegram blue, etc.) (#68) - Use semantic green for connection indicator instead of theme accent (#70) - Replace Sparkles icon with PinchChat logo in sidebar (#65) - Replace decorative dots in sidebar footer with GitHub link (#71)
This commit is contained in:
52
src/hooks/usePwaInstall.ts
Normal file
52
src/hooks/usePwaInstall.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
|
||||
interface BeforeInstallPromptEvent extends Event {
|
||||
prompt(): Promise<void>;
|
||||
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
|
||||
}
|
||||
|
||||
// Check standalone mode outside of React to avoid setState-in-effect
|
||||
const isStandalone = typeof window !== 'undefined' && window.matchMedia('(display-mode: standalone)').matches;
|
||||
|
||||
export function usePwaInstall() {
|
||||
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
|
||||
const [isInstalled, setIsInstalled] = useState(isStandalone);
|
||||
const installedRef = useRef(isStandalone);
|
||||
|
||||
useEffect(() => {
|
||||
if (installedRef.current) return;
|
||||
|
||||
const handler = (e: Event) => {
|
||||
e.preventDefault();
|
||||
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
||||
};
|
||||
|
||||
const installedHandler = () => {
|
||||
installedRef.current = true;
|
||||
setIsInstalled(true);
|
||||
setDeferredPrompt(null);
|
||||
};
|
||||
|
||||
window.addEventListener('beforeinstallprompt', handler);
|
||||
window.addEventListener('appinstalled', installedHandler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeinstallprompt', handler);
|
||||
window.removeEventListener('appinstalled', installedHandler);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const install = useCallback(async () => {
|
||||
if (!deferredPrompt) return false;
|
||||
await deferredPrompt.prompt();
|
||||
const { outcome } = await deferredPrompt.userChoice;
|
||||
setDeferredPrompt(null);
|
||||
return outcome === 'accepted';
|
||||
}, [deferredPrompt]);
|
||||
|
||||
return {
|
||||
canInstall: !!deferredPrompt && !isInstalled,
|
||||
isInstalled,
|
||||
install,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user