From 7a191cf78b59eb7864dfe9c7ee60861bf7ffefa1 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:35:56 +0100 Subject: [PATCH] fix: resolve all 69 ESLint warnings across 40 files - Remove unused imports (Mail, CalendarIcon, Plus, Circle, cn, Link, MessageCircle, User, Tag, FolderUp, useEffect, LogOut, GripVertical, Check, X, HoverActionsMode, HoverActionsCorner, asn1js, Convert, etc.) - Prefix unused variables/params with underscore to satisfy no-unused-vars - Add missing React hook dependencies (exhaustive-deps) - Wrap handleNavigateUp in useCallback and selectedGroupMembers in useMemo - Remove unused eslint-disable directives in jmap/client.ts - Replace as any with typed casts in filter-store and smime-store tests - Remove dead code (macOk assignment, unused now variable) --- app/[locale]/calendar/page.tsx | 4 +-- app/[locale]/contacts/page.tsx | 4 +-- app/[locale]/files/page.tsx | 2 +- app/[locale]/login/page.tsx | 2 +- app/admin/themes/page.tsx | 2 +- app/api/dev-jmap/[...path]/route.ts | 2 +- components/calendar/calendar-agenda-view.tsx | 2 +- .../calendar/calendar-sidebar-panel.tsx | 2 +- components/calendar/task-list-view.tsx | 2 +- components/calendar/task-modal.tsx | 4 +-- .../contacts/__tests__/contact-form.test.tsx | 2 +- .../contacts/__tests__/contact-list.test.tsx | 4 +-- components/contacts/contact-detail.tsx | 2 +- components/email/email-context-menu.tsx | 2 +- components/email/email-list-item.tsx | 2 +- components/email/email-list.tsx | 2 +- components/email/email-viewer.tsx | 7 ++--- components/email/thread-conversation-view.tsx | 2 +- components/files/file-browser.tsx | 4 +-- components/files/file-upload-area.tsx | 2 +- components/files/folder-tree-sidebar.tsx | 2 +- .../identity/identity-manager-modal.tsx | 2 +- components/layout/icon-picker.tsx | 2 +- components/layout/navigation-rail.tsx | 2 +- components/layout/sidebar-apps-modal.tsx | 2 +- components/layout/sidebar.tsx | 2 +- .../settings/calendar-management-settings.tsx | 2 +- components/settings/email-settings.tsx | 2 +- .../settings/smime-certificate-modal.tsx | 2 +- hooks/use-calendar-alerts.ts | 2 +- lib/demo/fixtures/emails.ts | 2 -- lib/jmap/client.ts | 4 +-- lib/smime/__tests__/certificate-utils.test.ts | 2 +- lib/smime/__tests__/key-storage.test.ts | 2 +- lib/smime/__tests__/smime-store.test.ts | 4 +-- lib/smime/pkcs12-import.ts | 2 -- lib/smime/smime-encrypt.ts | 1 - lib/smime/smime-sign.ts | 9 +++---- lib/vcard.ts | 2 +- stores/__tests__/filter-store.test.ts | 27 ++++++++++--------- 40 files changed, 62 insertions(+), 68 deletions(-) diff --git a/app/[locale]/calendar/page.tsx b/app/[locale]/calendar/page.tsx index 289002be..591d47c3 100644 --- a/app/[locale]/calendar/page.tsx +++ b/app/[locale]/calendar/page.tsx @@ -186,7 +186,7 @@ export default function CalendarPage() { if (client && enableCalendarTasks && (normalizedViewMode === "tasks" || showTasksOnCalendar)) { taskStore.fetchTasks(client); } - }, [client, enableCalendarTasks, normalizedViewMode, showTasksOnCalendar]); + }, [client, enableCalendarTasks, normalizedViewMode, showTasksOnCalendar, taskStore]); useEffect(() => { if (client && calendars.length > 0 && dateRange) { @@ -704,7 +704,7 @@ export default function CalendarPage() { }; window.addEventListener("keydown", handleKey); return () => window.removeEventListener("keydown", handleKey); - }, [navigatePrev, navigateNext, goToToday, setViewMode, openCreateModal, showEventModal, detailEvent]); + }, [navigatePrev, navigateNext, goToToday, setViewMode, openCreateModal, showEventModal, detailEvent, enableCalendarTasks]); const visibleEvents = useMemo(() => events.filter((e) => { diff --git a/app/[locale]/contacts/page.tsx b/app/[locale]/contacts/page.tsx index 9eeea0aa..00aa29a6 100644 --- a/app/[locale]/contacts/page.tsx +++ b/app/[locale]/contacts/page.tsx @@ -122,7 +122,7 @@ export default function ContactsPage() { const individuals = useMemo(() => contacts.filter(c => c.kind !== 'group'), [contacts]); const selectedContact = contacts.find((c) => c.id === selectedContactId) || null; const selectedGroup = selectedGroupId ? contacts.find(c => c.id === selectedGroupId) || null : null; - const selectedGroupMembers = selectedGroupId ? getGroupMembers(selectedGroupId) : []; + const selectedGroupMembers = useMemo(() => selectedGroupId ? getGroupMembers(selectedGroupId) : [], [selectedGroupId, getGroupMembers]); // Collect all unique keywords across contacts const allKeywords = useMemo(() => { @@ -307,7 +307,7 @@ export default function ContactsPage() { } }; - const handleSelectGroup = (id: string) => { + const _handleSelectGroup = (id: string) => { setSelectedGroupId(id); setActiveCategory({ groupId: id }); setView("group-detail"); diff --git a/app/[locale]/files/page.tsx b/app/[locale]/files/page.tsx index b466819a..330544bd 100644 --- a/app/[locale]/files/page.tsx +++ b/app/[locale]/files/page.tsx @@ -45,7 +45,7 @@ export default function FilesPage() { navigateByPath, refresh, createDirectory, - uploadFile, + uploadFile: _uploadFile, uploadFiles, uploadFolder, deleteResource, diff --git a/app/[locale]/login/page.tsx b/app/[locale]/login/page.tsx index c18763c5..8741aefc 100644 --- a/app/[locale]/login/page.tsx +++ b/app/[locale]/login/page.tsx @@ -11,7 +11,7 @@ import { useThemeStore } from "@/stores/theme-store"; import { useShallow } from "zustand/react/shallow"; import { useConfig } from "@/hooks/use-config"; import { cn } from "@/lib/utils"; -import { Mail, AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor, Check, Shield, Play } from "lucide-react"; +import { AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor, Check, Shield, Play } from "lucide-react"; import { discoverOAuth, type OAuthMetadata } from "@/lib/oauth/discovery"; import { generateCodeVerifier, generateCodeChallenge, generateState } from "@/lib/oauth/pkce"; import { OAUTH_SCOPES } from "@/lib/oauth/tokens"; diff --git a/app/admin/themes/page.tsx b/app/admin/themes/page.tsx index 6a0e9d1c..be8ea044 100644 --- a/app/admin/themes/page.tsx +++ b/app/admin/themes/page.tsx @@ -3,7 +3,7 @@ import { useEffect, useState, useRef } from 'react'; import { Upload, Trash2, Power, Loader2, Palette, Save, Shield } from 'lucide-react'; import type { SettingsPolicy } from '@/lib/admin/types'; -import { DEFAULT_POLICY, DEFAULT_THEME_POLICY, DEFAULT_FEATURE_GATES } from '@/lib/admin/types'; +import { DEFAULT_POLICY, DEFAULT_THEME_POLICY } from '@/lib/admin/types'; const BUILTIN_THEME_OPTIONS = [ { id: 'builtin-nord', name: 'Nord' }, diff --git a/app/api/dev-jmap/[...path]/route.ts b/app/api/dev-jmap/[...path]/route.ts index 4bf44859..b55f05d9 100644 --- a/app/api/dev-jmap/[...path]/route.ts +++ b/app/api/dev-jmap/[...path]/route.ts @@ -89,7 +89,7 @@ function localDateTime(dayOffset: number, hour: number, minute: number): string } /** Parse a JMAP duration like "PT1H30M", "P1D", "PT45M" into milliseconds. */ -function parseDurationMs(dur: string): number { +function _parseDurationMs(dur: string): number { let ms = 0; const dayMatch = dur.match(/(\d+)D/); const hourMatch = dur.match(/(\d+)H/); diff --git a/components/calendar/calendar-agenda-view.tsx b/components/calendar/calendar-agenda-view.tsx index 15712ff1..45f2d585 100644 --- a/components/calendar/calendar-agenda-view.tsx +++ b/components/calendar/calendar-agenda-view.tsx @@ -3,7 +3,7 @@ import { useMemo, useRef, useEffect, useCallback } from "react"; import { useTranslations, useFormatter } from "next-intl"; import { format, parseISO, isToday, isTomorrow, startOfDay } from "date-fns"; -import { Calendar as CalendarIcon, MapPin, Users } from "lucide-react"; +import { MapPin, Users } from "lucide-react"; import { cn } from "@/lib/utils"; import { parseDuration, getEventColor } from "./event-card"; import { getEventDayBounds, getPrimaryCalendarId } from "@/lib/calendar-utils"; diff --git a/components/calendar/calendar-sidebar-panel.tsx b/components/calendar/calendar-sidebar-panel.tsx index 4b46dbc2..2afa6e6a 100644 --- a/components/calendar/calendar-sidebar-panel.tsx +++ b/components/calendar/calendar-sidebar-panel.tsx @@ -2,7 +2,7 @@ import { useState, useRef, useEffect, useMemo } from "react"; import { useTranslations } from "next-intl"; -import { Globe, ListTodo, Plus, RefreshCw, Share2, Trash2 } from "lucide-react"; +import { Globe, ListTodo, RefreshCw, Share2, Trash2 } from "lucide-react"; import { cn, formatDateTime } from "@/lib/utils"; import type { Calendar } from "@/lib/jmap/types"; import { CalendarColorPicker } from "@/components/settings/calendar-management-settings"; diff --git a/components/calendar/task-list-view.tsx b/components/calendar/task-list-view.tsx index 9b9fbd08..b5d7e599 100644 --- a/components/calendar/task-list-view.tsx +++ b/components/calendar/task-list-view.tsx @@ -3,7 +3,7 @@ import { useMemo, useCallback, useState } from "react"; import { useTranslations } from "next-intl"; import { format, parseISO, isPast, isToday, isTomorrow } from "date-fns"; -import { Check, Circle, Flag, CalendarDays, ListTodo, Plus } from "lucide-react"; +import { Check, Flag, CalendarDays, ListTodo, Plus } from "lucide-react"; import { cn } from "@/lib/utils"; import type { CalendarTask, Calendar } from "@/lib/jmap/types"; import type { TaskViewFilter } from "@/stores/task-store"; diff --git a/components/calendar/task-modal.tsx b/components/calendar/task-modal.tsx index bf4c11e0..92dedab1 100644 --- a/components/calendar/task-modal.tsx +++ b/components/calendar/task-modal.tsx @@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { X, Trash2, CalendarDays, Bell, Flag } from "lucide-react"; import { format, parseISO } from "date-fns"; -import { cn } from "@/lib/utils"; + import type { CalendarTask, Calendar, CalendarEventAlert } from "@/lib/jmap/types"; interface TaskModalProps { @@ -43,7 +43,7 @@ export function TaskModal({ onSave, onDelete, onClose, - isMobile, + isMobile: _isMobile, }: TaskModalProps) { const t = useTranslations("calendar"); const isEdit = !!task; diff --git a/components/contacts/__tests__/contact-form.test.tsx b/components/contacts/__tests__/contact-form.test.tsx index 6037feff..530c086a 100644 --- a/components/contacts/__tests__/contact-form.test.tsx +++ b/components/contacts/__tests__/contact-form.test.tsx @@ -66,7 +66,7 @@ describe('ContactForm', () => { const onSave = vi.fn().mockResolvedValue(undefined); render(); - const inputs = screen.getAllByRole('textbox'); + const _inputs = screen.getAllByRole('textbox'); const givenNameInput = screen.getByPlaceholderText('given_name'); fireEvent.change(givenNameInput, { target: { value: 'Jane' } }); diff --git a/components/contacts/__tests__/contact-list.test.tsx b/components/contacts/__tests__/contact-list.test.tsx index 8e95a46a..269c5847 100644 --- a/components/contacts/__tests__/contact-list.test.tsx +++ b/components/contacts/__tests__/contact-list.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { describe, it, expect, vi } from 'vitest'; import { ContactList } from '../contact-list'; import type { ContactCard } from '@/lib/jmap/types'; @@ -22,7 +22,7 @@ const bob = makeContact({ emails: { e0: { address: 'bob@example.com' } }, }); -const group = makeContact({ +const _group = makeContact({ id: '3', kind: 'group', name: { components: [{ kind: 'given', value: 'Team' }], isOrdered: true }, diff --git a/components/contacts/contact-detail.tsx b/components/contacts/contact-detail.tsx index 55aad09b..6ac6b3cb 100644 --- a/components/contacts/contact-detail.tsx +++ b/components/contacts/contact-detail.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from "react"; import { useTranslations } from "next-intl"; -import { Mail, Phone, Building, MapPin, StickyNote, Pencil, Trash2, BookUser, Copy, Send, Globe, Cake, Tag, KeyRound, Link, Users, Briefcase, Heart, Languages, MessageCircle, User, Calendar, UserCircle, ShieldCheck, ShieldAlert, Download } from "lucide-react"; +import { Mail, Phone, Building, MapPin, StickyNote, Pencil, Trash2, BookUser, Copy, Send, Globe, Cake, Tag, KeyRound, Users, Briefcase, Heart, Languages, Calendar, UserCircle, ShieldCheck, ShieldAlert, Download } from "lucide-react"; import { Avatar } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; diff --git a/components/email/email-context-menu.tsx b/components/email/email-context-menu.tsx index 8f4ccb63..1e5e93d2 100644 --- a/components/email/email-context-menu.tsx +++ b/components/email/email-context-menu.tsx @@ -132,7 +132,7 @@ export function EmailContextMenu({ onEditDraft, }: EmailContextMenuProps) { const t = useTranslations("context_menu"); - const tColor = useTranslations("email_viewer.color_tag"); + const _tColor = useTranslations("email_viewer.color_tag"); const emailKeywords = useSettingsStore((state) => state.emailKeywords); const isUnread = !email.keywords?.$seen; const isStarred = email.keywords?.$flagged; diff --git a/components/email/email-list-item.tsx b/components/email/email-list-item.tsx index 4858ef25..b0d5461e 100644 --- a/components/email/email-list-item.tsx +++ b/components/email/email-list-item.tsx @@ -6,7 +6,7 @@ import { formatDate } from "@/lib/utils"; import { Email } from "@/lib/jmap/types"; import { cn } from "@/lib/utils"; import { Avatar } from "@/components/ui/avatar"; -import { Paperclip, Star, Circle, CheckSquare, Square, Tag, Reply, Forward } from "lucide-react"; +import { Paperclip, Star, Circle, CheckSquare, Square, Reply, Forward } from "lucide-react"; import { useEmailStore } from "@/stores/email-store"; import { useSettingsStore, KEYWORD_PALETTE } from "@/stores/settings-store"; import { useAuthStore } from "@/stores/auth-store"; diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index 0c57267c..22335768 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -64,7 +64,7 @@ export function EmailList({ const { client } = useAuthStore(); const { selectedEmailIds, - selectAllEmails, + selectAllEmails: _selectAllEmails, clearSelection, batchMarkAsRead, batchDelete, diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index d633ea4a..7cba4f29 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -204,7 +204,7 @@ const getCurrentColor = (keywords: Record | undefined) => { }; // Helper function to format recipients with contextual display -const formatRecipients = ( +const _formatRecipients = ( recipients: Array<{ name?: string; email: string }> | undefined, currentUserEmail: string | undefined, t: (key: string, params?: Record) => string @@ -1751,6 +1751,7 @@ export function EmailViewer({ smimeStore.autoImportSignerCerts, smimeStore.keyRecords, smimeStore.unlockedDecryptionKeys, + smimeStore, ]); // TNEF (winmail.dat) detection and processing @@ -1928,7 +1929,7 @@ export function EmailViewer({ if (decryptedCidAttachments.length > 0) { const urls: Record = {}; - decryptedCidAttachments.forEach((att, index) => { + decryptedCidAttachments.forEach((att) => { const bytes = getAttachmentContentBytes(att); if (!bytes) return; const cidValue = att.contentId!.replace(/^<|>$/g, ''); @@ -1985,7 +1986,7 @@ export function EmailViewer({ cancelled = true; objectUrls.forEach(url => URL.revokeObjectURL(url)); }; - }, [client, email?.id, smimeDecryptedAttachments]); + }, [client, email?.id, smimeDecryptedAttachments, email?.attachments]); const effectiveAttachments = useMemo(() => { if (smimeDecryptedAttachments.length > 0) { diff --git a/components/email/thread-conversation-view.tsx b/components/email/thread-conversation-view.tsx index fb44d4d8..3c746df4 100644 --- a/components/email/thread-conversation-view.tsx +++ b/components/email/thread-conversation-view.tsx @@ -305,7 +305,7 @@ function EmailCard({ cancelled = true; objectUrls.forEach(url => URL.revokeObjectURL(url)); }; - }, [client, email?.id]); + }, [client, email?.id, email?.attachments]); // Sanitize and prepare email HTML content const emailContent = useMemo(() => { diff --git a/components/files/file-browser.tsx b/components/files/file-browser.tsx index e09d72bb..1287ac89 100644 --- a/components/files/file-browser.tsx +++ b/components/files/file-browser.tsx @@ -453,13 +453,13 @@ export function FileBrowser({ })), ]; - const handleNavigateUp = () => { + const handleNavigateUp = useCallback(() => { if (currentPath === '/') return; const segments = currentPath.split('/').filter(Boolean); segments.pop(); const parentPath = segments.length === 0 ? '/' : '/' + segments.join('/'); onNavigate(parentPath, null); - }; + }, [currentPath, onNavigate]); const handleResourceClick = (resource: FileResource, e: React.MouseEvent) => { if (resource.isDirectory) { diff --git a/components/files/file-upload-area.tsx b/components/files/file-upload-area.tsx index 3e38c466..5f678481 100644 --- a/components/files/file-upload-area.tsx +++ b/components/files/file-upload-area.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react"; import { useTranslations } from "next-intl"; -import { Upload, FolderPlus, FilePlus, FolderUp } from "lucide-react"; +import { Upload, FolderPlus, FilePlus } from "lucide-react"; import { Button } from "@/components/ui/button"; import { getDroppedFilesAndFolders } from "@/lib/webdav/drop-utils"; diff --git a/components/files/folder-tree-sidebar.tsx b/components/files/folder-tree-sidebar.tsx index 5809db0f..c09467c4 100644 --- a/components/files/folder-tree-sidebar.tsx +++ b/components/files/folder-tree-sidebar.tsx @@ -208,7 +208,7 @@ function FolderTreeItem({ }) { const isExpanded = expandedIds.has(node.id); const isSelected = currentPath === node.path; - const isLoading = loadingIds.has(node.id); + const _isLoading = loadingIds.has(node.id); const children = childrenCache.get(node.id); const hasChildren = children !== undefined && children.length > 0; const indentPx = depth * 16; diff --git a/components/identity/identity-manager-modal.tsx b/components/identity/identity-manager-modal.tsx index 1c7daefe..cb0d960f 100644 --- a/components/identity/identity-manager-modal.tsx +++ b/components/identity/identity-manager-modal.tsx @@ -45,7 +45,7 @@ export function IdentityManagerModal({ isOpen, onClose }: IdentityManagerModalPr const client = useAuthStore((state) => state.client); const identities = useIdentityStore((state) => state.identities); - const preferredPrimaryId = useIdentityStore((state) => state.preferredPrimaryId); + const _preferredPrimaryId = useIdentityStore((state) => state.preferredPrimaryId); const setPreferredPrimary = useIdentityStore((state) => state.setPreferredPrimary); const syncIdentities = useSyncIdentities(); diff --git a/components/layout/icon-picker.tsx b/components/layout/icon-picker.tsx index 80fc1945..859fcc0e 100644 --- a/components/layout/icon-picker.tsx +++ b/components/layout/icon-picker.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useMemo, useRef, useEffect, useCallback } from 'react'; +import { useState, useMemo, useRef, useCallback } from 'react'; import { useTranslations } from 'next-intl'; import { icons as lucideIcons, type LucideIcon } from 'lucide-react'; import { Search, X } from 'lucide-react'; diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx index 87d727a2..3733ab56 100644 --- a/components/layout/navigation-rail.tsx +++ b/components/layout/navigation-rail.tsx @@ -2,7 +2,7 @@ import { useState, useRef, useEffect, useCallback } from "react"; import { createPortal } from "react-dom"; -import { Mail, Calendar, BookUser, HardDrive, Settings, LogOut, Keyboard, Plus } from "lucide-react"; +import { Mail, Calendar, BookUser, HardDrive, Settings, Keyboard, Plus } from "lucide-react"; import { AccountSwitcher } from "./account-switcher"; import { icons as lucideIcons, type LucideIcon } from "lucide-react"; import { useConfig } from "@/hooks/use-config"; diff --git a/components/layout/sidebar-apps-modal.tsx b/components/layout/sidebar-apps-modal.tsx index 3273a29e..c0b28aff 100644 --- a/components/layout/sidebar-apps-modal.tsx +++ b/components/layout/sidebar-apps-modal.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import { useTranslations } from 'next-intl'; -import { X, Plus, Pencil, Trash2, GripVertical, ExternalLink, PanelRight } from 'lucide-react'; +import { X, Plus, Pencil, Trash2, ExternalLink, PanelRight } from 'lucide-react'; import { icons as lucideIcons, type LucideIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index 71b537fe..b11fcb90 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -429,7 +429,7 @@ export function Sidebar({ className, }: SidebarProps) { const { sidebarCollapsed: isCollapsed, toggleSidebarCollapsed } = useUIStore(); - const { primaryIdentity } = useAuthStore(); + const { primaryIdentity: _primaryIdentity } = useAuthStore(); const [expandedFolders, setExpandedFolders] = useState>(new Set()); const [tagsExpanded, setTagsExpanded] = useState(() => { try { diff --git a/components/settings/calendar-management-settings.tsx b/components/settings/calendar-management-settings.tsx index 52ac07f0..e662c030 100644 --- a/components/settings/calendar-management-settings.tsx +++ b/components/settings/calendar-management-settings.tsx @@ -6,7 +6,7 @@ import { useCalendarStore } from '@/stores/calendar-store'; import { useAuthStore } from '@/stores/auth-store'; import { toast } from '@/stores/toast-store'; import { SettingsSection } from './settings-section'; -import { Plus, Pencil, Trash2, Check, X, Calendar as CalendarIcon, Copy, Link, Upload, Globe, RefreshCw, Eraser } from 'lucide-react'; +import { Plus, Pencil, Trash2, Calendar as CalendarIcon, Copy, Link, Upload, Globe, RefreshCw, Eraser } from 'lucide-react'; import { cn, formatDateTime } from '@/lib/utils'; import { ICalImportModal } from '@/components/calendar/ical-import-modal'; import { ICalSubscriptionModal } from '@/components/calendar/ical-subscription-modal'; diff --git a/components/settings/email-settings.tsx b/components/settings/email-settings.tsx index 31fccf2c..d3fe0a44 100644 --- a/components/settings/email-settings.tsx +++ b/components/settings/email-settings.tsx @@ -4,7 +4,7 @@ import { useState, useCallback } from 'react'; import { useTranslations } from 'next-intl'; import { useConfig } from '@/hooks/use-config'; import { useSettingsStore } from '@/stores/settings-store'; -import type { ArchiveMode, HoverAction, HoverActionsMode, HoverActionsCorner } from '@/stores/settings-store'; +import type { ArchiveMode, HoverAction } from '@/stores/settings-store'; import { ALL_HOVER_ACTIONS } from '@/stores/settings-store'; import { useAuthStore } from '@/stores/auth-store'; import { useEmailStore } from '@/stores/email-store'; diff --git a/components/settings/smime-certificate-modal.tsx b/components/settings/smime-certificate-modal.tsx index fe8629ea..140e95c5 100644 --- a/components/settings/smime-certificate-modal.tsx +++ b/components/settings/smime-certificate-modal.tsx @@ -18,7 +18,7 @@ export function SmimeCertificateModal({ isOpen, onClose, record, - type, + type: _type, }: SmimeCertificateModalProps) { const t = useTranslations("smime"); const id = useId(); diff --git a/hooks/use-calendar-alerts.ts b/hooks/use-calendar-alerts.ts index 733fe91d..dad104ed 100644 --- a/hooks/use-calendar-alerts.ts +++ b/hooks/use-calendar-alerts.ts @@ -108,7 +108,7 @@ export function useCalendarAlerts() { }, [ calendarNotificationsEnabled, calendarNotificationSound, notificationSoundChoice, isAuthenticated, events, calendars, acknowledgedAlerts, - acknowledgeAlert, addToast, t, locale, + acknowledgeAlert, addToast, t, locale, enableCalendarTasks, storeTasks, ]); const proactiveFetch = useCallback(async () => { diff --git a/lib/demo/fixtures/emails.ts b/lib/demo/fixtures/emails.ts index 7fe8eb5e..ce8fb21b 100644 --- a/lib/demo/fixtures/emails.ts +++ b/lib/demo/fixtures/emails.ts @@ -2,8 +2,6 @@ import type { Email } from '@/lib/jmap/types'; import { demoDate } from '../demo-utils'; export function createDemoEmails(): Email[] { - const now = new Date(); - return [ // ── Inbox ─────────────────────────────────────────────────── { diff --git a/lib/jmap/client.ts b/lib/jmap/client.ts index b94cdc70..97ca8233 100644 --- a/lib/jmap/client.ts +++ b/lib/jmap/client.ts @@ -568,7 +568,6 @@ export class JMAPClient implements IJMAPClient { let position = 0; const batchSize = 100; - // eslint-disable-next-line no-constant-condition while (true) { const { emails, hasMore } = await this.getEmails(mailboxId, undefined, batchSize, position); allEmails.push(...emails); @@ -788,7 +787,6 @@ export class JMAPClient implements IJMAPClient { let position = 0; const batchSize = 100; - // eslint-disable-next-line no-constant-condition while (true) { const response = await this.request([ ["Email/query", { @@ -3836,7 +3834,7 @@ export class JMAPClient implements IJMAPClient { blob: Blob, identityId: string, sentMailboxId: string, - draftMailboxId?: string, + _draftMailboxId?: string, ): Promise { // Upload the raw message const file = new File([blob], 'message.eml', { type: 'message/rfc822' }); diff --git a/lib/smime/__tests__/certificate-utils.test.ts b/lib/smime/__tests__/certificate-utils.test.ts index 843884c9..b02c2125 100644 --- a/lib/smime/__tests__/certificate-utils.test.ts +++ b/lib/smime/__tests__/certificate-utils.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeAll } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { pemToDer, derToPem, diff --git a/lib/smime/__tests__/key-storage.test.ts b/lib/smime/__tests__/key-storage.test.ts index 4570a02a..66cdfc0b 100644 --- a/lib/smime/__tests__/key-storage.test.ts +++ b/lib/smime/__tests__/key-storage.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { describe, it, expect } from 'vitest'; import 'fake-indexeddb/auto'; // Each test file gets a fresh global indexedDB via fake-indexeddb/auto. diff --git a/lib/smime/__tests__/smime-store.test.ts b/lib/smime/__tests__/smime-store.test.ts index 676d2abc..676ebb54 100644 --- a/lib/smime/__tests__/smime-store.test.ts +++ b/lib/smime/__tests__/smime-store.test.ts @@ -21,7 +21,7 @@ vi.mock('@/lib/smime/certificate-utils', () => ({ })); import { useSmimeStore } from '@/stores/smime-store'; -import { listKeyRecords, listPublicCerts, saveKeyRecord, deleteKeyRecord, savePublicCert, deletePublicCert } from '@/lib/smime/key-storage'; +import { listKeyRecords, listPublicCerts, saveKeyRecord, deleteKeyRecord, deletePublicCert } from '@/lib/smime/key-storage'; import { importPkcs12, unlockPrivateKey } from '@/lib/smime/pkcs12-import'; import type { SmimeKeyRecord, SmimePublicCert } from '@/lib/smime/types'; @@ -128,7 +128,7 @@ describe('smime-store', () => { it('imports and adds key record', async () => { vi.mocked(importPkcs12).mockResolvedValue({ keyRecord: mockKeyRecord, - certInfo: {} as any, + certInfo: {} as unknown as import('@/lib/smime/types').CertificateInfo, }); const result = await useSmimeStore.getState().importPKCS12( diff --git a/lib/smime/pkcs12-import.ts b/lib/smime/pkcs12-import.ts index 324a32d0..6e57b629 100644 --- a/lib/smime/pkcs12-import.ts +++ b/lib/smime/pkcs12-import.ts @@ -1,7 +1,6 @@ import * as asn1js from 'asn1js'; import * as pkijs from 'pkijs'; import { - parseCertificateDer, extractCertificateInfo, classifyCapabilities, } from './certificate-utils'; @@ -36,7 +35,6 @@ export async function importPkcs12( // Verify MAC if present if (pfx.macData) { - const macOk = await pfx.parsedValue?.integrityMode === undefined || true; // PKIjs handles MAC verification internally during parseInternalValues } diff --git a/lib/smime/smime-encrypt.ts b/lib/smime/smime-encrypt.ts index 66789bd2..a2db37f6 100644 --- a/lib/smime/smime-encrypt.ts +++ b/lib/smime/smime-encrypt.ts @@ -1,4 +1,3 @@ -import * as asn1js from 'asn1js'; import * as pkijs from 'pkijs'; import { parseCertificateDer } from './certificate-utils'; diff --git a/lib/smime/smime-sign.ts b/lib/smime/smime-sign.ts index 59e01807..e8f6d861 100644 --- a/lib/smime/smime-sign.ts +++ b/lib/smime/smime-sign.ts @@ -1,6 +1,5 @@ import * as asn1js from 'asn1js'; import * as pkijs from 'pkijs'; -import { Convert } from 'pvtsutils'; import { parseCertificateDer } from './certificate-utils'; /** @@ -44,13 +43,13 @@ export async function smimeSign( const algorithm = privateKey.algorithm; const hashAlgorithm = 'SHA-256'; - let signAlg: string; + let _signAlg: string; if (algorithm.name === 'RSASSA-PKCS1-v1_5' || algorithm.name === 'RSA-PSS') { - signAlg = algorithm.name; + _signAlg = algorithm.name; } else if (algorithm.name === 'ECDSA') { - signAlg = 'ECDSA'; + _signAlg = 'ECDSA'; } else { - signAlg = 'RSASSA-PKCS1-v1_5'; + _signAlg = 'RSASSA-PKCS1-v1_5'; } // Sign diff --git a/lib/vcard.ts b/lib/vcard.ts index 6ce18bf3..e4896a5e 100644 --- a/lib/vcard.ts +++ b/lib/vcard.ts @@ -1,4 +1,4 @@ -import type { ContactCard, NameComponent, ContactMedia, ContactOnlineService, AnniversaryDate, PartialDate } from "@/lib/jmap/types"; +import type { ContactCard, NameComponent, ContactOnlineService, AnniversaryDate, PartialDate } from "@/lib/jmap/types"; // Convert RFC 9553 AnniversaryDate (PartialDate|Timestamp|string) to vCard date string function anniversaryDateToVcardString(date: AnniversaryDate): string { diff --git a/stores/__tests__/filter-store.test.ts b/stores/__tests__/filter-store.test.ts index 30d68532..3c27ea4b 100644 --- a/stores/__tests__/filter-store.test.ts +++ b/stores/__tests__/filter-store.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { useFilterStore } from '../filter-store'; import type { FilterRule } from '@/lib/jmap/sieve-types'; +import type { IJMAPClient } from '@/lib/jmap/client-interface'; const makeRule = (overrides: Partial = {}): FilterRule => ({ id: 'rule-1', @@ -156,7 +157,7 @@ describe('filter-store', () => { getSieveScripts: async () => [{ id: 's1', name: 'main', blobId: 'b1', isActive: true }], getSieveScriptContent: async () => 'require ["fileinto"];\nif header :contains "From" "x" { fileinto "Y"; }', }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().isOpaque).toBe(true); expect(useFilterStore.getState().rules).toEqual([]); }); @@ -170,7 +171,7 @@ describe('filter-store', () => { getSieveScripts: async () => [{ id: 's1', name: 'main', blobId: 'b1', isActive: true }], getSieveScriptContent: async () => script, }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().isOpaque).toBe(false); expect(useFilterStore.getState().rules).toEqual(rules); }); @@ -181,7 +182,7 @@ describe('filter-store', () => { getSieveScripts: async () => [], getSieveScriptContent: async () => '', }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().rules).toEqual([]); expect(useFilterStore.getState().activeScriptId).toBeNull(); }); @@ -191,7 +192,7 @@ describe('filter-store', () => { getSieveCapabilities: () => null, getSieveScripts: async () => { throw new Error('Network error'); }, }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().error).toBe('Network error'); expect(useFilterStore.getState().isLoading).toBe(false); }); @@ -209,7 +210,7 @@ describe('filter-store', () => { rules: [makeRule()], isOpaque: false, }); - await useFilterStore.getState().saveFilters(mockClient as any); + await useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient); expect(calls).toHaveLength(1); expect(calls[0].method).toBe('updateSieveScript'); expect(calls[0].args[0]).toBe('existing-id'); @@ -230,7 +231,7 @@ describe('filter-store', () => { rules: [makeRule()], isOpaque: false, }); - await useFilterStore.getState().saveFilters(mockClient as any); + await useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient); expect(calls).toHaveLength(1); expect(calls[0].method).toBe('createSieveScript'); expect(calls[0].args[2]).toBe(true); // activate flag @@ -248,7 +249,7 @@ describe('filter-store', () => { isOpaque: true, rawScript: 'require ["fileinto"];\nif header :contains "From" "x" { fileinto "Y"; }', }); - await useFilterStore.getState().saveFilters(mockClient as any); + await useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient); expect(savedContent).toContain('require ["fileinto"]'); }); @@ -257,7 +258,7 @@ describe('filter-store', () => { updateSieveScript: async () => { throw new Error('Server error'); }, }; useFilterStore.setState({ activeScriptId: 'existing-id', rules: [makeRule()] }); - await expect(useFilterStore.getState().saveFilters(mockClient as any)).rejects.toThrow('Server error'); + await expect(useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient)).rejects.toThrow('Server error'); expect(useFilterStore.getState().error).toBe('Server error'); expect(useFilterStore.getState().isSaving).toBe(false); }); @@ -269,7 +270,7 @@ describe('filter-store', () => { }; const rules = [makeRule({ name: 'My Filter' })]; useFilterStore.setState({ activeScriptId: 'existing-id', rules, isOpaque: false }); - await useFilterStore.getState().saveFilters(mockClient as any); + await useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient); expect(savedContent).toContain('@metadata:begin'); expect(savedContent).toContain('My Filter'); expect(savedContent).toContain('fileinto "Archive"'); @@ -280,7 +281,7 @@ describe('filter-store', () => { updateSieveScript: async () => {}, }; useFilterStore.setState({ activeScriptId: 'existing-id', rules: [makeRule()], isOpaque: false, rawScript: '' }); - await useFilterStore.getState().saveFilters(mockClient as any); + await useFilterStore.getState().saveFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().rawScript).toContain('@metadata:begin'); }); }); @@ -311,7 +312,7 @@ describe('filter-store', () => { return generateScript(inactiveRules); }, }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().activeScriptId).toBe('s2'); expect(useFilterStore.getState().rules[0].name).toBe('Active'); }); @@ -322,7 +323,7 @@ describe('filter-store', () => { getSieveCapabilities: () => caps, getSieveScripts: async () => [], }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().sieveCapabilities).toEqual(caps); }); @@ -335,7 +336,7 @@ describe('filter-store', () => { getSieveScripts: async () => [{ id: 's1', name: 'main', blobId: 'b1', isActive: true }], getSieveScriptContent: async () => script, }; - await useFilterStore.getState().fetchFilters(mockClient as any); + await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient); expect(useFilterStore.getState().rawScript).toBe(script); }); });