progress
This commit is contained in:
@@ -263,8 +263,9 @@ class IMAPService:
|
||||
if not imap_folders:
|
||||
return False, "No folders found on IMAP server"
|
||||
|
||||
# Process each folder
|
||||
synced_count = 0
|
||||
# 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']
|
||||
|
||||
@@ -272,6 +273,17 @@ class IMAPService:
|
||||
if folder_name.lower() in ['inbox', '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
|
||||
for imap_folder in unique_folders:
|
||||
folder_name = imap_folder['name']
|
||||
|
||||
# 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
|
||||
|
||||
@@ -171,7 +171,7 @@ def update_folder(folder_id):
|
||||
# Folder not found
|
||||
folders = Folder.query.filter_by(user_id=current_user.id).all()
|
||||
return render_template('partials/folders_list.html', folders=folders)
|
||||
|
||||
|
||||
# Get form data
|
||||
name = request.form.get('name')
|
||||
rule_text = request.form.get('rule_text')
|
||||
@@ -234,6 +234,7 @@ def imap_config_modal():
|
||||
server=current_user.imap_config.get('server') if current_user.imap_config else None,
|
||||
port=current_user.imap_config.get('port') if current_user.imap_config else None,
|
||||
username=current_user.imap_config.get('username') if current_user.imap_config else None,
|
||||
password=current_user.imap_config.get('password') if current_user.imap_config else None,
|
||||
use_ssl=current_user.imap_config.get('use_ssl', True) if current_user.imap_config else True))
|
||||
response.headers['HX-Trigger'] = 'open-modal'
|
||||
return response
|
||||
@@ -243,8 +244,6 @@ def imap_config_modal():
|
||||
def test_imap_connection():
|
||||
"""Test IMAP connection with provided configuration."""
|
||||
try:
|
||||
import time
|
||||
time.sleep(5)
|
||||
# Get form data
|
||||
server = request.form.get('server')
|
||||
port = request.form.get('port')
|
||||
@@ -337,4 +336,23 @@ def sync_imap_folders():
|
||||
except Exception as e:
|
||||
logging.exception("Error syncing IMAP folders: %s", e)
|
||||
print(e)
|
||||
return jsonify({'error': 'An unexpected error occurred. Please try again.'}), 500
|
||||
|
||||
@main.route('/api/folders', methods=['GET'])
|
||||
@login_required
|
||||
def get_folders():
|
||||
"""Get folders with optional filtering."""
|
||||
# Get filter parameter from query string
|
||||
filter_type = request.args.get('filter', 'all')
|
||||
|
||||
# Get folders for the current authenticated user
|
||||
if filter_type == 'high':
|
||||
# Get high priority folders (priority = 1)
|
||||
folders = Folder.query.filter_by(user_id=current_user.id, priority=1).all()
|
||||
elif filter_type == 'normal':
|
||||
# Get normal priority folders (priority = 0 or not set)
|
||||
folders = Folder.query.filter_by(user_id=current_user.id).filter(Folder.priority != 1).all()
|
||||
else:
|
||||
# Get all folders
|
||||
folders = Folder.query.filter_by(user_id=current_user.id).all()
|
||||
|
||||
return render_template('partials/folders_list.html', folders=folders)
|
||||
|
||||
@@ -22,6 +22,19 @@
|
||||
.shake {
|
||||
animation: shake 0.5s cubic-bezier(.36,.07,.19,.97) both;
|
||||
}
|
||||
|
||||
.fade-in-htmx.htmx-added {
|
||||
opacity: 0;
|
||||
}
|
||||
.fade-in-htmx {
|
||||
opacity: 1;
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
/* Fade out transition for HTMX */
|
||||
.fade-out-htmx.htmx-swapping {
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
</style>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
||||
@@ -96,12 +96,12 @@
|
||||
<i class="fas fa-search absolute right-3 top-3 text-base-content/50"></i>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<button class="btn btn-sm btn-outline">All</button>
|
||||
<button class="btn btn-sm btn-outline">High Priority</button>
|
||||
<button class="btn btn-sm btn-outline">Normal</button>
|
||||
<button class="btn btn-sm btn-outline" hx-get="/api/folders?filter=all" hx-target="#folders-list" hx-swap="innerHTML swap1:s">All</button>
|
||||
<button class="btn btn-sm btn-outline" hx-get="/api/folders?filter=high" hx-target="#folders-list" hx-swap="innerHTML swap1:s">High Priority</button>
|
||||
<button class="btn btn-sm btn-outline" hx-get="/api/folders?filter=normal" hx-target="#folders-list" hx-swap="innerHTML swap1:s">Normal</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<section id="folders-list" class="mb-12">
|
||||
{% include 'partials/folders_list.html' %}
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div id="folder-{{ folder.id }}" class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg transition-shadow duration-200">
|
||||
<div id="folder-{{ folder.id }}" class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg">
|
||||
|
||||
<div class="card-body" data-loading-states>
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
@@ -14,10 +14,10 @@
|
||||
<i class="fas fa-edit" data-loading-class="!hidden"></i>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline btn-error"
|
||||
<button class="btn btn-sm btn-outline btn-error fade-me-out"
|
||||
hx-delete="/api/folders/{{ folder.id }}"
|
||||
hx-target="#folders-list"
|
||||
hx-swap="innerHTML"
|
||||
hx-swap="innerHTML swap:1s"
|
||||
hx-confirm="Are you sure you want to delete this folder?"
|
||||
data-loading-disable
|
||||
>
|
||||
@@ -30,8 +30,8 @@
|
||||
<!-- Email count badges placed below title but in a separate row -->
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<div class="flex space-x-1">
|
||||
<span class="badge badge-outline">{{ folder.total_count }} emails</span>
|
||||
<span class="badge badge-secondary" x-tooltip.raw="{% if folder.recent_emails %}<table class='text-xs'><tr><th class='text-left pr-2'>Subject</th><th class='text-left'>Date</th></tr>{% for email in folder.recent_emails %}<tr><td class='text-left pr-2 truncate max-w-[150px]'>{{ email.subject }}</td><td class='text-left'>{{ email.date[:10] if email.date else 'N/A' }}</td></tr>{% endfor %}</table>{% else %}No recent emails{% endif %}">{{ folder.pending_count }} pending</span>
|
||||
<span class="badge badge-outline cursor-pointer">{{ folder.total_count }} emails</span>
|
||||
<span class="badge badge-secondary cursor-pointer" x-tooltip.raw.html="{% if folder.recent_emails %}<table class='text-xs'><tr><th class='text-left pr-2'>Subject</th><th class='text-left'>Date</th></tr>{% for email in folder.recent_emails %}<tr><td class='text-left pr-2 truncate max-w-[150px]'>{{ email.subject }}</td><td class='text-left'>{{ email.date[:10] if email.date else 'N/A' }}</td></tr>{% endfor %}</table>{% else %}No recent emails{% endif %}">{{ folder.pending_count }} pending</span>
|
||||
</div>
|
||||
{% if folder.priority == 1 %}
|
||||
<span class="badge badge-error">High Priority</span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div id="folders-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div id="folders-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 fade-in-htmx">
|
||||
{% for folder in folders %}
|
||||
{% include 'partials/folder_card.html' %}
|
||||
{% else %}
|
||||
|
||||
@@ -53,7 +53,8 @@
|
||||
<label for="imap-password" class="block text-sm font-medium mb-1">Password</label>
|
||||
<input type="password" id="imap-password" name="password"
|
||||
class="input input-bordered w-full {% if errors and errors.password %}input-error{% endif %}"
|
||||
placeholder="App password or account password" required>
|
||||
placeholder="App password or account password" required
|
||||
value="{% if password is defined %}{{ password }}{% endif %}">
|
||||
{% if errors and errors.password %}
|
||||
<div class="text-error text-sm mt-1">{{ errors.password }}</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user