Skip to content
128 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

preparing for cloud work1//! 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
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.5//! system, a badge on the icon, a tray, a menu, `g1t://` links, the server
6//! to use, the app's own settings, and updates that install themselves
7//! (docs.g1t.sh/guides/desktop/).
preparing for cloud work8//!
9//! The site finds the app at `window.g1tDesktop` (bridge.js), the one
10//! contract between the two; what the page asks of it is answered by the
11//! commands in bridge.rs, each of which checks that the page asking is the
12//! configured server's.
13#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
14
15mod badge;
16mod bridge;
17mod config;
18mod links;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.19mod menu;
preparing for cloud work20mod navigation;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.21mod settings;
preparing for cloud work22mod tray;
23mod updates;
24
25use tauri::{RunEvent, WebviewUrl, WebviewWindowBuilder, WindowEvent};
26use tauri_plugin_window_state::{StateFlags, WindowExt};
27
28/// The one window's label.
29pub const WINDOW: &str = "main";
30
31fn main() {
32 tauri::Builder::default()
33 // A second launch (a g1t:// link, or the icon clicked again) goes to
34 // the app already running: the link reaches links.rs through the
35 // deep-link plugin, and the window comes forward.
36 .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| tray::show(app)))
37 .plugin(tauri_plugin_deep_link::init())
38 .plugin(tauri_plugin_notification::init())
39 .plugin(tauri_plugin_opener::init())
40 .plugin(tauri_plugin_updater::Builder::new().build())
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.41 // The window's size and place come back in setup, without the plugin
42 // also showing it: at sign-in g1t may start in the tray.
43 .plugin(tauri_plugin_window_state::Builder::new().skip_initial_state(WINDOW).with_denylist(&[settings::SETTINGS]).build())
44 // Opening at sign-in, which settings turn on; the argument tells that start from someone's.
45 .plugin(tauri_plugin_autostart::init(tauri_plugin_autostart::MacosLauncher::LaunchAgent, Some(vec![settings::AT_LOGIN])))
preparing for cloud work46 .manage(config::State::default())
47 .invoke_handler(tauri::generate_handler![
48 bridge::desktop_info,
49 bridge::notify,
50 bridge::set_badge,
51 bridge::open_url,
52 bridge::reachable,
53 bridge::set_server,
54 bridge::check_for_updates,
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.55 bridge::open_settings,
56 bridge::get_settings,
57 bridge::save_settings,
58 bridge::set_open_at_login,
59 bridge::change_server,
Checking for updates in the desktop app answers in its settings window, with a restart beside a downloaded version.60 bridge::restart_to_update,
preparing for cloud work61 ])
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.62 .on_menu_event(|app, event| menu::handle(app, event.id().as_ref()))
preparing for cloud work63 .setup(|app| {
64 let handle = app.handle().clone();
65 config::load(&handle);
66 config::allow_site(&handle)?;
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.67 let hidden = settings::starts_hidden(&handle);
68 let prefs = config::settings(&handle);
69 let builder = WebviewWindowBuilder::new(app, WINDOW, WebviewUrl::App("index.html".into()))
preparing for cloud work70 .title("g1t")
71 .inner_size(1280.0, 840.0)
72 .min_inner_size(720.0, 480.0)
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.73 .visible(false)
preparing for cloud work74 .initialization_script(bridge::script())
75 .on_navigation({
76 let handle = handle.clone();
77 move |url| navigation::allowed(&handle, url)
78 })
79 .on_page_load({
80 let handle = handle.clone();
81 move |_webview, payload| config::at(&handle, payload.url().clone())
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.82 });
83 // macOS has one menu bar, the app's; elsewhere the menu is the window's own.
84 #[cfg(target_os = "macos")]
85 app.set_menu(menu::build(&handle)?)?;
86 #[cfg(not(target_os = "macos"))]
87 let builder = builder.menu(menu::build(&handle)?);
88 let window = builder.build()?;
89 // Opened at sign-in, g1t waits in the tray; otherwise the window comes up where it was.
90 let _ = window.restore_state(StateFlags::all() - StateFlags::VISIBLE);
91 if !hidden {
92 let _ = window.show();
93 }
94 #[cfg(not(target_os = "macos"))]
95 if !prefs.menu_bar {
96 let _ = window.hide_menu();
97 }
98 if prefs.zoom != 1.0 {
99 let _ = window.set_zoom(prefs.zoom);
100 }
101 // Closing the window keeps g1t running, for notifications, unless settings say it quits.
preparing for cloud work102 window.on_window_event({
103 let handle = handle.clone();
104 move |event| {
105 if let WindowEvent::CloseRequested { api, .. } = event {
106 api.prevent_close();
The desktop app has settings of its own and a menu, and it is for using g1t, not browsing it.107 tray::close(&handle);
preparing for cloud work108 }
109 }
110 });
111 tray::install(&handle)?;
112 links::install(&handle);
113 updates::schedule(handle);
114 Ok(())
115 })
116 .build(tauri::generate_context!())
117 .expect("g1t could not start")
118 .run(|app, event| match event {
119 // The dock icon, clicked while the window is hidden.
120 #[cfg(target_os = "macos")]
121 RunEvent::Reopen { .. } => tray::show(app),
122 // No window open is not a reason to quit: the tray is still there.
123 RunEvent::ExitRequested { api, code: None, .. } => api.prevent_exit(),
124 _ => {
125 let _ = app;
126 }
127 });
128}