This commit is contained in:
Bryce
2025-08-09 20:36:18 -07:00
parent 707a8a3095
commit af637f29b4
9 changed files with 167 additions and 209 deletions

View File

@@ -138,11 +138,9 @@ def test_imap_connection():
db.session.commit()
# Redirect to folder selection modal after successful connection
response = make_response('')
response.headers['HX-Trigger'] = 'open-modal'
response.headers['HX-location'] = '/api/imap/folders/modal'
return imap_folders_modal()
#response.headers['HX-Trigger'] = 'open-modal'
else:
print(message)
response = make_response(render_template('partials/imap_config_modal.html',
errors={'general': message}, server=server, port=port, username=username, use_ssl=use_ssl))
response.headers['HX-Retarget'] = '#imap-modal'
@@ -315,114 +313,4 @@ def sync_selected_folders():
logging.exception("Error syncing selected IMAP folders: %s", e)
print(e)
db.session.rollback()
return jsonify({'error': 'An unexpected error occurred'}), 500
@imap_bp.route('/api/imap/sync', methods=['POST'])
@login_required
def sync_imap_folders():
"""Create and sync folders from IMAP server with processed email tracking."""
try:
if not current_user.imap_config:
return jsonify({'error': 'No IMAP configuration found. Please configure IMAP first.'}), 400
# Test connection first
imap_service = IMAPService(current_user)
# Get folders from IMAP server
imap_folders = imap_service.get_folders()
if not imap_folders:
return jsonify({'error': 'No folders found on IMAP server'}), 400
# Deduplicate folders by name to prevent creating multiple entries for the same folder
unique_folders = []
seen_names = set()
for imap_folder in imap_folders:
folder_name = imap_folder['name']
# Skip special folders that might not be needed
if folder_name.lower() in ['sent', 'drafts', 'spam', 'trash']:
continue
# Use case-insensitive comparison for deduplication
folder_name_lower = folder_name.lower()
if folder_name_lower not in seen_names:
unique_folders.append(imap_folder)
seen_names.add(folder_name_lower)
# Process each unique folder
synced_count = 0
processed_emails_service = ProcessedEmailsService(current_user)
# Create a list of folders to process
folders_to_process = []
for imap_folder in unique_folders:
folder_name = imap_folder['name'].strip()
# Handle nested folder names (convert slashes to underscores or keep as-is)
# According to requirements, nested folders should be created with slashes in the name
display_name = folder_name
# Check if folder already exists
existing_folder = Folder.query.filter_by(
user_id=current_user.id,
name=display_name
).first()
if not existing_folder:
# Create new folder
# Determine folder type - inbox should be 'tidy', others 'destination'
folder_type = 'tidy' if folder_name.lower().strip() == 'inbox' else 'destination'
new_folder = Folder(
user_id=current_user.id,
name=display_name,
rule_text=f"Auto-synced from IMAP folder: {folder_name}",
priority=0, # Default priority
folder_type=folder_type
)
db.session.add(new_folder)
synced_count += 1
folders_to_process.append(new_folder)
else:
# Update existing folder with email counts and recent emails
# Get all email UIDs in this folder
email_uids = imap_service.get_folder_email_uids(folder_name)
# Sync with processed emails service
new_emails_count = processed_emails_service.sync_folder_emails(display_name, email_uids)
print("NEW", new_emails_count)
# Update counts
pending_count = processed_emails_service.get_pending_count(display_name)
existing_folder.pending_count = pending_count
existing_folder.total_count = len(email_uids)
# Get the most recent emails for this folder
recent_emails = imap_service.get_recent_emails(folder_name, 3)
existing_folder.recent_emails = recent_emails
folders_to_process.append(existing_folder)
db.session.commit()
# Check if we should show the folder type selection modal
# Only show the modal if there are new folders to configure
if synced_count > 0:
# Return the folder type selection modal
response = make_response(render_template('partials/folder_type_selection_modal.html', folders=folders_to_process))
response.headers['hx-retarget'] = "#modal-holder"
response.headers['HX-Trigger'] = 'open-modal'
return response
else:
# Just trigger the folder list update
response = make_response('')
response.headers['HX-Trigger'] = 'close-modal, folder-list-invalidated'
return response
except Exception as e:
logging.exception("Error syncing IMAP folders: %s", e)
print(e)
db.session.rollback()
return jsonify({'error': 'An unexpected error occurred'}), 500

View File

@@ -38,7 +38,12 @@
/* Fade out transition for HTMX */
.fade-out-htmx.htmx-swapping {
opacity: 0;
transition: opacity 1s ease-out;
transition: opacity 280ms ease-out;
}
/* Fade out transition for HTMX */
.htmx-swapping .fade-out-htmx {
opacity: 0;
transition: opacity 280ms ease-out;
}
</style>
{% block head %}{% endblock %}

