Commit Graph

24 Commits

Author SHA1 Message Date
Ruben Rosario 747d40b1e9 Feature 1: date buttons in EditTask popup
- Add 3 buttons (Today, Tomorrow, Next Week) below Notes
- Popup height increased from 10 to 12
- Tab/Up/Down cycle through 5 fields (Title, Notes, 3 buttons)
- Enter on a date button saves task and sets due date
- Text editing keys restricted to fields 0 and 1
2026-06-21 16:07:16 +01:00
Ruben Rosario b3dcefcd65 Add created_at/updated_at to Task model, DB, and API
- Add created_at and updated_at fields to Task struct
- Preserve existing created_at on upsert in insert_task
- Parse updated field from Google Tasks API response
- Add created_at column to DB schema with migration
2026-06-21 16:03:40 +01:00
Ruben Rosario e45631b235 Remove POPUP_BG from EditTask popup, rely on Clear widget 2026-06-21 15:48:11 +01:00
Ruben Rosario 11bdb712f6 chore: checkpoint before removing POPUP_BG 2026-06-21 15:45:14 +01:00
Ruben Rosario 6254395570 Remove auto-sync timers, keep only manual sync
Sync engine no longer has push (30s) or pull (5min) intervals.
Sync only happens on launch (InitialSync), manual Ctrl+R (FullSync),
or after create/edit/delete (TriggerSync).
2026-06-21 14:38:23 +01:00
Ruben Rosario 3035859dcb Replace Ctrl+Tab with Ctrl+Left/Right for cycling lists
Ctrl+Right selects next list, Ctrl+Left selects previous list,
works from any Focus (Tabs/TaskList/Detail).
2026-06-21 14:34:01 +01:00
Ruben Rosario 6eee90f128 Add task count to task list panel header
Show 'X todo / Y done' in the Tasks panel title bar.

Also includes prior uncommitted work:
- Pagination in fetch_tasks (maxResults=100 + pageToken loop)
- fetch_tasks_since for incremental pull sync
- SyncStats struct with version/last_sync/last_pull/changed counts
- Periodic push (30s) and pull (5min) sync engine
- event::poll(100ms) for non-blocking UI refresh
- Ctrl+R full sync (push + pull)
- refresh_if_needed() to reload data after background sync
- Retry mechanism (MAX_SYNC_RETRIES=3) for sync queue items
- HTTP status code checks in fetch_lists/fetch_tasks/fetch_tasks_since
- Fix move_task URL to use reqwest query()
- Remove CASCADE via replace_all_lists (use insert_list instead)
- has_pending_sync() to prevent pull during pending push
2026-06-21 14:21:14 +01:00
Ruben Rosario ae9910bcbc refactor: read client_secret.json from disk instead of env vars 2026-06-21 10:23:25 +01:00
Ruben Rosario 9da086b7be fix: correct yup-oauth2 types (Authenticator, AccessToken, ApplicationSecret fields) 2026-06-21 10:10:22 +01:00
Ruben Rosario 532e13caef refactor: update main.rs for async ApiClient creation and use has_token 2026-06-21 10:04:41 +01:00
Ruben Rosario 1c95f5f6be refactor: simplify auth popup to show status instead of URL 2026-06-21 10:04:27 +01:00
Ruben Rosario a64fdea005 refactor: simplify auth process in App using start_and_wait_for_auth 2026-06-21 10:04:08 +01:00
Ruben Rosario f3e5ac0789 feat: replace manual OAuth with yup-oauth2 InstalledFlowAuthenticator 2026-06-21 10:03:34 +01:00
Ruben Rosario 469c30084d feat: add yup-oauth2 dependency, remove url and webbrowser 2026-06-21 10:03:11 +01:00
Ruben Rosario 0cbf9262c7 fix: replace device flow with loopback ip redirect flow (RFC 8252)
- Device Flow only works with 'TV and Limited Input devices' OAuth client type
- Desktop app type requires Authorization Code flow with localhost redirect
- New flow: start local TCP server on random port, open browser with auth URL,
  catch redirect containing authorization code, exchange for tokens
- Uses webbrowser crate to auto-open the browser
- Self-contained: no separate HTTP server framework needed, uses std::net
- Popup shows auth URL and waits for browser authorization
- Support for refresh_token for long-lived access
2026-06-20 20:55:08 +01:00
Ruben Rosario 64993b127c fix: improve invalid_client error message with setup steps
- Remove client_secret from initial /device/code request (desktop app type)
- Update error message with full Google Cloud Console setup checklist:
  1. Enable Google Tasks API
  2. Configure OAuth consent screen (Testing mode + test users + scopes)
  3. Create Desktop app OAuth client
