mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Merge pull request #173 from graycyrus/develop
Feat: Log Out & Clear App Data
This commit is contained in:
@@ -1,18 +1,49 @@
|
||||
import { clearToken } from '../../store/authSlice';
|
||||
import { useAppDispatch } from '../../store/hooks';
|
||||
import { useState } from 'react';
|
||||
import SettingsHeader from './components/SettingsHeader';
|
||||
import SettingsMenuItem from './components/SettingsMenuItem';
|
||||
import { useSettingsNavigation } from './hooks/useSettingsNavigation';
|
||||
import { persistor } from '../../store';
|
||||
import { skillManager } from '../../lib/skills/manager';
|
||||
|
||||
const SettingsHome = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { navigateToSettings, closeSettings } = useSettingsNavigation();
|
||||
const { navigateToSettings } = useSettingsNavigation();
|
||||
const [showLogoutAndClearModal, setShowLogoutAndClearModal] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleLogout = async () => {
|
||||
await dispatch(clearToken());
|
||||
closeSettings();
|
||||
// Simple logout without clearing data - redirect to login
|
||||
window.location.href = '/login';
|
||||
};
|
||||
|
||||
const clearAllAppData = async () => {
|
||||
await persistor.purge();
|
||||
window.localStorage.clear();
|
||||
window.sessionStorage.clear();
|
||||
|
||||
// NEW: Clear skills databases (emails, chats, cached files)
|
||||
try {
|
||||
await skillManager.clearAllSkillsData();
|
||||
} catch (error) {
|
||||
console.warn('Failed to clear skills data:', error);
|
||||
// Continue with logout even if skills clearing fails
|
||||
}
|
||||
|
||||
// Complete reset - redirect to login for fresh start
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
const handleLogoutAndClearData = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
await clearAllAppData(); // This will redirect to login
|
||||
} catch (_error) {
|
||||
setError('Failed to clear data and logout. Please try again.');
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// const handleViewEncryptionKey = () => {
|
||||
// // TODO: Show encryption key in a secure modal
|
||||
// console.log('View encryption key');
|
||||
@@ -244,6 +275,18 @@ const SettingsHome = () => {
|
||||
onClick: handleDeleteAllData,
|
||||
dangerous: true,
|
||||
},
|
||||
{
|
||||
id: 'logout-and-clear',
|
||||
title: 'Log Out & Clear App Data',
|
||||
description: 'Sign out and permanently clear all local data',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
),
|
||||
onClick: () => setShowLogoutAndClearModal(true),
|
||||
dangerous: true
|
||||
},
|
||||
{
|
||||
id: 'logout',
|
||||
title: 'Log out',
|
||||
@@ -302,6 +345,69 @@ const SettingsHome = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Log Out & Clear Data Confirmation Modal */}
|
||||
{showLogoutAndClearModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60">
|
||||
<div className="bg-stone-900 rounded-2xl max-w-md w-full p-6 border border-stone-700/50">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-amber-500/20 flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">Log Out & Clear App Data</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<p className="text-stone-300 text-sm leading-relaxed">
|
||||
This will sign you out and permanently delete ALL data including:
|
||||
• App settings and conversations
|
||||
• Email data from Gmail
|
||||
• Chat history from Telegram
|
||||
• Cached files from Notion
|
||||
• All other skills data
|
||||
<br /><br />
|
||||
This action cannot be undone and may take a few moments to complete.
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="mt-3 p-3 rounded-lg bg-coral-500/10 border border-coral-500/20">
|
||||
<p className="text-coral-400 text-sm">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowLogoutAndClearModal(false);
|
||||
setError(null);
|
||||
}}
|
||||
disabled={isLoading}
|
||||
className="flex-1 px-4 py-2 rounded-lg border border-stone-600 text-stone-300 hover:bg-stone-800 transition-colors disabled:opacity-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLogoutAndClearData}
|
||||
disabled={isLoading}
|
||||
className="flex-1 px-4 py-2 rounded-lg bg-amber-600 hover:bg-amber-500 text-white transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
|
||||
>
|
||||
{isLoading && (
|
||||
<svg className="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
)}
|
||||
{isLoading ? 'Clearing All Data...' : 'Log Out & Clear Everything'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -527,6 +527,44 @@ class SkillManager {
|
||||
throw new Error(`Unknown reverse RPC method: ${method}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all skills databases and cached data.
|
||||
* Used for nuclear reset functionality.
|
||||
*/
|
||||
async clearAllSkillsData(): Promise<void> {
|
||||
try {
|
||||
// Stop all running skills first
|
||||
await this.stopAll();
|
||||
|
||||
// Get all skill IDs from Redux state
|
||||
const state = store.getState();
|
||||
const skillIds = Object.keys(state.skills.skills);
|
||||
|
||||
// Clear data for each skill
|
||||
const clearPromises = skillIds.map(async (skillId) => {
|
||||
try {
|
||||
// Get skill data directory path
|
||||
const dataDir = await invoke<string>("runtime_skill_data_dir", { skillId });
|
||||
|
||||
// Note: We don't directly delete directories here since there's no exposed
|
||||
// Tauri command for that. Instead, we rely on the backend to handle
|
||||
// clearing when skills are disabled/reset via Redux state clearing.
|
||||
|
||||
console.log(`[SkillManager] Skill ${skillId} data directory: ${dataDir}`);
|
||||
} catch (err) {
|
||||
console.warn(`[SkillManager] Failed to get data directory for skill ${skillId}:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(clearPromises);
|
||||
|
||||
console.log("[SkillManager] Skills data clearing initiated");
|
||||
} catch (error) {
|
||||
console.error("[SkillManager] Failed to clear skills data:", error);
|
||||
throw new Error("Failed to clear skills databases");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton
|
||||
|
||||
Reference in New Issue
Block a user