Add menu functionality (#59)

* Added core::Channel and menu functionality. core::Channel may leak memory.

* Updated examples to v2 using Leptos.
This commit is contained in:
bicarlsen 2024-08-06 18:20:50 +02:00 committed by GitHub
parent 115009d4bf
commit ae49310ee1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 5712 additions and 4216 deletions

View file

@ -1,34 +1,569 @@
use anyhow::ensure;
use tauri_sys::app;
use futures::stream::StreamExt;
use leptos::{ev::MouseEvent, *};
use std::rc::Rc;
pub async fn get_name() -> anyhow::Result<()> {
let name = app::get_name().await?;
#[component]
pub fn App() -> impl IntoView {
view! {
<main class="container">
<div>
<h2>"core"</h2>
<Core/>
</div>
ensure!(name == "tauri-sys-test");
<div>
<h2>"events"</h2>
<Events/>
</div>
Ok(())
<div>
<h2>"window"</h2>
<Window/>
</div>
<div>
<h2>"menu"</h2>
<Menu/>
</div>
</main>
}
}
pub async fn get_version() -> anyhow::Result<()> {
let version = app::get_version().await?;
#[component]
fn Core() -> impl IntoView {
let (convert_path, set_convert_path) = create_signal("".to_string());
let (converted_path, set_converted_path) = create_signal("".to_string());
ensure!(version.major == 0);
ensure!(version.minor == 0);
ensure!(version.patch == 0);
ensure!(version.build.is_empty());
ensure!(version.pre.is_empty());
let do_convert_path = move |_| {
let converted = tauri_sys::core::convert_file_src(convert_path());
set_converted_path(converted);
};
Ok(())
view! {
<div>
<div>
<label>
"Convert path"
<input
prop:value=convert_path
on:input=move |e| set_convert_path(event_target_value(&e))
/>
</label>
<button on:click=do_convert_path>"Convert"</button>
</div>
<div>{converted_path}</div>
</div>
}
}
pub async fn get_tauri_version() -> anyhow::Result<()> {
let version = app::get_tauri_version().await?;
#[component]
fn Events() -> impl IntoView {
let (listen_event, set_listen_event) = create_signal(None);
let (emit_count, set_emit_count) = create_signal(0);
ensure!(version.major == 1);
ensure!(version.minor == 5);
ensure!(version.patch == 3);
ensure!(version.build.is_empty());
ensure!(version.pre.is_empty());
spawn_local(async move {
let mut listener = tauri_sys::event::listen::<i32>("event::listen")
.await
.unwrap();
Ok(())
while let Some(event) = listener.next().await {
tracing::debug!(?event);
let tauri_sys::event::Event {
event: _,
id: _,
payload,
} = event;
set_listen_event.set(Some(payload));
}
});
spawn_local(async move {
let mut listener = tauri_sys::event::listen::<i32>("event::emit")
.await
.unwrap();
while let Some(event) = listener.next().await {
tracing::debug!(?event);
let tauri_sys::event::Event {
event: _,
id: _,
payload,
} = event;
set_emit_count.set(payload);
}
});
let trigger_listen_events = move |_| {
spawn_local(async move {
tauri_sys::core::invoke::<()>("trigger_listen_events", &()).await;
});
};
let trigger_emit_event = move |_| {
spawn_local(async move {
tauri_sys::event::emit("event::emit", &emit_count.with_untracked(|n| n + 1))
.await
.unwrap();
});
};
view! {
<div>
<div>
<button on:click=trigger_listen_events>"Trigger listen events"</button>
<div>
<strong>"Last listen event: "</strong>
{move || listen_event()}
</div>
</div>
<div>
<button on:click=trigger_emit_event>"Trigger emit event"</button>
<div>
<strong>"Events emitted: "</strong>
{move || emit_count()}
</div>
</div>
</div>
}
}
#[component]
fn Window() -> impl IntoView {
view! {
<div>
<div>
<h3>"Windows"</h3>
<WindowWindows/>
</div>
<div>
<h3>"Monitors"</h3>
<WindowMonitors/>
</div>
<div>
<h3>"Events"</h3>
<WindowEvents/>
</div>
</div>
}
}
#[component]
fn WindowWindows() -> impl IntoView {
let current_window = create_action(|_| async move { tauri_sys::window::get_current() });
let all_windows = create_action(|_| async move { tauri_sys::window::get_all() });
let refresh = move |_| {
current_window.dispatch(());
all_windows.dispatch(());
};
current_window.dispatch(());
all_windows.dispatch(());
view! {
<div>
<div style="display: flex; justify-content: center; gap: 10px;">
<div>"Current window:"</div>
{move || {
current_window
.value()
.with(|window| match window {
None => "Loading".to_string(),
Some(window) => window.label().clone(),
})
}}
</div>
<div style="display: flex; justify-content: center; gap: 10px;">
<div>"All windows:"</div>
{move || {
all_windows
.value()
.with(|windows| match windows {
None => "Loading".to_string(),
Some(windows) => {
let out = windows
.iter()
.map(|window| { window.label().clone() })
.collect::<Vec<_>>()
.join(", ");
format!("[{out}]")
}
})
}}
</div>
<button on:click=refresh>"Refresh"</button>
</div>
}
}
#[component]
fn WindowMonitors() -> impl IntoView {
let current_monitor =
create_action(|_| async move { tauri_sys::window::current_monitor().await });
let primary_monitor =
create_action(|_| async move { tauri_sys::window::primary_monitor().await });
let available_monitors =
create_action(|_| async move { tauri_sys::window::available_monitors().await });
let monitor_from_point = create_action(|(x, y): &(isize, isize)| {
let x = x.clone();
let y = y.clone();
async move { tauri_sys::window::monitor_from_point(x, y).await }
});
// let cursor_position =
// create_action(|_| async move { tauri_sys::window::cursor_position().await });
let refresh = move |_| {
current_monitor.dispatch(());
primary_monitor.dispatch(());
available_monitors.dispatch(());
};
let oninput_monitor_from_point = move |e| {
let value = event_target_value(&e);
let Some((x, y)) = value.split_once(',') else {
return;
};
let Ok(x) = x.parse::<isize>() else {
return;
};
let Ok(y) = y.parse::<isize>() else {
return;
};
monitor_from_point.dispatch((x, y));
};
current_monitor.dispatch(());
primary_monitor.dispatch(());
available_monitors.dispatch(());
view! {
<div>
<div>
<div style="display: flex; justify-content: center; gap: 10px;">
<div>"Current monitor:"</div>
{move || {
current_monitor
.value()
.with(|monitor| match monitor {
None => "Loading".into_view(),
Some(Some(monitor)) => view! { <Monitor monitor/> }.into_view(),
Some(None) => "Could not detect monitor.".into_view(),
})
}}
</div>
<div style="display: flex; justify-content: center; gap: 10px;">
<div>"Primary monitor:"</div>
{move || {
primary_monitor
.value()
.with(|monitor| match monitor {
None => "Loading".into_view(),
Some(Some(monitor)) => view! { <Monitor monitor/> }.into_view(),
Some(None) => "Could not detect monitor.".into_view(),
})
}}
</div>
<div style="display: flex; justify-content: center; gap: 10px;">
<div>"Available monitors:"</div>
{move || {
available_monitors
.value()
.with(|monitors| match monitors {
None => "Loading".into_view(),
Some(monitors) => {
view! {
{monitors
.iter()
.map(|monitor| view! { <Monitor monitor/> })
.collect::<Vec<_>>()}
}
.into_view()
}
})
}}
</div>
<button on:click=refresh>"Refresh"</button>
</div>
<div>
<label>"Monitor from point" <input on:input=oninput_monitor_from_point/></label>
<div style="margin: 0 auto;">
{move || {
monitor_from_point
.value()
.with(|monitor| match monitor {
None => "Enter an `x, y` coordinate.".into_view(),
Some(Some(monitor)) => view! { <Monitor monitor/> }.into_view(),
Some(None) => "Could not detect monitor.".into_view(),
})
}}
</div>
</div>
<div>
// {move || {
// cursor_position
// .value()
// .with(|position| {
// position
// .as_ref()
// .map(|position| {
// view! {
// {position.x()}
// ", "
// {position.y()}
// }
// })
// })
// }}
<div>"Cursor position: "</div>
<div style="width: 50vw; height: 30vh; margin: 0 auto; border: 2px solid black; border-radius: 5px;">
// on:mousemove=move |_| cursor_position.dispatch(())
"TODO (See https://github.com/tauri-apps/tauri/issues/10340)"
</div>
</div>
</div>
}
}
#[component]
fn WindowEvents() -> impl IntoView {
use tauri_sys::window::{DragDropEvent, DragDropPayload, DragOverPayload};
let (count, set_count) = create_signal(0);
let increment_count = create_action(|count: &usize| {
let count = count.clone();
let window = tauri_sys::window::get_current();
async move {
web_sys::console::debug_1(&"0".into());
window.emit("count", count).await.unwrap();
}
});
let (drag_drop, set_drag_drop) = create_signal(().into_view());
spawn_local(async move {
let mut window = tauri_sys::window::get_current();
let mut listener = window.listen::<usize>("count").await.unwrap();
while let Some(event) = listener.next().await {
set_count(event.payload);
}
});
spawn_local(async move {
let window = tauri_sys::window::get_current();
let mut listener = window.on_drag_drop_event().await.unwrap();
while let Some(event) = listener.next().await {
match event.payload {
DragDropEvent::Enter(payload) => {
let out = view! {
<div>
<strong>"Enter"</strong>
<div>
"Paths: ["
{payload
.paths()
.iter()
.map(|path| path.to_string_lossy().to_string())
.collect::<Vec<_>>()
.join(", ")} "]"
</div>
<div>
"Position: " {payload.position().x()} ", " {payload.position().y()}
</div>
</div>
};
set_drag_drop(out.into_view());
}
DragDropEvent::Over(payload) => {
let out = view! {
<div>
<strong>"Over"</strong>
<div>
"Position: " {payload.position().x()} ", " {payload.position().y()}
</div>
</div>
};
set_drag_drop(out.into_view());
}
DragDropEvent::Drop(payload) => {
let out = view! {
<div>
<strong>"Drop"</strong>
<div>
"Paths: ["
{payload
.paths()
.iter()
.map(|path| path.to_string_lossy().to_string())
.collect::<Vec<_>>()
.join(", ")} "]"
</div>
<div>
"Position: " {payload.position().x()} ", " {payload.position().y()}
</div>
</div>
};
set_drag_drop(out.into_view());
}
DragDropEvent::Leave => {
let out = view! { <strong>"Leave"</strong> };
set_drag_drop(out.into_view());
}
}
}
});
view! {
<div>
<div>
"Count: " {count}
<button on:click=move |_| increment_count.dispatch(count() + 1)>"+"</button>
</div>
<div>
<h3>"Drag drop event"</h3>
<div>{drag_drop}</div>
</div>
</div>
}
}
#[component]
fn Monitor<'a>(monitor: &'a tauri_sys::window::Monitor) -> impl IntoView {
view! {
<div style="display: inline-block; text-align: left;">
<div>"Name: " {monitor.name().clone()}</div>
<div>"Size: " {monitor.size().width()} " x " {monitor.size().height()}</div>
<div>"Position: " {monitor.position().x()} ", " {monitor.position().y()}</div>
<div>"Scale: " {monitor.scale_factor()}</div>
</div>
}
}
#[component]
fn Menu() -> impl IntoView {
let (event_manual, set_event_manual) = create_signal::<Option<String>>(None);
let (event_with_items, set_event_with_items) = create_signal::<Option<String>>(None);
let default_menu = move |e: MouseEvent| {
spawn_local(async move {
let menu = tauri_sys::menu::Menu::default().await;
});
};
let menu_manual = create_local_resource(
|| (),
move |_| async move {
let menu = tauri_sys::menu::Menu::with_id("tauri-sys-menu").await;
let mut item_open =
tauri_sys::menu::item::MenuItem::with_id("Item 1 - Manual", "manual-item_1").await;
let mut item_close =
tauri_sys::menu::item::MenuItem::with_id("Item 2 - Manual", "manual-item_2").await;
menu.append_item(&item_open).await.unwrap();
menu.append_item(&item_close).await.unwrap();
spawn_local(async move {
let mut listener_item_open = item_open.listen().fuse();
let mut listener_item_close = item_close.listen().fuse();
loop {
futures::select! {
event = listener_item_open.next() => match event{
None => continue,
Some(event) => set_event_manual(Some(event.clone())),
},
event = listener_item_close.next() => match event{
None => continue,
Some(event) => set_event_manual(Some(event.clone())),
},
}
}
});
Rc::new(menu)
},
);
let menu_with_items = create_local_resource(
|| (),
move |_| async move {
let mut item_open = tauri_sys::menu::item::MenuItemOptions::new("Item 1 - w/ items");
item_open.set_id("w_items-item_1");
let mut item_close = tauri_sys::menu::item::MenuItemOptions::new("Item 2 - w/ items");
item_close.set_id("w_items-item_2");
let items = vec![item_open.into(), item_close.into()];
let (menu, mut listeners) =
tauri_sys::menu::Menu::with_id_and_items("tauri-sys_menu_w_items", items).await;
let mut listener_item_open = listeners.remove(0).unwrap().fuse();
let mut listener_item_close = listeners.remove(0).unwrap().fuse();
spawn_local(async move {
loop {
futures::select! {
event = listener_item_open.next() => match event{
None => continue,
Some(event) => {
set_event_with_items(Some(event.clone()))
},
},
event = listener_item_close.next() => match event{
None => continue,
Some(event) => set_event_with_items(Some(event.clone())),
},
}
}
});
Rc::new(menu)
},
);
let open_menu_manual = move |_e: MouseEvent| {
let menu = menu_manual.get().unwrap();
spawn_local(async move {
menu.popup().await.unwrap();
});
};
let open_menu_with_items = move |_e: MouseEvent| {
let menu = menu_with_items.get().unwrap();
spawn_local(async move {
menu.popup().await.unwrap();
});
};
view! {
<div
on:mousedown=open_menu_manual
style="margin: 0 auto 2em; width: 50vw; height: 10em; border: 1px black solid; border-radius: 5px;"
>
{event_manual}
</div>
<div
on:mousedown=open_menu_with_items
style="margin: auto; width: 50vw; height: 10em; border: 1px black solid; border-radius: 5px;"
>
{event_with_items}
</div>
}
}

View file

@ -1,12 +0,0 @@
use anyhow::ensure;
use tauri_sys::clipboard;
pub async fn test() -> anyhow::Result<()> {
clipboard::write_text("foobar").await?;
let text = clipboard::read_text().await?;
ensure!(text == "foobar".to_string());
Ok(())
}

View file

@ -1,93 +0,0 @@
use anyhow::ensure;
use tauri_sys::dialog::{FileDialogBuilder, MessageDialogBuilder, MessageDialogKind};
pub async fn ask() -> anyhow::Result<()> {
let works = MessageDialogBuilder::new()
.set_title("Tauri")
.set_kind(MessageDialogKind::Warning)
.ask("Does this work? \n Click Yes to mark this test as passing")
.await?;
ensure!(works);
Ok(())
}
pub async fn confirm() -> anyhow::Result<()> {
let works = MessageDialogBuilder::new()
.set_title("Tauri")
.set_kind(MessageDialogKind::Warning)
.confirm("Does this work? \n Click Ok to mark this test as passing")
.await?;
ensure!(works);
Ok(())
}
pub async fn message() -> anyhow::Result<()> {
MessageDialogBuilder::new()
.set_title("Tauri")
.set_kind(MessageDialogKind::Warning)
.message("This is a message just for you!")
.await?;
Ok(())
}
pub async fn pick_file() -> anyhow::Result<()> {
let file = FileDialogBuilder::new()
.set_title("Select a file to mark this test as passing")
.pick_file()
.await?;
ensure!(file.is_some());
Ok(())
}
pub async fn pick_files() -> anyhow::Result<()> {
let file = FileDialogBuilder::new()
.set_title("Select a multiple files to mark this test as passing")
.pick_files()
.await?;
ensure!(file.is_some());
ensure!(file.unwrap().count() > 1);
Ok(())
}
pub async fn pick_folder() -> anyhow::Result<()> {
let file = FileDialogBuilder::new()
.set_title("Select a folder to mark this test as passing")
.pick_folder()
.await?;
ensure!(file.is_some());
Ok(())
}
pub async fn pick_folders() -> anyhow::Result<()> {
let file = FileDialogBuilder::new()
.set_title("Select a multiple folders to mark this test as passing")
.pick_folders()
.await?;
ensure!(file.is_some());
ensure!(file.unwrap().count() > 1);
Ok(())
}
pub async fn save() -> anyhow::Result<()> {
let file = FileDialogBuilder::new()
.set_title("Select a file to mark this test as passing")
.save()
.await?;
ensure!(file.is_some());
Ok(())
}

View file

@ -1,38 +0,0 @@
use anyhow::ensure;
use futures::StreamExt;
use tauri_sys::{event, tauri};
pub async fn emit() -> anyhow::Result<()> {
event::emit("javascript-event", &"bar").await?;
ensure!(tauri::invoke::<_, bool>("verify_receive", &()).await?);
Ok(())
}
pub async fn listen() -> anyhow::Result<()> {
let events = event::listen::<u32>("rust-event-listen").await?;
tauri::invoke::<_, ()>("emit_event_5_times", &()).await?;
let events: Vec<u32> = events
.take(5)
.map(|e| e.payload)
.collect()
.await;
ensure!(events == vec![0, 1, 2, 3, 4]);
Ok(())
}
pub async fn once() -> anyhow::Result<()> {
// this causes enough delay for `once` to register it's event listener before the event gets triggered
wasm_bindgen_futures::spawn_local(async {
tauri::invoke::<_, ()>("emit_event", &()).await.unwrap();
});
let event = event::once::<String>("rust-event-once").await?;
ensure!(event.payload == "Hello World from Rust!");
Ok(())
}

View file

@ -1,31 +0,0 @@
use std::time::Duration;
use futures::StreamExt;
use tauri_sys::global_shortcut;
pub async fn register_all() -> anyhow::Result<()> {
let task = async {
let shortcuts = ["CommandOrControl+Shift+C", "Ctrl+Alt+F12"];
let streams = futures::future::try_join_all(shortcuts.map(|s| async move {
let stream = global_shortcut::register(s).await?;
anyhow::Ok(stream.map(move |_| s))
}))
.await?;
let mut events = futures::stream::select_all(streams);
while let Some(shortcut) = events.next().await {
log::debug!("Shortcut {} triggered", shortcut)
}
anyhow::Ok(())
};
let timeout = gloo_timers::future::sleep(Duration::from_secs(20));
futures::future::select(Box::pin(task), timeout).await;
Ok(())
}

View file

@ -1,196 +1,33 @@
mod app;
mod clipboard;
mod dialog;
mod event;
mod notification;
mod os;
mod tauri_log;
mod window;
mod global_shortcut;
extern crate console_error_panic_hook;
use log::LevelFilter;
use std::future::Future;
use std::panic;
use sycamore::prelude::*;
use sycamore::suspense::Suspense;
use tauri_log::TauriLogger;
#[cfg(feature = "ci")]
async fn exit_with_error(e: String) {
use serde::Serialize;
#[derive(Serialize)]
struct Args {
e: String,
}
tauri_sys::tauri::invoke::<_, ()>("exit_with_error", &Args { e })
.await
.unwrap();
}
#[derive(Props)]
pub struct TestProps<'a, F>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
name: &'a str,
test: F,
}
#[component]
pub async fn TestInner<'a, G: Html, F>(cx: Scope<'a>, props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
let res = props.test.await;
view! { cx,
tr {
td { code { (props.name.to_string()) } }
td { (if let Err(e) = &res {
#[cfg(feature = "ci")]
{
wasm_bindgen_futures::spawn_local(exit_with_error(e.to_string()));
unreachable!()
}
#[cfg(not(feature = "ci"))]
format!("{:?}", e)
} else {
format!("")
})
}
}
}
}
#[component]
pub fn Test<'a, G: Html, F>(cx: Scope<'a>, props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
let fallback = view! { cx,
tr {
td { code { (props.name.to_string()) } }
td {
span(class="loader") { "" }
}
}
};
view! { cx,
Suspense(fallback=fallback) {
TestInner(name=props.name, test=props.test)
}
}
}
#[cfg(not(feature = "ci"))]
#[component]
pub fn InteractiveTest<'a, G: Html, F>(cx: Scope<'a>, props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
let mut test = Some(props.test);
let render_test = create_signal(cx, false);
let run_test = |_| {
render_test.set(true);
};
view! { cx,
(if *render_test.get() {
let test = test.take().unwrap();
view! { cx,
Test(name=props.name, test=test)
}
} else {
view! { cx,
tr {
td { code { (props.name.to_string()) } }
td {
button(on:click=run_test) { "Run Interactive Test"}
}
}
}
})
}
}
#[cfg(feature = "ci")]
#[component]
pub async fn InteractiveTest<'a, G: Html, F>(cx: Scope<'a>, _props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
view! { cx, "Interactive tests are not run in CI mode" }
}
#[component]
pub async fn Terminate<'a, G: Html>(cx: Scope<'a>) -> View<G> {
#[cfg(feature = "ci")]
sycamore::suspense::await_suspense(cx, async {
tauri_sys::process::exit(0).await;
})
.await;
view! {
cx,
}
}
static LOGGER: TauriLogger = TauriLogger;
use app::*;
use leptos::*;
fn main() {
log::set_logger(&LOGGER)
.map(|()| log::set_max_level(LevelFilter::Trace))
.unwrap();
panic::set_hook(Box::new(|info| {
console_error_panic_hook::hook(info);
#[cfg(feature = "ci")]
wasm_bindgen_futures::spawn_local(exit_with_error(format!("{}", info)));
}));
sycamore::render(|cx| {
view! { cx,
table {
tbody {
// Suspense(fallback=view!{ cx, "Running Tests..." }) {
Test(name="app::get_name",test=app::get_name())
Test(name="app::get_version",test=app::get_version())
Test(name="app::get_tauri_version",test=app::get_tauri_version())
Test(name="clipboard::read_text | clipboard::write_text",test=clipboard::test())
Test(name="event::emit",test=event::emit())
Test(name="event::listen",test=event::listen())
Test(name="event::once",test=event::once())
InteractiveTest(name="dialog::message",test=dialog::message())
InteractiveTest(name="dialog::ask",test=dialog::ask())
InteractiveTest(name="dialog::confirm",test=dialog::confirm())
InteractiveTest(name="dialog::pick_file",test=dialog::pick_file())
InteractiveTest(name="dialog::pick_files",test=dialog::pick_files())
InteractiveTest(name="dialog::pick_folder",test=dialog::pick_folder())
InteractiveTest(name="dialog::pick_folders",test=dialog::pick_folders())
InteractiveTest(name="dialog::save",test=dialog::save())
Test(name="os::arch",test=os::arch())
Test(name="os::platform",test=os::platform())
Test(name="os::tempdir",test=os::tempdir())
Test(name="os::kind",test=os::kind())
Test(name="os::version",test=os::version())
Test(name="notification::is_permission_granted",test=notification::is_permission_granted())
Test(name="notification::request_permission",test=notification::request_permission())
InteractiveTest(name="notification::show_notification",test=notification::show_notification())
InteractiveTest(name="global_shortcut::register_all",test=global_shortcut::register_all())
Test(name="window::WebviewWindow::new",test=window::create_window())
Terminate
// }
}
}
}
});
#[cfg(debug_assertions)]
tracing::enable();
console_error_panic_hook::set_once();
mount_to_body(|| {
view! { <App/> }
})
}
#[cfg(debug_assertions)]
mod tracing {
use tracing::level_filters::LevelFilter;
use tracing_subscriber::prelude::*;
use tracing_web::MakeConsoleWriter;
const MAX_LOG_LEVEL: LevelFilter = LevelFilter::DEBUG;
pub fn enable() {
let fmt_layer = tracing_subscriber::fmt::layer()
.with_ansi(false) // Only partially supported across browsers
.pretty()
.without_time()
.with_writer(MakeConsoleWriter) // write events to the console
.with_filter(MAX_LOG_LEVEL);
tracing_subscriber::registry().with(fmt_layer).init();
}
}

