436 lines
No EOL
17 KiB
HTML
436 lines
No EOL
17 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Account Settings - Flask Files{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<div class="settings-card">
|
|
<div class="settings-header">
|
|
<h2>Account Settings</h2>
|
|
<a href="{{ url_for('auth.profile') }}" class="btn secondary">
|
|
<i class="fas fa-user"></i> Back to Profile
|
|
</a>
|
|
</div>
|
|
|
|
<div class="settings-content">
|
|
<div class="settings-section">
|
|
<h3>Appearance</h3>
|
|
<div class="theme-settings">
|
|
<div class="setting-label">Theme</div>
|
|
<div class="theme-options">
|
|
<button class="theme-btn {% if theme_preference == 'light' %}active{% endif %}"
|
|
data-theme="light">
|
|
<i class="fas fa-sun"></i> Light
|
|
</button>
|
|
<button class="theme-btn {% if theme_preference == 'dark' %}active{% endif %}"
|
|
data-theme="dark">
|
|
<i class="fas fa-moon"></i> Dark
|
|
</button>
|
|
<button class="theme-btn {% if theme_preference == 'system' %}active{% endif %}"
|
|
data-theme="system">
|
|
<i class="fas fa-desktop"></i> System
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="display-settings">
|
|
<div class="setting-group">
|
|
<div class="setting-label">File View</div>
|
|
<div class="setting-controls">
|
|
<div class="view-options">
|
|
<button class="view-btn {% if view_preference == 'grid' %}active{% endif %}"
|
|
data-view="grid">
|
|
<i class="fas fa-th"></i> Grid
|
|
</button>
|
|
<button class="view-btn {% if view_preference == 'list' %}active{% endif %}"
|
|
data-view="list">
|
|
<i class="fas fa-list"></i> List
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>Notifications</h3>
|
|
<div class="setting-group">
|
|
<div class="setting-label">Email Notifications</div>
|
|
<div class="setting-controls">
|
|
<label class="toggle">
|
|
<input type="checkbox" id="email-notifications" {% if notifications.email %}checked{% endif
|
|
%}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-description">
|
|
Receive email notifications about file shares and new comments
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-section">
|
|
<h3>Privacy</h3>
|
|
<div class="setting-group">
|
|
<div class="setting-label">Public Profile</div>
|
|
<div class="setting-controls">
|
|
<label class="toggle">
|
|
<input type="checkbox" id="public-profile" {% if privacy.public_profile %}checked{% endif
|
|
%}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-description">
|
|
Allow others to see your profile and shared files
|
|
</div>
|
|
</div>
|
|
|
|
<div class="setting-group">
|
|
<div class="setting-label">Share Statistics</div>
|
|
<div class="setting-controls">
|
|
<label class="toggle">
|
|
<input type="checkbox" id="share-statistics" {% if privacy.share_statistics %}checked{%
|
|
endif %}>
|
|
<span class="toggle-slider"></span>
|
|
</label>
|
|
</div>
|
|
<div class="setting-description">
|
|
Collect anonymous usage statistics to improve the service
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-section dangerous-zone">
|
|
<h3>Danger Zone</h3>
|
|
<p class="warning-text">
|
|
These actions are permanent and cannot be undone
|
|
</p>
|
|
|
|
<div class="danger-actions">
|
|
<button id="delete-files-btn" class="btn dangerous">
|
|
<i class="fas fa-trash-alt"></i> Delete All Files
|
|
</button>
|
|
|
|
<button id="delete-account-btn" class="btn dangerous">
|
|
<i class="fas fa-user-times"></i> Delete Account
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Files Confirmation Modal -->
|
|
<div id="delete-files-modal" class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3>Delete All Files</h3>
|
|
<button class="modal-close">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="warning-text">
|
|
Are you sure you want to delete all your files? This action cannot be undone.
|
|
</p>
|
|
<div class="form-group">
|
|
<label for="delete-files-confirm">Type "DELETE" to confirm</label>
|
|
<input type="text" id="delete-files-confirm">
|
|
</div>
|
|
<div class="form-actions">
|
|
<button class="btn secondary modal-cancel">Cancel</button>
|
|
<button id="confirm-delete-files" class="btn dangerous" disabled>Delete All Files</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Account Confirmation Modal -->
|
|
<div id="delete-account-modal" class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3>Delete Account</h3>
|
|
<button class="modal-close">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="warning-text">
|
|
Are you sure you want to delete your account? This will permanently delete all your files and personal
|
|
data.
|
|
</p>
|
|
<div class="form-group">
|
|
<label for="delete-account-confirm">Type "{{ current_user.username }}" to confirm</label>
|
|
<input type="text" id="delete-account-confirm">
|
|
</div>
|
|
<div class="form-actions">
|
|
<button class="btn secondary modal-cancel">Cancel</button>
|
|
<button id="confirm-delete-account" class="btn dangerous" disabled>Delete Account</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
// Theme settings
|
|
const themeButtons = document.querySelectorAll('.theme-btn');
|
|
|
|
themeButtons.forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
const theme = this.dataset.theme;
|
|
setTheme(theme);
|
|
|
|
// Update active button
|
|
themeButtons.forEach(btn => btn.classList.remove('active'));
|
|
this.classList.add('active');
|
|
|
|
// Save preference
|
|
fetch('{{ url_for("auth.set_theme") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: JSON.stringify({ theme: theme })
|
|
});
|
|
});
|
|
});
|
|
|
|
function setTheme(theme) {
|
|
const html = document.documentElement;
|
|
|
|
if (theme === 'light') {
|
|
html.setAttribute('data-theme', 'light');
|
|
} else if (theme === 'dark') {
|
|
html.setAttribute('data-theme', 'dark');
|
|
} else if (theme === 'system') {
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
html.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
|
|
}
|
|
}
|
|
|
|
// View settings
|
|
const viewButtons = document.querySelectorAll('.view-btn');
|
|
|
|
viewButtons.forEach(button => {
|
|
button.addEventListener('click', function () {
|
|
const view = this.dataset.view;
|
|
|
|
// Update active button
|
|
viewButtons.forEach(btn => btn.classList.remove('active'));
|
|
this.classList.add('active');
|
|
|
|
// Save preference
|
|
localStorage.setItem('files_view', view);
|
|
|
|
// Send to server for persistence
|
|
fetch('{{ url_for("auth.set_view_preference") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: JSON.stringify({ view: view })
|
|
});
|
|
});
|
|
});
|
|
|
|
// Toggle settings
|
|
const toggles = document.querySelectorAll('.toggle input[type="checkbox"]');
|
|
|
|
toggles.forEach(toggle => {
|
|
toggle.addEventListener('change', function () {
|
|
const setting = this.id;
|
|
const value = this.checked;
|
|
|
|
// Save preference
|
|
fetch('{{ url_for("auth.update_setting") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: JSON.stringify({
|
|
setting: setting,
|
|
value: value
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showAlert('Setting updated successfully', 'success');
|
|
} else {
|
|
showAlert(data.error || 'Failed to update setting', 'error');
|
|
// Revert toggle
|
|
this.checked = !value;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showAlert('An error occurred: ' + error, 'error');
|
|
// Revert toggle
|
|
this.checked = !value;
|
|
});
|
|
});
|
|
});
|
|
|
|
// Delete files functionality
|
|
const deleteFilesBtn = document.getElementById('delete-files-btn');
|
|
const deleteFilesModal = document.getElementById('delete-files-modal');
|
|
const deleteFilesConfirmInput = document.getElementById('delete-files-confirm');
|
|
const confirmDeleteFilesBtn = document.getElementById('confirm-delete-files');
|
|
|
|
deleteFilesBtn.addEventListener('click', function () {
|
|
openModal(deleteFilesModal);
|
|
deleteFilesConfirmInput.value = '';
|
|
confirmDeleteFilesBtn.disabled = true;
|
|
deleteFilesConfirmInput.focus();
|
|
});
|
|
|
|
deleteFilesConfirmInput.addEventListener('input', function () {
|
|
confirmDeleteFilesBtn.disabled = this.value !== 'DELETE';
|
|
});
|
|
|
|
confirmDeleteFilesBtn.addEventListener('click', function () {
|
|
if (deleteFilesConfirmInput.value === 'DELETE') {
|
|
fetch('{{ url_for("auth.delete_all_files") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
closeModal(deleteFilesModal);
|
|
if (data.success) {
|
|
showAlert('All files have been deleted', 'success');
|
|
setTimeout(() => {
|
|
window.location.href = '{{ url_for("files.browser") }}';
|
|
}, 1500);
|
|
} else {
|
|
showAlert(data.error || 'Failed to delete files', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showAlert('An error occurred: ' + error, 'error');
|
|
});
|
|
}
|
|
});
|
|
|
|
// Delete account functionality
|
|
const deleteAccountBtn = document.getElementById('delete-account-btn');
|
|
const deleteAccountModal = document.getElementById('delete-account-modal');
|
|
const deleteAccountConfirmInput = document.getElementById('delete-account-confirm');
|
|
const confirmDeleteAccountBtn = document.getElementById('confirm-delete-account');
|
|
const usernameToConfirm = "{{ current_user.username }}";
|
|
|
|
deleteAccountBtn.addEventListener('click', function () {
|
|
openModal(deleteAccountModal);
|
|
deleteAccountConfirmInput.value = '';
|
|
confirmDeleteAccountBtn.disabled = true;
|
|
deleteAccountConfirmInput.focus();
|
|
});
|
|
|
|
deleteAccountConfirmInput.addEventListener('input', function () {
|
|
confirmDeleteAccountBtn.disabled = this.value !== usernameToConfirm;
|
|
});
|
|
|
|
confirmDeleteAccountBtn.addEventListener('click', function () {
|
|
if (deleteAccountConfirmInput.value === usernameToConfirm) {
|
|
fetch('{{ url_for("auth.delete_account") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
closeModal(deleteAccountModal);
|
|
if (data.success) {
|
|
showAlert('Your account has been deleted', 'success');
|
|
setTimeout(() => {
|
|
window.location.href = '{{ url_for("auth.logout") }}';
|
|
}, 1500);
|
|
} else {
|
|
showAlert(data.error || 'Failed to delete account', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showAlert('An error occurred: ' + error, 'error');
|
|
});
|
|
}
|
|
});
|
|
|
|
// Modal utilities
|
|
function openModal(modal) {
|
|
modal.classList.add('visible');
|
|
document.body.classList.add('modal-open');
|
|
}
|
|
|
|
function closeModal(modal) {
|
|
modal.classList.remove('visible');
|
|
document.body.classList.remove('modal-open');
|
|
}
|
|
|
|
// Close modals when clicking outside or on close button
|
|
document.addEventListener('click', function (e) {
|
|
if (e.target.classList.contains('modal')) {
|
|
closeModal(e.target);
|
|
} else if (e.target.classList.contains('modal-close') || e.target.classList.contains('modal-cancel')) {
|
|
const modal = e.target.closest('.modal');
|
|
closeModal(modal);
|
|
}
|
|
});
|
|
|
|
// Escape key to close modals
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Escape') {
|
|
document.querySelectorAll('.modal.visible').forEach(modal => {
|
|
closeModal(modal);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Helper function to show alerts
|
|
function showAlert(message, type = 'info') {
|
|
// Create alerts container if it doesn't exist
|
|
let alertsContainer = document.querySelector('.alerts');
|
|
if (!alertsContainer) {
|
|
alertsContainer = document.createElement('div');
|
|
alertsContainer.className = 'alerts';
|
|
document.body.appendChild(alertsContainer);
|
|
}
|
|
|
|
// Create alert
|
|
const alert = document.createElement('div');
|
|
alert.className = `alert ${type}`;
|
|
alert.innerHTML = `
|
|
<div class="alert-content">${message}</div>
|
|
<button class="close" aria-label="Close">×</button>
|
|
`;
|
|
|
|
// Add to container
|
|
alertsContainer.appendChild(alert);
|
|
|
|
// Setup dismiss
|
|
const closeBtn = alert.querySelector('.close');
|
|
closeBtn.addEventListener('click', function () {
|
|
alert.classList.add('fade-out');
|
|
setTimeout(() => {
|
|
alert.remove();
|
|
}, 300);
|
|
});
|
|
|
|
// Auto dismiss
|
|
setTimeout(() => {
|
|
if (alert.parentNode) {
|
|
alert.classList.add('fade-out');
|
|
setTimeout(() => {
|
|
if (alert.parentNode) {
|
|
alert.remove();
|
|
}
|
|
}, 300);
|
|
}
|
|
}, 5000);
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |