...
This commit is contained in:
117
static/js/app.js
117
static/js/app.js
@@ -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
68
static/js/confirmation.js
Normal 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');
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user