Compare commits
1 Commits
b6f3f5c0aa
...
c264de53b5
| Author | SHA1 | Date | |
|---|---|---|---|
| c264de53b5 |
@@ -7,6 +7,7 @@ export const PortalLayer = style({
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
inset: 0,
|
inset: 0,
|
||||||
zIndex: config.zIndex.Max,
|
zIndex: config.zIndex.Max,
|
||||||
|
pointerEvents: 'auto',
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Dialog + overlay padding must never exceed the viewport height. */
|
/** Dialog + overlay padding must never exceed the viewport height. */
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import FocusTrap from 'focus-trap-react';
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
config,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
IconButton,
|
IconButton,
|
||||||
Overlay,
|
Overlay,
|
||||||
@@ -48,6 +47,9 @@ export function UpdatesDialog({
|
|||||||
setShowOlderList(false);
|
setShowOlderList(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const portalTarget =
|
||||||
|
document.getElementById('portalContainer') ?? document.body;
|
||||||
|
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<div className={css.PortalLayer}>
|
<div className={css.PortalLayer}>
|
||||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||||
@@ -55,8 +57,7 @@ export function UpdatesDialog({
|
|||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
onDeactivate: onClose,
|
clickOutsideDeactivates: false,
|
||||||
clickOutsideDeactivates: true,
|
|
||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -181,6 +182,6 @@ export function UpdatesDialog({
|
|||||||
</OverlayCenter>
|
</OverlayCenter>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
portalTarget
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ type UpdaterInfo = {
|
|||||||
export function useOpenReleaseNotesDialog(): () => void {
|
export function useOpenReleaseNotesDialog(): () => void {
|
||||||
const setDialogState = useSetAtom(releaseNotesDialogAtom);
|
const setDialogState = useSetAtom(releaseNotesDialogAtom);
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
setDialogState({ open: true, manual: true });
|
// Defer so the opening tap does not trip FocusTrap click-outside on Android.
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
setDialogState({ open: true, manual: true });
|
||||||
|
});
|
||||||
}, [setDialogState]);
|
}, [setDialogState]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
|
import bundledManifest from '../../../public/update/manifest.json';
|
||||||
import { trimTrailingSlash } from '../utils/common';
|
import { trimTrailingSlash } from '../utils/common';
|
||||||
|
|
||||||
|
const bundledMarkdownByFile = import.meta.glob('../../../public/update/*.md', {
|
||||||
|
query: '?raw',
|
||||||
|
import: 'default',
|
||||||
|
eager: true,
|
||||||
|
}) as Record<string, string>;
|
||||||
|
|
||||||
function getUpdateBaseUrl(): string {
|
function getUpdateBaseUrl(): string {
|
||||||
const basePath = trimTrailingSlash(import.meta.env.BASE_URL || './');
|
const basePath = trimTrailingSlash(import.meta.env.BASE_URL || './');
|
||||||
const relative =
|
const relative =
|
||||||
@@ -64,9 +71,26 @@ function parseUpdateMarkdown(file: string, source: string): ParsedUpdateDoc {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadBundledManifest(): UpdateManifest {
|
||||||
|
const manifest = bundledManifest as UpdateManifest;
|
||||||
|
return {
|
||||||
|
current: manifest.current,
|
||||||
|
older: (manifest.older ?? []).filter((entry) => entry?.file),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadBundledDocument(file: string): ParsedUpdateDoc | null {
|
||||||
|
const safeFile = file.replace(/^\/+/, '');
|
||||||
|
const entry = Object.entries(bundledMarkdownByFile).find(([path]) =>
|
||||||
|
path.endsWith(`/${safeFile}`)
|
||||||
|
);
|
||||||
|
if (!entry) return null;
|
||||||
|
return parseUpdateMarkdown(safeFile, entry[1]);
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveUpdateAssetUrl(src: string | undefined): string | undefined {
|
export function resolveUpdateAssetUrl(src: string | undefined): string | undefined {
|
||||||
if (!src) return undefined;
|
if (!src) return undefined;
|
||||||
if (/^(https?:|data:|blob:)/i.test(src)) return src;
|
if (/^(https?:|data:|blob:|capacitor:)/i.test(src)) return src;
|
||||||
if (src.startsWith('/')) return src;
|
if (src.startsWith('/')) return src;
|
||||||
|
|
||||||
const normalized = src.replace(/^\.\//, '');
|
const normalized = src.replace(/^\.\//, '');
|
||||||
@@ -76,15 +100,21 @@ export function resolveUpdateAssetUrl(src: string | undefined): string | undefin
|
|||||||
export async function loadUpdateManifest(): Promise<UpdateManifest | null> {
|
export async function loadUpdateManifest(): Promise<UpdateManifest | null> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(getUpdateFileUrl('manifest.json'), { cache: 'no-cache' });
|
const response = await fetch(getUpdateFileUrl('manifest.json'), { cache: 'no-cache' });
|
||||||
if (!response.ok) return null;
|
if (response.ok) {
|
||||||
|
const data = (await response.json()) as UpdateManifest;
|
||||||
|
if (data?.current && Array.isArray(data.older)) {
|
||||||
|
return {
|
||||||
|
current: data.current,
|
||||||
|
older: data.older.filter((entry) => entry?.file),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through to bundled copy (Capacitor / offline)
|
||||||
|
}
|
||||||
|
|
||||||
const data = (await response.json()) as UpdateManifest;
|
try {
|
||||||
if (!data?.current || !Array.isArray(data.older)) return null;
|
return loadBundledManifest();
|
||||||
|
|
||||||
return {
|
|
||||||
current: data.current,
|
|
||||||
older: data.older.filter((entry) => entry?.file),
|
|
||||||
};
|
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -96,13 +126,15 @@ export async function loadUpdateDocument(file: string): Promise<ParsedUpdateDoc
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(getUpdateFileUrl(safeFile), { cache: 'no-cache' });
|
const response = await fetch(getUpdateFileUrl(safeFile), { cache: 'no-cache' });
|
||||||
if (!response.ok) return null;
|
if (response.ok) {
|
||||||
|
const source = await response.text();
|
||||||
const source = await response.text();
|
return parseUpdateMarkdown(safeFile, source);
|
||||||
return parseUpdateMarkdown(safeFile, source);
|
}
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
// fall through to bundled copy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return loadBundledDocument(safeFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadCurrentUpdateDocument(): Promise<ParsedUpdateDoc | null> {
|
export async function loadCurrentUpdateDocument(): Promise<ParsedUpdateDoc | null> {
|
||||||
|
|||||||
Reference in New Issue
Block a user