mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-21 21:31:12 +02:00
add granola sync toggle
This commit is contained in:
parent
949d36c04d
commit
1ce2b3201f
3 changed files with 102 additions and 1 deletions
|
|
@ -16,6 +16,8 @@ import type { FSWatcher } from 'chokidar';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
import { RunEvent } from 'packages/shared/dist/runs.js';
|
import { RunEvent } from 'packages/shared/dist/runs.js';
|
||||||
|
import container from '@x/core/dist/di/container.js';
|
||||||
|
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
|
||||||
|
|
||||||
type InvokeChannels = ipc.InvokeChannels;
|
type InvokeChannels = ipc.InvokeChannels;
|
||||||
type IPCChannels = ipc.IPCChannels;
|
type IPCChannels = ipc.IPCChannels;
|
||||||
|
|
@ -300,5 +302,15 @@ export function setupIpcHandlers() {
|
||||||
'oauth:get-connected-providers': async () => {
|
'oauth:get-connected-providers': async () => {
|
||||||
return await getConnectedProviders();
|
return await getConnectedProviders();
|
||||||
},
|
},
|
||||||
|
'granola:getConfig': async () => {
|
||||||
|
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');
|
||||||
|
const config = await repo.getConfig();
|
||||||
|
return { enabled: config.enabled };
|
||||||
|
},
|
||||||
|
'granola:setConfig': async (_event, args) => {
|
||||||
|
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');
|
||||||
|
await repo.setConfig({ enabled: args.enabled });
|
||||||
|
return { success: true };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Loader2, Plug } from "lucide-react"
|
import { useState, useEffect, useCallback } from "react"
|
||||||
|
import { Loader2, Plug, Database } from "lucide-react"
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
SidebarContent,
|
SidebarContent,
|
||||||
|
|
@ -14,12 +15,59 @@ import {
|
||||||
} from "@/components/ui/sidebar"
|
} from "@/components/ui/sidebar"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
import { useOAuth, useAvailableProviders } from "@/hooks/useOAuth"
|
import { useOAuth, useAvailableProviders } from "@/hooks/useOAuth"
|
||||||
|
import { toast } from "@/lib/toast"
|
||||||
|
|
||||||
type ConnectedAccountsSidebarProps = React.ComponentProps<typeof Sidebar>
|
type ConnectedAccountsSidebarProps = React.ComponentProps<typeof Sidebar>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for managing Granola sync config
|
||||||
|
*/
|
||||||
|
function useGranolaConfig() {
|
||||||
|
const [enabled, setEnabled] = useState<boolean>(false);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const loadConfig = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const result = await window.ipc.invoke('granola:getConfig', null);
|
||||||
|
setEnabled(result.enabled);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load Granola config:', error);
|
||||||
|
setEnabled(false);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadConfig();
|
||||||
|
}, [loadConfig]);
|
||||||
|
|
||||||
|
const updateConfig = useCallback(async (newEnabled: boolean) => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
await window.ipc.invoke('granola:setConfig', { enabled: newEnabled });
|
||||||
|
setEnabled(newEnabled);
|
||||||
|
toast(
|
||||||
|
newEnabled ? 'Granola sync enabled' : 'Granola sync disabled',
|
||||||
|
'success'
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update Granola config:', error);
|
||||||
|
toast('Failed to update Granola sync settings', 'error');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { enabled, isLoading, updateConfig };
|
||||||
|
}
|
||||||
|
|
||||||
export function ConnectedAccountsSidebar({ ...props }: ConnectedAccountsSidebarProps) {
|
export function ConnectedAccountsSidebar({ ...props }: ConnectedAccountsSidebarProps) {
|
||||||
const { providers, isLoading: providersLoading } = useAvailableProviders()
|
const { providers, isLoading: providersLoading } = useAvailableProviders()
|
||||||
|
const { enabled: granolaEnabled, isLoading: granolaLoading, updateConfig: updateGranolaConfig } = useGranolaConfig()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sidebar collapsible="none" className="hidden flex-1 md:flex" {...props}>
|
<Sidebar collapsible="none" className="hidden flex-1 md:flex" {...props}>
|
||||||
|
|
@ -49,6 +97,33 @@ export function ConnectedAccountsSidebar({ ...props }: ConnectedAccountsSidebarP
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
|
<SidebarGroup>
|
||||||
|
<SidebarGroupLabel>Data Sources</SidebarGroupLabel>
|
||||||
|
<SidebarGroupContent>
|
||||||
|
<SidebarMenu>
|
||||||
|
<SidebarMenuItem>
|
||||||
|
<div className="flex items-center gap-2 px-2 py-1.5 w-full">
|
||||||
|
<Switch
|
||||||
|
checked={granolaEnabled}
|
||||||
|
onCheckedChange={updateGranolaConfig}
|
||||||
|
disabled={granolaLoading}
|
||||||
|
className="shrink-0"
|
||||||
|
/>
|
||||||
|
<Database className="size-4 shrink-0" />
|
||||||
|
<div className="flex flex-col min-w-0 flex-1">
|
||||||
|
<span className="truncate text-sm">Granola Sync</span>
|
||||||
|
<span className="text-xs text-muted-foreground truncate">
|
||||||
|
Sync notes from Granola
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{granolaLoading && (
|
||||||
|
<Loader2 className="size-3 animate-spin shrink-0" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SidebarMenuItem>
|
||||||
|
</SidebarMenu>
|
||||||
|
</SidebarGroupContent>
|
||||||
|
</SidebarGroup>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,20 @@ const ipcSchemas = {
|
||||||
providers: z.array(z.string()),
|
providers: z.array(z.string()),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
'granola:getConfig': {
|
||||||
|
req: z.null(),
|
||||||
|
res: z.object({
|
||||||
|
enabled: z.boolean(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
'granola:setConfig': {
|
||||||
|
req: z.object({
|
||||||
|
enabled: z.boolean(),
|
||||||
|
}),
|
||||||
|
res: z.object({
|
||||||
|
success: z.literal(true),
|
||||||
|
}),
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue