chore: removed dropbox sync

This commit is contained in:
2026-02-14 10:12:37 +01:00
parent 6ff84c2d6a
commit d2d04b9b20
14 changed files with 61 additions and 2936 deletions

View File

@@ -70,24 +70,7 @@ const App: React.FC = () => {
}
}
// Re-configure Dropbox sync from saved credentials
const savedCreds = localStorage.getItem('bds-credentials');
if (savedCreds) {
try {
const creds = JSON.parse(savedCreds);
if (creds.dropboxAccessToken && creds.dropboxAppKey) {
await window.electronAPI?.dropbox?.configure({
accessToken: creds.dropboxAccessToken,
appKey: creds.dropboxAppKey,
remotePath: creds.dropboxRemotePath || '/blog',
});
}
} catch (e) {
console.error('Failed to restore sync configuration:', e);
}
}
// Check sync status (uses Dropbox configuration)
// Check sync status
const syncConfigured = await window.electronAPI?.sync.isConfigured();
setSyncConfigured(syncConfigured || false);

View File

@@ -4,7 +4,7 @@ import { showToast } from '../Toast';
import './SettingsView.css';
// Export category IDs for sidebar navigation
export type SettingsCategory = 'project' | 'editor' | 'content' | 'ai' | 'sync' | 'publishing' | 'data';
export type SettingsCategory = 'project' | 'editor' | 'content' | 'ai' | 'publishing' | 'data';
// Scroll to a settings section by category ID
export const scrollToSettingsSection = (category: SettingsCategory) => {
@@ -17,10 +17,6 @@ export const scrollToSettingsSection = (category: SettingsCategory) => {
// Settings categories
interface Credentials {
// Dropbox File Sync
dropboxAccessToken: string;
dropboxAppKey: string;
dropboxRemotePath: string;
// FTP Publishing
ftpHost: string;
ftpUser: string;
@@ -32,9 +28,6 @@ interface Credentials {
}
const defaultCredentials: Credentials = {
dropboxAccessToken: '',
dropboxAppKey: '',
dropboxRemotePath: '/blog',
ftpHost: '',
ftpUser: '',
ftpPassword: '',
@@ -101,8 +94,6 @@ export const SettingsView: React.FC = () => {
const [searchQuery, setSearchQuery] = useState('');
const [credentials, setCredentials] = useState<Credentials>(defaultCredentials);
const [showSecrets, setShowSecrets] = useState(false);
const [dropboxConfigured, setDropboxConfigured] = useState(false);
const [dropboxLastSync, setDropboxLastSync] = useState<string | null>(null);
const contentRef = useRef<HTMLDivElement>(null);
// Project settings
@@ -193,15 +184,6 @@ export const SettingsView: React.FC = () => {
} catch (error) {
console.error('Failed to load AI settings:', error);
}
// Check Dropbox status
const dbxConfigured = await window.electronAPI?.dropbox?.isConfigured();
setDropboxConfigured(dbxConfigured || false);
if (dbxConfigured) {
const lastSync = await window.electronAPI?.dropbox?.getLastSyncTime();
setDropboxLastSync(lastSync || null);
}
} catch (error) {
console.error('Failed to load settings:', error);
}
@@ -209,27 +191,6 @@ export const SettingsView: React.FC = () => {
loadSettings();
}, [activeProject?.id]); // Reload when project changes
const handleSaveDropbox = async () => {
try {
localStorage.setItem('bds-credentials', JSON.stringify(credentials));
if (credentials.dropboxAccessToken && credentials.dropboxAppKey) {
await window.electronAPI?.dropbox?.configure({
accessToken: credentials.dropboxAccessToken,
appKey: credentials.dropboxAppKey,
remotePath: credentials.dropboxRemotePath || '/blog',
});
setDropboxConfigured(true);
showToast.success('Dropbox sync configured');
} else {
showToast.success('Credentials saved');
}
} catch (error) {
console.error('Failed to save Dropbox credentials:', error);
showToast.error('Failed to configure Dropbox sync');
}
};
const handleSavePublishing = async () => {
try {
localStorage.setItem('bds-credentials', JSON.stringify(credentials));
@@ -240,14 +201,9 @@ export const SettingsView: React.FC = () => {
}
};
const handleClearCredentials = (type: 'dropbox' | 'ftp' | 'ssh') => {
const handleClearCredentials = (type: 'ftp' | 'ssh') => {
const newCreds = { ...credentials };
switch (type) {
case 'dropbox':
newCreds.dropboxAccessToken = '';
newCreds.dropboxAppKey = '';
newCreds.dropboxRemotePath = '/blog';
break;
case 'ftp':
newCreds.ftpHost = '';
newCreds.ftpUser = '';
@@ -264,36 +220,6 @@ export const SettingsView: React.FC = () => {
showToast.success(`${type.charAt(0).toUpperCase() + type.slice(1)} credentials cleared`);
};
const handleDropboxSync = async () => {
try {
showToast.loading('Starting Dropbox sync...');
await window.electronAPI?.dropbox?.syncAll();
showToast.dismiss();
showToast.success('Dropbox sync completed');
const lastSync = await window.electronAPI?.dropbox?.getLastSyncTime();
setDropboxLastSync(lastSync || null);
} catch (error) {
showToast.dismiss();
showToast.error('Dropbox sync failed');
}
};
const handleTestDropboxConnection = async () => {
showToast.loading('Testing Dropbox connection...');
try {
const status = await window.electronAPI?.dropbox?.getStatus();
showToast.dismiss();
if (status) {
showToast.success('Dropbox connection active');
} else {
showToast.error('Dropbox connection failed');
}
} catch {
showToast.dismiss();
showToast.error('Dropbox connection failed');
}
};
// Save project settings
const handleSaveProject = async () => {
if (!activeProject) return;
@@ -338,7 +264,6 @@ export const SettingsView: React.FC = () => {
const editorKeywords = ['editor', 'mode', 'wysiwyg', 'markdown', 'preview', 'visual'];
const contentKeywords = ['content', 'categories', 'post', 'article', 'picture', 'aside', 'page'];
const aiKeywords = ['ai', 'assistant', 'chat', 'model', 'prompt', 'system', 'api', 'key', 'claude', 'gpt', 'opencode'];
const syncKeywords = ['sync', 'dropbox', 'file', 'backup', 'token', 'remote'];
const publishingKeywords = ['publishing', 'ftp', 'ssh', 'deploy', 'server', 'host', 'upload'];
const dataKeywords = ['data', 'database', 'rebuild', 'maintenance', 'posts', 'media', 'links', 'folder', 'filesystem'];
@@ -746,97 +671,6 @@ export const SettingsView: React.FC = () => {
</SettingSection>
);
const renderSyncSettings = () => (
<>
<SettingSection
id="settings-section-sync"
title="File Sync — Dropbox"
description="Synchronize your blog files (posts and media) to Dropbox for backup and cross-device access."
hidden={!sectionHasMatches(syncKeywords)}
>
<SettingRow
id="dropbox-token"
label="Access Token"
description="Your Dropbox API access token. Generate one from the Dropbox App Console."
>
<div className="setting-input-group">
<input
id="dropbox-token"
type={showSecrets ? 'text' : 'password'}
placeholder="Your Dropbox access token"
value={credentials.dropboxAccessToken}
onChange={(e) => setCredentials({ ...credentials, dropboxAccessToken: e.target.value })}
/>
<button
className="setting-toggle-visibility"
onClick={() => setShowSecrets(!showSecrets)}
title={showSecrets ? 'Hide secrets' : 'Show secrets'}
>
{showSecrets ? '🔒' : '👁'}
</button>
</div>
</SettingRow>
<SettingRow
id="dropbox-appkey"
label="App Key"
description="The App Key from your Dropbox developer application."
>
<input
id="dropbox-appkey"
type="text"
placeholder="Your Dropbox App Key"
value={credentials.dropboxAppKey}
onChange={(e) => setCredentials({ ...credentials, dropboxAppKey: e.target.value })}
/>
</SettingRow>
<SettingRow
id="dropbox-path"
label="Remote Path"
description="The folder path in Dropbox where blog files will be synced. Default: /blog"
>
<input
id="dropbox-path"
type="text"
placeholder="/blog"
value={credentials.dropboxRemotePath}
onChange={(e) => setCredentials({ ...credentials, dropboxRemotePath: e.target.value })}
/>
</SettingRow>
<div className="setting-actions">
<button className="primary" onClick={handleSaveDropbox}>
{dropboxConfigured ? 'Update Configuration' : 'Enable Dropbox Sync'}
</button>
<button className="secondary" onClick={handleTestDropboxConnection}>
Test Connection
</button>
{dropboxConfigured && (
<button className="secondary" onClick={handleDropboxSync}>
Sync Now
</button>
)}
<button className="secondary danger" onClick={() => handleClearCredentials('dropbox')}>
Clear
</button>
</div>
{dropboxConfigured && (
<div className="setting-status success">
<span className="status-icon"></span>
<span>
Dropbox sync is configured
{dropboxLastSync && (
<span className="status-detail"> · Last sync: {new Date(dropboxLastSync).toLocaleString()}</span>
)}
</span>
</div>
)}
</SettingSection>
</>
);
const renderPublishingSettings = () => (
<>
<SettingSection
@@ -878,13 +712,22 @@ export const SettingsView: React.FC = () => {
label="Password"
description="Your FTP account password."
>
<input
id="ftp-password"
type={showSecrets ? 'text' : 'password'}
placeholder="Password"
value={credentials.ftpPassword}
onChange={(e) => setCredentials({ ...credentials, ftpPassword: e.target.value })}
/>
<div className="setting-input-group">
<input
id="ftp-password"
type={showSecrets ? 'text' : 'password'}
placeholder="Password"
value={credentials.ftpPassword}
onChange={(e) => setCredentials({ ...credentials, ftpPassword: e.target.value })}
/>
<button
className="setting-toggle-visibility"
onClick={() => setShowSecrets(!showSecrets)}
title={showSecrets ? 'Hide password' : 'Show password'}
>
{showSecrets ? '🔒' : '👁'}
</button>
</div>
</SettingRow>
<div className="setting-actions">
@@ -1095,7 +938,6 @@ export const SettingsView: React.FC = () => {
sectionHasMatches(editorKeywords) ||
sectionHasMatches(contentKeywords) ||
sectionHasMatches(aiKeywords) ||
sectionHasMatches(syncKeywords) ||
sectionHasMatches(publishingKeywords) ||
sectionHasMatches(dataKeywords);
@@ -1131,7 +973,6 @@ export const SettingsView: React.FC = () => {
{renderEditorSettings()}
{renderContentSettings()}
{renderAISettings()}
{renderSyncSettings()}
{renderPublishingSettings()}
{renderDataSettings()}
</>

View File

@@ -1031,7 +1031,7 @@ const TagsNav: React.FC = () => {
};
const SettingsNav: React.FC = () => {
const { syncConfigured, tabs, activeTabId, openTab } = useAppStore();
const { tabs, activeTabId, openTab } = useAppStore();
const [activeSection, setActiveSection] = useState<SettingsCategory | null>(null);
// Check if settings panel is currently active
@@ -1086,14 +1086,6 @@ const SettingsNav: React.FC = () => {
<span className="settings-nav-entry-icon">🤖</span>
<span>AI Assistant</span>
</button>
<button
className={`settings-nav-entry ${activeSection === 'sync' ? 'active' : ''}`}
onClick={() => handleNavClick('sync')}
>
<span className="settings-nav-entry-icon">🔄</span>
<span>Sync</span>
{syncConfigured && <span className="settings-nav-badge"></span>}
</button>
<button
className={`settings-nav-entry ${activeSection === 'publishing' ? 'active' : ''}`}
onClick={() => handleNavClick('publishing')}

View File

@@ -114,27 +114,6 @@ export interface SyncResult {
errors: string[];
}
export interface DropboxConfig {
accessToken: string;
appKey: string;
remotePath?: string;
}
export interface DropboxSyncResult {
uploaded: number;
downloaded: number;
conflicts: number;
errors?: string[];
}
export interface DropboxConflict {
id: string;
localPath: string;
remotePath: string;
localModified: string;
remoteModified: string;
}
export interface PaginatedPostsResult {
items: PostData[];
hasMore: boolean;
@@ -348,19 +327,6 @@ export interface ElectronAPI {
cancel: (taskId: string) => Promise<boolean>;
clearCompleted: () => Promise<void>;
};
dropbox: {
configure: (config: DropboxConfig) => Promise<void>;
isConfigured: () => Promise<boolean>;
getStatus: () => Promise<string>;
syncAll: () => Promise<DropboxSyncResult>;
startWatching: () => Promise<void>;
stopWatching: () => Promise<void>;
startPolling: () => Promise<void>;
stopPolling: () => Promise<void>;
getConflicts: () => Promise<DropboxConflict[]>;
resolveConflict: (conflictId: string, resolution: 'local-wins' | 'remote-wins') => Promise<void>;
getLastSyncTime: () => Promise<string | null>;
};
app: {
getDataPaths: () => Promise<{ database: string; posts: string; media: string }>;
openFolder: (folderPath: string) => Promise<string>;