View file

@ -1,28 +0,0 @@
use anyhow::ensure;
use tauri_sys::notification::{self, Permission};
pub async fn is_permission_granted() -> anyhow::Result<()> {
let granted = notification::is_permission_granted().await?;
ensure!(granted);
Ok(())
}
pub async fn request_permission() -> anyhow::Result<()> {
let permission = notification::request_permission().await?;
ensure!(permission == Permission::Granted);
Ok(())
}
pub async fn show_notification() -> anyhow::Result<()> {
let mut n = notification::Notification::default();
n.set_title("TAURI");
n.set_body("Tauri is awesome!");
n.show()?;
Ok(())
}

View file

@ -1,41 +0,0 @@
use tauri_sys::os;
pub async fn arch() -> anyhow::Result<()> {
let arch = os::arch().await?;
log::debug!("{:?}", arch);
Ok(())
}
pub async fn platform() -> anyhow::Result<()> {
let platform = os::platform().await?;
log::debug!("{:?}", platform);
Ok(())
}
pub async fn tempdir() -> anyhow::Result<()> {
let tempdir = os::tempdir().await?;
log::info!("{:?}", tempdir);
Ok(())
}
pub async fn kind() -> anyhow::Result<()> {
let kind = os::kind().await?;
log::debug!("{:?}", kind);
Ok(())
}
pub async fn version() -> anyhow::Result<()> {
let version = os::version().await?;
log::debug!("{:?}", version);
Ok(())
}

