Skip to content
95 linesCodeBlameRaw
1//! The g1t desktop app: g1t.sh, or a g1t of your own, in a window of its
2//! own. The window loads the live site, so every change to g1t reaches it
3//! the moment it is deployed and nothing here needs a new build for it;
4//! only what is native lives in this program: notifications from the
5//! system, a badge on the icon, a tray, `g1t://` links, the server to use,
6//! and updates that install themselves (docs.g1t.sh/guides/desktop/).
7//!
8//! The site finds the app at `window.g1tDesktop` (bridge.js), the one
9//! contract between the two; what the page asks of it is answered by the
10//! commands in bridge.rs, each of which checks that the page asking is the
11//! configured server's.
12#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
13
14mod badge;
15mod bridge;
16mod config;
17mod links;
18mod navigation;
19mod tray;
20mod updates;
21
22use tauri::{RunEvent, WebviewUrl, WebviewWindowBuilder, WindowEvent};
23use tauri_plugin_window_state::{StateFlags, WindowExt};
24
25/// The one window's label.
26pub const WINDOW: &str = "main";
27
28fn main() {
29 tauri::Builder::default()
30 // A second launch (a g1t:// link, or the icon clicked again) goes to
31 // the app already running: the link reaches links.rs through the
32 // deep-link plugin, and the window comes forward.
33 .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| tray::show(app)))
34 .plugin(tauri_plugin_deep_link::init())
35 .plugin(tauri_plugin_notification::init())
36 .plugin(tauri_plugin_opener::init())
37 .plugin(tauri_plugin_updater::Builder::new().build())
38 .plugin(tauri_plugin_window_state::Builder::new().build())
39 .manage(config::State::default())
40 .invoke_handler(tauri::generate_handler![
41 bridge::desktop_info,
42 bridge::notify,
43 bridge::set_badge,
44 bridge::open_url,
45 bridge::reachable,
46 bridge::set_server,
47 bridge::check_for_updates,
48 ])
49 .setup(|app| {
50 let handle = app.handle().clone();
51 config::load(&handle);
52 config::allow_site(&handle)?;
53 let window = WebviewWindowBuilder::new(app, WINDOW, WebviewUrl::App("index.html".into()))
54 .title("g1t")
55 .inner_size(1280.0, 840.0)
56 .min_inner_size(720.0, 480.0)
57 .initialization_script(bridge::script())
58 .on_navigation({
59 let handle = handle.clone();
60 move |url| navigation::allowed(&handle, url)
61 })
62 .on_page_load({
63 let handle = handle.clone();
64 move |_webview, payload| config::at(&handle, payload.url().clone())
65 })
66 .build()?;
67 let _ = window.restore_state(StateFlags::all());
68 // Closing the window keeps g1t running, for notifications; Quit in the tray ends it.
69 window.on_window_event({
70 let handle = handle.clone();
71 move |event| {
72 if let WindowEvent::CloseRequested { api, .. } = event {
73 api.prevent_close();
74 tray::hide(&handle);
75 }
76 }
77 });
78 tray::install(&handle)?;
79 links::install(&handle);
80 updates::schedule(handle);
81 Ok(())
82 })
83 .build(tauri::generate_context!())
84 .expect("g1t could not start")
85 .run(|app, event| match event {
86 // The dock icon, clicked while the window is hidden.
87 #[cfg(target_os = "macos")]
88 RunEvent::Reopen { .. } => tray::show(app),
89 // No window open is not a reason to quit: the tray is still there.
90 RunEvent::ExitRequested { api, code: None, .. } => api.prevent_exit(),
91 _ => {
92 let _ = app;
93 }
94 });
95}