added button
This commit is contained in:
31
public/sw.js
Normal file
31
public/sw.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const CACHE = 'gj-v1'
|
||||
|
||||
self.addEventListener('install', (e) => {
|
||||
e.waitUntil(
|
||||
caches.open(CACHE).then((cache) =>
|
||||
cache.addAll(['/', '/manifest.json', '/icon.svg'])
|
||||
)
|
||||
)
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (e) => {
|
||||
e.waitUntil(
|
||||
caches.keys().then((keys) =>
|
||||
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
||||
)
|
||||
)
|
||||
self.clients.claim()
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', (e) => {
|
||||
// Only cache GET requests for same-origin non-API resources
|
||||
if (
|
||||
e.request.method !== 'GET' ||
|
||||
e.request.url.includes('/api/')
|
||||
) return
|
||||
|
||||
e.respondWith(
|
||||
caches.match(e.request).then((cached) => cached || fetch(e.request))
|
||||
)
|
||||
})
|
||||
@@ -3,6 +3,12 @@ import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
})
|
||||
}
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function SettingsPage() {
|
||||
const navigate = useNavigate()
|
||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||
const { canInstall, isIOS, isInstalled, triggerInstall } = usePWAInstall()
|
||||
const [showIOSModal, setShowIOSModal] = useState(false)
|
||||
const [installModal, setInstallModal] = useState<'ios' | 'chrome' | null>(null)
|
||||
|
||||
// Edit profile modal state
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
@@ -286,14 +286,21 @@ export default function SettingsPage() {
|
||||
</section>
|
||||
|
||||
{/* App */}
|
||||
{!isInstalled && (isIOS || canInstall) && (
|
||||
<section className="settings-section">
|
||||
<h3 className="settings-section-title">APP</h3>
|
||||
<div className="settings-card">
|
||||
<button
|
||||
type="button"
|
||||
className="settings-item settings-item-button"
|
||||
onClick={() => isIOS ? setShowIOSModal(true) : triggerInstall()}
|
||||
onClick={() => {
|
||||
if (isIOS) {
|
||||
setInstallModal('ios')
|
||||
} else if (canInstall) {
|
||||
triggerInstall()
|
||||
} else {
|
||||
setInstallModal('chrome')
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="settings-item-icon settings-item-icon-purple">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
@@ -311,7 +318,6 @@ export default function SettingsPage() {
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Data & Look */}
|
||||
<section className="settings-section">
|
||||
@@ -526,12 +532,15 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* iOS "Add to Home Screen" instructions modal */}
|
||||
{showIOSModal && (
|
||||
<div className="confirm-modal-overlay" onClick={() => setShowIOSModal(false)}>
|
||||
{/* Add to Home Screen instructions modal */}
|
||||
{installModal && (
|
||||
<div className="confirm-modal-overlay" onClick={() => setInstallModal(null)}>
|
||||
<div className="confirm-modal ios-install-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="ios-install-icon">🌱</div>
|
||||
<h3 className="confirm-modal-title">Add to Home Screen</h3>
|
||||
|
||||
{installModal === 'ios' ? (
|
||||
<>
|
||||
<p className="ios-install-subtitle">Follow these steps in Safari:</p>
|
||||
<ol className="ios-install-steps">
|
||||
<li>
|
||||
@@ -563,11 +572,45 @@ export default function SettingsPage() {
|
||||
Tap <strong>Add</strong> to confirm
|
||||
</li>
|
||||
</ol>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="ios-install-subtitle">Follow these steps in Chrome:</p>
|
||||
<ol className="ios-install-steps">
|
||||
<li>
|
||||
<span className="ios-install-step-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" />
|
||||
</svg>
|
||||
</span>
|
||||
Tap the <strong>⋮ menu</strong> in the top-right corner
|
||||
</li>
|
||||
<li>
|
||||
<span className="ios-install-step-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
|
||||
<line x1="12" y1="18" x2="12.01" y2="18" />
|
||||
</svg>
|
||||
</span>
|
||||
Tap <strong>Add to Home screen</strong>
|
||||
</li>
|
||||
<li>
|
||||
<span className="ios-install-step-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
</span>
|
||||
Tap <strong>Add</strong> to confirm
|
||||
</li>
|
||||
</ol>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="edit-modal-save"
|
||||
style={{ width: '100%', marginTop: '1rem' }}
|
||||
onClick={() => setShowIOSModal(false)}
|
||||
onClick={() => setInstallModal(null)}
|
||||
>
|
||||
Got it
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user