Skip to main content

max / goingson

15.4 KB · 372 lines History Blame Raw
1 //! GoingsOn - Tauri application library.
2 //!
3 //! This library exposes the core modules for testing purposes.
4 //! The main application entry point is in `main.rs` (desktop)
5 //! or via `mobile_entry_point` (iOS/Android).
6
7 pub mod backup_scheduler;
8 pub mod blob_gc;
9 pub mod commands;
10 pub mod email;
11 pub mod email_sync_scheduler;
12 pub mod export;
13 pub mod external_sync;
14 pub mod jmap;
15 pub mod oauth;
16 pub mod state;
17 pub mod sync_scheduler;
18 pub mod sync_service;
19 pub mod tz;
20
21 // Desktop-only: file watching for external DB changes
22 #[cfg(not(any(target_os = "ios", target_os = "android")))]
23 pub mod db_watcher;
24
25 // Desktop-only: OS notifications for snooze expiry
26 #[cfg(not(any(target_os = "ios", target_os = "android")))]
27 pub mod notifications;
28
29 #[cfg(test)]
30 pub mod test_utils;
31
32 /// The single source of truth for the Tauri command registration list.
33 ///
34 /// Both the desktop entry point (`main.rs`) and the mobile entry point
35 /// (`build_mobile_app` below) expand this macro, so the desktop and mobile
36 /// handler lists cannot drift: adding a `#[tauri::command]` and forgetting to
37 /// register it here is a build error, and omitting a command from one platform
38 /// is impossible because there is only one list. Paths are written as
39 /// `$crate::commands::*` so the macro resolves correctly whether it is invoked
40 /// from inside this crate (mobile) or from the `goingson_desktop` consumer crate
41 /// (the desktop binary).
42 #[macro_export]
43 macro_rules! all_commands {
44 () => {
45 ::tauri::generate_handler![
46 // Attachments
47 $crate::commands::add_attachment,
48 $crate::commands::list_attachments,
49 $crate::commands::delete_attachment,
50 $crate::commands::open_attachment,
51 $crate::commands::save_attachment,
52 $crate::commands::convert_email_attachments,
53 $crate::commands::open_email_blob,
54 $crate::commands::save_email_blob,
55 $crate::commands::get_file_size,
56 // Projects
57 $crate::commands::list_projects,
58 $crate::commands::get_project,
59 $crate::commands::create_project,
60 $crate::commands::update_project,
61 $crate::commands::delete_project,
62 // Tasks
63 $crate::commands::list_tasks,
64 $crate::commands::list_tasks_filtered,
65 $crate::commands::get_task,
66 $crate::commands::create_task,
67 $crate::commands::quick_add_task,
68 $crate::commands::update_task,
69 $crate::commands::delete_task,
70 $crate::commands::bulk_set_task_project,
71 $crate::commands::bulk_set_task_priority,
72 $crate::commands::start_task,
73 $crate::commands::complete_task,
74 $crate::commands::list_snoozed_tasks,
75 $crate::commands::snooze_task,
76 $crate::commands::unsnooze_task,
77 $crate::commands::list_waiting_tasks,
78 $crate::commands::mark_task_waiting,
79 $crate::commands::clear_task_waiting,
80 // Task Overview
81 $crate::commands::get_task_overview,
82 // Annotations
83 $crate::commands::list_annotations,
84 $crate::commands::add_annotation,
85 $crate::commands::delete_annotation,
86 // Subtasks
87 $crate::commands::list_subtasks,
88 $crate::commands::add_subtask,
89 $crate::commands::add_subtask_link,
90 $crate::commands::toggle_subtask,
91 $crate::commands::update_subtask,
92 $crate::commands::delete_subtask,
93 // Status tokens (e.g. linked commits)
94 $crate::commands::list_task_status_tokens,
95 $crate::commands::record_task_status_token,
96 $crate::commands::delete_task_status_token,
97 // Milestones
98 $crate::commands::list_milestones,
99 $crate::commands::create_milestone,
100 $crate::commands::update_milestone,
101 $crate::commands::delete_milestone,
102 $crate::commands::reorder_milestones,
103 // Events
104 $crate::commands::list_events,
105 $crate::commands::get_event,
106 $crate::commands::create_event,
107 $crate::commands::update_event,
108 $crate::commands::delete_event,
109 $crate::commands::bulk_delete_events,
110 $crate::commands::list_events_between,
111 $crate::commands::list_upcoming_events,
112 $crate::commands::get_event_status_indicator,
113 $crate::commands::list_snoozed_events,
114 $crate::commands::snooze_event,
115 $crate::commands::unsnooze_event,
116 // Emails
117 $crate::commands::list_emails,
118 $crate::commands::list_emails_threaded,
119 $crate::commands::get_email,
120 $crate::commands::build_reply_prefill,
121 $crate::commands::build_forward_prefill,
122 $crate::commands::fetch_email_full_body,
123 $crate::commands::open_email_in_browser,
124 $crate::commands::create_email,
125 $crate::commands::send_email,
126 $crate::commands::save_email_draft,
127 $crate::commands::list_email_drafts,
128 $crate::commands::send_email_draft,
129 $crate::commands::set_email_labels,
130 $crate::commands::list_email_folders,
131 $crate::commands::list_email_labels,
132 $crate::commands::move_email_to_folder,
133 $crate::commands::delete_email,
134 $crate::commands::mark_email_read,
135 $crate::commands::mark_email_unread,
136 $crate::commands::archive_email,
137 $crate::commands::unarchive_email,
138 $crate::commands::mark_all_emails_read,
139 $crate::commands::link_email_to_project,
140 $crate::commands::get_unread_email_count,
141 $crate::commands::list_snoozed_emails,
142 $crate::commands::snooze_email,
143 $crate::commands::unsnooze_email,
144 $crate::commands::list_waiting_emails,
145 $crate::commands::mark_email_waiting,
146 $crate::commands::clear_email_waiting,
147 // Contacts
148 $crate::commands::list_contacts,
149 $crate::commands::get_contact,
150 $crate::commands::create_contact,
151 $crate::commands::update_contact,
152 $crate::commands::delete_contact,
153 $crate::commands::add_contact_email,
154 $crate::commands::remove_contact_email,
155 $crate::commands::add_contact_phone,
156 $crate::commands::remove_contact_phone,
157 $crate::commands::add_contact_social_handle,
158 $crate::commands::remove_contact_social_handle,
159 $crate::commands::add_contact_custom_field,
160 $crate::commands::remove_contact_custom_field,
161 $crate::commands::update_contact_email,
162 $crate::commands::update_contact_phone,
163 $crate::commands::update_contact_social_handle,
164 $crate::commands::update_contact_custom_field,
165 $crate::commands::find_contact_by_email,
166 $crate::commands::validate_email_addresses,
167 $crate::commands::promote_contact,
168 $crate::commands::list_tasks_for_contact,
169 $crate::commands::list_events_for_contact,
170 $crate::commands::list_emails_for_contact,
171 $crate::commands::get_contact_activity,
172 $crate::commands::list_contacts_filtered,
173 $crate::commands::list_contact_email_directory,
174 $crate::commands::bulk_delete_contacts,
175 $crate::commands::bulk_tag_contacts,
176 // Snooze Options
177 $crate::commands::get_snooze_options,
178 // Email Accounts
179 $crate::commands::list_email_accounts,
180 $crate::commands::get_email_account,
181 $crate::commands::create_email_account,
182 $crate::commands::update_email_account,
183 $crate::commands::update_email_sync_interval,
184 $crate::commands::update_email_signature,
185 $crate::commands::update_email_notify,
186 $crate::commands::delete_email_account,
187 $crate::commands::test_email_account,
188 $crate::commands::sync_email_account,
189 // Project Dashboard
190 $crate::commands::list_tasks_for_project,
191 $crate::commands::list_events_for_project,
192 $crate::commands::list_emails_for_project,
193 $crate::commands::list_unlinked_emails,
194 // Email Threading
195 $crate::commands::list_emails_by_thread,
196 // Stats
197 $crate::commands::get_dashboard_stats,
198 // App info
199 $crate::commands::get_changelog,
200 $crate::commands::parse_natural_date,
201 // Window
202 $crate::commands::open_compose_window,
203 $crate::commands::set_window_title,
204 $crate::commands::open_external_url,
205 // Search
206 $crate::commands::search,
207 // Daily Notes
208 $crate::commands::get_daily_note,
209 $crate::commands::upsert_daily_note,
210 // Day Planning
211 $crate::commands::get_day_planning,
212 $crate::commands::schedule_task,
213 $crate::commands::unschedule_task,
214 // Saved Views
215 $crate::commands::list_saved_views,
216 $crate::commands::list_pinned_views,
217 $crate::commands::get_saved_view,
218 $crate::commands::create_saved_view,
219 $crate::commands::update_saved_view,
220 $crate::commands::delete_saved_view,
221 $crate::commands::toggle_view_pinned,
222 // OAuth
223 $crate::commands::list_oauth_providers,
224 $crate::commands::start_oauth,
225 $crate::commands::poll_oauth_result,
226 $crate::commands::complete_oauth,
227 $crate::commands::refresh_oauth_tokens,
228 $crate::commands::disconnect_oauth,
229 $crate::commands::reconnect_oauth,
230 // Export/Backup
231 $crate::commands::get_export_summary,
232 $crate::commands::export_json,
233 $crate::commands::export_tasks_csv,
234 $crate::commands::export_events_ics,
235 $crate::commands::create_backup,
236 $crate::commands::list_backups,
237 $crate::commands::restore_backup,
238 $crate::commands::delete_backup,
239 $crate::commands::get_backup_settings,
240 $crate::commands::save_backup_settings,
241 // Monthly Review
242 $crate::commands::get_monthly_review,
243 $crate::commands::upsert_monthly_goal,
244 $crate::commands::update_monthly_goal_status,
245 $crate::commands::delete_monthly_goal,
246 $crate::commands::save_monthly_reflection,
247 // Weekly Review
248 $crate::commands::get_weekly_review,
249 $crate::commands::complete_weekly_review,
250 $crate::commands::set_task_focus,
251 $crate::commands::clear_all_focus,
252 $crate::commands::set_vacation_days,
253 $crate::commands::check_weekly_review_nudge,
254 // Sync
255 $crate::commands::sync_get_tiers,
256 $crate::commands::sync_status,
257 $crate::commands::sync_start_auth,
258 $crate::commands::sync_complete_auth,
259 $crate::commands::sync_disconnect,
260 $crate::commands::sync_now,
261 $crate::commands::sync_setup_encryption_new,
262 $crate::commands::sync_setup_encryption_existing,
263 $crate::commands::sync_update_settings,
264 $crate::commands::sync_account_info,
265 $crate::commands::sync_subscription_status,
266 $crate::commands::sync_subscribe,
267 // Themes
268 $crate::commands::list_themes,
269 $crate::commands::get_theme,
270 $crate::commands::get_custom_themes_dir,
271 $crate::commands::import_theme,
272 $crate::commands::export_theme,
273 // Import (CSV/TSV)
274 $crate::commands::preview_import,
275 $crate::commands::execute_import,
276 // Import (VCF/ICS)
277 $crate::commands::preview_vcf,
278 $crate::commands::import_vcf,
279 $crate::commands::preview_ics,
280 $crate::commands::import_ics,
281 // Time Tracking
282 $crate::commands::start_timer,
283 $crate::commands::stop_timer,
284 $crate::commands::discard_timer,
285 $crate::commands::get_active_timer,
286 $crate::commands::list_time_sessions,
287 $crate::commands::log_manual_time,
288 $crate::commands::get_time_summary_panel,
289 // Preferences
290 $crate::commands::get_preferences,
291 $crate::commands::set_update_check_on_launch,
292 ]
293 };
294 }
295
296 /// Build the Tauri app with all commands registered.
297 /// Shared between desktop `main()` and mobile entry point.
298 pub fn build_mobile_app() -> tauri::Builder<tauri::Wry> {
299 use state::AppState;
300 use std::sync::Arc;
301 use tauri::Manager;
302 use tokio_util::sync::CancellationToken;
303
304 let mut builder = tauri::Builder::default();
305
306 // Desktop-only plugins
307 #[cfg(not(any(target_os = "ios", target_os = "android")))]
308 {
309 builder = builder
310 .plugin(tauri_plugin_shell::init())
311 .plugin(tauri_plugin_notification::init())
312 .plugin(tauri_plugin_window_state::Builder::new().build());
313 }
314
315 builder
316 .plugin(tauri_plugin_dialog::init())
317 .setup(|app| {
318 // Initialize database
319 let app_handle = app.handle().clone();
320 tauri::async_runtime::block_on(async move {
321 let state = AppState::new(&app_handle).await
322 .expect("Failed to initialize app state");
323 // Reclaim orphaned attachment blobs before the schedulers spawn
324 // and before the UI can create attachments — race-free here.
325 blob_gc::reconcile(&state.pool, &state.data_dir).await;
326 app_handle.manage(Arc::new(state));
327 });
328
329 // Background services that work on all platforms
330 let cancel_token = CancellationToken::new();
331 app.manage(cancel_token.clone());
332
333 let backup_handle = app.handle().clone();
334 let backup_cancel = cancel_token.clone();
335 tauri::async_runtime::spawn(async move {
336 backup_scheduler::start_backup_scheduler(backup_handle, backup_cancel).await;
337 });
338
339 let sync_handle = app.handle().clone();
340 let email_cancel = cancel_token.clone();
341 tauri::async_runtime::spawn(async move {
342 email_sync_scheduler::start_email_sync_scheduler(sync_handle, email_cancel).await;
343 });
344
345 let cloud_sync_handle = app.handle().clone();
346 let cloud_cancel = cancel_token;
347 tauri::async_runtime::spawn(async move {
348 sync_scheduler::start_sync_scheduler(cloud_sync_handle, cloud_cancel).await;
349 });
350
351 // Clean up stale email preview + attachment temp files from previous sessions
352 tauri::async_runtime::spawn(async {
353 commands::email::cleanup_stale_temp_files().await;
354 commands::attachment::cleanup_stale_attachment_temp().await;
355 });
356
357 Ok(())
358 })
359 .invoke_handler(crate::all_commands!())
360 }
361
362 /// Mobile entry point for iOS/Android.
363 #[cfg(mobile)]
364 #[tauri::mobile_entry_point]
365 fn main() {
366 tracing_subscriber::fmt::init();
367
368 build_mobile_app()
369 .run(tauri::generate_context!())
370 .expect("error while running tauri application");
371 }
372