Skip to main content

max / balanced_breakfast

5.7 KB · 165 lines History Blame Raw
1 //! Tauri app builder and command registration
2 pub mod commands;
3 pub mod state;
4 pub mod sync_scheduler;
5 pub mod sync_service;
6
7 #[cfg(mobile)]
8 #[tauri::mobile_entry_point]
9 fn mobile_main() {
10 build_app()
11 .run(tauri::generate_context!())
12 .expect("error while running tauri application");
13 }
14
15 use state::AppState;
16 use std::sync::Arc;
17 use tauri::Manager;
18 #[cfg(not(any(target_os = "ios", target_os = "android")))]
19 use tauri::Emitter;
20
21 /// Build the Tauri app with all commands registered.
22 pub fn build_app() -> tauri::Builder<tauri::Wry> {
23 let builder = tauri::Builder::default();
24
25 // Desktop-only plugins
26 #[cfg(not(any(target_os = "ios", target_os = "android")))]
27 let builder = builder
28 .plugin(tauri_plugin_shell::init())
29 .plugin(tauri_plugin_window_state::Builder::new().build())
30 .plugin(tauri_plugin_updater::Builder::new().build());
31
32 builder
33 .plugin(tauri_plugin_dialog::init())
34 .setup(|app| {
35 let app_handle = app.handle().clone();
36 tauri::async_runtime::block_on(async move {
37 let state = match AppState::new(&app_handle).await {
38 Ok(s) => s,
39 Err(e) => {
40 tracing::error!(error = %e, "Fatal: failed to initialize app state");
41 eprintln!("Fatal: failed to initialize app state: {e}");
42 return Err(e);
43 }
44 };
45 let state = Arc::new(state);
46 app_handle.manage(state.clone());
47
48 // Start background auto-fetch
49 state::spawn_auto_fetch(app_handle.clone(), state.clone());
50
51 // Start background stale-item cleanup
52 state::spawn_stale_cleanup(state.clone());
53
54 // Start sync scheduler
55 let sync_handle = sync_scheduler::start_sync_scheduler(app_handle);
56 state.set_sync_scheduler_handle(sync_handle);
57 Ok(())
58 })
59 .map_err(|e: String| -> Box<dyn std::error::Error> { e.into() })?;
60
61 // Check for OTA updates after a short delay (desktop only)
62 #[cfg(not(any(target_os = "ios", target_os = "android")))]
63 {
64 let update_handle = app.handle().clone();
65 tauri::async_runtime::spawn(async move {
66 tokio::time::sleep(std::time::Duration::from_secs(10)).await;
67 check_for_updates(update_handle).await;
68 });
69 }
70
71 Ok(())
72 })
73 .invoke_handler(tauri::generate_handler![
74 commands::list_sources,
75 commands::list_items,
76 commands::get_item,
77 commands::mark_item_read,
78 commands::mark_item_unread,
79 commands::star_item,
80 commands::unstar_item,
81 commands::get_unread_count,
82 commands::mark_all_read,
83 commands::list_plugins,
84 commands::get_plugin_schema,
85 commands::get_feeds_by_busser,
86 commands::get_feed,
87 commands::create_feed,
88 commands::update_feed,
89 commands::delete_feed,
90 commands::delete_feeds_by_busser,
91 commands::fetch_all,
92 commands::reset_circuit_breaker,
93 commands::set_feed_tags,
94 commands::list_all_tags,
95 commands::export_opml,
96 commands::import_opml,
97 commands::list_themes,
98 commands::get_theme,
99 commands::get_custom_themes_dir,
100 commands::import_theme,
101 commands::export_theme,
102 commands::sync_get_tiers,
103 commands::sync_status,
104 commands::sync_start_auth,
105 commands::sync_complete_auth,
106 commands::sync_disconnect,
107 commands::sync_now,
108 commands::sync_setup_encryption_new,
109 commands::sync_setup_encryption_existing,
110 commands::sync_update_settings,
111 commands::sync_subscription_status,
112 commands::sync_subscribe,
113 commands::get_config,
114 commands::set_config,
115 commands::list_query_feeds,
116 commands::create_query_feed,
117 commands::update_query_feed,
118 commands::delete_query_feed,
119 commands::extract_reader_view,
120 commands::download_and_open,
121 commands::list_bookmarks,
122 commands::create_bookmark,
123 commands::create_bookmark_from_item,
124 commands::update_bookmark,
125 commands::delete_bookmark,
126 commands::set_bookmark_tags,
127 commands::list_bookmark_tags,
128 commands::is_bookmarked,
129 commands::get_bookmark_count,
130 commands::export_bookmark_html,
131 ])
132 }
133
134 /// Check for OTA updates and emit an event to the frontend if one is available.
135 #[cfg(not(any(target_os = "ios", target_os = "android")))]
136 async fn check_for_updates(app: tauri::AppHandle) {
137 use tauri_plugin_updater::UpdaterExt;
138
139 let updater = match app.updater() {
140 Ok(u) => u,
141 Err(e) => {
142 tracing::warn!("Failed to initialize updater: {e}");
143 return;
144 }
145 };
146 match updater.check().await {
147 Ok(Some(update)) => {
148 tracing::info!("Update available: v{}", update.version);
149 let _ = app.emit(
150 "update-available",
151 serde_json::json!({
152 "version": update.version,
153 "body": update.body.unwrap_or_default(),
154 }),
155 );
156 }
157 Ok(None) => {
158 tracing::info!("App is up to date");
159 }
160 Err(e) => {
161 tracing::warn!("Update check failed: {e}");
162 }
163 }
164 }
165