Files
Netcatty/components/FileOpenerDialog.tsx
T
陈大猫 834a677cfe chore: remove debug console.log and unused exports (#445)
* chore: remove 65 debug console.log statements from production code

Remove bracketed debug traces ([SFTP navigateTo], [SFTPBackend],
[ManagedSourceSync], [AutoSync], [CloudSync], [Settings], etc.)
across 16 files. These were development logging that shipped to
production, creating noise in the console.

Also clean up dead variables left behind after log removal
(hotkeyDebug, results, verification reads).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove 43 unused exports and dead type definitions

Remove export keywords from symbols that are never imported outside
their defining file. Symbols still used internally keep their
definitions; symbols not used at all are removed entirely.

Removed entirely: TerminalLine, SessionLogsSettings, KDFParams,
SyncManagerConfig, GoogleTokenResponse, OneDriveTokenResponse,
getSyncStatusColor, resolveHostTerminalAppearance,
TerminalAppearanceDefaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 21:29:58 +08:00

132 lines
4.6 KiB
TypeScript

/**
* FileOpenerDialog - Dialog for choosing how to open a file
*/
import { Edit2, FolderOpen } from 'lucide-react';
import React, { useCallback, useState } from 'react';
import { useI18n } from '../application/i18n/I18nProvider';
import type { FileOpenerType, SystemAppInfo } from '../lib/sftpFileUtils';
import { getFileExtension, isKnownBinaryFile } from '../lib/sftpFileUtils';
import { Button } from './ui/button';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from './ui/dialog';
interface FileOpenerDialogProps {
open: boolean;
onClose: () => void;
fileName: string;
onSelect: (openerType: FileOpenerType, setAsDefault: boolean, systemApp?: SystemAppInfo) => void;
onSelectSystemApp: () => Promise<SystemAppInfo | null>;
}
export const FileOpenerDialog: React.FC<FileOpenerDialogProps> = ({
open,
onClose,
fileName,
onSelect,
onSelectSystemApp,
}) => {
const { t } = useI18n();
const [isSelectingApp, setIsSelectingApp] = useState(false);
const [rememberChoice, setRememberChoice] = useState(true);
const extension = getFileExtension(fileName);
// Show edit option for files that are not known binary formats
const canEdit = !isKnownBinaryFile(fileName);
// For files without extension, we use 'file' as virtual extension
// So we always allow setting default (hasExtension is always true)
const displayExtension = extension === 'file' ? t('sftp.opener.noExtension') : `.${extension}`;
const handleSelectBuiltIn = useCallback((openerType: FileOpenerType) => {
onSelect(openerType, rememberChoice);
onClose();
}, [rememberChoice, onSelect, onClose]);
const handleSelectSystemApp = useCallback(async () => {
setIsSelectingApp(true);
try {
const result = await onSelectSystemApp();
if (result) {
onSelect('system-app', rememberChoice, result);
onClose();
}
} catch (e) {
console.error('Failed to select application:', e);
} finally {
setIsSelectingApp(false);
}
}, [onSelectSystemApp, rememberChoice, onSelect, onClose]);
return (
<Dialog open={open} onOpenChange={(isOpen) => {
// Don't close while selecting system app
if (!isOpen && !isSelectingApp) {
onClose();
}
}}>
<DialogContent className="sm:max-w-[400px]">
<DialogHeader className="min-w-0">
<DialogTitle>{t('sftp.opener.title')}</DialogTitle>
<DialogDescription className="max-w-full overflow-hidden text-ellipsis whitespace-nowrap">
{fileName}
</DialogDescription>
</DialogHeader>
<div className="py-4 space-y-2">
{canEdit && (
<Button
variant="outline"
className="w-full justify-start gap-3 h-12"
onClick={() => handleSelectBuiltIn('builtin-editor')}
>
<Edit2 size={18} className="text-primary" />
<div className="text-left">
<div className="font-medium text-sm">{t('sftp.opener.builtInEditor')}</div>
<div className="text-xs text-muted-foreground">{t('sftp.opener.editDescription')}</div>
</div>
</Button>
)}
{/* System application option */}
<Button
variant="outline"
className="w-full justify-start gap-3 h-12"
onClick={handleSelectSystemApp}
disabled={isSelectingApp}
>
<FolderOpen size={18} className="text-primary" />
<div className="text-left">
<div className="font-medium text-sm">{t('sftp.opener.systemApp')}</div>
<div className="text-xs text-muted-foreground">{t('sftp.opener.systemAppDescription')}</div>
</div>
</Button>
</div>
{/* Remember choice checkbox - always show, use 'file' for no extension */}
<div className="flex items-center gap-2 pb-2">
<input
type="checkbox"
id="remember-choice"
checked={rememberChoice}
onChange={(e) => setRememberChoice(e.target.checked)}
className="rounded border-border h-4 w-4 accent-primary"
/>
<label
htmlFor="remember-choice"
className="text-sm text-muted-foreground cursor-pointer select-none"
>
{t('sftp.opener.setDefault', { ext: displayExtension })}
</label>
</div>
<DialogFooter>
<Button variant="outline" onClick={onClose}>
{t('common.cancel')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default FileOpenerDialog;