View File

@@ -1,4 +1,4 @@
<div id="folder-selection-modal" class="modal-box step-2 slide-left-enter-from w-11/12 max-w-4xl" @click.away="$refs.modal.close()">
<div id="folder-selection-modal" class="modal-box step-2 w-11/12 max-w-4xl fade-in-htmx" @click.away="$refs.modal.close()">
<div class="flex items-center mb-4">
<div class="steps flex-1">
<span class="step">Step 1</span>
@@ -6,9 +6,8 @@
</div>
<h3 class="font-bold text-lg">Configure IMAP Folders</h3>
</div>
<p class="mb-4">The following folders were found on your IMAP server. Select which folders you want to sync and configure their processing types.</p>
<form id="folder-selection-form" hx-post="/api/imap/sync-selected" hx-target="#modal-holder" hx-swap="innerHTML slide-left:300ms">
<form id="folder-selection-form" hx-post="/api/imap/sync-selected" hx-target="#modal-holder" hx-swap="innerHTML">
<div class="overflow-x-auto mb-4">
<table class="table">

View File

@@ -1,59 +0,0 @@
<div id="folder-type-modal" class="modal-box step-2 slide-left-enter-from" @click.away="$refs.modal.close()">
<div class="flex items-center mb-4">
<div class="steps flex-1">
<span class="step">Step 1</span>
<span class="step step-primary">Step 2</span>
</div>
<h3 class="font-bold text-lg">Configure Folder Types</h3>
</div>
<p class="mb-4">Select the processing type for each folder. Inbox will default to Tidy, while Archive/Spam/Drafts will default to Ignore.</p>
<div class="overflow-x-auto mb-4">
<table class="table">
<thead>
<tr>
<th>Folder Name</th>
<th>Processing Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for folder in folders %}
<tr>
<td>{{ folder.name }}</td>
<td>
<select class="select select-bordered select-sm"
name="folder_type_{{ folder.id }}"
hx-put="/api/folders/{{ folder.id }}/type"
hx-target="#modal-holder"
hx-swap="outerHTML">
<option value="tidy" {% if folder.folder_type == 'tidy' %}selected{% endif %}>Tidy</option>
<option value="destination" {% if folder.folder_type == 'destination' %}selected{% endif %}>Destination</option>
<option value="ignore" {% if folder.folder_type == 'ignore' %}selected{% endif %}>Ignore</option>
</select>
</td>
<td>
{% if folder.folder_type == 'tidy' %}
Processed by AI to organize emails
{% elif folder.folder_type == 'destination' %}
Target for organized emails
{% else %}
Ignored during processing
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-action">
<button type="button" class="btn btn-outline" hx-get="/api/imap/config" hx-target="#modal-holder" hx-swap="innerHTML slide-right:300ms">
<i class="fas fa-arrow-left mr-2"></i>Back
</button>
<button type="button" class="btn btn-primary" hx-post="/api/imap/sync">
Save and Continue
</button>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<div id="imap-modal" @click.away="$refs.modal.close()" class="modal-box step-1 translate-up-enter-from w-11/12 max-w-4xl" x-data="{ errors: {{ 'true' if errors else 'false' }} }" x-init="$nextTick(() => { if (errors) { document.querySelector('#submit-btn').classList.add('shake'); } })" >
<div id="imap-modal" @click.away="$refs.modal.close()" class="modal-box step-1 w-11/12 max-w-4xl fade-out-htmx" x-data="{ errors: {{ 'true' if errors else 'false' }} }" x-init="$nextTick(() => { if (errors) { document.querySelector('#submit-btn').classList.add('shake'); } })" >
<div class="flex items-center mb-4">
<div class="steps flex-1">
<span class="step step-primary">Step 1</span>
@@ -21,7 +21,7 @@
</div>
{% endif %}
<form id="imap-form" hx-post="/api/imap/test" hx-target="#imap-modal" hx-swap="outerHTML">
<form id="imap-form" hx-post="/api/imap/test" hx-target="#modal-holder" hx-swap="innerHTML swap:300ms" hx-trigger="submit once">
<div class="mb-4">
<label for="imap-server" class="block text-sm font-medium mb-1">IMAP Server</label>
<input type="text" id="imap-server" name="server"
@@ -84,12 +84,4 @@
</div>
</form>
{% if success %}
<div class="mt-4 pt-4 border-t border-base-300" data-loading-states>
<button class="btn btn-success w-full" hx-post="/api/imap/sync" hx-target="#modal-holder" hx-swap="innerHTML slide-left:300ms" data-loading-disable>
<span data-loading-class="!hidden"><i class="fas fa-sync mr-2"></i>Configure Folder Types</span>
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
</button>
</div>
{% endif %}
</div>