refactor(core): app hooks (#1332)

This commit is contained in:
Lucas Fernandes Nogueira 2021-03-09 19:41:02 -03:00 committed by GitHub
parent 99dbc42cf1
commit dd5a945b7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 21 deletions

View File

@ -1,5 +1,5 @@
use futures::future::BoxFuture;
use serde::Serialize;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use tauri_api::{config::Config, private::AsTauriContext};
@ -23,7 +23,9 @@ pub use webview_manager::{WebviewDispatcher, WebviewManager};
type InvokeHandler<A> = dyn Fn(WebviewManager<A>, String, JsonValue) -> BoxFuture<'static, crate::Result<InvokeResponse>>
+ Send
+ Sync;
type Setup<A> = dyn Fn(WebviewManager<A>) -> BoxFuture<'static, ()> + Send + Sync;
type ManagerHook<A> = dyn Fn(WebviewManager<A>) -> BoxFuture<'static, ()> + Send + Sync;
type PageLoadHook<A> =
dyn Fn(WebviewManager<A>, PageLoadPayload) -> BoxFuture<'static, ()> + Send + Sync;
/// `App` runtime information.
pub struct Context {
@ -63,12 +65,27 @@ impl<T: Serialize> From<T> for InvokeResponse {
}
}
/// The payload for the "page_load" hook.
#[derive(Debug, Clone, Deserialize)]
pub struct PageLoadPayload {
url: String,
}
impl PageLoadPayload {
/// The page URL.
pub fn url(&self) -> &str {
&self.url
}
}
/// The application runner.
pub struct App<A: ApplicationExt> {
/// The JS message handler.
invoke_handler: Option<Box<InvokeHandler<A>>>,
/// The setup callback, invoked when the webview is ready.
setup: Option<Box<Setup<A>>>,
/// The page load hook, invoked when the webview performs a navigation.
on_page_load: Option<Box<PageLoadHook<A>>>,
/// The setup hook, invoked when the webviews have been created.
setup: Option<Box<ManagerHook<A>>>,
/// The context the App was created with
pub(crate) context: Context,
pub(crate) dispatchers: Arc<Mutex<HashMap<String, WebviewDispatcher<A::Dispatcher>>>>,
@ -118,10 +135,22 @@ impl<A: ApplicationExt + 'static> App<A> {
}
}
/// Runs the setup callback if defined.
pub(crate) async fn run_setup(&self, dispatcher: &WebviewManager<A>) {
/// Runs the setup hook if defined.
pub(crate) async fn run_setup(&self, dispatcher: WebviewManager<A>) {
if let Some(ref setup) = self.setup {
let fut = setup(dispatcher.clone());
let fut = setup(dispatcher);
fut.await;
}
}
/// Runs the on page load hook if defined.
pub(crate) async fn run_on_page_load(
&self,
dispatcher: &WebviewManager<A>,
payload: PageLoadPayload,
) {
if let Some(ref on_page_load) = self.on_page_load {
let fut = on_page_load(dispatcher.clone(), payload);
fut.await;
}
}
@ -197,8 +226,10 @@ where
{
/// The JS message handler.
invoke_handler: Option<Box<InvokeHandler<A>>>,
/// The setup callback, invoked when the webview is ready.
setup: Option<Box<Setup<A>>>,
/// The setup hook.
setup: Option<Box<ManagerHook<A>>>,
/// Page load hook.
on_page_load: Option<Box<PageLoadHook<A>>>,
config: PhantomData<C>,
/// The webview dispatchers.
dispatchers: Arc<Mutex<HashMap<String, WebviewDispatcher<A::Dispatcher>>>>,
@ -212,6 +243,7 @@ impl<A: ApplicationExt + 'static, C: AsTauriContext> AppBuilder<C, A> {
Self {
invoke_handler: None,
setup: None,
on_page_load: None,
config: Default::default(),
dispatchers: Default::default(),
webviews: Default::default(),
@ -232,7 +264,7 @@ impl<A: ApplicationExt + 'static, C: AsTauriContext> AppBuilder<C, A> {
self
}
/// Defines the setup callback.
/// Defines the setup hook.
pub fn setup<
T: futures::Future<Output = ()> + Send + Sync + 'static,
F: Fn(WebviewManager<A>) -> T + Send + Sync + 'static,
@ -246,6 +278,20 @@ impl<A: ApplicationExt + 'static, C: AsTauriContext> AppBuilder<C, A> {
self
}
/// Defines the page load hook.
pub fn on_page_load<
T: futures::Future<Output = ()> + Send + Sync + 'static,
F: Fn(WebviewManager<A>, PageLoadPayload) -> T + Send + Sync + 'static,
>(
mut self,
on_page_load: F,
) -> Self {
self.on_page_load = Some(Box::new(move |webview_manager, payload| {
Box::pin(on_page_load(webview_manager, payload))
}));
self
}
/// Adds a plugin to the runtime.
pub fn plugin(
self,
@ -283,6 +329,7 @@ impl<A: ApplicationExt + 'static, C: AsTauriContext> AppBuilder<C, A> {
Ok(App {
invoke_handler: self.invoke_handler,
setup: self.setup,
on_page_load: self.on_page_load,
context,
dispatchers: self.dispatchers,
webviews: Some(self.webviews),
@ -303,6 +350,7 @@ fn run<A: ApplicationExt + 'static>(mut application: App<A>) -> crate::Result<()
let application = Arc::new(application);
let mut webview_app = A::new()?;
let mut main_webview_manager = None;
for webview in webviews {
let webview_label = webview.label.to_string();
@ -311,6 +359,9 @@ fn run<A: ApplicationExt + 'static>(mut application: App<A>) -> crate::Result<()
application.dispatchers.clone(),
webview_label.to_string(),
);
if main_webview_manager.is_none() {
main_webview_manager = Some(webview_manager.clone());
}
let (webview_builder, rpc_handler, custom_protocol) =
crate::async_runtime::block_on(application.init_webview(webview))?;
@ -322,6 +373,10 @@ fn run<A: ApplicationExt + 'static>(mut application: App<A>) -> crate::Result<()
));
}
if let Some(main_webview_manager) = main_webview_manager {
crate::async_runtime::block_on(application.run_setup(main_webview_manager));
}
webview_app.run();
Ok(())

View File

@ -12,7 +12,7 @@ use crate::{
use super::{
webview::{CustomProtocol, WebviewBuilderExtPrivate, WebviewRpcHandler},
App, Context, RpcRequest, Webview, WebviewManager,
App, Context, PageLoadPayload, RpcRequest, Webview, WebviewManager,
};
use serde::Deserialize;
@ -85,10 +85,10 @@ pub(super) fn initialization_script(
{tauri_initialization_script}
{event_initialization_script}
if (window.rpc) {{
window.__TAURI__.invoke("__initialized")
window.__TAURI__.invoke("__initialized", {{ url: window.location.href }})
}} else {{
window.addEventListener('DOMContentLoaded', function () {{
window.__TAURI__.invoke("__initialized")
window.__TAURI__.invoke("__initialized", {{ url: window.location.href }})
}})
}}
{plugin_initialization_script}
@ -154,7 +154,6 @@ pub(super) fn build_webview<A: ApplicationExt + 'static>(
plugin_initialization_script: &str,
context: &Context,
) -> crate::Result<BuiltWebview<A>> {
// TODO let debug = cfg!(debug_assertions);
let webview_url = match &webview.url {
WindowUrl::App => content_url.to_string(),
WindowUrl::Custom(url) => url.to_string(),
@ -324,8 +323,11 @@ async fn on_message<A: ApplicationExt + 'static>(
message: Message,
) -> crate::Result<InvokeResponse> {
if &command == "__initialized" {
application.run_setup(&webview_manager).await;
crate::plugin::ready(A::plugin_store(), &webview_manager).await;
let payload: PageLoadPayload = serde_json::from_value(message.inner)?;
application
.run_on_page_load(&webview_manager, payload.clone())
.await;
crate::plugin::on_page_load(A::plugin_store(), &webview_manager, payload).await;
Ok(().into())
} else {
let response = if let Some(module) = &message.tauri_module {

View File

@ -1,4 +1,6 @@
use crate::{api::config::PluginConfig, async_runtime::Mutex, ApplicationExt, WebviewManager};
use crate::{
api::config::PluginConfig, async_runtime::Mutex, ApplicationExt, PageLoadPayload, WebviewManager,
};
use futures::future::join_all;
use serde_json::Value as JsonValue;
@ -30,9 +32,9 @@ pub trait Plugin<A: ApplicationExt + 'static>: Send + Sync {
#[allow(unused_variables)]
async fn created(&mut self, webview_manager: WebviewManager<A>) {}
/// Callback invoked when the webview is ready.
/// Callback invoked when the webview performs a navigation.
#[allow(unused_variables)]
async fn ready(&mut self, webview_manager: WebviewManager<A>) {}
async fn on_page_load(&mut self, webview_manager: WebviewManager<A>, payload: PageLoadPayload) {}
/// Add invoke_handler API extension commands.
#[allow(unused_variables)]
@ -109,14 +111,15 @@ pub(crate) async fn created<A: ApplicationExt + 'static>(
join_all(futures).await;
}
pub(crate) async fn ready<A: ApplicationExt + 'static>(
pub(crate) async fn on_page_load<A: ApplicationExt + 'static>(
store: &PluginStore<A>,
webview_manager: &crate::WebviewManager<A>,
payload: PageLoadPayload,
) {
let mut plugins = store.lock().await;
let mut futures = Vec::new();
for plugin in plugins.iter_mut() {
futures.push(plugin.ready(webview_manager.clone()));
futures.push(plugin.on_page_load(webview_manager.clone(), payload.clone()));
}
join_all(futures).await;
}