fix: settings window rendering/loading issue (#889)

This commit fixes(I guess?) the issue that the Settings window may not be
rendered or loaded, you will see that the whole window is gray in that case.

Background, aka, why this issue exists
=============================================================

In commit [1], we wrapped all the backend setup routines in a tauri command, so
that frontend code can call it before invoking any other backend interfaces to
guarantee that these interfaces won't be called until the data/state they rely
on is ready.

The implementation in [1] had an issue that it didn't work with window reloading.
To fix this issue, we made another commit [2].  Commit [2] fixed the refresh
issue, but it also caused the settings window issue that this commit tries to fix.

The backend setup tauri command needs a state to track whether the setup has
completed.  In the previous implementation, this was done in the frontend.  In
this commit, it is moved to the backend.

Why didn't you guys move that state to backend in previous commits, e.g., commit [2]?
=============================================================

We tried, but failed.  In the previous tries, the backend would send an event
to the frontend, but the frontend couldn't receive it, for reasons we still
don’t understand.  And this weird issue still exists, we just happen to find
a way to work around it.

[1]: f93c527561
[2]: 993da9a8ad

Co-authored-by: ayang <473033518@qq.com>
This commit is contained in:
SteveLauC
2025-08-28 09:01:08 +08:00
committed by GitHub
parent 86540ad1a9
commit efaaf73cd7
4 changed files with 22 additions and 37 deletions

View File

@@ -34,6 +34,7 @@ Information about release notes of Coco App is provided here.
- fix: set up hotkey on main thread or Windows will complain #879
- fix: resolve deeplink login issue #881
- fix: use kill_on_drop() to avoid zombie proc in error case #887
- fix: settings window rendering/loading issue 889
### ✈️ Improvements

View File

@@ -2,7 +2,8 @@ use crate::GLOBAL_TAURI_APP_HANDLE;
use crate::autostart;
use crate::common::register::SearchSourceRegistry;
use crate::util::app_lang::update_app_lang;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use tauri::{AppHandle, Manager, WebviewWindow};
#[cfg(target_os = "macos")]
@@ -41,9 +42,11 @@ pub fn default(
);
}
/// Use this variable to track if tauri command `backend_setup()` gets called
/// by the frontend.
pub(super) static BACKEND_SETUP_FUNC_INVOKED: OnceLock<()> = OnceLock::new();
/// Indicates if the setup job is completed.
static BACKEND_SETUP_COMPLETED: AtomicBool = AtomicBool::new(false);
/// The function `backup_setup()` may be called concurrently, use this lock to
/// synchronize that only 1 async task can do the actual setup job.
static MUTEX_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
/// This function includes the setup job that has to be coordinated with the
/// frontend, or the App will panic due to races[1]. The way we coordinate is to
@@ -60,9 +63,17 @@ pub(super) static BACKEND_SETUP_FUNC_INVOKED: OnceLock<()> = OnceLock::new();
/// called. If the frontend code invokes `list_extensions()` before `init_extension()`
/// gets executed, we get a panic.
#[tauri::command]
#[function_name::named]
pub(crate) async fn backend_setup(tauri_app_handle: AppHandle, app_lang: String) {
if BACKEND_SETUP_FUNC_INVOKED.get().is_some() {
if BACKEND_SETUP_COMPLETED.load(Ordering::Relaxed) {
return;
}
// Race to let one async task do the setup job
let _guard = MUTEX_LOCK.lock().await;
// Re-check in case the current async task is not the first one that acquires
// the lock
if BACKEND_SETUP_COMPLETED.load(Ordering::Relaxed) {
return;
}
@@ -102,7 +113,5 @@ pub(crate) async fn backend_setup(tauri_app_handle: AppHandle, app_lang: String)
update_app_lang(app_lang).await;
// Invoked, now update the state
BACKEND_SETUP_FUNC_INVOKED
.set(())
.unwrap_or_else(|_| panic!("tauri command {}() gets called twice!", function_name!()));
BACKEND_SETUP_COMPLETED.store(true, Ordering::Relaxed);
}

View File

@@ -1,46 +1,22 @@
import { useMount, useSessionStorageState } from "ahooks";
import { useEffect } from "react";
import { useMount } from "ahooks";
import { useState } from "react";
import { invoke } from "@tauri-apps/api/core";
import LayoutOutlet from "./outlet";
import { useAppStore } from "@/stores/appStore";
import platformAdapter from "@/utils/platformAdapter";
import { CHAT_WINDOW_LABEL, MAIN_WINDOW_LABEL } from "@/constants";
const Layout = () => {
const { language } = useAppStore();
const [ready, setReady] = useSessionStorageState("rust_ready", {
defaultValue: false,
});
const [ready, setReady] = useState(false);
useMount(async () => {
const label = await platformAdapter.getCurrentWindowLabel();
if (label === CHAT_WINDOW_LABEL) {
setReady(true);
}
if (ready || label !== MAIN_WINDOW_LABEL) return;
await invoke("backend_setup", {
appLang: language,
});
setReady(true);
platformAdapter.emitEvent("rust_ready");
});
useEffect(() => {
const unlisten = platformAdapter.listenEvent("rust_ready", () => {
setReady(true);
});
return () => {
unlisten.then((fn) => fn());
};
}, []);
return ready && <LayoutOutlet />;
};

View File

@@ -47,7 +47,6 @@ export interface EventPayloads {
"check-update": any;
oauth_success: any;
extension_install_success: any;
rust_ready: boolean;
}
// Window operation interface