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:
+94
-8
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy, onMount, tick } from "svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { open as openDialog } from "@tauri-apps/plugin-dialog";
|
||||
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";
|
||||
@@ -268,6 +269,8 @@
|
||||
const BACKGROUND_FETCH_INTERVAL = 180_000;
|
||||
const BACKGROUND_FETCH_AFTER_SWITCH_GRACE_MS = 30_000;
|
||||
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 backgroundRepoStatusTimer: ReturnType<typeof setInterval> | undefined;
|
||||
let backgroundFetchInFlight = false;
|
||||
@@ -346,14 +349,7 @@
|
||||
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
||||
|
||||
onMount(() => {
|
||||
initAnalytics();
|
||||
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();
|
||||
void runStartupSequence();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -368,6 +364,96 @@
|
||||
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) => {
|
||||
if (errorMessage === message) errorMessage = "";
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user