- Copy credentials exactly without extra whitespace
2026-06-20 20:36:12 +01:00
Ruben Rosario df1c5f5c2a fix: include client_secret in device code request and improve error guidance
- Include client_secret in the initial POST to /device/code endpoint
- Add check for empty GOOGLE_CLIENT_SECRET with clear error message
- Improve 'Invalid client type' error to suggest creating Desktop app OAuth client
- Include error code in addition to description for better diagnostics
2026-06-20 20:30:26 +01:00
Ruben Rosario 985e8c9bc9 fix: show oauth url and code properly on device auth popup
- Auth flow now waits for user's Enter before starting
- Start auth only when user presses Enter on DeviceAuth popup
- Proper error handling: missing GOOGLE_CLIENT_ID shows clear message
- Error messages displayed in popup with Retry option
- Popup shows instructions before auth, URL+code during auth
- Handle both verification_url and verification_uri field names from Google
- Check HTTP status code and show error_description on failures
- AuthError propagated to render function for display
- Popup border turns green when URL+code are ready
2026-06-20 19:56:41 +01:00
Ruben Rosario 320a9c2572 fix: wire initial sync, oauth flow, and eliminate warnings
- Add token_file_exists() to ApiClient for sync token check
- App::new now checks for token on startup; shows DeviceAuth popup if missing
- Background thread starts OAuth Device Flow automatically when no token
- App::poll_auth() called each frame to detect auth completion
- Auth completion triggers SyncCommand::InitialSync
- run_initial_sync fetches all lists and tasks via Google Tasks API
- Stores results in local DB via replace_all_lists / replace_all_tasks
- App::check_initial_load() refreshes UI from DB after initial sync
- Removed all compile warnings (dead_code annotations)
2026-06-20 19:51:10 +01:00
Ruben Rosario 71befdf9f8 feat(api): google tasks oauth, sync engine, and background worker
- ApiClient with manual OAuth2 Device Flow (no yup-oauth2 dependency)
- Devide auth: POST device/code -> show URL+code -> poll token endpoint
- Token persistence in ~/.config/task_app/token.json
- CRUD: create_task, update_task, delete_task, move_task via Google Tasks API
- fetch_lists and fetch_tasks for initial sync import
- Db wraps Connection in std::sync::Mutex for thread-safe sharing via Arc
- Sync engine: background thread with tokio runtime, processes queue every 30s
- process_sync_queue drains sync_queue and calls API methods
- trigger_sync() called after every local mutation (create/update/delete/reorder)
- Network status propagated to UI (Online/Offline/Syncing)
- Initial sync skeleton ready for full import flow
2026-06-20 19:41:47 +01:00
Ruben Rosario 3b6726a726 feat(app): keyboard event handling with panel switching and CRUD
- App struct with full state (lists, tasks, focus, popups, DB)
- Tab cycles focus: Tabs -> TaskList -> Detail -> Tabs
- Left/Right arrows switch lists when focus on Tabs
- Up/Down navigate tasks (TaskList) or scroll (Detail)
- Alt+Up/Down reorder tasks with position persistence
- n: create new list or task, d: delete, e: edit title
- Enter on Detail opens DatePicker popup
- InputPopup with full text editing (navigation, insert, delete)
- ConfirmDelete popup before destructive actions
- DatePicker adjusts draft_date with Up/Down
- main.rs: terminal setup, event loop, raw mode, alternate screen
2026-06-20 19:38:12 +01:00
Ruben Rosario efc3c1c84c feat(ui): render layout, tabs, panes, popups, and status bar
- ui/mod.rs: AppView struct, Focus/Popup/NetworkStatus enums, draw() layout function
- Top-Tabs + Bottom-Split layout (50/50 left/right)
- TabsBar: list selector with highlight on active
- TaskListPane: checkbox + title + due date per task
- DetailPane: title, status, due, notes of selected task
- InputPopup: centered modal with cursor
- DatePickerPopup: date/time edit modal with instructions
- ConfirmDeletePopup: confirmation dialog
- DeviceAuthPopup: OAuth URL + code display
- StatusBar: ONLINE/OFFLINE/SYNCING with color coding
2026-06-20 19:37:13 +01:00
Ruben Rosario 3626331a70 feat(infra): add sqlite storage with position-based ordering
- Db struct with rusqlite Connection (WAL mode, foreign keys)
- Tables: task_lists, tasks (with position column), sync_queue
- CRUD: get/insert/update/delete for lists and tasks
- reorder_task shifts positions of sibling tasks
- replace_all_lists and replace_all_tasks for sync import
- push_sync and drain_sync for offline queue management
- All reads sorted by position ASC
2026-06-20 19:35:56 +01:00
Ruben Rosario adf3889863 chore: initial project setup
- Cargo init with dependencies (ratatui, crossterm, tokio, reqwest, rusqlite, serde, chrono, dirs)
- Module structure: domain/, ui/, infrastructure/
- Domain models (TaskList, Task, TaskStatus, SyncAction, SyncQueueItem)
- .gitignore for target/ and *.db
- Rustls-based TLS (no OpenSSL dependency)
2026-06-20 19:35:19 +01:00