View file

@ -1,75 +0,0 @@
use log::{Metadata, Record};
use serde::Serialize;
use tauri_sys::tauri;
#[derive(Debug, Serialize)]
struct LogArgs {
level: Level,
message: String,
location: String,
file: Option<String>,
line: Option<u32>,
}
#[derive(Debug)]
enum Level {
Trace,
Debug,
Info,
Warn,
Error,
}
impl From<log::Level> for Level {
fn from(l: log::Level) -> Self {
match l {
log::Level::Error => Level::Error,
log::Level::Warn => Level::Warn,
log::Level::Info => Level::Info,
log::Level::Debug => Level::Debug,
log::Level::Trace => Level::Trace,
}
}
}
impl Serialize for Level {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(match self {
Level::Trace => 1,
Level::Debug => 2,
Level::Info => 3,
Level::Warn => 4,
Level::Error => 5,
})
}
}
pub struct TauriLogger;
impl log::Log for TauriLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::Level::Trace
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
let args = LogArgs {
level: record.level().into(),
location: record.target().to_string(),
message: format!("{}", record.args()),
file: record.file().map(ToString::to_string),
line: record.line(),
};
wasm_bindgen_futures::spawn_local(async move {
tauri::invoke::<_, ()>("plugin:log|log", &args)
.await
.unwrap();
});
}
}
fn flush(&self) {}
}

View file

@ -1,15 +0,0 @@
use anyhow::ensure;
use tauri_sys::window;
pub async fn create_window() -> anyhow::Result<()> {
let win = window::WebviewWindowBuilder::new("foo-win")
.set_url("/")
.build()
.await?;
ensure!(win.is_visible().await?);
win.close().await?;
Ok(())
}