diff --git a/apps/x/apps/main/src/composio-handler.ts b/apps/x/apps/main/src/composio-handler.ts
index f72613ad..e5b25d1a 100644
--- a/apps/x/apps/main/src/composio-handler.ts
+++ b/apps/x/apps/main/src/composio-handler.ts
@@ -3,7 +3,6 @@ import { createAuthServer } from './auth-server.js';
import * as composioClient from '@x/core/dist/composio/client.js';
import { composioAccountsRepo } from '@x/core/dist/composio/repo.js';
import type { LocalConnectedAccount } from '@x/core/dist/composio/types.js';
-import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js';
const REDIRECT_URI = 'http://localhost:8081/oauth/callback';
@@ -152,9 +151,6 @@ export async function initiateConnection(toolkitSlug: string): Promise<{
if (accountStatus.status === 'ACTIVE') {
emitComposioEvent({ toolkitSlug, success: true });
- if (toolkitSlug === 'gmail') {
- triggerGmailSync();
- }
} else {
emitComposioEvent({
toolkitSlug,
diff --git a/apps/x/apps/main/src/main.ts b/apps/x/apps/main/src/main.ts
index c276301b..6ddab7bc 100644
--- a/apps/x/apps/main/src/main.ts
+++ b/apps/x/apps/main/src/main.ts
@@ -5,7 +5,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
import { dirname } from "node:path";
import { updateElectronApp, UpdateSourceType } from "update-electron-app";
import { init as initGmailSync } from "@x/core/dist/knowledge/sync_gmail.js";
-
+import { init as initCalendarSync } from "@x/core/dist/knowledge/sync_calendar.js";
import { init as initFirefliesSync } from "@x/core/dist/knowledge/sync_fireflies.js";
import { init as initGranolaSync } from "@x/core/dist/knowledge/granola/sync.js";
import { init as initGraphBuilder } from "@x/core/dist/knowledge/build_graph.js";
@@ -134,6 +134,9 @@ app.whenReady().then(async () => {
// start gmail sync
initGmailSync();
+ // start calendar sync
+ initCalendarSync();
+
// start fireflies sync
initFirefliesSync();
diff --git a/apps/x/apps/main/src/oauth-handler.ts b/apps/x/apps/main/src/oauth-handler.ts
index 58ab0809..5b55e8b7 100644
--- a/apps/x/apps/main/src/oauth-handler.ts
+++ b/apps/x/apps/main/src/oauth-handler.ts
@@ -7,6 +7,7 @@ import { getProviderConfig, getAvailableProviders } from '@x/core/dist/auth/prov
import container from '@x/core/dist/di/container.js';
import { IOAuthRepo } from '@x/core/dist/auth/repo.js';
import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
+import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js';
import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js';
import { triggerSync as triggerFirefliesSync } from '@x/core/dist/knowledge/sync_fireflies.js';
import { emitOAuthEvent } from './ipc.js';
@@ -193,6 +194,7 @@ export async function connectProvider(provider: string): Promise<{ success: bool
// Trigger immediate sync for relevant providers
if (provider === 'google') {
+ triggerGmailSync();
triggerCalendarSync();
} else if (provider === 'fireflies-ai') {
triggerFirefliesSync();
diff --git a/apps/x/apps/renderer/src/components/connectors-popover.tsx b/apps/x/apps/renderer/src/components/connectors-popover.tsx
index 882a8d48..1799ab75 100644
--- a/apps/x/apps/renderer/src/components/connectors-popover.tsx
+++ b/apps/x/apps/renderer/src/components/connectors-popover.tsx
@@ -41,12 +41,8 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
const [granolaEnabled, setGranolaEnabled] = useState(false)
const [granolaLoading, setGranolaLoading] = useState(true)
- // Composio state (Gmail + Slack)
+ // Composio/Slack state
const [composioApiKeyOpen, setComposioApiKeyOpen] = useState(false)
- const [composioApiKeyTarget, setComposioApiKeyTarget] = useState<'gmail' | 'slack'>('gmail')
- const [gmailConnected, setGmailConnected] = useState(false)
- const [gmailLoading, setGmailLoading] = useState(true)
- const [gmailConnecting, setGmailConnecting] = useState(false)
const [slackConnected, setSlackConnected] = useState(false)
const [slackLoading, setSlackLoading] = useState(true)
const [slackConnecting, setSlackConnecting] = useState(false)
@@ -97,20 +93,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
}, [])
- // Load Gmail connection status
- const refreshGmailStatus = useCallback(async () => {
- try {
- setGmailLoading(true)
- const result = await window.ipc.invoke('composio:get-connection-status', { toolkitSlug: 'gmail' })
- setGmailConnected(result.isConnected)
- } catch (error) {
- console.error('Failed to load Gmail status:', error)
- setGmailConnected(false)
- } finally {
- setGmailLoading(false)
- }
- }, [])
-
// Load Slack connection status
const refreshSlackStatus = useCallback(async () => {
try {
@@ -125,53 +107,6 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
}, [])
- // Connect to Gmail via Composio
- const startGmailConnect = useCallback(async () => {
- try {
- setGmailConnecting(true)
- const result = await window.ipc.invoke('composio:initiate-connection', { toolkitSlug: 'gmail' })
- if (!result.success) {
- toast.error(result.error || 'Failed to connect to Gmail')
- setGmailConnecting(false)
- }
- // Success will be handled by composio:didConnect event
- } catch (error) {
- console.error('Failed to connect to Gmail:', error)
- toast.error('Failed to connect to Gmail')
- setGmailConnecting(false)
- }
- }, [])
-
- // Handle Gmail connect button click
- const handleConnectGmail = useCallback(async () => {
- const configResult = await window.ipc.invoke('composio:is-configured', null)
- if (!configResult.configured) {
- setComposioApiKeyTarget('gmail')
- setComposioApiKeyOpen(true)
- return
- }
- await startGmailConnect()
- }, [startGmailConnect])
-
- // Disconnect from Gmail
- const handleDisconnectGmail = useCallback(async () => {
- try {
- setGmailLoading(true)
- const result = await window.ipc.invoke('composio:disconnect', { toolkitSlug: 'gmail' })
- if (result.success) {
- setGmailConnected(false)
- toast.success('Disconnected from Gmail')
- } else {
- toast.error('Failed to disconnect from Gmail')
- }
- } catch (error) {
- console.error('Failed to disconnect from Gmail:', error)
- toast.error('Failed to disconnect from Gmail')
- } finally {
- setGmailLoading(false)
- }
- }, [])
-
// Connect to Slack via Composio
const startSlackConnect = useCallback(async () => {
try {
@@ -191,9 +126,9 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
// Handle Slack connect button click
const handleConnectSlack = useCallback(async () => {
+ // Check if Composio is configured
const configResult = await window.ipc.invoke('composio:is-configured', null)
if (!configResult.configured) {
- setComposioApiKeyTarget('slack')
setComposioApiKeyOpen(true)
return
}
@@ -206,17 +141,13 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
await window.ipc.invoke('composio:set-api-key', { apiKey })
setComposioApiKeyOpen(false)
toast.success('Composio API key saved')
- // Start the connection for whichever toolkit triggered the API key prompt
- if (composioApiKeyTarget === 'gmail') {
- await startGmailConnect()
- } else {
- await startSlackConnect()
- }
+ // Now start the Slack connection
+ await startSlackConnect()
} catch (error) {
console.error('Failed to save Composio API key:', error)
toast.error('Failed to save API key')
}
- }, [composioApiKeyTarget, startGmailConnect, startSlackConnect])
+ }, [startSlackConnect])
// Disconnect from Slack
const handleDisconnectSlack = useCallback(async () => {
@@ -242,8 +173,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
// Refresh Granola
refreshGranolaConfig()
- // Refresh Composio connections
- refreshGmailStatus()
+ // Refresh Slack status
refreshSlackStatus()
// Refresh OAuth providers
@@ -272,7 +202,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
)
setProviderStates(newStates)
- }, [providers, refreshGranolaConfig, refreshGmailStatus, refreshSlackStatus])
+ }, [providers, refreshGranolaConfig, refreshSlackStatus])
// Refresh statuses when popover opens or providers list changes
useEffect(() => {
@@ -297,7 +227,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
if (success) {
const displayName = provider === 'fireflies-ai' ? 'Fireflies' : provider.charAt(0).toUpperCase() + provider.slice(1)
- // Show detailed message for providers that sync in background
+ // Show detailed message for Google and Fireflies (includes sync info)
if (provider === 'google' || provider === 'fireflies-ai') {
toast.success(`Connected to ${displayName}`, {
description: 'Syncing your data in the background. This may take a few minutes before changes appear.',
@@ -321,19 +251,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
const cleanup = window.ipc.on('composio:didConnect', (event) => {
const { toolkitSlug, success, error } = event
- if (toolkitSlug === 'gmail') {
- setGmailConnected(success)
- setGmailConnecting(false)
-
- if (success) {
- toast.success('Connected to Gmail', {
- description: 'Syncing your emails in the background. This may take a few minutes.',
- duration: 8000,
- })
- } else {
- toast.error(error || 'Failed to connect to Gmail')
- }
- } else if (toolkitSlug === 'slack') {
+ if (toolkitSlug === 'slack') {
setSlackConnected(success)
setSlackConnecting(false)
@@ -513,55 +431,16 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
) : (
<>
- {/* Email Section - Gmail via Composio */}
-
- Email
-
-
-
-
-
+ {/* Email & Calendar Section - Google */}
+ {providers.includes('google') && (
+ <>
+
+ Email & Calendar
-
- Gmail
- {gmailLoading ? (
- Checking...
- ) : (
- Sync emails
- )}
-
-
-
- {gmailLoading ? (
-
- ) : gmailConnected ? (
-
- ) : (
-
- )}
-
-
-
-
+ {renderOAuthProvider('google', 'Google',
, 'Sync emails and calendar')}
+
+ >
+ )}
{/* Meeting Notes Section - Granola & Fireflies */}
@@ -658,7 +537,7 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
open={composioApiKeyOpen}
onOpenChange={setComposioApiKeyOpen}
onSubmit={handleComposioApiKeySubmit}
- isSubmitting={gmailConnecting || slackConnecting}
+ isSubmitting={slackConnecting}
/>
>
)
diff --git a/apps/x/apps/renderer/src/components/onboarding-modal.tsx b/apps/x/apps/renderer/src/components/onboarding-modal.tsx
index 0675697c..074ad645 100644
--- a/apps/x/apps/renderer/src/components/onboarding-modal.tsx
+++ b/apps/x/apps/renderer/src/components/onboarding-modal.tsx
@@ -42,12 +42,8 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
const [granolaEnabled, setGranolaEnabled] = useState(false)
const [granolaLoading, setGranolaLoading] = useState(true)
- // Composio state (Gmail + Slack)
+ // Composio/Slack state
const [composioApiKeyOpen, setComposioApiKeyOpen] = useState(false)
- const [composioApiKeyTarget, setComposioApiKeyTarget] = useState<'gmail' | 'slack'>('gmail')
- const [gmailConnected, setGmailConnected] = useState(false)
- const [gmailLoading, setGmailLoading] = useState(true)
- const [gmailConnecting, setGmailConnecting] = useState(false)
const [slackConnected, setSlackConnected] = useState(false)
const [slackLoading, setSlackLoading] = useState(true)
const [slackConnecting, setSlackConnecting] = useState(false)
@@ -105,47 +101,6 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
}
}, [])
- // Load Gmail connection status
- const refreshGmailStatus = useCallback(async () => {
- try {
- setGmailLoading(true)
- const result = await window.ipc.invoke('composio:get-connection-status', { toolkitSlug: 'gmail' })
- setGmailConnected(result.isConnected)
- } catch (error) {
- console.error('Failed to load Gmail status:', error)
- setGmailConnected(false)
- } finally {
- setGmailLoading(false)
- }
- }, [])
-
- // Connect to Gmail via Composio
- const startGmailConnect = useCallback(async () => {
- try {
- setGmailConnecting(true)
- const result = await window.ipc.invoke('composio:initiate-connection', { toolkitSlug: 'gmail' })
- if (!result.success) {
- toast.error(result.error || 'Failed to connect to Gmail')
- setGmailConnecting(false)
- }
- } catch (error) {
- console.error('Failed to connect to Gmail:', error)
- toast.error('Failed to connect to Gmail')
- setGmailConnecting(false)
- }
- }, [])
-
- // Handle Gmail connect button click
- const handleConnectGmail = useCallback(async () => {
- const configResult = await window.ipc.invoke('composio:is-configured', null)
- if (!configResult.configured) {
- setComposioApiKeyTarget('gmail')
- setComposioApiKeyOpen(true)
- return
- }
- await startGmailConnect()
- }, [startGmailConnect])
-
// Load Slack connection status
const refreshSlackStatus = useCallback(async () => {
try {
@@ -179,9 +134,9 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
// Connect to Slack via Composio (checks if configured first)
const handleConnectSlack = useCallback(async () => {
+ // Check if Composio is configured
const configResult = await window.ipc.invoke('composio:is-configured', null)
if (!configResult.configured) {
- setComposioApiKeyTarget('slack')
setComposioApiKeyOpen(true)
return
}
@@ -194,24 +149,20 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
await window.ipc.invoke('composio:set-api-key', { apiKey })
setComposioApiKeyOpen(false)
toast.success('Composio API key saved')
- if (composioApiKeyTarget === 'gmail') {
- await startGmailConnect()
- } else {
- await startSlackConnect()
- }
+ // Now start the Slack connection
+ await startSlackConnect()
} catch (error) {
console.error('Failed to save Composio API key:', error)
toast.error('Failed to save API key')
}
- }, [composioApiKeyTarget, startGmailConnect, startSlackConnect])
+ }, [startSlackConnect])
// Check connection status for all providers
const refreshAllStatuses = useCallback(async () => {
// Refresh Granola
refreshGranolaConfig()
- // Refresh Composio connections
- refreshGmailStatus()
+ // Refresh Slack status
refreshSlackStatus()
// Refresh OAuth providers
@@ -240,7 +191,7 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
)
setProviderStates(newStates)
- }, [providers, refreshGranolaConfig, refreshGmailStatus, refreshSlackStatus])
+ }, [providers, refreshGranolaConfig, refreshSlackStatus])
// Refresh statuses when modal opens or providers list changes
useEffect(() => {
@@ -279,16 +230,7 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
const cleanup = window.ipc.on('composio:didConnect', (event) => {
const { toolkitSlug, success, error } = event
- if (toolkitSlug === 'gmail') {
- setGmailConnected(success)
- setGmailConnecting(false)
-
- if (success) {
- toast.success('Connected to Gmail')
- } else {
- toast.error(error || 'Failed to connect to Gmail')
- }
- } else if (toolkitSlug === 'slack') {
+ if (toolkitSlug === 'slack') {
setSlackConnected(success)
setSlackConnecting(false)
@@ -435,48 +377,6 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
)
- // Render Gmail row (Composio)
- const renderGmailRow = () => (
-
-
-
-
-
-
- Gmail
- {gmailLoading ? (
- Checking...
- ) : (
- Sync emails
- )}
-
-
-
- {gmailLoading ? (
-
- ) : gmailConnected ? (
-
-
- Connected
-
- ) : (
-
- )}
-
-
- )
-
// Render Slack row
const renderSlackRow = () => (
@@ -570,13 +470,15 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
) : (
<>
- {/* Email Section - Gmail via Composio */}
-
-
-
Email
+ {/* Email & Calendar Section */}
+ {providers.includes('google') && (
+
+
+ Email & Calendar
+
+ {renderOAuthProvider('google', 'Google',
, 'Sync emails and calendar events')}
- {renderGmailRow()}
-
+ )}
{/* Meeting Notes Section */}
@@ -611,7 +513,7 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
// Step 2: Completion
const CompletionStep = () => {
- const hasConnections = connectedProviders.length > 0 || gmailConnected || granolaEnabled || slackConnected
+ const hasConnections = connectedProviders.length > 0 || granolaEnabled || slackConnected
return (
@@ -634,10 +536,10 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
Connected accounts:
- {gmailConnected && (
+ {connectedProviders.includes('google') && (
- Gmail (Email)
+ Google (Email & Calendar)
)}
{connectedProviders.includes('fireflies-ai') && (
@@ -676,7 +578,7 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
open={composioApiKeyOpen}
onOpenChange={setComposioApiKeyOpen}
onSubmit={handleComposioApiKeySubmit}
- isSubmitting={gmailConnecting || slackConnecting}
+ isSubmitting={slackConnecting}
/>