feat(splashscreen): enhance startup sequence and splashscreen handling
This update improves the splashscreen management during application startup. The splashscreen will now remain visible for a minimum duration and will only close after the main application is ready, ensuring a smoother user experience. - Introduced a new startup sequence to manage background tasks. - Added error handling for splashscreen closure to maintain functionality. - Optimized the timing for displaying and hiding the splashscreen.
This commit is contained in:
+13
-5
@@ -22,14 +22,22 @@ use git::{
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn close_splashscreen(app: tauri::AppHandle) {
|
fn close_splashscreen(app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
if let Some(window) = app.get_webview_window("splashscreen") {
|
||||||
|
let _ = window.hide();
|
||||||
|
window
|
||||||
|
.destroy()
|
||||||
|
.map_err(|error| format!("failed to destroy splashscreen: {error}"))?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.show();
|
window
|
||||||
|
.show()
|
||||||
|
.map_err(|error| format!("failed to show main window: {error}"))?;
|
||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
}
|
}
|
||||||
if let Some(window) = app.get_webview_window("splashscreen") {
|
|
||||||
let _ = window.close();
|
Ok(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|||||||
+94
-8
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, onMount, tick } from "svelte";
|
import { onDestroy, onMount, tick } from "svelte";
|
||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { open as openDialog } from "@tauri-apps/plugin-dialog";
|
import { open as openDialog } from "@tauri-apps/plugin-dialog";
|
||||||
import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater";
|
import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater";
|
||||||
import { AlertCircle, BookOpen, Cherry, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte";
|
import { AlertCircle, BookOpen, Cherry, Download, FolderOpen, GitBranch, GitMerge, LoaderCircle, Plus, Search, Star, X } from "@lucide/svelte";
|
||||||
@@ -268,6 +269,8 @@
|
|||||||
const BACKGROUND_FETCH_INTERVAL = 180_000;
|
const BACKGROUND_FETCH_INTERVAL = 180_000;
|
||||||
const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000;
|
const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000;
|
||||||
const BACKGROUND_FETCH_OTHER_BATCH_SIZE = 2;
|
const BACKGROUND_FETCH_OTHER_BATCH_SIZE = 2;
|
||||||
|
const STARTUP_SPLASH_MIN_VISIBLE_MS = 850;
|
||||||
|
const STARTUP_FETCH_MAX_WAIT_MS = 20_000;
|
||||||
let backgroundFetchTimer: ReturnType<typeof setInterval> | undefined;
|
let backgroundFetchTimer: ReturnType<typeof setInterval> | undefined;
|
||||||
let backgroundRepoStatusTimer: ReturnType<typeof setInterval> | undefined;
|
let backgroundRepoStatusTimer: ReturnType<typeof setInterval> | undefined;
|
||||||
let backgroundFetchInFlight = false;
|
let backgroundFetchInFlight = false;
|
||||||
@@ -346,14 +349,7 @@
|
|||||||
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
initAnalytics();
|
void runStartupSequence();
|
||||||
loadRepoLists();
|
|
||||||
autoRefreshTimer = setInterval(() => { void autoRefreshTick(); }, AUTO_REFRESH_INTERVAL);
|
|
||||||
backgroundRepoStatusTimer = setInterval(() => { void backgroundRepoStatusTick(false); }, BACKGROUND_REPO_STATUS_INTERVAL);
|
|
||||||
backgroundFetchTimer = setInterval(() => { void backgroundFetchTick(); }, BACKGROUND_FETCH_INTERVAL);
|
|
||||||
void backgroundRepoStatusTick(false);
|
|
||||||
void checkForUpdates();
|
|
||||||
void initCommitAi();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@@ -368,6 +364,96 @@
|
|||||||
if (activeFileHistoryRequestId) void cancelFileHistory(activeFileHistoryRequestId);
|
if (activeFileHistoryRequestId) void cancelFileHistory(activeFileHistoryRequestId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function wait(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForAnimationFrame(): Promise<void> {
|
||||||
|
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForStartupPaint() {
|
||||||
|
await tick();
|
||||||
|
await waitForAnimationFrame();
|
||||||
|
await waitForAnimationFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeStartupSplashscreen() {
|
||||||
|
try {
|
||||||
|
await invoke("close_splashscreen");
|
||||||
|
} catch {
|
||||||
|
// Browser preview and failed startup paths should keep working without Tauri.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startBackgroundTimers() {
|
||||||
|
autoRefreshTimer = setInterval(() => { void autoRefreshTick(); }, AUTO_REFRESH_INTERVAL);
|
||||||
|
backgroundRepoStatusTimer = setInterval(() => { void backgroundRepoStatusTick(false); }, BACKGROUND_REPO_STATUS_INTERVAL);
|
||||||
|
backgroundFetchTimer = setInterval(() => { void backgroundFetchTick(); }, BACKGROUND_FETCH_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runStartupSequence() {
|
||||||
|
const startupStartedAt = performance.now();
|
||||||
|
|
||||||
|
initAnalytics();
|
||||||
|
loadRepoLists();
|
||||||
|
|
||||||
|
void checkForUpdates();
|
||||||
|
void initCommitAi();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await waitForStartupPaint();
|
||||||
|
await Promise.race([
|
||||||
|
fetchOpenRepositoriesDuringStartup(),
|
||||||
|
wait(STARTUP_FETCH_MAX_WAIT_MS),
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
const remainingSplashTime = STARTUP_SPLASH_MIN_VISIBLE_MS - (performance.now() - startupStartedAt);
|
||||||
|
if (remainingSplashTime > 0) await wait(remainingSplashTime);
|
||||||
|
|
||||||
|
await closeStartupSplashscreen();
|
||||||
|
startBackgroundTimers();
|
||||||
|
void backgroundRepoStatusTick(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchOpenRepositoriesDuringStartup() {
|
||||||
|
const paths = uniqueRepoPaths(repoTabs.map((tab) => tab.path));
|
||||||
|
if (paths.length === 0 || backgroundFetchInFlight) return;
|
||||||
|
|
||||||
|
let fetchedRepositories = 0;
|
||||||
|
let failedRepositories = 0;
|
||||||
|
backgroundFetchInFlight = true;
|
||||||
|
trackEvent("startup_open_repositories_fetch_started", {
|
||||||
|
open_repositories: paths.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const path of paths) {
|
||||||
|
try {
|
||||||
|
const nextStatus = await fetchRemote(path);
|
||||||
|
if (sameRepoPath(path, activeRepoPath)) applyStatus(nextStatus);
|
||||||
|
else updateRepoManagementStatus(path, nextStatus);
|
||||||
|
fetchedRepositories += 1;
|
||||||
|
} catch {
|
||||||
|
failedRepositories += 1;
|
||||||
|
try {
|
||||||
|
updateRepoManagementStatus(path, await getStatus(path));
|
||||||
|
} catch {
|
||||||
|
// Keep the cached tab data if this repo is unavailable at startup.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
backgroundFetchInFlight = false;
|
||||||
|
trackEvent("startup_open_repositories_fetch_finished", {
|
||||||
|
open_repositories: paths.length,
|
||||||
|
fetched_repositories: fetchedRepositories,
|
||||||
|
failed_repositories: failedRepositories,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$: scheduleAutoHideError("errorMessage", errorMessage, (message) => {
|
$: scheduleAutoHideError("errorMessage", errorMessage, (message) => {
|
||||||
if (errorMessage === message) errorMessage = "";
|
if (errorMessage === message) errorMessage = "";
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { mount } from "svelte";
|
import { mount } from "svelte";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
|
||||||
|
|
||||||
import App from "./App.svelte";
|
import App from "./App.svelte";
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
@@ -12,8 +11,4 @@ if (!target) {
|
|||||||
|
|
||||||
const app = mount(App, { target });
|
const app = mount(App, { target });
|
||||||
|
|
||||||
void invoke("close_splashscreen").catch(() => {
|
|
||||||
// Browser preview and failed startup paths should keep working without Tauri.
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
Reference in New Issue
Block a user