email list sync button uses new email-sync handler, job duration logging
Rewired EmailList sync button to call /email/accounts/:id/sync instead of the old gmail-sync job. Shows sync button for connected and synced accounts. Allow manual incremental sync for synced accounts. Added duration logging to queue runner: start/complete/fail markers with elapsed time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,14 +6,13 @@ import { toast } from 'sonner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useClient } from 'hooks/useClient';
|
||||
import { useGlobal } from 'hooks/useGlobal';
|
||||
import { useJobs } from 'hooks/useJobs';
|
||||
import type { EmailSummary } from 'types';
|
||||
|
||||
type GoogleStatus = {
|
||||
configured: boolean;
|
||||
connected: boolean;
|
||||
email: string | null;
|
||||
picture: string | null;
|
||||
type EmailAccountRow = {
|
||||
id: number;
|
||||
provider: string;
|
||||
email: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
const LIMIT = 50;
|
||||
@@ -42,13 +41,13 @@ export const EmailList = () => {
|
||||
const [selectedId, setSelectedId] = useGlobal<string | null>('EMAIL_SELECTED', null);
|
||||
const [folder, setFolder] = useGlobal<string>('EMAIL_FOLDER', 'inbox');
|
||||
const [page, setPage] = useState(1);
|
||||
const { jobs, createJob } = useJobs({ type: 'gmail-sync' });
|
||||
const isSyncing = jobs.some((j) => j.status === 'queued' || j.status === 'running');
|
||||
|
||||
const { data: googleStatus } = useQuery({
|
||||
queryKey: ['google-status'],
|
||||
queryFn: () => client.get<GoogleStatus>('/integrations/google/status'),
|
||||
const { data: emailAccounts = [], refetch: refetchAccounts } = useQuery({
|
||||
queryKey: ['email-accounts'],
|
||||
queryFn: () => client.get<EmailAccountRow[]>('/email/accounts'),
|
||||
});
|
||||
const syncableAccount = emailAccounts.find((a) => a.status === 'connected' || a.status === 'synced');
|
||||
const isSyncing = emailAccounts.some((a) => a.status === 'syncing' || a.status === 'queued');
|
||||
const hasAccounts = emailAccounts.length > 0;
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['email-messages', page, folder],
|
||||
@@ -75,17 +74,23 @@ export const EmailList = () => {
|
||||
};
|
||||
|
||||
const handleSync = async () => {
|
||||
if (!syncableAccount) return;
|
||||
try {
|
||||
await createJob({ lane: 'google-api', type: 'gmail-sync', notify: false });
|
||||
toast.success('Gmail sync started');
|
||||
} catch {
|
||||
toast.error('Failed to start sync');
|
||||
await client.post(`/email/accounts/${syncableAccount.id}/sync`, {});
|
||||
toast.success('Sync started');
|
||||
refetchAccounts();
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to start sync');
|
||||
}
|
||||
};
|
||||
|
||||
// Refresh email list when a sync job completes
|
||||
// Poll accounts while syncing, refresh email list when done
|
||||
const prevSyncing = useRef(false);
|
||||
useEffect(() => {
|
||||
if (isSyncing) {
|
||||
const interval = setInterval(refetchAccounts, 5000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
if (prevSyncing.current && !isSyncing) {
|
||||
queryClient.invalidateQueries({ queryKey: ['email-messages'] });
|
||||
}
|
||||
@@ -134,16 +139,14 @@ export const EmailList = () => {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 text-sm opacity-50">
|
||||
<Mail className="h-8 w-8" />
|
||||
{googleStatus && !googleStatus.configured ? (
|
||||
<span>Google integration not configured. Contact your administrator.</span>
|
||||
) : googleStatus && !googleStatus.connected ? (
|
||||
{!hasAccounts ? (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<span>Connect your Google account to sync emails</span>
|
||||
<span>Add an email account to get started</span>
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link to="/settings/integrations">Connect Google</Link>
|
||||
<Link to="/settings/integrations">Add Account</Link>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
) : syncableAccount ? (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<span>No emails synced yet</span>
|
||||
<Button variant="outline" size="sm" onClick={handleSync} disabled={isSyncing}>
|
||||
@@ -151,6 +154,13 @@ export const EmailList = () => {
|
||||
Sync Now
|
||||
</Button>
|
||||
</div>
|
||||
) : isSyncing ? (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Loader2 className="h-5 w-5 animate-spin" />
|
||||
<span>Syncing emails...</span>
|
||||
</div>
|
||||
) : (
|
||||
<span>No emails synced yet</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -179,18 +189,17 @@ export const EmailList = () => {
|
||||
})}
|
||||
</div>
|
||||
<span className="text-xs opacity-50">{total}</span>
|
||||
<button
|
||||
onClick={handleSync}
|
||||
disabled={isSyncing}
|
||||
className="p-0.5 rounded hover:bg-black/10 transition-colors cursor-pointer disabled:cursor-default disabled:opacity-50 shrink-0"
|
||||
title={isSyncing ? 'Syncing...' : 'Sync emails'}
|
||||
>
|
||||
{isSyncing ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
) : (
|
||||
{isSyncing ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin shrink-0 opacity-50" title="Syncing..." />
|
||||
) : syncableAccount ? (
|
||||
<button
|
||||
onClick={handleSync}
|
||||
className="p-0.5 rounded hover:bg-black/10 transition-colors cursor-pointer shrink-0"
|
||||
title="Sync emails"
|
||||
>
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
)}
|
||||
</button>
|
||||
</button>
|
||||
) : null}
|
||||
{totalPages > 1 && (
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user