(WIP) Adding google calendar support#1520
(WIP) Adding google calendar support#1520githubdoramon wants to merge 4 commits intofastrepl:mainfrom
Conversation
📝 WalkthroughWalkthroughAdds a full Google Calendar integration: new Tauri plugin with OAuth, calendar/events/contacts APIs, commands, worker, and UI for account management and calendar selection. Renames event URL field from google_event_url to event_external_url across interface, DB schema, seed data, and usages. Expands calendar DB metadata and adjusts migrations and ops. Wires plugin into desktop app and sync flows. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Desktop as Desktop App (UI)
participant Tauri as Tauri App
participant GPlugin as Google Calendar Plugin
participant OAuth as Google OAuth
participant Store as Token Store
participant DB as User DB
User->>Desktop: Click "Add Google Account"
Desktop->>Tauri: invoke add_google_account()
Tauri->>GPlugin: add_google_account()
GPlugin->>OAuth: Build auth URL (combined scopes)
GPlugin-->>Tauri: Return auth URL
Tauri-->>Desktop: URL
Desktop->>OAuth: Open auth URL (browser)
OAuth-->>GPlugin: Redirect with code (local callback)
GPlugin->>OAuth: Exchange code for tokens
OAuth-->>GPlugin: Access + Refresh token
GPlugin->>Store: Persist tokens + account info
GPlugin->>DB: Sync calendars (initial)
DB-->>GPlugin: Upsert calendars (connected)
GPlugin-->>Tauri: Done
Tauri-->>Desktop: Success
sequenceDiagram
autonumber
participant Worker as Plugin Worker (cron)
participant GPlugin as Google Calendar Plugin
participant Store as Token Store
participant API as Google Calendar API
participant DB as User DB
rect rgba(200,230,255,0.3)
note over Worker,GPlugin: Every 10 minutes
Worker->>GPlugin: sync_calendars_with_db(user)
GPlugin->>Store: Load accounts + tokens
loop per account
GPlugin->>API: GET /users/me/calendarList (paginate)
API-->>GPlugin: Calendars
GPlugin->>DB: Upsert calendars (status, account_id, last_sync_at)
end
end
rect rgba(200,255,200,0.3)
note over Worker,GPlugin: Every 5 minutes
Worker->>GPlugin: sync_events_with_db(user)
GPlugin->>DB: Get selected calendars
loop per selected calendar
GPlugin->>Store: Resolve account token
GPlugin->>API: GET /calendars/{id}/events (paginate)
API-->>GPlugin: Events
GPlugin->>DB: Upsert events (event_external_url, times, attendees)
end
end
sequenceDiagram
autonumber
participant UI as Desktop UI
participant Apple as Apple Calendar Plugin
participant Google as Google Calendar Plugin
UI->>Google: sync_events(null)
UI->>Apple: sync_events()
par Parallel sync
Google-->>UI: Ok/Error (logged)
Apple-->>UI: Ok/Error (logged)
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/desktop/src/components/settings/views/calendar.tsx (1)
28-29: Hardcoded accordion values may cause UX issues.The accordion
defaultValueandAccordionItemvalueare both hardcoded to"apple". This means:
- The accordion will always default to the Apple Calendar item being open, even if Google Calendar is the first integration shown.
- All integration types will share the same accordion item value, which could cause unexpected behavior.
Consider deriving these values dynamically:
-<Accordion type="single" collapsible defaultValue={"apple"}> - <AccordionItem value="apple"> +<Accordion type="single" collapsible defaultValue={type}> + <AccordionItem value={type}>crates/db-user/src/calendars_ops.rs (1)
225-233: Update theCalendarliteral intest_calendarsto include the new optional fields
Incrates/db-user/src/calendars_ops.rs(around line 225), theCalendarstruct literal omitsconnection_status,account_id,last_sync_error, andlast_sync_at. Either supply values for those fields or append..Default::default()to the struct literal so the test compiles.
🧹 Nitpick comments (25)
plugins/google-calendar/src/error.rs (2)
1-32: Consider adding #[non_exhaustive] for future compatibility.The Error enum lacks
#[non_exhaustive], meaning adding new variants will be a breaking change. For a plugin that's likely to evolve, this could limit future flexibility.Apply this diff:
+#[non_exhaustive] #[derive(Debug, thiserror::Error)] pub enum Error {
3-31: Overlapping error semantics between Auth, OAuth, and token variants.The variants
Auth,OAuth,InvalidToken, andTokenExpiredhave overlapping semantics. Consider whether OAuth and token-related errors could be subsumed underAuthfor simpler error handling, or whether the distinction provides meaningful value for error recovery.plugins/google-calendar/src/worker.rs (1)
46-64: Optional: pull cron expressions into named consts for clarity.Keeps schedule intent DRY and easier to tweak.
Apply:
pub async fn monitor(state: WorkerState) -> Result<(), std::io::Error> { use std::str::FromStr; + const EVERY_10_MIN_AT_SEC0: &str = "0 */10 * * * *"; + const EVERY_5_MIN_AT_SEC0: &str = "0 */5 * * * *"; + apalis::prelude::Monitor::new() .register({ WorkerBuilder::new(CALENDARS_SYNC_WORKER_NAME) .data(state.clone()) .backend(apalis_cron::CronStream::new( - apalis_cron::Schedule::from_str("0 */10 * * * *").unwrap(), + apalis_cron::Schedule::from_str(EVERY_10_MIN_AT_SEC0).unwrap(), )) .build_fn(perform_calendars_sync) }) .register({ WorkerBuilder::new(EVENTS_SYNC_WORKER_NAME) .data(state) .backend(apalis_cron::CronStream::new( - apalis_cron::Schedule::from_str("0 */5 * * * *").unwrap(), + apalis_cron::Schedule::from_str(EVERY_5_MIN_AT_SEC0).unwrap(), )) .build_fn(perform_events_sync) })No behavior change. As per coding guidelines.
crates/db-user/src/calendars_migration_2.sql (1)
1-11: LGTM with optional refactor suggestion.The four new columns support calendar sync status tracking. The nullable fields are appropriate for existing calendar rows. Using TEXT for timestamps is consistent with the existing events table pattern.
Consider combining the four ALTER TABLE statements into a single statement for slightly better performance during migration:
-ALTER TABLE calendars -ADD COLUMN connection_status TEXT DEFAULT 'connected'; - -ALTER TABLE calendars -ADD COLUMN account_id TEXT; - -ALTER TABLE calendars -ADD COLUMN last_sync_error TEXT; - -ALTER TABLE calendars -ADD COLUMN last_sync_at TEXT; +ALTER TABLE calendars +ADD COLUMN connection_status TEXT DEFAULT 'connected', +ADD COLUMN account_id TEXT, +ADD COLUMN last_sync_error TEXT, +ADD COLUMN last_sync_at TEXT;plugins/google-calendar/build.rs (1)
17-17: Remove trailing whitespace.Line 17 contains trailing whitespace after
"start_worker".Apply this diff:
- "start_worker", + "start_worker",crates/db-user/src/calendars_types.rs (1)
12-15: Consider usingDateTime<Utc>forlast_sync_at.The
last_sync_atfield is typed asOption<String>, which is inconsistent with the existing pattern in this codebase. TheEventstruct (inevents_types.rs) usesDateTime<Utc>for temporal fields likestart_dateandend_date. UsingDateTime<Utc>would provide type safety, enable datetime operations, and avoid error-prone string parsing.If changing the type, update the field definition:
- pub last_sync_at: Option<String>, + pub last_sync_at: Option<DateTime<Utc>>,Note: This would require corresponding changes in the database schema, migrations, and all usage sites.
plugins/google-calendar/Cargo.toml (3)
24-24: Don’t ship Tauri’stestfeature to dependents; keep it only for dev/tests.Enable
taurinormally for the library and movefeatures = ["test"]under dev-dependencies for test-only usage (your tests already need it).-[dependencies] -tauri = { workspace = true, features = ["test"] } +[dependencies] +tauri = { workspace = true } [dev-dependencies] specta-typescript = { workspace = true } +tauri = { workspace = true, features = ["test"] }Also applies to: 13-15
42-42: Use rustls only (avoid pulling native-tls) by disabling reqwest default features.Right now both TLS stacks may compile. Trim size and avoid ambiguity.
-reqwest = { version = "0.11", features = ["json", "rustls-tls"] } +reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }
4-4: Polish package metadata (authors/description).Replace placeholders to aid crates.io/docs and internal tooling.
-authors = ["You"] -description = "" +authors = ["HyprNote Team"] +description = "Tauri plugin providing Google Calendar multi-account sync and bindings."Also applies to: 8-8
plugins/google-calendar/src/lib.rs (2)
78-89: Update tests to the non-generic builder.Keep tests compiling with the Wry-specific builder.
- make_specta_builder::<tauri::Wry>() + make_specta_builder() .export( specta_typescript::Typescript::default()
20-25: Consider an async-aware mutex for plugin state.Using
std::sync::Mutexin async contexts can block threads; prefertauri::async_runtime::Mutexortokio::sync::Mutexfor state accessed from async commands. Requires a small change inext.rscall sites.plugins/apple-calendar/src/sync.rs (3)
2-2: Clean up unused imports.
serde_jsonandCalendarSourcearen’t used.-use serde_json; - -use hypr_calendar_interface::{CalendarSource, EventFilter}; +use hypr_calendar_interface::EventFilter;Also applies to: 4-4
131-132: Remove the unused “handled_system_event_ids” tracking.The HashSet is written but never read; you already avoid duplicates via
to_update/already_exists.- let mut handled_system_event_ids = std::collections::HashSet::<String>::new(); + // no-op: removed unused handled set- // Mark this system event as handled - handled_system_event_ids.insert(composite_id); + // already accounted via to_updateAlso applies to: 194-195
47-52: Avoid taking a reference to a temporary Vec just to clone.Use
cloned().unwrap_or_default()for clarity.- let events = system_events_per_tracking_id - .get(&db_calendar.tracking_id) - .unwrap_or(&vec![]) - .clone(); + let events = system_events_per_tracking_id + .get(&db_calendar.tracking_id) + .cloned() + .unwrap_or_default();plugins/google-calendar/src/contacts_api.rs (1)
15-17: Confirm population ofaccount_email.
Contact.account_emailisn’t set inget_contactsresults. If callers rely on this metadata, ensure it’s filled upstream before exposing to UI or storage. Otherwise, consider removing it here to avoid confusion.Also applies to: 91-118
apps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsx (2)
84-89: Polling every 2s: consider gating or reducing frequency.A constant 2s
refetchIntervalcan be noisy. Consider gating to when the settings view is visible/active, or relaxing the interval. This keeps UX snappy without background churn.
93-110: New-account detection by count is brittle.Using only count assumes the newest account is last. If ordering changes, the toast may reference the wrong account. Track a Set of emails and diff to find additions.
plugins/google-calendar/src/oauth.rs (2)
67-81: Enable incremental auth.Add
include_granted_scopes=trueto the authorization URL to support incremental consent.Apply this diff:
url.query_pairs_mut() .append_pair("client_id", &self.config.client_id) .append_pair("redirect_uri", &self.config.redirect_uri) .append_pair("response_type", "code") .append_pair("scope", &scope_str) .append_pair("access_type", "offline") .append_pair("prompt", "consent") + .append_pair("include_granted_scopes", "true") .append_pair("state", state);
46-59: Scope minimization.If write access isn’t required, drop
"https://www.googleapis.com/auth/calendar"and keep onlycalendar.readonlyto adhere to least‑privilege. If writes are needed, ignore.plugins/google-calendar/src/calendar_api.rs (3)
99-104: Reuse a single configured Client (add timeout) to harden external calls.Constructing a default reqwest::Client without a timeout risks hung requests; we also duplicate this pattern across contacts_api.rs. Prefer one configured client (e.g., timeout, user-agent) reused across APIs or passed in; this reduces pools and avoids hangs.
Tip: Extract a small ApiClient wrapper shared by calendar_api.rs and contacts_api.rs to consolidate make_request and client config. Based on learnings
162-187: Prefer URL/query building over manual string concatenation.Manual query assembly is error‑prone and can miss proper encoding. Use reqwest::Url with .query_pairs_mut() or the request builder’s .query(&[("k", v)]) to build timeMin, timeMax, pageToken, singleEvents, orderBy.
198-222: Fallback-to-primary triggers on any error; consider narrowing the condition.The fallback executes for any Err, potentially masking real issues (e.g., 401/429). If you keep this behavior, at least gate it to specific statuses (e.g., 404 invalid calendarId).
plugins/google-calendar/src/ext.rs (3)
446-456: Selection should use the stored selected flag, not connection_status.You derive “selected” from connection_status == "syncing". This can drift from the actual selected boolean you persist. Use the selected field for truth.
- for calendar in calendars { - let selected = calendar.connection_status.as_deref() == Some("syncing"); - - calendar_selections.push(CalendarSelection { + for calendar in calendars { + calendar_selections.push(CalendarSelection { calendar_id: calendar.tracking_id.clone(), calendar_name: calendar.name.clone(), - selected, + selected: calendar.selected, color: None, }); }Also applies to: 419-459
766-786: Compile-time client secret embeds in binary; prefer runtime/configured source.option_env! bakes GOOGLE_CLIENT_SECRET into the app. For desktop OAuth, client secret isn’t truly secret, but embedding hinders rotation and distribution variants. Consider loading from runtime config/Store or OS keystore.
624-638: Upsert prevents duplicate UUIDs on sync
Theupsert_eventSQL usesON CONFLICT(tracking_id) DO UPDATE, so the newly generated UUID is only applied on first insert and won’t create duplicate rows on subsequent syncs. If your intent is to allow the same Google event ID across multiple calendars, consider extending the conflict target to(tracking_id, calendar_id).
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (30)
Cargo.lockis excluded by!**/*.lockplugins/db/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/google-calendar/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/google-calendar/permissions/autogenerated/commands/add_google_account.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/calendar_access_status.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/contacts_access_status.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_auth_status.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_calendar_selections.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_calendars.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_calendars_for_account.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_connected_accounts.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_contacts.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/get_contacts_for_account.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/handle_oauth_callback.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/refresh_tokens.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/remove_google_account.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/request_calendar_access.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/request_combined_access.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/request_contacts_access.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/revoke_access.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/search_contacts.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/set_calendar_selected.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/start_worker.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/stop_worker.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/sync_calendars.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/sync_contacts.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/commands/sync_events.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/google-calendar/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**
📒 Files selected for processing (43)
Cargo.toml(1 hunks)apps/desktop/package.json(1 hunks)apps/desktop/src-tauri/Cargo.toml(1 hunks)apps/desktop/src-tauri/capabilities/default.json(1 hunks)apps/desktop/src-tauri/src/lib.rs(1 hunks)apps/desktop/src/components/human-profile/upcoming-events.tsx(1 hunks)apps/desktop/src/components/left-sidebar/events-list.tsx(2 hunks)apps/desktop/src/components/organization-profile/upcoming-events.tsx(1 hunks)apps/desktop/src/components/settings/components/ai/stt-view-local.tsx(3 hunks)apps/desktop/src/components/settings/components/calendar/calendar-icon-with-text.tsx(2 hunks)apps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsx(1 hunks)apps/desktop/src/components/settings/views/calendar.tsx(2 hunks)apps/desktop/src/components/workspace-calendar/event-card.tsx(1 hunks)apps/desktop/src/locales/en/messages.ts(1 hunks)apps/desktop/src/locales/ko/messages.ts(1 hunks)crates/calendar-apple/src/lib.rs(1 hunks)crates/calendar-google/src/lib.rs(1 hunks)crates/calendar-interface/src/lib.rs(2 hunks)crates/db-core/src/lib.rs(1 hunks)crates/db-user/src/calendars_migration_2.sql(1 hunks)crates/db-user/src/calendars_ops.rs(3 hunks)crates/db-user/src/calendars_types.rs(1 hunks)crates/db-user/src/events_migration_3.sql(1 hunks)crates/db-user/src/events_ops.rs(6 hunks)crates/db-user/src/events_types.rs(1 hunks)crates/db-user/src/lib.rs(2 hunks)plugins/apple-calendar/src/sync.rs(5 hunks)plugins/db/seed/dev.json(19 hunks)plugins/db/seed/onboarding.json(1 hunks)plugins/db/seed/schema.json(2 hunks)plugins/google-calendar/Cargo.toml(1 hunks)plugins/google-calendar/build.rs(1 hunks)plugins/google-calendar/js/index.ts(1 hunks)plugins/google-calendar/package.json(1 hunks)plugins/google-calendar/src/calendar_api.rs(1 hunks)plugins/google-calendar/src/commands.rs(1 hunks)plugins/google-calendar/src/contacts_api.rs(1 hunks)plugins/google-calendar/src/error.rs(1 hunks)plugins/google-calendar/src/ext.rs(1 hunks)plugins/google-calendar/src/lib.rs(1 hunks)plugins/google-calendar/src/oauth.rs(1 hunks)plugins/google-calendar/src/worker.rs(1 hunks)plugins/google-calendar/tsconfig.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,tsx,rs}
⚙️ CodeRabbit configuration file
**/*.{js,ts,tsx,rs}: 1. Do not add any error handling. Keep the existing one.
2. No unused imports, variables, or functions.
3. For comments, keep it minimal. It should be about "Why", not "What".
Files:
crates/db-user/src/events_types.rscrates/calendar-interface/src/lib.rscrates/db-user/src/events_ops.rscrates/db-core/src/lib.rsapps/desktop/src/locales/ko/messages.tsapps/desktop/src/components/organization-profile/upcoming-events.tsxplugins/google-calendar/src/error.rsapps/desktop/src-tauri/src/lib.rsplugins/google-calendar/js/index.tsplugins/google-calendar/src/oauth.rsplugins/google-calendar/build.rsapps/desktop/src/components/settings/components/calendar/calendar-icon-with-text.tsxapps/desktop/src/components/human-profile/upcoming-events.tsxapps/desktop/src/components/workspace-calendar/event-card.tsxplugins/apple-calendar/src/sync.rscrates/db-user/src/calendars_ops.rsapps/desktop/src/components/left-sidebar/events-list.tsxcrates/db-user/src/calendars_types.rscrates/calendar-apple/src/lib.rscrates/db-user/src/lib.rsapps/desktop/src/locales/en/messages.tsapps/desktop/src/components/settings/components/ai/stt-view-local.tsxcrates/calendar-google/src/lib.rsplugins/google-calendar/src/commands.rsapps/desktop/src/components/settings/views/calendar.tsxplugins/google-calendar/src/ext.rsplugins/google-calendar/src/calendar_api.rsplugins/google-calendar/src/lib.rsapps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsxplugins/google-calendar/src/contacts_api.rsplugins/google-calendar/src/worker.rs
🧬 Code graph analysis (12)
apps/desktop/src/locales/ko/messages.ts (1)
apps/desktop/src/locales/en/messages.ts (1)
messages(1-1)
apps/desktop/src-tauri/src/lib.rs (2)
plugins/tracing/src/lib.rs (1)
init(28-62)plugins/connector/src/lib.rs (1)
init(53-59)
plugins/google-calendar/build.rs (1)
apps/desktop/src-tauri/src/lib.rs (1)
main(13-249)
crates/db-user/src/calendars_ops.rs (2)
crates/db-core/src/lib.rs (1)
conn(17-22)plugins/google-calendar/src/ext.rs (2)
get_calendars_needing_reconnection(60-60)get_calendars_needing_reconnection(494-526)
apps/desktop/src/locales/en/messages.ts (1)
apps/desktop/src/locales/ko/messages.ts (1)
messages(1-1)
plugins/google-calendar/src/commands.rs (3)
plugins/google-calendar/src/ext.rs (38)
sync_calendars(28-28)sync_calendars(114-125)get_calendars(29-29)get_calendars(375-407)sync_events(31-31)sync_events(160-171)sync_contacts(37-37)sync_contacts(247-250)get_contacts(38-38)get_contacts(252-255)search_contacts(39-39)search_contacts(257-260)revoke_access(41-41)revoke_access(262-265)refresh_tokens(42-42)refresh_tokens(267-270)get_connected_accounts(49-49)get_connected_accounts(272-286)add_google_account(50-50)add_google_account(65-112)remove_google_account(51-51)remove_google_account(288-373)get_calendars_for_account(52-52)get_calendars_for_account(409-412)get_contacts_for_account(53-53)get_contacts_for_account(414-417)get_calendar_selections(56-56)get_calendar_selections(419-459)set_calendar_selected(57-57)set_calendar_selected(461-492)start_worker(45-45)start_worker(548-574)stop_worker(46-46)stop_worker(576-583)get_calendars_needing_reconnection(60-60)get_calendars_needing_reconnection(494-526)attempt_reconnect_account(61-61)attempt_reconnect_account(528-546)plugins/google-calendar/src/contacts_api.rs (2)
get_contacts(91-118)search_contacts(120-143)crates/db-user/src/calendars_ops.rs (1)
get_calendars_needing_reconnection(181-200)
apps/desktop/src/components/settings/views/calendar.tsx (2)
apps/desktop/src/types/index.ts (1)
CalendarIntegration(21-23)apps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsx (1)
GoogleCalendarIntegrationDetails(81-305)
plugins/google-calendar/src/ext.rs (6)
plugins/apple-calendar/src/sync.rs (4)
sync_calendars(9-24)sync_events(26-65)db(435-440)db(462-476)plugins/google-calendar/src/commands.rs (19)
sync_calendars(36-38)get_calendars(42-46)sync_events(50-57)sync_contacts(61-63)get_contacts(67-71)search_contacts(75-80)revoke_access(84-86)refresh_tokens(90-92)start_worker(138-140)stop_worker(144-147)get_connected_accounts(96-98)add_google_account(102-104)remove_google_account(108-110)get_calendars_for_account(114-116)get_contacts_for_account(120-122)get_calendar_selections(126-128)set_calendar_selected(132-134)get_calendars_needing_reconnection(151-155)attempt_reconnect_account(159-164)plugins/google-calendar/src/contacts_api.rs (3)
get_contacts(91-118)search_contacts(120-143)new(62-67)plugins/google-calendar/src/oauth.rs (2)
new(60-65)refresh_token(123-153)apps/desktop/src-tauri/src/ext.rs (2)
state(95-95)app(176-176)plugins/google-calendar/src/worker.rs (1)
monitor(43-67)
plugins/google-calendar/src/calendar_api.rs (1)
plugins/google-calendar/src/contacts_api.rs (2)
new(62-67)make_request(69-89)
plugins/google-calendar/src/lib.rs (3)
apps/desktop/src-tauri/src/lib.rs (3)
make_specta_builder(251-263)make_specta_builder(271-271)export_types(270-280)plugins/google-calendar/src/ext.rs (38)
sync_calendars(28-28)sync_calendars(114-125)get_calendars(29-29)get_calendars(375-407)sync_events(31-31)sync_events(160-171)sync_contacts(37-37)sync_contacts(247-250)get_contacts(38-38)get_contacts(252-255)search_contacts(39-39)search_contacts(257-260)revoke_access(41-41)revoke_access(262-265)refresh_tokens(42-42)refresh_tokens(267-270)get_connected_accounts(49-49)get_connected_accounts(272-286)add_google_account(50-50)add_google_account(65-112)remove_google_account(51-51)remove_google_account(288-373)get_calendars_for_account(52-52)get_calendars_for_account(409-412)get_contacts_for_account(53-53)get_contacts_for_account(414-417)get_calendar_selections(56-56)get_calendar_selections(419-459)set_calendar_selected(57-57)set_calendar_selected(461-492)start_worker(45-45)start_worker(548-574)stop_worker(46-46)stop_worker(576-583)get_calendars_needing_reconnection(60-60)get_calendars_needing_reconnection(494-526)attempt_reconnect_account(61-61)attempt_reconnect_account(528-546)plugins/google-calendar/src/commands.rs (19)
sync_calendars(36-38)get_calendars(42-46)sync_events(50-57)sync_contacts(61-63)get_contacts(67-71)search_contacts(75-80)revoke_access(84-86)refresh_tokens(90-92)get_connected_accounts(96-98)add_google_account(102-104)remove_google_account(108-110)get_calendars_for_account(114-116)get_contacts_for_account(120-122)get_calendar_selections(126-128)set_calendar_selected(132-134)start_worker(138-140)stop_worker(144-147)get_calendars_needing_reconnection(151-155)attempt_reconnect_account(159-164)
apps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsx (2)
packages/ui/src/components/ui/toast.tsx (1)
toast(67-86)packages/ui/src/components/ui/button.tsx (1)
Button(37-89)
plugins/google-calendar/src/contacts_api.rs (3)
plugins/google-calendar/src/calendar_api.rs (4)
new(99-104)make_request(106-128)make_request(190-190)make_request(206-206)plugins/google-calendar/src/ext.rs (4)
get_contacts(38-38)get_contacts(252-255)search_contacts(39-39)search_contacts(257-260)plugins/google-calendar/src/commands.rs (2)
get_contacts(67-71)search_contacts(75-80)
🔇 Additional comments (37)
apps/desktop/src/components/settings/components/ai/stt-view-local.tsx (1)
15-15: Verify that this change belongs in this PR.This modification expands the default models to include "QuantizedBase". However, the PR is titled "Adding google calendar support", and this STT model configuration appears unrelated to that objective.
Please confirm whether this change was intentionally included or if it should be moved to a separate PR focused on STT model improvements.
apps/desktop/src/components/organization-profile/upcoming-events.tsx (1)
68-70: Field rename is consistent.The change from
google_event_urltoevent_external_urlis correctly applied in both the conditional check and href attribute, matching the pattern used in other components.apps/desktop/src/components/workspace-calendar/event-card.tsx (1)
88-90: Field rename is correct with proper null check.The change from
google_event_urltoevent_external_urlis properly guarded with a conditional check and preserves the existing error handling.apps/desktop/src/components/human-profile/upcoming-events.tsx (1)
61-63: Unable to verify Event type in this repo
Eventis imported from@hypr/plugin-db, so please confirm in that package that theEventtype definesevent_external_urland that allgoogle_event_urlreferences have been removed.apps/desktop/src-tauri/capabilities/default.json (1)
40-40: LGTM!The permission addition follows the established pattern and is correctly positioned after auth-related permissions.
apps/desktop/src/components/settings/components/calendar/calendar-icon-with-text.tsx (2)
1-1: LGTM!Replacing the custom GoogleIcon with the Remix Icon library improves consistency with AppleIcon and reduces maintenance overhead.
11-11: LGTM!The explicit size prop ensures consistent icon sizing across calendar types.
plugins/google-calendar/tsconfig.json (1)
1-10: Approve TypeScript configuration Extended base config at plugins/tsconfig.base.json exists and path mappings are correct.apps/desktop/src-tauri/src/lib.rs (1)
78-78: LGTM: plugin wired into startup chain.Order looks fine next to auth/db/specta-driven plugins; no additional handling added.
plugins/db/seed/onboarding.json (1)
60-60: Verified no leftovergoogle_event_url
All seeds, types, and code now useevent_external_url; the only remaininggoogle_event_urlreferences are in the initial migration (crates/db-user/src/events_migration.sql) and its rename migration, which are expected.crates/db-user/src/lib.rs (2)
145-145: LGTM! Migration array size updated correctly.The array size increase from 27 to 29 matches the two new migrations appended below.
173-174: LGTM! Migrations appended correctly.The new migrations are properly appended to the end of the array, following the "append only" policy. The ordering (calendars_migration_2 before events_migration_3) is appropriate.
apps/desktop/src-tauri/Cargo.toml (1)
35-35: LGTM! Dependency added correctly.The new Google Calendar plugin dependency follows the project's workspace dependency pattern and maintains alphabetical ordering.
crates/db-user/src/events_ops.rs (3)
42-42: LGTM! UPDATE query field renamed consistently.Both the SQL column name and parameter binding correctly use the new
event_external_urlfield name.Also applies to: 55-55
88-88: LGTM! INSERT/UPSERT queries field renamed consistently.All occurrences of the field in the upsert operation (column list, VALUES clause, UPDATE SET clause, and parameter binding) correctly use the new
event_external_urlname.Also applies to: 100-100, 108-108, 121-121
248-248: LGTM! Test updated correctly.The test construction and assertion now use
event_external_urlinstead of the old field name.Also applies to: 255-255
crates/db-user/src/events_migration_3.sql (1)
1-1: LGTM! Column rename migration is correct.The migration properly renames the column from
google_event_urlto the more genericevent_external_url, supporting multi-provider calendar integration. The SQLite RENAME COLUMN syntax is correct and preserves existing data.Cargo.toml (1)
111-111: LGTM!The workspace dependency for the Google Calendar plugin is correctly added with the standard path pattern and proper alphabetical ordering.
plugins/google-calendar/package.json (1)
1-11: LGTM!The package structure follows the standard Tauri plugin pattern with:
- Appropriate workspace privacy setting
- Correct TypeScript entry point
- Standard codegen script for type generation
- Compatible Tauri API dependency
apps/desktop/package.json (1)
24-24: LGTM!The Google Calendar plugin dependency is correctly added with the workspace protocol and proper alphabetical ordering among other plugin dependencies.
crates/calendar-google/src/lib.rs (1)
106-106: Approve field renameNo references to
google_event_urlremain;event_external_urlis applied consistently across the interface definitions, database schema, and all provider implementations.crates/db-user/src/events_types.rs (1)
15-15: LGTM! Field rename generalizes the naming.The rename from
google_event_urltoevent_external_urlis a sensible generalization that reflects support for multiple calendar platforms (Apple, Google, Outlook) rather than being Google-specific.plugins/google-calendar/build.rs (1)
21-23: LGTM! Standard Tauri plugin build pattern.The build script correctly registers the plugin commands using the standard Tauri plugin builder pattern.
crates/calendar-interface/src/lib.rs (2)
47-47: LGTM! Field rename aligns with broader refactoring.The rename from
google_event_urltoevent_external_urlis consistent with the changes in other files and generalizes the field name for multi-platform support.
88-88: LGTM! Usage correctly updated to new field name.The reference to the renamed field is properly updated in the Google platform branch.
plugins/db/seed/dev.json (1)
64-311: LGTM! Seed data correctly updated with field rename.All event objects in the seed data have been consistently updated to use
event_external_urlinstead ofgoogle_event_url, with values properly preserved.apps/desktop/src/components/settings/views/calendar.tsx (2)
1-10: LGTM! Google Calendar integration added correctly.The import and supportedIntegrations array are properly updated to include Google Calendar support.
34-35: LGTM! Conditional rendering is correct.The conditional rendering logic properly displays the appropriate integration details component based on the integration type.
plugins/db/seed/schema.json (2)
163-186: LGTM! Calendar sync status fields added correctly.The new optional fields (
connection_status,account_id,last_sync_error,last_sync_at) provide the necessary metadata to track synchronization state and account associations for multi-account calendar integrations.
239-239: LGTM! Field rename improves generality.Renaming
google_event_urltoevent_external_urlmakes the field platform-agnostic and better reflects its purpose for any external calendar provider.crates/calendar-apple/src/lib.rs (1)
255-255: LGTM! Field rename applied correctly.The Event construction now uses
event_external_url(previouslygoogle_event_url), aligning with the platform-agnostic rename across the codebase. Setting it toNonefor Apple Calendar events is appropriate.crates/db-user/src/calendars_ops.rs (2)
78-135: LGTM! Calendar upsert extended correctly.The upsert operation now properly handles the new fields (
connection_status,account_id,last_sync_error,last_sync_at) in both the INSERT and UPDATE paths, with corresponding parameter bindings.
181-200: LGTM! Reconnection query is well-structured.The
get_calendars_needing_reconnectionmethod correctly queries calendars by user and connection status, providing a clean interface for identifying calendars requiring reconnection.apps/desktop/src/components/left-sidebar/events-list.tsx (1)
40-56: Error swallowing may mask sync failures.The try/catch block logs errors but doesn't rethrow them, which means:
- The mutation always resolves successfully (never rejects), even when both syncs fail.
- The
onSuccesscallback (lines 58-64) fires and invalidates queries regardless of sync failure.- Users won't see any indication that sync failed, and the UI may show stale data.
Consider one of these approaches:
Option 1: Let errors propagate and handle them in the mutation callbacks:
- try { - const result = await Promise.all([ - googleCalendarCommands.syncEvents(null), - appleCalendarCommands.syncEvents(), - ]); - const elapsedTime = Date.now() - startTime; - - if (elapsedTime < 500) { - await new Promise(resolve => setTimeout(resolve, 500 - elapsedTime)); - } - - return result; - } catch (error) { - console.error("error", error); - } + const result = await Promise.all([ + googleCalendarCommands.syncEvents(null), + appleCalendarCommands.syncEvents(), + ]); + const elapsedTime = Date.now() - startTime; + + if (elapsedTime < 500) { + await new Promise(resolve => setTimeout(resolve, 500 - elapsedTime)); + } + + return result;Then add an
onErrorcallback to the mutation to handle failures appropriately.Option 2: Use
Promise.allSettledif partial sync is acceptable:- try { - const result = await Promise.all([ + const results = await Promise.allSettled([ googleCalendarCommands.syncEvents(null), appleCalendarCommands.syncEvents(), ]); + + // Check if any syncs failed + const failures = results.filter(r => r.status === 'rejected'); + if (failures.length > 0) { + console.error("Some syncs failed:", failures); + } + const elapsedTime = Date.now() - startTime; if (elapsedTime < 500) { await new Promise(resolve => setTimeout(resolve, 500 - elapsedTime)); } - return result; - } catch (error) { - console.error("error", error); - } + return results;Likely an incorrect or invalid review comment.
plugins/apple-calendar/src/sync.rs (2)
102-106: Calendar metadata defaults look good; confirm allowed values.Ensure
connection_status = "connected"andaccount_id = "local"match the new DB contract/enums for calendars.
175-176: Field rename toevent_external_urlis consistent.Updates/new events preserve or clear the external link as expected.
If any downstream code still references
google_event_url, I can scan the repo and list stragglers.Also applies to: 219-221, 301-306, 325-329
plugins/google-calendar/src/commands.rs (1)
136-141: LGTM: start/stop worker commands are thin pass-throughs.Clear delegation to app extension; no extra error handling introduced.
| setProviderToLocal, | ||
| }: STTViewProps) { | ||
|
|
||
| console.log("sttModels", sttModels); |
There was a problem hiding this comment.
Remove debugging console.log statements.
These console.log statements should be removed before merging. Debug logs in production code clutter the console and may inadvertently expose information.
Apply this diff to remove the debug statements:
-
- console.log("sttModels", sttModels);
const { userId } = useHypr(); ), [sttModels]);
-
- console.log("modelsToShow", modelsToShow);
-Also applies to: 132-132
🤖 Prompt for AI Agents
In apps/desktop/src/components/settings/components/ai/stt-view-local.tsx around
lines 60 and 132, remove the debugging console.log statements (e.g.,
console.log("sttModels", sttModels); and the one at line 132) so no debug output
remains in production code; update the file by deleting those console.log lines
and run a quick lint/check to ensure no unused variables or trailing commas are
introduced.
| <Checkbox | ||
| id={`calendar-${calSelection.calendarId}`} | ||
| checked={calSelection.selected} | ||
| onCheckedChange={(checked) => | ||
| toggleCalendarSelectedMutation.mutate({ | ||
| calendarId: calSelection.calendarId, | ||
| selected: checked as boolean | ||
| }) | ||
| } |
There was a problem hiding this comment.
Do not cast CheckedState; coerce to strict boolean.
onCheckedChange may receive "indeterminate" (Radix CheckedState). Casting with as boolean is unsafe and can pass a string at runtime. Use a strict check.
Apply this diff:
- onCheckedChange={(checked) =>
- toggleCalendarSelectedMutation.mutate({
- calendarId: calSelection.calendarId,
- selected: checked as boolean
- })
- }
+ onCheckedChange={(checked) =>
+ toggleCalendarSelectedMutation.mutate({
+ calendarId: calSelection.calendarId,
+ selected: checked === true,
+ })
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Checkbox | |
| id={`calendar-${calSelection.calendarId}`} | |
| checked={calSelection.selected} | |
| onCheckedChange={(checked) => | |
| toggleCalendarSelectedMutation.mutate({ | |
| calendarId: calSelection.calendarId, | |
| selected: checked as boolean | |
| }) | |
| } | |
| <Checkbox | |
| id={`calendar-${calSelection.calendarId}`} | |
| checked={calSelection.selected} | |
| onCheckedChange={(checked) => | |
| toggleCalendarSelectedMutation.mutate({ | |
| calendarId: calSelection.calendarId, | |
| selected: checked === true, | |
| }) | |
| } |
🤖 Prompt for AI Agents
In
apps/desktop/src/components/settings/components/calendar/google-calendar-integration-details.tsx
around lines 51 to 59, the code unsafely casts Radix CheckedState to boolean
using "as boolean"; replace the cast with a strict boolean coercion (e.g.,
compute selected = checked === true or Boolean(checked) so only true becomes
true and "indeterminate"/false become false) and pass that strict boolean into
toggleCalendarSelectedMutation.mutate({ calendarId: calSelection.calendarId,
selected }); ensure no TypeScript "as boolean" cast remains.
| // Split multiple SQL statements and execute each one separately | ||
| let migration_content = migration.as_ref().trim(); | ||
| if migration_content.is_empty() { | ||
| continue; | ||
| } | ||
|
|
||
| // Split by semicolon and execute each statement | ||
| for statement in migration_content.split(';') { | ||
| let statement = statement.trim(); | ||
| if !statement.is_empty() { | ||
| tx.execute(statement, ()).await?; | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
Naive semicolon splitting may break complex SQL statements.
Splitting by semicolon without parsing can fail when:
- String literals contain semicolons:
INSERT INTO t VALUES ('text;data') - Triggers or procedures contain semicolons in their body
- Comments contain semicolons:
-- comment with ; inside
This could cause migration failures or partial execution.
Consider these alternatives:
Option 1: Use libsql's batch execution (if available)
for migration in migrations.iter().skip(current_version as usize) {
- // Split multiple SQL statements and execute each one separately
- let migration_content = migration.as_ref().trim();
- if migration_content.is_empty() {
- continue;
- }
-
- // Split by semicolon and execute each statement
- for statement in migration_content.split(';') {
- let statement = statement.trim();
- if !statement.is_empty() {
- tx.execute(statement, ()).await?;
- }
- }
+ let migration_content = migration.as_ref().trim();
+ if !migration_content.is_empty() {
+ tx.execute_batch(migration_content).await?;
+ }
}Option 2: Keep one statement per migration file
Ensure each migration file contains a single statement, avoiding the need for splitting entirely.
Check if libsql supports batch execution:
🏁 Script executed:
#!/bin/bash
# Search for batch execution methods in libsql usage
rg -n 'execute_batch|executescript|batch' --type rust -g '**/Cargo.toml' -g '**/src/**'
# Check libsql documentation
ast-grep --pattern 'impl $_ {
$$$
execute_batch($$$) {
$$$
}
$$$
}'Length of output: 3278
Avoid naive semicolon-based splitting in migrations
Splitting the SQL by ; will break on semicolons in literals, comments, and procedural blocks—use a proper SQL parser (e.g. the sqlparser crate) to split statements reliably or enforce one statement per migration file.
| pub async fn update_calendar_sync_status( | ||
| &self, | ||
| tracking_id: impl AsRef<str>, | ||
| connection_status: impl AsRef<str>, | ||
| sync_error: Option<String>, | ||
| sync_time: impl AsRef<str>, | ||
| ) -> Result<(), crate::Error> { | ||
| let conn = self.conn()?; | ||
|
|
||
| conn.execute( | ||
| "UPDATE calendars SET connection_status = ?, last_sync_error = ?, last_sync_at = ? WHERE tracking_id = ?", | ||
| vec![ | ||
| connection_status.as_ref(), | ||
| sync_error.as_deref().unwrap_or_default(), | ||
| sync_time.as_ref(), | ||
| tracking_id.as_ref(), | ||
| ], | ||
| ) | ||
| .await?; | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
Verify last_sync_error column nullability.
Line 171 uses sync_error.as_deref().unwrap_or_default() to convert None to an empty string. Ensure this behavior aligns with the database schema:
- If
last_sync_erroris nullable, passing an empty string instead of NULL may not be semantically correct. - If
last_sync_erroris NOT NULL, the current approach is appropriate.
Run the following script to check the schema definition:
🏁 Script executed:
#!/bin/bash
# Check if last_sync_error column is nullable in the calendars table schema
rg -nP -C3 'last_sync_error' --type=sqlLength of output: 467
Pass SQL NULL for None sync errors instead of ""
The last_sync_error column is defined as nullable (ADD COLUMN last_sync_error TEXT; in calendars_migration_2.sql), so mapping None to an empty string changes its meaning. Update the parameter binding in update_calendar_sync_status to use Option<&str> (e.g. sync_error.as_deref()) so that None is stored as SQL NULL.
🤖 Prompt for AI Agents
In crates/db-user/src/calendars_ops.rs around lines 158 to 179, the code
currently binds last_sync_error as sync_error.as_deref().unwrap_or_default()
which stores an empty string instead of SQL NULL for None; change the parameter
binding to pass an Option<&str> by using sync_error.as_deref() directly (i.e.,
do not unwrap_or_default) so the DB layer can bind NULL for None, and ensure the
execute parameter vector accepts the Option<&str> type (adjust imports/traits or
convert to a DB-friendly nullable binder if needed).
| authors = ["You"] | ||
| edition = "2021" | ||
| exclude = ["/js", "/node_modules"] | ||
| links = "tauri-plugin-google-calendar" |
There was a problem hiding this comment.
Remove Cargo links — it’s for native “-sys” crates and will cause Cargo link uniqueness conflicts.
This crate doesn’t build a native library; keeping links can break builds in workspaces and is incorrect for a pure Rust plugin.
Apply this diff:
-links = "tauri-plugin-google-calendar"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| links = "tauri-plugin-google-calendar" |
🤖 Prompt for AI Agents
In plugins/google-calendar/Cargo.toml around line 7, remove the Cargo.toml key
`links = "tauri-plugin-google-calendar"` because `links` is only for native
“-sys” crates and will cause link uniqueness conflicts in workspaces; delete
that line from the manifest, save the file, and run a local Cargo build/metadata
check to ensure the workspace builds correctly without the `links` entry.
| pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> { | ||
| let specta_builder = make_specta_builder(); | ||
|
|
||
| tauri::plugin::Builder::new(PLUGIN_NAME) | ||
| .invoke_handler(specta_builder.invoke_handler()) | ||
| .setup(|app, _api| { | ||
| app.manage(ManagedState::default()); | ||
| Ok(()) | ||
| }) | ||
| .build() | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Constrain the plugin to Wry and remove generic R here.
This aligns the plugin with the commands and enables removing the unsafe transmute in ext.rs (follow-up).
-pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
- let specta_builder = make_specta_builder();
+pub fn init() -> tauri::plugin::TauriPlugin<tauri::Wry> {
+ let specta_builder = make_specta_builder();
- tauri::plugin::Builder::new(PLUGIN_NAME)
+ tauri::plugin::Builder::<tauri::Wry>::new(PLUGIN_NAME)
.invoke_handler(specta_builder.invoke_handler())
.setup(|app, _api| {
app.manage(ManagedState::default());
Ok(())
})
.build()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> { | |
| let specta_builder = make_specta_builder(); | |
| tauri::plugin::Builder::new(PLUGIN_NAME) | |
| .invoke_handler(specta_builder.invoke_handler()) | |
| .setup(|app, _api| { | |
| app.manage(ManagedState::default()); | |
| Ok(()) | |
| }) | |
| .build() | |
| } | |
| pub fn init() -> tauri::plugin::TauriPlugin<tauri::Wry> { | |
| let specta_builder = make_specta_builder(); | |
| tauri::plugin::Builder::<tauri::Wry>::new(PLUGIN_NAME) | |
| .invoke_handler(specta_builder.invoke_handler()) | |
| .setup(|app, _api| { | |
| app.manage(ManagedState::default()); | |
| Ok(()) | |
| }) | |
| .build() | |
| } |
🤖 Prompt for AI Agents
In plugins/google-calendar/src/lib.rs around lines 62 to 72, change the function
to be concrete for the Wry runtime instead of generic R: replace the signature
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> with a
concrete pub fn init() -> tauri::plugin::TauriPlugin<tauri::Wry> (and remove the
generic parameter everywhere inside), update any local variables or calls that
referenced R to use tauri::Wry, and adjust imports if necessary; this constrains
the plugin to Wry and lets you remove the unsafe transmute in ext.rs in a
follow-up.
| @@ -0,0 +1,67 @@ | |||
| use apalis::prelude::{Data, Error, WorkerBuilder, WorkerFactoryFn}; | |||
There was a problem hiding this comment.
Remove unused import to satisfy “no unused imports”.
WorkerFactoryFn isn’t used.
Apply:
-use apalis::prelude::{Data, Error, WorkerBuilder, WorkerFactoryFn};
+use apalis::prelude::{Data, Error, WorkerBuilder};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| use apalis::prelude::{Data, Error, WorkerBuilder, WorkerFactoryFn}; | |
| use apalis::prelude::{Data, Error, WorkerBuilder}; |
🤖 Prompt for AI Agents
In plugins/google-calendar/src/worker.rs around lines 1 to 1, the use statement
imports WorkerFactoryFn which is unused and triggers a “no unused imports” lint;
remove WorkerFactoryFn from the apalis::prelude use list (leaving Data, Error,
WorkerBuilder) so the import only contains symbols actually used in the file.
| #[allow(unused)] | ||
| #[derive(Default, Debug, Clone)] | ||
| pub struct Job(DateTime<Utc>); | ||
|
|
||
| #[derive(Clone)] | ||
| pub struct WorkerState { | ||
| pub db: hypr_db_user::UserDatabase, | ||
| pub user_id: String, | ||
| pub app_handle: tauri::AppHandle<tauri::Wry>, | ||
| } | ||
|
|
||
| impl From<DateTime<Utc>> for Job { | ||
| fn from(t: DateTime<Utc>) -> Self { | ||
| Job(t) | ||
| } | ||
| } |
There was a problem hiding this comment.
Drop the Job wrapper; it causes type mismatch with CronStream and adds a likely non-compiling Default derive.
- CronStream yields
DateTime<Utc>; worker fns expectingJobwon’t match unless you map the stream. #[derive(Default)]onJob(DateTime<Utc>)likely fails becauseDateTime<Utc>doesn’t implementDefault.
Apply:
-#[allow(unused)]
-#[derive(Default, Debug, Clone)]
-pub struct Job(DateTime<Utc>);
-
-impl From<DateTime<Utc>> for Job {
- fn from(t: DateTime<Utc>) -> Self {
- Job(t)
- }
-}Then update worker function signatures to take DateTime<Utc> directly (see next comment).
As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[allow(unused)] | |
| #[derive(Default, Debug, Clone)] | |
| pub struct Job(DateTime<Utc>); | |
| #[derive(Clone)] | |
| pub struct WorkerState { | |
| pub db: hypr_db_user::UserDatabase, | |
| pub user_id: String, | |
| pub app_handle: tauri::AppHandle<tauri::Wry>, | |
| } | |
| impl From<DateTime<Utc>> for Job { | |
| fn from(t: DateTime<Utc>) -> Self { | |
| Job(t) | |
| } | |
| } | |
| #[derive(Clone)] | |
| pub struct WorkerState { | |
| pub db: hypr_db_user::UserDatabase, | |
| pub user_id: String, | |
| pub app_handle: tauri::AppHandle<tauri::Wry>, | |
| } |
🤖 Prompt for AI Agents
In plugins/google-calendar/src/worker.rs around lines 7 to 22, the newtype
wrapper Job(DateTime<Utc>) should be removed because CronStream yields
DateTime<Utc>, the #[derive(Default)] is invalid for DateTime<Utc>, and the From
impl is unnecessary; delete the Job struct and its impl, remove the Default and
unused attributes, then update any worker function signatures and call sites to
accept DateTime<Utc> directly (and adjust any pattern matches/uses accordingly)
so types line up with CronStream without extra mapping.
| #[tracing::instrument(skip(ctx), name = CALENDARS_SYNC_WORKER_NAME)] | ||
| pub async fn perform_calendars_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> { | ||
| ctx.app_handle.sync_calendars_with_db(ctx.db.clone(), ctx.user_id.clone()) | ||
| .await | ||
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Use string literals in #[instrument(name = ...)] and align param type with CronStream item.
instrument’s name expects a string literal; passing a const identifier is not accepted. Also, switch _job: Job → _tick: DateTime<Utc> to match the backend.
Apply:
-#[tracing::instrument(skip(ctx), name = CALENDARS_SYNC_WORKER_NAME)]
-pub async fn perform_calendars_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> {
+#[tracing::instrument(skip(ctx), name = "google_calendar_calendars_sync")]
+pub async fn perform_calendars_sync(_tick: DateTime<Utc>, ctx: Data<WorkerState>) -> Result<(), Error> {
ctx.app_handle.sync_calendars_with_db(ctx.db.clone(), ctx.user_id.clone())
.await
.map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?;
Ok(())
}Note: No new error handling added; only signature and attribute fixes. As per coding guidelines.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[tracing::instrument(skip(ctx), name = CALENDARS_SYNC_WORKER_NAME)] | |
| pub async fn perform_calendars_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> { | |
| ctx.app_handle.sync_calendars_with_db(ctx.db.clone(), ctx.user_id.clone()) | |
| .await | |
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | |
| Ok(()) | |
| } | |
| #[tracing::instrument(skip(ctx), name = "google_calendar_calendars_sync")] | |
| pub async fn perform_calendars_sync(_tick: DateTime<Utc>, ctx: Data<WorkerState>) -> Result<(), Error> { | |
| ctx.app_handle.sync_calendars_with_db(ctx.db.clone(), ctx.user_id.clone()) | |
| .await | |
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | |
| Ok(()) | |
| } |
🤖 Prompt for AI Agents
In plugins/google-calendar/src/worker.rs around lines 27 to 33, the tracing
instrument attribute currently passes a const identifier for name and the
function signature uses Job; change the attribute to use a string literal e.g.
#[tracing::instrument(skip(ctx), name = "calendars_sync_worker")] and update the
function signature to accept the CronStream item type: pub async fn
perform_calendars_sync(_tick: chrono::DateTime<chrono::Utc>, ctx:
Data<WorkerState>) -> Result<(), Error> (rename the parameter to _tick to match
the backend); ensure chrono::DateTime and chrono::Utc are in scope or import
them at top of the file.
| #[tracing::instrument(skip(ctx), name = EVENTS_SYNC_WORKER_NAME)] | ||
| pub async fn perform_events_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> { | ||
| ctx.app_handle.sync_events_with_db(ctx.db.clone(), ctx.user_id.clone(), None) | ||
| .await | ||
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Mirror the same fixes for events worker.
Use a literal name and DateTime<Utc> in the signature.
Apply:
-#[tracing::instrument(skip(ctx), name = EVENTS_SYNC_WORKER_NAME)]
-pub async fn perform_events_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> {
+#[tracing::instrument(skip(ctx), name = "google_calendar_events_sync")]
+pub async fn perform_events_sync(_tick: DateTime<Utc>, ctx: Data<WorkerState>) -> Result<(), Error> {
ctx.app_handle.sync_events_with_db(ctx.db.clone(), ctx.user_id.clone(), None)
.await
.map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?;
Ok(())
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #[tracing::instrument(skip(ctx), name = EVENTS_SYNC_WORKER_NAME)] | |
| pub async fn perform_events_sync(_job: Job, ctx: Data<WorkerState>) -> Result<(), Error> { | |
| ctx.app_handle.sync_events_with_db(ctx.db.clone(), ctx.user_id.clone(), None) | |
| .await | |
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | |
| Ok(()) | |
| } | |
| #[tracing::instrument(skip(ctx), name = "google_calendar_events_sync")] | |
| pub async fn perform_events_sync(_tick: DateTime<Utc>, ctx: Data<WorkerState>) -> Result<(), Error> { | |
| ctx.app_handle.sync_events_with_db(ctx.db.clone(), ctx.user_id.clone(), None) | |
| .await | |
| .map_err(|e| Error::Failed(Arc::new(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)))?; | |
| Ok(()) | |
| } |
🤖 Prompt for AI Agents
In plugins/google-calendar/src/worker.rs around lines 35 to 41, change the
worker to use a literal instrument name and accept a DateTime<Utc> instead of
the unused Job: replace the attribute name = EVENTS_SYNC_WORKER_NAME with a
literal string (e.g. name = "events_sync_worker"), change the fn signature from
perform_events_sync(_job: Job, ctx: Data<WorkerState>) to
perform_events_sync(_scheduled_at: DateTime<Utc>, ctx: Data<WorkerState>) and
pass Some(_scheduled_at) into ctx.app_handle.sync_events_with_db(...), and add
the necessary chrono imports (chrono::DateTime, chrono::Utc) while removing the
unused Job import.
|
@githubdoramon thanks for the effort. unfortunately, we are doing partial rewrite of Hyprnote, (targeting early next week), so it is bit tricky to merge this right now. we will take this as inspiration. thank you! |
Adds the possibility to connect google calendar from multiple accounts, and choose subcalendars to sync