fix(client): fix PWA manifest so "Install as App" works properly (#2192)

This commit is contained in:
SMPTY
2026-09-05 03:15:08 -04:00
committed by GitHub
parent 9ec12f4927
commit dd718230ad
13 changed files with 35 additions and 34 deletions
+4 -2
View File
@@ -7,13 +7,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#101010" />
<meta name="ontime" content="ontime - time keeping for live events" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Ontime" />
<link rel="apple-touch-icon" href="ontime-logo.png" />
<link rel="icon" type="image/png" href="ontime-logo.png" />
<link rel="manifest" href="site.webmanifest" />
<link rel="manifest" href="manifest.json" />
<meta name="robots" content="noindex" />
<title>ontime</title>
<title>Ontime</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
+9 -7
View File
@@ -1,19 +1,21 @@
{
"name": "ontime",
"short_name": "ontime",
"name": "Ontime",
"short_name": "Ontime",
"icons": [
{
"src": "favicon.ico",
"type": "image/x-icon"
"src": "ontime-logo-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "ontime-logo.png",
"src": "ontime-logo-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"scope": "./",
"start_url": "./",
"display": "",
"theme_color": "#121212",
"display": "standalone",
"theme_color": "#101010",
"background_color": "#101010"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

-8
View File
@@ -1,8 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [{ "src": "ontime-logo.png", "sizes": "295x295", "type": "image/png" }],
"theme_color": "#121212",
"background_color": "#101010",
"display": "standalone"
}
@@ -6,6 +6,6 @@ import { useEffect } from 'react';
*/
export function useWindowTitle(title: string) {
useEffect(() => {
document.title = `ontime - ${title}`;
document.title = `Ontime - ${title}`;
}, []);
}
@@ -40,7 +40,7 @@ export default function ShutdownPanel() {
{!isOntimeCloud && (
<Panel.Section>
<Button variant='destructive' onClick={handler.open} disabled={!canShutdown}>
Shutdown ontime
Shutdown Ontime
</Button>
{!canShutdown && <Panel.Description>Only available from the machine running Ontime.</Panel.Description>}
</Panel.Section>
+2 -2
View File
@@ -6,7 +6,7 @@
http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'unsafe-inline'; script-src 'self'"
/>
<title>ontime</title>
<title>Ontime</title>
<style>
body {
-webkit-user-select: none;
@@ -92,7 +92,7 @@
<body>
<div class="container">
<img src="../assets/logo.png" />
<h1>ontime · event timers</h1>
<h1>Ontime · event timers</h1>
<div class="lds-ellipsis">
<div></div>
<div></div>
+1 -1
View File
@@ -16,7 +16,7 @@
body {
width: 100%;
height: 100%;
background: #121212;
background: #101010;
color: #ffffff;
font-family: sans-serif;
overflow: hidden;
@@ -29,15 +29,19 @@ function makeHeadersWithFailingAuthorization(cookie?: string) {
describe('isPublicAssetRequest()', () => {
it('allows root public assets without a prefix', () => {
expect(isPublicAssetRequest('/site.webmanifest', '')).toBe(true);
expect(isPublicAssetRequest('/manifest.json', '')).toBe(true);
});
it('allows prefixed public assets in cloud deployments', () => {
expect(isPublicAssetRequest('/stage-hash/site.webmanifest', '/stage-hash')).toBe(true);
expect(isPublicAssetRequest('/stage-hash/manifest.json', '/stage-hash')).toBe(true);
expect(isPublicAssetRequest('/stage-hash/ontime-logo.png?cache=1', '/stage-hash')).toBe(true);
});
it('allows the PWA install icons', () => {
expect(isPublicAssetRequest('/ontime-logo-192.png', '')).toBe(true);
expect(isPublicAssetRequest('/ontime-logo-512.png', '')).toBe(true);
});
it('keeps non-public paths protected', () => {
expect(isPublicAssetRequest('/stage-hash/data', '/stage-hash')).toBe(false);
expect(isPublicAssetRequest('/backstage', '')).toBe(false);
+2 -1
View File
@@ -17,8 +17,9 @@ const publicAssets = new Set([
'/favicon.ico',
'/manifest.json',
'/ontime-logo.png',
'/ontime-logo-192.png',
'/ontime-logo-512.png',
'/robots.txt',
'/site.webmanifest',
]);
export function isPublicAssetRequest(originalUrl: string, prefix: string): boolean {
+7 -7
View File
@@ -6,7 +6,7 @@ test.describe('pages routes are available', () => {
test('editor', async ({ page }) => {
await page.goto('/editor');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
await expect(page.getByTestId('editor-container')).toBeVisible();
await expect(page.getByTestId('panel-rundown')).toBeVisible();
await expect(page.getByTestId('panel-timer-control')).toBeVisible();
@@ -16,38 +16,38 @@ test.describe('pages routes are available', () => {
test('cuesheet', async ({ page }) => {
await page.goto('/cuesheet');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
await expect(page.getByTestId('cuesheet')).toBeVisible();
});
test('operator', async ({ page }) => {
await page.goto('/op');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
});
test('timer', async ({ page }) => {
await page.goto('/timer');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
});
test('backstage', async ({ page }) => {
await page.goto('/backstage');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
});
test('studio', async ({ page }) => {
await page.goto('/studio');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
});
test('countdown', async ({ page }) => {
await page.goto('/countdown?sub=32d31');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
await expect(page.getByText('Albania')).toBeVisible();
await expect(page.getByText('Latvia')).toBeHidden();
+2 -2
View File
@@ -92,12 +92,12 @@ test.describe('test view navigation feature', () => {
test('not-found', async ({ page }) => {
await page.goto('/not-found');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
await expect(page.getByRole('heading', { name: 'Not found' })).toBeVisible();
await page.goto('/preset/not-found');
await expect(page).toHaveTitle(/ontime/);
await expect(page).toHaveTitle(/ontime/i);
await expect(page.getByRole('heading', { name: 'Not found' })).toBeVisible();
});
});