Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Inject javascript from on_window_ready or on_webview_ready (using state data) #9099

Closed
martpie opened this issue Mar 6, 2024 · 5 comments

Comments

@martpie
Copy link

martpie commented Mar 6, 2024

Describe the problem

I want the inline the initial values of my app user config to my window. This data is coming from managed state in Tauri. I previously was calling it async from the front-end, but it means adding some dynamic imports to prevent race conditions.

Typically, my front-end main looks like the following:

(async function instantiateMuseeks() {
  //  Async instantiations: we need to execute a couple of async tasks before
  // rendering the app
  await Promise.all([attachConsole(), config.init()]).catch(logAndNotifyError);
  window.__museeks_osType = await type();

  // We import the app content/components asynchronously, because some parts (like stores)
  // are instantiated synchronously, and require to read data from `window`  (hacky, I know).
  // Using top-level import would cause race conditions as the config is is not initiated and did not
  // expose its values to `window`
  const router = (await import('./views/router')).default;
  const queryClient = (await import('./lib/query')).queryClient;

  const wrap = document.getElementById('wrap');

  if (wrap) {
    const root = ReactDOM.createRoot(wrap);
    root.render(
      <React.StrictMode>
        <QueryClientProvider client={queryClient}>
          <RouterProvider router={router} />
        </QueryClientProvider>
      </React.StrictMode>,
    );
  } else {
    document.body.innerHTML = '<div style="text-align: center;">x_x</div>';
  }
})();

This works, but is not great, and delays to time-to-readiness of the app. In Electron, some of this work could be done in preload.

So I decided to moving some window global variables setup to the back-end, having discovered the [initialization_script](https://docs.rs/tauri/2.0.0-beta.8/tauri/webview/struct.WebviewWindowBuilder.html#method.initialization_script) feature.

Unfortunately, when statically creating a window from tauri.conf.json, it is not possible to modify the webview attributed of it via plugin's on_window_ready or on_webview_ready.

Describe the solution you'd like

Make it possible to append additional initialization_script from plugin, dynamically.

Plugin::Builder::js_init_script is not good enough, because I need to access State-managed data, and this helper only accepts static values.

This is really a convenience feature request, things are working ok right now, but if you think it makes sense and it's not too complicated, it would be absolutely awesome.

Alternatives considered

My current solution is to manually create the window during setup

#[tokio::main]
async fn main() {
    // Is there any way to instantiate that in the plugin directly?
    let db = plugins::database::setup().await.ok().unwrap();

    tauri::Builder::default()
        .plugin(plugins::config::init())
        // ...
        .setup(|app| {
            // TODO: Create an alert window in case something goes wrong here

            // We need to inject some stuff in our window, so we are creating it manually
            // Ideally, this would be in the Config plugin, but there seems to be no way
            // to append initialization_scripts to statically created windows.
            let config_manager = app.state::<ConfigManager>();
            let config = config_manager.get().clone();
            let config_json = serde_json::to_string(&config)?;

            let initial_config_script = format!(
                r#"
                    window.__MUSEEKS_INITIAL_CONFIG = {};
                    window.__MUSEEKS_PLATFORM = {:?};
                "#,
                config_json,
                tauri_plugin_os::type_().to_string()
            );

            tauri::WebviewWindowBuilder::new(
                app,
                "main", /* the unique window label */
                tauri::WebviewUrl::App("index.html".into()),
            )
            .title("Museeks")
            .visible(false)
            .hidden_title(true)
            .title_bar_style(tauri::TitleBarStyle::Overlay)
            .inner_size(900.0, 550.0)
            .min_inner_size(900.0, 550.0)
            .fullscreen(false)
            .resizable(true)
            .disable_file_drop_handler()
            .initialization_script(initial_config_script.as_str()) // All of this for that
            .build()?;

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

I would highly prefer to be able to inject that JS via my plugin (plugins::config::init()) instead + static window definition in tauri.conf.json, separation of concerns, all that ;)

Additional context

martpie/museeks#748 for additional context and more code :D

@martpie
Copy link
Author

martpie commented Mar 6, 2024

If you think it makes sense but don't have bandwidth, I can also have a look and try to create a PR for that :)

@amrbashir
Copy link
Member

Plugin::Builder::js_init_script is not good enough, because I need to access State-managed data, and this helper only accepts static values.

Not sure why Plugin::builder::js_init_script is not good enough, the same code you showed above with WindowBuilder::initialization_script could be done on the Plugin::builder::js_init_script as well.

@amrbashir
Copy link
Member

If you really want to use on_webview_ready, you can do window.eval(your_script)

@martpie
Copy link
Author

martpie commented Mar 6, 2024

Eval seems like a good solution, I didn't know about it.

Regarding why not js_init_script: I may be missing something, but it is not possible to read managed state from there is it?

@martpie
Copy link
Author

martpie commented Mar 6, 2024

Oh I guess I can instantiate my config from the init method of my plugin, the move it to the scope of the plugin. Duh.

Thank you for the hints!

@martpie martpie closed this as completed Mar 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants