Fix updates dialog on Android by portaling above Settings and resolving Capacitor asset URLs.
All checks were successful
Trigger cinny-mobile / dispatch (push) Successful in 2s
All checks were successful
Trigger cinny-mobile / dispatch (push) Successful in 2s
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"build": "vite build",
|
"build": "node ../scripts/generate-update-manifest.mjs && vite build",
|
||||||
"lint": "yarn check:eslint && yarn check:prettier",
|
"lint": "yarn check:eslint && yarn check:prettier",
|
||||||
"check:eslint": "eslint src/*",
|
"check:eslint": "eslint src/*",
|
||||||
"check:prettier": "prettier --check .",
|
"check:prettier": "prettier --check .",
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ import { color, config, toRem } from 'folds';
|
|||||||
|
|
||||||
const MOBILE_BREAKPOINT = '480px';
|
const MOBILE_BREAKPOINT = '480px';
|
||||||
|
|
||||||
/** Dialog height cap — 85% of the viewport, minus safe areas. */
|
export const PortalLayer = style({
|
||||||
|
position: 'fixed',
|
||||||
|
inset: 0,
|
||||||
|
zIndex: config.zIndex.Max,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Dialog + overlay padding must never exceed the viewport height. */
|
||||||
const DIALOG_MAX_HEIGHT =
|
const DIALOG_MAX_HEIGHT =
|
||||||
'calc(85vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px))';
|
'calc(85vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px))';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
config,
|
||||||
Dialog,
|
Dialog,
|
||||||
IconButton,
|
IconButton,
|
||||||
Overlay,
|
Overlay,
|
||||||
@@ -46,7 +48,8 @@ export function UpdatesDialog({
|
|||||||
setShowOlderList(false);
|
setShowOlderList(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return createPortal(
|
||||||
|
<div className={css.PortalLayer}>
|
||||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||||
<OverlayCenter className={css.OverlayFrame}>
|
<OverlayCenter className={css.OverlayFrame}>
|
||||||
<FocusTrap
|
<FocusTrap
|
||||||
@@ -177,5 +180,7 @@ export function UpdatesDialog({
|
|||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
</OverlayCenter>
|
</OverlayCenter>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { trimTrailingSlash } from '../utils/common';
|
import { trimTrailingSlash } from '../utils/common';
|
||||||
|
|
||||||
const UPDATE_BASE_PATH = `${trimTrailingSlash(import.meta.env.BASE_URL)}/update`;
|
function getUpdateBaseUrl(): string {
|
||||||
|
const basePath = trimTrailingSlash(import.meta.env.BASE_URL || './');
|
||||||
|
const relative =
|
||||||
|
basePath === '.' || basePath === '' ? 'update/' : `${basePath}/update/`;
|
||||||
|
return new URL(relative, window.location.href).href.replace(/\/$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUpdateFileUrl(file: string): string {
|
||||||
|
return new URL(file, `${getUpdateBaseUrl()}/`).href;
|
||||||
|
}
|
||||||
|
|
||||||
export type UpdateManifestEntry = {
|
export type UpdateManifestEntry = {
|
||||||
file: string;
|
file: string;
|
||||||
@@ -61,12 +70,12 @@ export function resolveUpdateAssetUrl(src: string | undefined): string | undefin
|
|||||||
if (src.startsWith('/')) return src;
|
if (src.startsWith('/')) return src;
|
||||||
|
|
||||||
const normalized = src.replace(/^\.\//, '');
|
const normalized = src.replace(/^\.\//, '');
|
||||||
return `${UPDATE_BASE_PATH}/${normalized}`;
|
return new URL(normalized, `${getUpdateBaseUrl()}/`).href;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadUpdateManifest(): Promise<UpdateManifest | null> {
|
export async function loadUpdateManifest(): Promise<UpdateManifest | null> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${UPDATE_BASE_PATH}/manifest.json`, { cache: 'no-cache' });
|
const response = await fetch(getUpdateFileUrl('manifest.json'), { cache: 'no-cache' });
|
||||||
if (!response.ok) return null;
|
if (!response.ok) return null;
|
||||||
|
|
||||||
const data = (await response.json()) as UpdateManifest;
|
const data = (await response.json()) as UpdateManifest;
|
||||||
@@ -86,7 +95,7 @@ export async function loadUpdateDocument(file: string): Promise<ParsedUpdateDoc
|
|||||||
if (!safeFile || safeFile.includes('..')) return null;
|
if (!safeFile || safeFile.includes('..')) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${UPDATE_BASE_PATH}/${safeFile}`, { cache: 'no-cache' });
|
const response = await fetch(getUpdateFileUrl(safeFile), { cache: 'no-cache' });
|
||||||
if (!response.ok) return null;
|
if (!response.ok) return null;
|
||||||
|
|
||||||
const source = await response.text();
|
const source = await response.text();
|
||||||
|
|||||||
@@ -699,10 +699,10 @@ export function ClientNonUIFeatures({ children }: ClientNonUIFeaturesProps) {
|
|||||||
<MessageNotifications />
|
<MessageNotifications />
|
||||||
<BackgroundSyncSetup />
|
<BackgroundSyncSetup />
|
||||||
<PaarrotAPIInitializer />
|
<PaarrotAPIInitializer />
|
||||||
<UpdatesDialogHost />
|
|
||||||
<TaskbarFlashStopper />
|
<TaskbarFlashStopper />
|
||||||
<AndroidShareIntentHandler />
|
<AndroidShareIntentHandler />
|
||||||
{children}
|
{children}
|
||||||
|
<UpdatesDialogHost />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user