feat: Implement collection deletion and loading spinners

- Add API endpoint and handler to delete collections
- Introduce LoadingSpinner component for async operations
- Show loading spinners during file loading and preview rendering
- Enhance modal accessibility by removing aria-hidden attribute
- Refactor delete functionality to distinguish between collections and files/folders
- Remove unused collection definitions from config
This commit is contained in:
Mahmoud-Emad
2025-10-27 11:32:20 +03:00
parent afcd074913
commit 3961628b3d
15 changed files with 557 additions and 32 deletions

View File

@@ -104,6 +104,10 @@ class MarkdownEditorApp:
if path == '/fs/' and method == 'POST':
return self.handle_create_collection(environ, start_response)
# API to delete a collection
if path.startswith('/api/collections/') and method == 'DELETE':
return self.handle_delete_collection(environ, start_response)
# Check if path starts with a collection name (for SPA routing)
# This handles URLs like /notes/ttt or /documents/file.md
# MUST be checked BEFORE WebDAV routing to prevent WebDAV from intercepting SPA routes
@@ -205,7 +209,68 @@ class MarkdownEditorApp:
print(f"Error creating collection: {e}")
start_response('500 Internal Server Error', [('Content-Type', 'application/json')])
return [json.dumps({'error': str(e)}).encode('utf-8')]
def handle_delete_collection(self, environ, start_response):
"""Delete a collection"""
try:
# Extract collection name from path: /api/collections/{name}
path = environ.get('PATH_INFO', '')
collection_name = path.split('/')[-1]
if not collection_name:
start_response('400 Bad Request', [('Content-Type', 'application/json')])
return [json.dumps({'error': 'Collection name is required'}).encode('utf-8')]
# Check if collection exists
if collection_name not in self.collections:
start_response('404 Not Found', [('Content-Type', 'application/json')])
return [json.dumps({'error': f'Collection "{collection_name}" not found'}).encode('utf-8')]
# Get collection path
collection_config = self.collections[collection_name]
collection_path = Path(collection_config['path'])
# Delete the collection directory and all its contents
import shutil
if collection_path.exists():
shutil.rmtree(collection_path)
print(f"Deleted collection directory: {collection_path}")
# Remove from collections dict
del self.collections[collection_name]
# Update config file
self.save_config()
# Remove from WebDAV provider mapping
provider_key = f'/fs/{collection_name}'
if hasattr(self.webdav_app, 'provider_map') and provider_key in self.webdav_app.provider_map:
del self.webdav_app.provider_map[provider_key]
print(f"Removed provider from provider_map: {provider_key}")
# Remove from sorted_share_list if it exists
if hasattr(self.webdav_app, 'sorted_share_list') and provider_key in self.webdav_app.sorted_share_list:
self.webdav_app.sorted_share_list.remove(provider_key)
print(f"Removed from sorted_share_list: {provider_key}")
print(f"Deleted collection '{collection_name}'")
response_body = json.dumps({'success': True, 'name': collection_name}).encode('utf-8')
start_response('200 OK', [
('Content-Type', 'application/json'),
('Content-Length', str(len(response_body))),
('Access-Control-Allow-Origin', '*')
])
return [response_body]
except Exception as e:
print(f"Error deleting collection: {e}")
import traceback
traceback.print_exc()
start_response('500 Internal Server Error', [('Content-Type', 'application/json')])
return [json.dumps({'error': str(e)}).encode('utf-8')]
def handle_static(self, environ, start_response):
"""Serve static files"""
path = environ.get('PATH_INFO', '')[1:] # Remove leading /