This commit is contained in:
2025-10-26 08:42:43 +04:00
parent 5c9e07eee0
commit 98a529a3cc
10 changed files with 118 additions and 198 deletions

View File

@@ -102,6 +102,12 @@ document.addEventListener('DOMContentLoaded', async () => {
fileTree.selectNode(path);
}
});
window.eventBus.on('file-deleted', async () => {
if (fileTree) {
await fileTree.load();
}
});
});
// Listen for column resize events to refresh editor
@@ -131,117 +137,12 @@ function setupContextMenuHandlers() {
hideContextMenu();
await handleContextAction(action, targetPath, isDir);
await window.fileTreeActions.execute(action, targetPath, isDir);
});
}
async function handleContextAction(action, targetPath, isDir) {
switch (action) {
case 'open':
if (!isDir) {
await editor.loadFile(targetPath);
}
break;
case 'new-file':
if (isDir) {
const filename = prompt('Enter filename:');
if (filename) {
await fileTree.createFile(targetPath, filename);
}
}
break;
case 'new-folder':
if (isDir) {
const foldername = prompt('Enter folder name:');
if (foldername) {
await fileTree.createFolder(targetPath, foldername);
}
}
break;
case 'upload':
if (isDir) {
showFileUploadDialog(targetPath, async (path, file) => {
await fileTree.uploadFile(path, file);
});
}
break;
case 'download':
if (isDir) {
await fileTree.downloadFolder(targetPath);
} else {
await fileTree.downloadFile(targetPath);
}
break;
case 'rename':
const newName = prompt('Enter new name:', targetPath.split('/').pop());
if (newName) {
const parentPath = targetPath.split('/').slice(0, -1).join('/');
const newPath = parentPath ? `${parentPath}/${newName}` : newName;
try {
await webdavClient.move(targetPath, newPath);
await fileTree.load();
showNotification('Renamed', 'success');
} catch (error) {
console.error('Failed to rename:', error);
showNotification('Failed to rename', 'error');
}
}
break;
case 'copy':
clipboard = { path: targetPath, operation: 'copy' };
showNotification('Copied to clipboard', 'info');
updatePasteVisibility();
break;
case 'cut':
clipboard = { path: targetPath, operation: 'cut' };
showNotification('Cut to clipboard', 'info');
updatePasteVisibility();
break;
case 'paste':
if (clipboard && isDir) {
const filename = clipboard.path.split('/').pop();
const destPath = `${targetPath}/${filename}`;
try {
if (clipboard.operation === 'copy') {
await webdavClient.copy(clipboard.path, destPath);
showNotification('Copied', 'success');
} else {
await webdavClient.move(clipboard.path, destPath);
showNotification('Moved', 'success');
clipboard = null;
updatePasteVisibility();
}
await fileTree.load();
} catch (error) {
console.error('Failed to paste:', error);
showNotification('Failed to paste', 'error');
}
}
break;
case 'delete':
if (confirm(`Delete ${targetPath}?`)) {
try {
await webdavClient.delete(targetPath);
await fileTree.load();
showNotification('Deleted', 'success');
} catch (error) {
console.error('Failed to delete:', error);
showNotification('Failed to delete', 'error');
}
}
break;
}
}
// All context actions are now handled by FileTreeActions, so this function is no longer needed.
// async function handleContextAction(action, targetPath, isDir) { ... }
function updatePasteVisibility() {
const pasteItem = document.getElementById('pasteMenuItem');

68
static/js/confirmation.js Normal file
View File

@@ -0,0 +1,68 @@
/**
* Confirmation Modal Manager
* Handles showing and hiding a Bootstrap modal for confirmations and prompts.
*/
class Confirmation {
constructor(modalId) {
this.modalElement = document.getElementById(modalId);
this.modal = new bootstrap.Modal(this.modalElement);
this.messageElement = this.modalElement.querySelector('#confirmationMessage');
this.inputElement = this.modalElement.querySelector('#confirmationInput');
this.confirmButton = this.modalElement.querySelector('#confirmButton');
this.titleElement = this.modalElement.querySelector('.modal-title');
this.currentResolver = null;
}
_show(message, title, showInput = false, defaultValue = '') {
return new Promise((resolve) => {
this.currentResolver = resolve;
this.titleElement.textContent = title;
this.messageElement.textContent = message;
if (showInput) {
this.inputElement.style.display = 'block';
this.inputElement.value = defaultValue;
this.inputElement.focus();
} else {
this.inputElement.style.display = 'none';
}
this.confirmButton.onclick = () => this._handleConfirm(showInput);
this.modalElement.addEventListener('hidden.bs.modal', () => this._handleCancel(), { once: true });
this.modal.show();
});
}
_handleConfirm(isPrompt) {
if (this.currentResolver) {
const value = isPrompt ? this.inputElement.value : true;
this.currentResolver(value);
this._cleanup();
}
}
_handleCancel() {
if (this.currentResolver) {
this.currentResolver(null); // Resolve with null for cancellation
this._cleanup();
}
}
_cleanup() {
this.confirmButton.onclick = null;
this.modal.hide();
this.currentResolver = null;
}
confirm(message, title = 'Confirmation') {
return this._show(message, title, false);
}
prompt(message, defaultValue = '', title = 'Prompt') {
return this._show(message, title, true, defaultValue);
}
}
// Make it globally available
window.ConfirmationManager = new Confirmation('confirmationModal');

View File

@@ -164,32 +164,19 @@ class MarkdownEditor {
*/
async deleteFile() {
if (!this.currentFile) {
if (window.showNotification) {
window.showNotification('No file selected', 'warning');
}
window.showNotification('No file selected', 'warning');
return;
}
if (!confirm(`Delete ${this.currentFile}?`)) {
return;
}
try {
await this.webdavClient.delete(this.currentFile);
if (window.showNotification) {
const confirmed = await window.ConfirmationManager.confirm(`Are you sure you want to delete ${this.currentFile}?`, 'Delete File');
if (confirmed) {
try {
await this.webdavClient.delete(this.currentFile);
window.showNotification(`Deleted ${this.currentFile}`, 'success');
}
this.newFile();
// Trigger file tree reload
if (window.fileTree) {
await window.fileTree.load();
}
} catch (error) {
console.error('Failed to delete file:', error);
if (window.showNotification) {
this.newFile();
window.eventBus.dispatch('file-deleted');
} catch (error) {
console.error('Failed to delete file:', error);
window.showNotification('Failed to delete file', 'danger');
}
}

View File

@@ -152,6 +152,7 @@ class FileTreeActions {
const cleanup = () => {
dialog.remove();
document.querySelector('.modal-backdrop').remove();
document.body.classList.remove('modal-open');
};
@@ -221,7 +222,7 @@ class FileTreeActions {
<button type="button" class="btn-close"></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" placeholder="${placeholder}" autofocus>
<input type="text" class="form-control" value="${placeholder}" autofocus>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary">Cancel</button>