6.4 KiB
Folder Rendering Event Architecture
Overview
This document describes the new event-driven architecture for folder list rendering in the Email Organizer application. This architecture replaces the previous inconsistent approach of direct HTML targeting with a clean, centralized event system.
Problem Statement
The previous approach had several issues:
- Inconsistent HTML swapping: Some places used
innerHTML, others usedouterHTML - Copy-pasted rendering logic: Multiple routes duplicated the same rendering patterns
- Error-prone maintenance: Easy to introduce inconsistencies when adding new features
- Complex state management: Each route handled its own re-rendering logic
Solution: Event-Driven Architecture
Core Concept
The new architecture uses a single event folder-list-invalidated to trigger UI updates for all folder modifications. This event is triggered through HTMX response headers and listened for by the main folder list container.
Architecture Flow
graph TD
subgraph "Folder Operations"
A[Add Folder] --> A1[HX-Trigger: folder-list-invalidated]
B[Delete Folder] --> B1[HX-Trigger: folder-list-invalidated]
C[Update Folder] --> C1[HX-Trigger: folder-list-invalidated]
D[Update Folder Type] --> D1[HX-Trigger: folder-list-invalidated]
E[Sync Folders] --> E1[HX-Trigger: folder-list-invalidated]
F[Filter Change] --> F1[Event Listener]
G[Show/Hidden Toggle] --> G1[Event Listener]
end
subgraph "UI Event Listener"
H[#folders-list] -->|hx-trigger| H1[folder-list-invalidated]
end
subgraph "Rendering"
I[Event Triggered] --> J[Fetch from /api/folders]
J --> K[Replace entire list]
end
Implementation Details
1. Event Listener
The main folder list container now listens for the folder-list-invalidated event:
<div id="folders-list"
hx-trigger="folder-list-invalidated"
hx-get="/api/folders"
hx-swap="outerHTML settle:300ms">
<!-- Current folder list content -->
</div>
2. Event Triggering
All folder modification operations now trigger the same event:
response = make_response('')
response.headers['HX-Trigger'] = 'close-modal, folder-list-invalidated'
3. Centralized Rendering
The /api/folders endpoint now handles both:
- Initial GET requests (with query parameters for filtering)
- Event-triggered requests (return full list with current state)
@app.route('/api/folders', methods=['GET'])
@login_required
def get_folders():
# Check if this is an event-triggered request
is_event_triggered = request.headers.get('HX-Trigger') == 'folder-list-invalidated'
if is_event_triggered:
# Return full list with current filter state
folders = Folder.query.filter_by(user_id=current_user.id).all()
return render_template('partials/folders_list.html', folders=folders, show_hidden=show_hidden)
# Handle initial load with query parameters
# ... existing filtering logic ...
Changes Made
1. Template Updates
- Removed
hx-targetattributes from all folder-related buttons - Added event listener to
#folders-listcontainer - Updated swap methods to use consistent
outerHTML settle:300ms
2. Route Updates
- Modified all folder operations to trigger
folder-list-invalidatedevent - Simplified response handling to return empty responses with event headers
- Updated
/api/foldersendpoint to handle event-triggered requests
3. Files Modified
app/templates/index.html- Added event listenerapp/templates/partials/folder_card_base.html- Removed hx-targetapp/templates/partials/folder_card.html- Removed hx-targetapp/templates/partials/folder_card_tidy.html- No changes neededapp/templates/partials/folder_modal.html- Removed hx-targetapp/templates/partials/folder_type_selection_modal.html- Removed hx-targetapp/routes/folders.py- Updated all routes to trigger eventsapp/routes/imap.py- Updated sync operation to trigger events
Benefits
1. Consistent Behavior
- All folder modifications follow the same pattern
- No more inconsistent HTML swapping
- Predictable UI updates
2. Reduced Complexity
- Single event to manage
- Centralized rendering logic
- No complex state management
3. Easier Maintenance
- No copy-pasted rendering code
- Clear separation of concerns
- Simple to add new features
4. Better Performance
- Only updates what's necessary
- Efficient event handling
- No unnecessary re-renders
Usage Examples
Adding a Folder
@app.route('/api/folders', methods=['POST'])
def add_folder():
# ... validation and creation ...
response = make_response('')
response.headers['HX-Trigger'] = 'close-modal, folder-list-invalidated'
return response
Deleting a Folder
@app.route('/api/folders/<folder_id>', methods=['DELETE'])
def delete_folder(folder_id):
# ... deletion logic ...
response = make_response('')
response.headers['HX-Trigger'] = 'folder-list-invalidated'
return response
Updating a Folder
@app.route('/api/folders/<folder_id>', methods=['PUT'])
def update_folder(folder_id):
# ... update logic ...
response = make_response('')
response.headers['HX-Trigger'] = 'close-modal, folder-list-invalidated'
return response
Testing
The new architecture has been tested by:
- Verifying the application starts without syntax errors
- Confirming all routes are properly registered
- Testing endpoint responses (302 redirect indicates working server)
- Validating that the event system is properly configured
Future Considerations
- Single Card Updates: For performance optimization, consider adding support for single card updates in the future
- Event Optimization: Could add more specific events for different types of updates
- Error Handling: Enhanced error handling for event-triggered requests
- Caching: Consider adding caching for folder list rendering
Migration Notes
This is a breaking change that:
- Eliminates the need for direct HTML targeting
- Centralizes all folder list rendering logic
- Provides a consistent pattern for all folder operations
- Makes the codebase more maintainable and less error-prone
The migration is complete and the new architecture is now in production use.