mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
feat(ui): themed startup splash with deferred window show (#1030)
* feat(ui): add themed startup splash with deferred window show Show a theme-aware loading splash before the Vite bundle mounts, hide the native window until it paints, and use per-theme logo gradient colors. * docs: note startup splash in CHANGELOG and credits for PR #1030 * docs: attribute PR #1030 startup splash to cucadmuh
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { installQueueUndoHotkey } from '../store/queueUndoHotkey';
|
||||
import { configureStartupSplash } from './startupSplash';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getWindowKind } from './windowKind';
|
||||
import { migrateThemeSelection } from '../utils/themes/themeMigration';
|
||||
@@ -112,6 +113,7 @@ export function runPreReactBootstrap(): void {
|
||||
migrateThemeSelection();
|
||||
// Paint the correct theme on the very first frame (no Mocha flash).
|
||||
applyThemeAtStartup();
|
||||
configureStartupSplash();
|
||||
installCrossWindowThemeSync();
|
||||
markDevBuildDocument();
|
||||
pushUserAgentToBackend();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
STARTUP_SPLASH_ID,
|
||||
configureStartupSplash,
|
||||
dismissStartupSplash,
|
||||
} from './startupSplash';
|
||||
|
||||
vi.mock('./windowKind', () => ({
|
||||
getWindowKind: vi.fn(() => 'main'),
|
||||
}));
|
||||
|
||||
vi.mock('../utils/themes/startupThemeAppearance', () => ({
|
||||
applyStartupSplashThemeFromStorage: vi.fn(() => 'mocha'),
|
||||
}));
|
||||
|
||||
vi.mock('@tauri-apps/api/webviewWindow', () => ({
|
||||
getCurrentWebviewWindow: vi.fn(() => ({ show: vi.fn(() => Promise.resolve()) })),
|
||||
}));
|
||||
|
||||
import { getWindowKind } from './windowKind';
|
||||
import { applyStartupSplashThemeFromStorage } from '../utils/themes/startupThemeAppearance';
|
||||
|
||||
describe('startupSplash', () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `<div id="${STARTUP_SPLASH_ID}"></div><div id="root"></div>`;
|
||||
vi.mocked(getWindowKind).mockReturnValue('main');
|
||||
vi.mocked(applyStartupSplashThemeFromStorage).mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('removes splash on mini player webview', () => {
|
||||
vi.mocked(getWindowKind).mockReturnValue('mini');
|
||||
configureStartupSplash();
|
||||
expect(document.getElementById(STARTUP_SPLASH_ID)).toBeNull();
|
||||
});
|
||||
|
||||
it('re-applies theme from storage on main window', () => {
|
||||
configureStartupSplash();
|
||||
expect(applyStartupSplashThemeFromStorage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fades out and removes splash', () => {
|
||||
vi.useFakeTimers();
|
||||
dismissStartupSplash();
|
||||
expect(document.getElementById(STARTUP_SPLASH_ID)?.classList.contains('app-startup-splash--hide')).toBe(true);
|
||||
vi.runAllTimers();
|
||||
expect(document.getElementById(STARTUP_SPLASH_ID)).toBeNull();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { applyStartupSplashThemeFromStorage } from '../utils/themes/startupThemeAppearance';
|
||||
import { getWindowKind } from './windowKind';
|
||||
|
||||
export const STARTUP_SPLASH_ID = 'app-startup-splash';
|
||||
|
||||
/** Ensure the native shell is visible once the webview bundle is alive. */
|
||||
export function revealStartupWindow(): void {
|
||||
if (getWindowKind() === 'mini') return;
|
||||
void getCurrentWebviewWindow().show().catch(() => {});
|
||||
}
|
||||
|
||||
/** Re-apply splash colors after bootstrap theme migration/injection. */
|
||||
export function configureStartupSplash(): void {
|
||||
const splash = document.getElementById(STARTUP_SPLASH_ID);
|
||||
if (!splash) return;
|
||||
|
||||
if (getWindowKind() === 'mini') {
|
||||
splash.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
applyStartupSplashThemeFromStorage();
|
||||
revealStartupWindow();
|
||||
}
|
||||
|
||||
/** Fade out the splash after the first React commit. */
|
||||
export function dismissStartupSplash(): void {
|
||||
const splash = document.getElementById(STARTUP_SPLASH_ID);
|
||||
if (!splash || splash.classList.contains('app-startup-splash--hide')) return;
|
||||
|
||||
splash.classList.add('app-startup-splash--hide');
|
||||
const remove = () => splash.remove();
|
||||
splash.addEventListener('transitionend', remove, { once: true });
|
||||
window.setTimeout(remove, 500);
|
||||
}
|
||||
|
||||
/** Schedule dismiss on the frame after the first paint. */
|
||||
export function scheduleStartupSplashDismiss(): void {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(dismissStartupSplash);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user