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 './index.css'
|
||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('/sw.js')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default function SettingsPage() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null)
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
const { canInstall, isIOS, isInstalled, triggerInstall } = usePWAInstall()
|
const { canInstall, isIOS, isInstalled, triggerInstall } = usePWAInstall()
|
||||||
const [showIOSModal, setShowIOSModal] = useState(false)
|
const [installModal, setInstallModal] = useState<'ios' | 'chrome' | null>(null)
|
||||||
|
|
||||||
// Edit profile modal state
|
// Edit profile modal state
|
||||||
const [showEditModal, setShowEditModal] = useState(false)
|
const [showEditModal, setShowEditModal] = useState(false)
|
||||||
@@ -286,14 +286,21 @@ export default function SettingsPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* App */}
|
{/* App */}
|
||||||
{!isInstalled && (isIOS || canInstall) && (
|
<section className="settings-section">
|
||||||
<section className="settings-section">
|
|
||||||
<h3 className="settings-section-title">APP</h3>
|
<h3 className="settings-section-title">APP</h3>
|
||||||
<div className="settings-card">
|
<div className="settings-card">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="settings-item settings-item-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">
|
<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">
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Data & Look */}
|
{/* Data & Look */}
|
||||||
<section className="settings-section">
|
<section className="settings-section">
|
||||||
@@ -526,48 +532,85 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* iOS "Add to Home Screen" instructions modal */}
|
{/* Add to Home Screen instructions modal */}
|
||||||
{showIOSModal && (
|
{installModal && (
|
||||||
<div className="confirm-modal-overlay" onClick={() => setShowIOSModal(false)}>
|
<div className="confirm-modal-overlay" onClick={() => setInstallModal(null)}>
|
||||||
<div className="confirm-modal ios-install-modal" onClick={(e) => e.stopPropagation()}>
|
<div className="confirm-modal ios-install-modal" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="ios-install-icon">🌱</div>
|
<div className="ios-install-icon">🌱</div>
|
||||||
<h3 className="confirm-modal-title">Add to Home Screen</h3>
|
<h3 className="confirm-modal-title">Add to Home Screen</h3>
|
||||||
<p className="ios-install-subtitle">Follow these steps in Safari:</p>
|
|
||||||
<ol className="ios-install-steps">
|
{installModal === 'ios' ? (
|
||||||
<li>
|
<>
|
||||||
<span className="ios-install-step-icon">
|
<p className="ios-install-subtitle">Follow these steps in Safari:</p>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<ol className="ios-install-steps">
|
||||||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
|
<li>
|
||||||
<polyline points="16 6 12 2 8 6" />
|
<span className="ios-install-step-icon">
|
||||||
<line x1="12" y1="2" x2="12" y2="15" />
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
</svg>
|
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" />
|
||||||
</span>
|
<polyline points="16 6 12 2 8 6" />
|
||||||
Tap the <strong>Share</strong> button at the bottom of Safari
|
<line x1="12" y1="2" x2="12" y2="15" />
|
||||||
</li>
|
</svg>
|
||||||
<li>
|
</span>
|
||||||
<span className="ios-install-step-icon">
|
Tap the <strong>Share</strong> button at the bottom of Safari
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
</li>
|
||||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
<li>
|
||||||
<line x1="12" y1="8" x2="12" y2="16" />
|
<span className="ios-install-step-icon">
|
||||||
<line x1="8" y1="12" x2="16" y2="12" />
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
</svg>
|
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||||
</span>
|
<line x1="12" y1="8" x2="12" y2="16" />
|
||||||
Scroll down and tap <strong>Add to Home Screen</strong>
|
<line x1="8" y1="12" x2="16" y2="12" />
|
||||||
</li>
|
</svg>
|
||||||
<li>
|
</span>
|
||||||
<span className="ios-install-step-icon">
|
Scroll down and tap <strong>Add to Home Screen</strong>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
</li>
|
||||||
<polyline points="20 6 9 17 4 12" />
|
<li>
|
||||||
</svg>
|
<span className="ios-install-step-icon">
|
||||||
</span>
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
Tap <strong>Add</strong> to confirm
|
<polyline points="20 6 9 17 4 12" />
|
||||||
</li>
|
</svg>
|
||||||
</ol>
|
</span>
|
||||||
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="edit-modal-save"
|
className="edit-modal-save"
|
||||||
style={{ width: '100%', marginTop: '1rem' }}
|
style={{ width: '100%', marginTop: '1rem' }}
|
||||||
onClick={() => setShowIOSModal(false)}
|
onClick={() => setInstallModal(null)}
|
||||||
>
|
>
|
||||||
Got it
|
Got it
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user