169 lines · 5.2 KB · TypeScript
RawDownload
/** * SettingsView - Main settings panel with all app configuration options. * Provides UI for managing notifications, display settings, and advanced options. */ import { useState } from 'react'; import { useStore } from '@renderer/store';import { Loader2 } from 'lucide-react'; import { useSettingsConfig, useSettingsHandlers } from './hooks';import {  AdvancedSection,  ConnectionSection,  GeneralSection,  NotificationsSection,  WorkspaceSection,} from './sections';import { type SettingsSection, SettingsTabs } from './SettingsTabs'; export const SettingsView = (): React.JSX.Element | null => {  const [activeSection, setActiveSection] = useState<SettingsSection>('general');  const pendingSettingsSection = useStore((s) => s.pendingSettingsSection);  const clearPendingSettingsSection = useStore((s) => s.clearPendingSettingsSection);   // Consume pending section during render (React-recommended pattern for adjusting state on prop change)  const [prevPending, setPrevPending] = useState<string | null>(null);  if (pendingSettingsSection !== prevPending) {    setPrevPending(pendingSettingsSection);    if (pendingSettingsSection) {      setActiveSection(pendingSettingsSection as SettingsSection);      clearPendingSettingsSection();    }  }   const {    config,    safeConfig,    loading,    saving,    error,    setError,    setSaving,    setConfig,    setOptimisticConfig,    updateConfig,    ignoredRepositoryItems,    excludedRepositoryIds,    isSnoozed,  } = useSettingsConfig();   const handlers = useSettingsHandlers({    config,    setSaving,    setError,    setConfig,    setOptimisticConfig,    updateConfig,  });   // Loading state  if (loading) {    return (      <div        className="flex flex-1 items-center justify-center"        style={{ backgroundColor: 'var(--color-surface)' }}      >        <div className="flex items-center gap-3" style={{ color: 'var(--color-text-muted)' }}>          <Loader2 className="size-5 animate-spin" />          <span>Loading settings...</span>        </div>      </div>    );  }   // Error state  if (error && !config) {    return (      <div        className="flex flex-1 items-center justify-center"        style={{ backgroundColor: 'var(--color-surface)' }}      >        <div className="text-center">          <p className="mb-4 text-red-400">{error}</p>          <button            onClick={() => window.location.reload()}            className="rounded-md px-4 py-2 transition-colors"            style={{              backgroundColor: 'var(--color-surface-raised)',              color: 'var(--color-text-secondary)',            }}          >            Retry          </button>        </div>      </div>    );  }   if (!config) return null;   return (    <div className="flex-1 overflow-auto" style={{ backgroundColor: 'var(--color-surface)' }}>      <div className="mx-auto max-w-2xl px-6 py-8">        {/* Header */}        <div className="mb-6">          <h1 className="text-lg font-medium" style={{ color: 'var(--color-text)' }}>            Settings          </h1>          <p className="text-sm" style={{ color: 'var(--color-text-muted)' }}>            Manage your app preferences          </p>          {error && (            <div className="mt-4 rounded-md border border-red-500/20 bg-red-500/10 px-3 py-2">              <p className="text-sm text-red-400">{error}</p>            </div>          )}        </div>         {/* Tabs */}        <SettingsTabs activeSection={activeSection} onSectionChange={setActiveSection} />         {/* Content */}        <div className="mt-4">          {activeSection === 'general' && (            <GeneralSection              safeConfig={safeConfig}              saving={saving}              onGeneralToggle={handlers.handleGeneralToggle}              onThemeChange={handlers.handleThemeChange}            />          )}           {activeSection === 'connection' && <ConnectionSection />}           {activeSection === 'workspace' && <WorkspaceSection />}           {activeSection === 'notifications' && (            <NotificationsSection              safeConfig={safeConfig}              saving={saving}              isSnoozed={isSnoozed}              ignoredRepositoryItems={ignoredRepositoryItems}              excludedRepositoryIds={excludedRepositoryIds}              onNotificationToggle={handlers.handleNotificationToggle}              onSnooze={handlers.handleSnooze}              onClearSnooze={handlers.handleClearSnooze}              onAddIgnoredRepository={handlers.handleAddIgnoredRepository}              onRemoveIgnoredRepository={handlers.handleRemoveIgnoredRepository}              onAddTrigger={handlers.handleAddTrigger}              onUpdateTrigger={handlers.handleUpdateTrigger}              onRemoveTrigger={handlers.handleRemoveTrigger}            />          )}           {activeSection === 'advanced' && (            <AdvancedSection              saving={saving}              onResetToDefaults={handlers.handleResetToDefaults}              onExportConfig={handlers.handleExportConfig}              onImportConfig={handlers.handleImportConfig}              onOpenInEditor={handlers.handleOpenInEditor}            />          )}        </div>      </div>    </div>  );};