Keep admin tabs in one row
This commit is contained in:
11
src/App.vue
11
src/App.vue
@@ -381,14 +381,14 @@ const pwaInstallCopy = computed(() => {
|
||||
? 'Install the app when prompted, then continue signup inside L484.'
|
||||
: 'Open Chrome menu, choose Install app or Add to Home screen, then launch L484 from the app icon.'
|
||||
return deferredInstallPrompt.value
|
||||
? 'Install L484 as a desktop app when prompted, then continue signup.'
|
||||
: 'Use your browser menu or address bar install icon to install L484, then open the installed app.'
|
||||
? 'Use the browser install prompt to install L484, then continue signup.'
|
||||
: 'Install L484 using the browser install icon in the address bar. The install prompt appears here when the browser makes it available.'
|
||||
})
|
||||
const pwaInstallPrimaryLabel = computed(() => {
|
||||
if (isPwaStandalone.value) return 'Continue signup'
|
||||
if (deferredInstallPrompt.value) return 'Install app'
|
||||
if (installPlatform.value === 'ios') return 'I installed it'
|
||||
return 'Continue after install'
|
||||
return 'Install app'
|
||||
})
|
||||
const membershipBtcAmount = computed(() =>
|
||||
bitcoinUsdPrice.value ? MEMBERSHIP_MONTHLY_USD / bitcoinUsdPrice.value : 0,
|
||||
@@ -774,6 +774,11 @@ const handlePwaInstallPrimary = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (installPlatform.value === 'desktop') {
|
||||
formError.value = 'The browser install prompt is not available yet. Use the install icon in the address bar, or refresh once and press Install app again.'
|
||||
return
|
||||
}
|
||||
|
||||
formError.value = installPlatform.value === 'ios'
|
||||
? 'Install L484 from Safari using Share, Add to Home Screen, then reopen it from the app icon.'
|
||||
: 'Install L484 from your browser menu or address bar, then reopen the installed app to continue.'
|
||||
|
||||
@@ -2,14 +2,14 @@ import { ref } from 'vue'
|
||||
|
||||
const permission = ref(typeof Notification === 'undefined' ? 'unsupported' : Notification.permission)
|
||||
|
||||
const urlBase64ToUint8Array = (base64String) => {
|
||||
const urlBase64ToArrayBuffer = (base64String) => {
|
||||
const clean = String(base64String || '').trim().replace(/^["']|["']$/g, '')
|
||||
const padding = '='.repeat((4 - (clean.length % 4)) % 4)
|
||||
const base64 = (clean + padding).replace(/-/g, '+').replace(/_/g, '/')
|
||||
const rawData = window.atob(base64)
|
||||
const key = new Uint8Array([...rawData].map((char) => char.charCodeAt(0)))
|
||||
if (key.length !== 65 || key[0] !== 4) throw new Error('VAPID public key is not valid.')
|
||||
return key
|
||||
return key.buffer
|
||||
}
|
||||
|
||||
export const notificationPermission = permission
|
||||
@@ -34,13 +34,16 @@ const subscribeWithRetry = async (registration, applicationServerKey) => {
|
||||
try {
|
||||
return await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey })
|
||||
} catch (error) {
|
||||
await registration.update().catch(() => {})
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 350))
|
||||
await registration.unregister().catch(() => {})
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 500))
|
||||
const freshRegistration = await navigator.serviceWorker.register('/sw.js', { scope: '/' })
|
||||
const readyRegistration = await navigator.serviceWorker.ready
|
||||
const activeRegistration = readyRegistration || freshRegistration
|
||||
try {
|
||||
return await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey })
|
||||
return await activeRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey })
|
||||
} catch {
|
||||
const message = error instanceof Error ? error.message : 'Push service error.'
|
||||
throw new Error(`Push registration failed: ${message}`)
|
||||
throw new Error(`Push registration failed: ${message}. Close and reopen the installed app, then try again.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,14 +57,14 @@ export const subscribeToNotifications = async () => {
|
||||
const keyData = await keyResponse.json().catch(() => ({}))
|
||||
if (!keyResponse.ok || !keyData.publicKey) throw new Error('VAPID public key is not configured.')
|
||||
if (!keyData.configured) throw new Error('VAPID private key is not configured on the server.')
|
||||
const applicationServerKey = urlBase64ToUint8Array(keyData.publicKey)
|
||||
const applicationServerKey = urlBase64ToArrayBuffer(keyData.publicKey)
|
||||
|
||||
const requested = await Notification.requestPermission()
|
||||
permission.value = requested
|
||||
if (requested !== 'granted') throw new Error('Notification permission was not granted.')
|
||||
|
||||
const registration = await navigator.serviceWorker.register('/sw.js')
|
||||
await navigator.serviceWorker.ready
|
||||
await navigator.serviceWorker.register('/sw.js', { scope: '/' })
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
const existing = await registration.pushManager.getSubscription()
|
||||
if (existing) return saveSubscription(existing)
|
||||
|
||||
|
||||
@@ -1476,7 +1476,7 @@ body.menu-open {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
gap: clamp(0.45rem, 1.2vw, 1rem);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(5, 5, 5, 0.88);
|
||||
padding: 0.65rem var(--admin-page-x);
|
||||
@@ -1490,19 +1490,19 @@ body.menu-open {
|
||||
.admin-profile-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
gap: clamp(0.45rem, 0.8vw, 0.75rem);
|
||||
border: 1px solid rgba(255, 255, 255, 0.13);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.055);
|
||||
padding: 0.55rem 0.7rem;
|
||||
padding: 0.55rem clamp(0.5rem, 0.8vw, 0.7rem);
|
||||
color: white;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.admin-avatar {
|
||||
display: grid;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
width: clamp(1.9rem, 2.4vw, 2.25rem);
|
||||
height: clamp(1.9rem, 2.4vw, 2.25rem);
|
||||
place-items: center;
|
||||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||||
border-radius: 999px;
|
||||
@@ -1517,14 +1517,14 @@ body.menu-open {
|
||||
}
|
||||
|
||||
.admin-profile-text strong {
|
||||
font-size: 0.82rem;
|
||||
font-size: clamp(0.68rem, 0.9vw, 0.82rem);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.admin-profile-text small {
|
||||
color: rgba(255, 255, 255, 0.48);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 0.68rem;
|
||||
font-size: clamp(0.58rem, 0.75vw, 0.68rem);
|
||||
}
|
||||
|
||||
.admin-profile-menu {
|
||||
@@ -1586,8 +1586,9 @@ body.menu-open {
|
||||
|
||||
.admin-tabbar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
gap: 0.5rem;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
gap: clamp(0.22rem, 0.7vw, 0.5rem);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -1616,10 +1617,10 @@ body.menu-open {
|
||||
}
|
||||
|
||||
.desktop-admin-tabbar .admin-tab {
|
||||
gap: 0.42rem;
|
||||
padding: 0.64rem 0.5rem;
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.1em;
|
||||
gap: clamp(0.22rem, 0.5vw, 0.42rem);
|
||||
padding: 0.62rem clamp(0.28rem, 0.7vw, 0.5rem);
|
||||
font-size: clamp(0.5rem, 0.75vw, 0.62rem);
|
||||
letter-spacing: clamp(0.055em, 0.15vw, 0.1em);
|
||||
}
|
||||
|
||||
.admin-tab svg {
|
||||
|
||||
Reference in New Issue
Block a user