mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-25 18:17:21 +00:00
fix(properties): restore standalone window startup
This commit is contained in:
@@ -5,7 +5,10 @@
|
|||||||
"windows": ["properties-*"],
|
"windows": ["properties-*"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
|
"core:window:allow-minimize",
|
||||||
"core:window:allow-set-title",
|
"core:window:allow-set-title",
|
||||||
|
"core:window:allow-start-dragging",
|
||||||
|
"core:window:allow-toggle-maximize",
|
||||||
"core:event:allow-listen",
|
"core:event:allow-listen",
|
||||||
"core:event:allow-unlisten",
|
"core:event:allow-unlisten",
|
||||||
"dialog:default",
|
"dialog:default",
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ impl PropertiesWindowRegistry {
|
|||||||
Ok(self.session_for_window(label)?.as_deref() == Some(session_id))
|
Ok(self.session_for_window(label)?.as_deref() == Some(session_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn is_ready(&self, label: &str) -> Result<bool, String> {
|
pub fn is_ready(&self, label: &str) -> Result<bool, String> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.state
|
.state
|
||||||
@@ -238,6 +239,17 @@ pub fn ensure_main_window(caller: &tauri::WebviewWindow) -> Result<(), String> {
|
|||||||
.ok_or_else(|| "This command is available only to the main window".to_string())
|
.ok_or_else(|| "This command is available only to the main window".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_to_main<T: Serialize + Clone>(
|
||||||
|
app: &tauri::AppHandle,
|
||||||
|
event: &str,
|
||||||
|
payload: T,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let main_window = app
|
||||||
|
.get_webview_window(MAIN_WINDOW_LABEL)
|
||||||
|
.ok_or_else(|| "Firelink main window is unavailable".to_string())?;
|
||||||
|
main_window.emit(event, payload).map_err(|error| error.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn registered_download_for_caller(
|
fn registered_download_for_caller(
|
||||||
caller: &tauri::WebviewWindow,
|
caller: &tauri::WebviewWindow,
|
||||||
registry: &PropertiesWindowRegistry,
|
registry: &PropertiesWindowRegistry,
|
||||||
@@ -319,9 +331,10 @@ pub fn open_download_properties_window(
|
|||||||
|
|
||||||
let label = registry.allocate(&id)?;
|
let label = registry.allocate(&id)?;
|
||||||
if let Some(window) = app.get_webview_window(&label) {
|
if let Some(window) = app.get_webview_window(&label) {
|
||||||
if !registry.is_ready(&label)? {
|
// Visibility belongs to the native window owner, not to the renderer
|
||||||
return Ok(label);
|
// handshake. A delayed or lost snapshot must leave a usable loading
|
||||||
}
|
// window on screen instead of making the open request appear to do
|
||||||
|
// nothing.
|
||||||
let _ = window.unminimize();
|
let _ = window.unminimize();
|
||||||
let _ = window.show();
|
let _ = window.show();
|
||||||
let _ = window.set_focus();
|
let _ = window.set_focus();
|
||||||
@@ -337,21 +350,25 @@ pub fn open_download_properties_window(
|
|||||||
.min_inner_size(760.0, 560.0)
|
.min_inner_size(760.0, 560.0)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.always_on_top(false)
|
.always_on_top(false)
|
||||||
.visible(false);
|
.visible(true);
|
||||||
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
||||||
let builder = builder.decorations(false);
|
let builder = builder.decorations(false);
|
||||||
let build_result = builder.build();
|
let build_result = builder.build();
|
||||||
|
if let Ok(window) = &build_result {
|
||||||
|
// Keep the initial loading/error shell visible even if the child
|
||||||
|
// renderer has not completed its bridge handshake yet.
|
||||||
|
let _ = window.show();
|
||||||
|
let _ = window.set_focus();
|
||||||
|
}
|
||||||
if let Err(error) = build_result {
|
if let Err(error) = build_result {
|
||||||
// Two rapid main-window requests can race between the native lookup
|
// Two rapid main-window requests can race between the native lookup
|
||||||
// above and builder creation. If the first request won, retain the
|
// above and builder creation. If the first request won, retain the
|
||||||
// registry entry and focus its window instead of treating the second
|
// registry entry and focus its window instead of treating the second
|
||||||
// request as a failed open.
|
// request as a failed open.
|
||||||
if let Some(window) = app.get_webview_window(&label) {
|
if let Some(window) = app.get_webview_window(&label) {
|
||||||
if registry.is_ready(&label)? {
|
let _ = window.unminimize();
|
||||||
let _ = window.unminimize();
|
let _ = window.show();
|
||||||
let _ = window.show();
|
let _ = window.set_focus();
|
||||||
let _ = window.set_focus();
|
|
||||||
}
|
|
||||||
return Ok(label);
|
return Ok(label);
|
||||||
}
|
}
|
||||||
let _ = registry.remove_window(&label);
|
let _ = registry.remove_window(&label);
|
||||||
@@ -385,8 +402,8 @@ pub fn properties_window_send_ready(
|
|||||||
}
|
}
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
app.emit_to(
|
emit_to_main(
|
||||||
MAIN_WINDOW_LABEL,
|
&app,
|
||||||
PROPERTIES_WINDOW_READY_EVENT,
|
PROPERTIES_WINDOW_READY_EVENT,
|
||||||
PropertiesWindowReadyEvent {
|
PropertiesWindowReadyEvent {
|
||||||
window_label: caller.label().to_string(),
|
window_label: caller.label().to_string(),
|
||||||
@@ -394,7 +411,6 @@ pub fn properties_window_send_ready(
|
|||||||
session_id,
|
session_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(|error| error.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -438,8 +454,8 @@ pub fn properties_window_send_action(
|
|||||||
if !registry.session_matches(caller.label(), &session_id)? {
|
if !registry.session_matches(caller.label(), &session_id)? {
|
||||||
return Err("Properties window session is no longer current".to_string());
|
return Err("Properties window session is no longer current".to_string());
|
||||||
}
|
}
|
||||||
app.emit_to(
|
emit_to_main(
|
||||||
MAIN_WINDOW_LABEL,
|
&app,
|
||||||
PROPERTIES_WINDOW_ACTION_REQUEST_EVENT,
|
PROPERTIES_WINDOW_ACTION_REQUEST_EVENT,
|
||||||
PropertiesWindowActionEvent {
|
PropertiesWindowActionEvent {
|
||||||
window_label: caller.label().to_string(),
|
window_label: caller.label().to_string(),
|
||||||
@@ -450,7 +466,6 @@ pub fn properties_window_send_action(
|
|||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.map_err(|error| error.to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -416,6 +416,11 @@ export const PropertiesWindowApp = () => {
|
|||||||
window.clearInterval(readyRetryTimer);
|
window.clearInterval(readyRetryTimer);
|
||||||
readyRetryTimer = undefined;
|
readyRetryTimer = undefined;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Native visibility is established by the opener. Keep the
|
||||||
|
// loading shell usable when the optional ready-state update is
|
||||||
|
// delayed or rejected, and let the next snapshot retry it.
|
||||||
|
setErrorMessage(errorText(error));
|
||||||
} finally {
|
} finally {
|
||||||
revealInFlightRef.current = false;
|
revealInFlightRef.current = false;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-5
@@ -7,13 +7,11 @@ import "@fontsource-variable/outfit/wght.css";
|
|||||||
import "@fontsource-variable/roboto/wght.css";
|
import "@fontsource-variable/roboto/wght.css";
|
||||||
import "@fontsource-variable/vazirmatn/wght.css";
|
import "@fontsource-variable/vazirmatn/wght.css";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import App from "./App";
|
|
||||||
import { i18nReady } from "./i18n";
|
import { i18nReady } from "./i18n";
|
||||||
import { ErrorBoundary } from "./components/ErrorBoundary";
|
import { ErrorBoundary } from "./components/ErrorBoundary";
|
||||||
import { ToastProvider } from "./contexts/ToastContext";
|
import { ToastProvider } from "./contexts/ToastContext";
|
||||||
import { error as logError, warn as logWarn, initLogger } from "./utils/logger";
|
import { error as logError, warn as logWarn, initLogger } from "./utils/logger";
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
import { PropertiesWindowApp } from './components/PropertiesWindowApp';
|
|
||||||
|
|
||||||
const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-');
|
const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-');
|
||||||
|
|
||||||
@@ -45,14 +43,22 @@ console.warn = (...values: unknown[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rootElement = document.getElementById("root");
|
const rootElement = document.getElementById("root");
|
||||||
const renderApp = () => {
|
const renderApp = async () => {
|
||||||
if (!rootElement) return;
|
if (!rootElement) return;
|
||||||
|
|
||||||
|
// Keep the child entrypoint isolated from the main application module. App
|
||||||
|
// imports the persistent Zustand stores, whose module initialization issues
|
||||||
|
// main-window-only IPC commands. Loading it in a Properties child creates a
|
||||||
|
// second persistence owner and can race the bridge handshake.
|
||||||
|
const RootComponent = isPropertiesWindow
|
||||||
|
? (await import('./components/PropertiesWindowApp')).PropertiesWindowApp
|
||||||
|
: (await import('./App')).default;
|
||||||
|
|
||||||
createRoot(rootElement).render(
|
createRoot(rootElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
{isPropertiesWindow ? <PropertiesWindowApp /> : <App />}
|
<RootComponent />
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
@@ -61,7 +67,7 @@ const renderApp = () => {
|
|||||||
|
|
||||||
void i18nReady.then(renderApp).catch(error => {
|
void i18nReady.then(renderApp).catch(error => {
|
||||||
console.error('Failed to initialize localization:', error);
|
console.error('Failed to initialize localization:', error);
|
||||||
renderApp();
|
void renderApp();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prevent the webview's default context menu ("Reload", etc.) on right-click.
|
// Prevent the webview's default context menu ("Reload", etc.) on right-click.
|
||||||
|
|||||||
Reference in New Issue
Block a user