From f628f870ad3f23b11e84319f1ae439266a9a2330 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Fri, 20 Feb 2026 20:59:38 +0530 Subject: [PATCH] feat: implement Gmail metadata synchronization to backend - Added a new utility function `syncGmailMetadataToBackend` to emit Gmail profile and email summaries to the backend via the `integration:metadata-sync` socket event. - Updated `SkillProvider` to call the new synchronization function, ensuring Gmail skill state is sent to the backend when updated. --- src/lib/skills/gmailMetadataSync.ts | 62 +++++++++++++++++++++++++++++ src/providers/SkillProvider.tsx | 7 +++- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/lib/skills/gmailMetadataSync.ts diff --git a/src/lib/skills/gmailMetadataSync.ts b/src/lib/skills/gmailMetadataSync.ts new file mode 100644 index 000000000..e834119c4 --- /dev/null +++ b/src/lib/skills/gmailMetadataSync.ts @@ -0,0 +1,62 @@ +/** + * Send Gmail profile (and optionally emails) to the backend via the + * `integration:metadata-sync` socket event so the server can merge them + * into the user's Google OAuth integration metadata. + */ +import { emitViaRustSocket } from '../../utils/tauriSocket'; + +const INTEGRATION_METADATA_SYNC_EVENT = 'integration:metadata-sync'; +const PROVIDER_GOOGLE = 'google'; + +/** Gmail profile shape from skill state (snake_case). */ +interface GmailProfileLike { + email_address: string; + messages_total: number; + threads_total: number; + history_id: string; +} + +/** Single email summary from skill state. */ +interface GmailEmailSummaryLike { + id: string; + threadId: string; + snippet?: string; + subject?: string; + from?: string; + date?: string; +} + +/** Gmail skill state slice we care about for metadata sync. */ +export interface GmailStateForSync { + profile?: GmailProfileLike | null; + emails?: GmailEmailSummaryLike[] | null; +} + +/** + * Emit `integration:metadata-sync` with Gmail profile and emails so the + * backend can merge them into the user's Google OAuth integration. + * No-op when profile is missing or not in Tauri. + */ +export function syncGmailMetadataToBackend(gmailState: GmailStateForSync | undefined): void { + if (!gmailState?.profile || typeof gmailState.profile !== 'object') return; + + const profile = gmailState.profile as GmailProfileLike; + const metadata: Record = { + email_address: profile.email_address, + messages_total: profile.messages_total, + threads_total: profile.threads_total, + history_id: profile.history_id, + }; + + if (Array.isArray(gmailState.emails) && gmailState.emails.length > 0) { + metadata.emails = gmailState.emails as GmailEmailSummaryLike[]; + } + + const payload = { + requestId: crypto.randomUUID(), + provider: PROVIDER_GOOGLE, + metadata, + }; + + void emitViaRustSocket(INTEGRATION_METADATA_SYNC_EVENT, payload); +} diff --git a/src/providers/SkillProvider.tsx b/src/providers/SkillProvider.tsx index 31b1820a6..0584d8dd8 100644 --- a/src/providers/SkillProvider.tsx +++ b/src/providers/SkillProvider.tsx @@ -19,6 +19,10 @@ import { type GmailProfile, } from '../store/gmailSlice'; import { setSkillError, setSkillState, setSkillStatus } from '../store/skillsSlice'; +import { + syncGmailMetadataToBackend, + type GmailStateForSync, +} from '../lib/skills/gmailMetadataSync'; import { DEV_AUTO_LOAD_SKILL, IS_DEV } from '../utils/config'; // --------------------------------------------------------------------------- @@ -71,7 +75,7 @@ function parseSkillStatePayload( return { skillId, state }; } -/** Sync profile and emails from gmail skill state into gmailSlice. */ +/** Sync profile and emails from gmail skill state into gmailSlice and send to backend via socket. */ function syncGmailStateToSlice( gmailState: Record | undefined, dispatch: ReturnType @@ -89,6 +93,7 @@ function syncGmailStateToSlice( Array.isArray(gmailState.emails) ? (gmailState.emails as GmailEmailSummary[]) : [] ) ); + syncGmailMetadataToBackend(gmailState as GmailStateForSync); } export default function SkillProvider({ children }: { children: ReactNode }) {