Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
264
native/desktop/maplefile/frontend/src/components/Navigation.jsx
Normal file
264
native/desktop/maplefile/frontend/src/components/Navigation.jsx
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import './Navigation.css';
|
||||
import { LogoutWithOptions, GetLocalDataSize } from '../../wailsjs/go/app/Application';
|
||||
|
||||
function Navigation() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [showLogoutConfirm, setShowLogoutConfirm] = useState(false);
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
const [deleteLocalData, setDeleteLocalData] = useState(true); // Default to delete for security
|
||||
const [localDataSize, setLocalDataSize] = useState(0);
|
||||
|
||||
const isActive = (path) => {
|
||||
return location.pathname === path || location.pathname.startsWith(path + '/');
|
||||
};
|
||||
|
||||
// Format bytes to human-readable size
|
||||
const formatBytes = (bytes) => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
const handleLogoutClick = (e) => {
|
||||
e.preventDefault();
|
||||
// Get local data size when opening the modal
|
||||
GetLocalDataSize()
|
||||
.then((size) => {
|
||||
setLocalDataSize(size);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to get local data size:', error);
|
||||
setLocalDataSize(0);
|
||||
});
|
||||
setShowLogoutConfirm(true);
|
||||
};
|
||||
|
||||
const handleLogoutConfirm = () => {
|
||||
setIsLoggingOut(true);
|
||||
LogoutWithOptions(deleteLocalData)
|
||||
.then(() => {
|
||||
// Reset state before navigating
|
||||
setDeleteLocalData(true);
|
||||
navigate('/login');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Logout failed:', error);
|
||||
alert('Logout failed: ' + error.message);
|
||||
setIsLoggingOut(false);
|
||||
setShowLogoutConfirm(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleLogoutCancel = useCallback(() => {
|
||||
if (!isLoggingOut) {
|
||||
setShowLogoutConfirm(false);
|
||||
setDeleteLocalData(true); // Reset to default
|
||||
}
|
||||
}, [isLoggingOut]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Escape' && showLogoutConfirm && !isLoggingOut) {
|
||||
handleLogoutCancel();
|
||||
}
|
||||
};
|
||||
|
||||
if (showLogoutConfirm) {
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [showLogoutConfirm, isLoggingOut, handleLogoutCancel]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="navigation">
|
||||
<div className="nav-header">
|
||||
<h2>MapleFile</h2>
|
||||
</div>
|
||||
<ul className="nav-menu">
|
||||
<li className={isActive('/dashboard') ? 'active' : ''}>
|
||||
<Link to="/dashboard">Dashboard</Link>
|
||||
</li>
|
||||
<li className={isActive('/file-manager') ? 'active' : ''}>
|
||||
<Link to="/file-manager">File Manager</Link>
|
||||
</li>
|
||||
<li className={isActive('/search') ? 'active' : ''}>
|
||||
<Link to="/search">Search</Link>
|
||||
</li>
|
||||
<li className={isActive('/tags/search') ? 'active' : ''}>
|
||||
<Link to="/tags/search">Search by Tags</Link>
|
||||
</li>
|
||||
<li className={isActive('/me') ? 'active' : ''}>
|
||||
<Link to="/me">Profile</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" onClick={handleLogoutClick}>Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* Logout Confirmation Modal */}
|
||||
{showLogoutConfirm && (
|
||||
<div
|
||||
onClick={handleLogoutCancel}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '8px',
|
||||
padding: '30px',
|
||||
maxWidth: '500px',
|
||||
width: '90%',
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.3)',
|
||||
}}>
|
||||
<h3 style={{ margin: '0 0 15px 0', color: '#2c3e50', textAlign: 'center' }}>
|
||||
Sign Out
|
||||
</h3>
|
||||
<p style={{ margin: '0 0 20px 0', color: '#666', textAlign: 'center' }}>
|
||||
You are about to sign out. You'll need to log in again next time.
|
||||
</p>
|
||||
|
||||
{/* Security Warning */}
|
||||
<div style={{
|
||||
backgroundColor: '#fef3c7',
|
||||
border: '1px solid #f59e0b',
|
||||
borderRadius: '8px',
|
||||
padding: '16px',
|
||||
marginBottom: '20px',
|
||||
}}>
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '12px' }}>
|
||||
<svg style={{ width: '20px', height: '20px', color: '#d97706', flexShrink: 0, marginTop: '2px' }} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<div>
|
||||
<p style={{ fontWeight: '600', color: '#d97706', margin: '0 0 4px 0', fontSize: '14px' }}>
|
||||
Security Notice
|
||||
</p>
|
||||
<p style={{ color: '#666', margin: 0, fontSize: '13px', lineHeight: '1.5' }}>
|
||||
For your security, we recommend deleting locally saved data when signing out. This includes your cached files and metadata{localDataSize > 0 ? ` (${formatBytes(localDataSize)})` : ''}. If you keep local data, anyone with access to this device may be able to view your files when you log in again.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data deletion options */}
|
||||
<div style={{ marginBottom: '25px' }}>
|
||||
<label style={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '12px',
|
||||
cursor: 'pointer',
|
||||
marginBottom: '12px',
|
||||
padding: '10px',
|
||||
borderRadius: '6px',
|
||||
backgroundColor: deleteLocalData ? '#fef2f2' : 'transparent',
|
||||
border: deleteLocalData ? '1px solid #fca5a5' : '1px solid transparent',
|
||||
}}>
|
||||
<input
|
||||
type="radio"
|
||||
name="deleteLocalData"
|
||||
checked={deleteLocalData === true}
|
||||
onChange={() => setDeleteLocalData(true)}
|
||||
style={{ marginTop: '4px' }}
|
||||
/>
|
||||
<div>
|
||||
<span style={{ fontWeight: '500', color: '#2c3e50', fontSize: '14px' }}>
|
||||
Delete all local data (Recommended)
|
||||
</span>
|
||||
<p style={{ color: '#666', margin: '4px 0 0 0', fontSize: '12px' }}>
|
||||
All cached files and metadata will be removed. You'll need to re-download files from the cloud next time.
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label style={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '12px',
|
||||
cursor: 'pointer',
|
||||
padding: '10px',
|
||||
borderRadius: '6px',
|
||||
backgroundColor: !deleteLocalData ? '#eff6ff' : 'transparent',
|
||||
border: !deleteLocalData ? '1px solid #93c5fd' : '1px solid transparent',
|
||||
}}>
|
||||
<input
|
||||
type="radio"
|
||||
name="deleteLocalData"
|
||||
checked={deleteLocalData === false}
|
||||
onChange={() => setDeleteLocalData(false)}
|
||||
style={{ marginTop: '4px' }}
|
||||
/>
|
||||
<div>
|
||||
<span style={{ fontWeight: '500', color: '#2c3e50', fontSize: '14px' }}>
|
||||
Keep local data for faster login
|
||||
</span>
|
||||
<p style={{ color: '#666', margin: '4px 0 0 0', fontSize: '12px' }}>
|
||||
Your cached files will be preserved. Only use this on trusted personal devices.
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', gap: '10px', justifyContent: 'flex-end' }}>
|
||||
<button
|
||||
onClick={handleLogoutCancel}
|
||||
disabled={isLoggingOut}
|
||||
style={{
|
||||
padding: '10px 25px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: '#f5f5f5',
|
||||
color: '#333',
|
||||
cursor: isLoggingOut ? 'not-allowed' : 'pointer',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLogoutConfirm}
|
||||
disabled={isLoggingOut}
|
||||
style={{
|
||||
padding: '10px 25px',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: '#3b82f6',
|
||||
color: 'white',
|
||||
cursor: isLoggingOut ? 'not-allowed' : 'pointer',
|
||||
fontSize: '14px',
|
||||
opacity: isLoggingOut ? 0.7 : 1,
|
||||
}}
|
||||
>
|
||||
{isLoggingOut ? 'Signing out...' : 'Sign Out'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
Loading…
Add table
Add a link
Reference in a new issue