gb/EMAIL.md
Rodrigo Rodriguez (Pragmatismo) f3bad05e76
Some checks failed
BotServer CI / build (push) Failing after 13s
Fix LXD socket handling in container mode
2026-03-15 18:19:22 -03:00

12 KiB

Email App Improvement Specification

Outlook + Gmail + Lotus Notes Best Features

Current State Analysis

The Email app (botui/ui/suite/mail/mail.html) has:

  • 3-panel layout: Sidebar, List, Content
  • Folders: Inbox, Starred, Sent, Scheduled, Drafts, Tracking, Spam, Trash
  • Compose modal: Rich text editor with formatting toolbar
  • Features: Templates, Signatures, Rules, Auto-responder, Multi-account
  • Scheduling: Send later functionality
  • Tracking: Email open tracking
  • Labels: Color-coded labels

🎯 Best-of-Breed Improvements

1. Snooze Emails (Gmail)

What: Temporarily hide emails until a specific time

Implementation:

<div class="snooze-menu">
    <button onclick="snoozeUntil('later-today')">
        Later today (6:00 PM)
    </button>
    <button onclick="snoozeUntil('tomorrow')">
        Tomorrow (8:00 AM)
    </button>
    <button onclick="snoozeUntil('this-weekend')">
        This weekend (Sat 9:00 AM)
    </button>
    <button onclick="snoozeUntil('next-week')">
        Next week (Mon 8:00 AM)
    </button>
    <button onclick="snoozeCustom()">
        Pick date & time...
    </button>
</div>

<script>
async function snoozeUntil(preset) {
    const emailIds = getSelectedEmails();
    
    await fetch('/api/email/snooze', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email_ids: emailIds, preset })
    });
    
    refreshMailList();
}
</script>

2. Smart Compose (Gmail AI)

What: AI-powered sentence completion while typing

Implementation:

let composeTimeout;

document.getElementById('compose-body').addEventListener('input', async (e) => {
    clearTimeout(composeTimeout);
    
    composeTimeout = setTimeout(async () => {
        const text = e.target.textContent;
        if (text.length < 10) return;
        
        const response = await fetch('/api/email/smart-compose', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ context: text })
        });
        
        const { suggestion } = await response.json();
        if (suggestion) showSuggestion(suggestion);
    }, 500);
});

// Accept with Tab key
document.addEventListener('keydown', (e) => {
    if (e.key === 'Tab' && hasSuggestion()) {
        e.preventDefault();
        acceptSuggestion();
    }
});
</script>

3. Nudges (Gmail Reminder)

What: Remind to follow up on emails without replies

Implementation:

<div class="nudge-banner">
    <span>You haven't replied to <strong>John Doe</strong> in 3 days</span>
    <button onclick="replyToNudge('email-123')">Reply</button>
    <button onclick="dismissNudge('email-123')">Dismiss</button>
</div>

<script>
async function checkNudges() {
    const response = await fetch('/api/email/nudges');
    const { nudges } = await response.json();
    nudges.forEach(showNudgeBanner);
}

setInterval(checkNudges, 3600000); // Check hourly
</script>

4. Conversation Threading (Outlook)

What: Group related emails together

Implementation:

<div class="mail-thread" data-thread-id="abc123">
    <div class="thread-header" onclick="toggleThread(this)">
        <span class="thread-icon"></span>
        <span class="thread-sender">John Doe</span>
        <span class="thread-subject">Project Discussion</span>
        <span class="thread-count">3 messages</span>
        <span class="thread-time">Today</span>
    </div>
    
    <div class="thread-messages" style="display: none;">
        <!-- Individual messages -->
    </div>
</div>

5. Follow-Up Flags (Lotus Notes)

What: Flag emails with specific follow-up dates

Implementation:

<div class="follow-up-menu">
    <button onclick="flagForFollowUp('today')">
        🚩 Today
    </button>
    <button onclick="flagForFollowUp('tomorrow')">
        🚩 Tomorrow
    </button>
    <button onclick="flagForFollowUp('this-week')">
        🚩 This Week
    </button>
    <button onclick="flagForFollowUp('next-week')">
        🚩 Next Week
    </button>
    <button onclick="flagForFollowUp('custom')">
        🚩 Custom Date...
    </button>
    <button onclick="clearFlag()">
        Clear Flag
    </button>
</div>

<script>
async function flagForFollowUp(when) {
    const emailIds = getSelectedEmails();
    
    await fetch('/api/email/flag', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email_ids: emailIds, follow_up: when })
    });
    
    refreshMailList();
}
</script>

6. To-Do Integration (Lotus Notes)

What: Convert emails to tasks

Implementation:

<button class="action-btn" onclick="convertToTask()">
    <svg><!-- checkbox icon --></svg>
    <span>Add to Tasks</span>
</button>

<script>
async function convertToTask() {
    const email = getCurrentEmail();
    
    const response = await fetch('/api/tasks/from-email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            title: email.subject,
            description: email.body,
            due_date: null,
            email_id: email.id
        })
    });
    
    const { task_id } = await response.json();
    showNotification(`Task created: ${email.subject}`);
}
</script>

7. Reading Pane Options (Outlook)

What: Toggle between right pane, bottom pane, or no pane

Implementation:

<div class="view-options">
    <button onclick="setReadingPane('right')" title="Right pane">
        <svg><!-- layout icon --></svg>
    </button>
    <button onclick="setReadingPane('bottom')" title="Bottom pane">
        <svg><!-- layout icon --></svg>
    </button>
    <button onclick="setReadingPane('off')" title="Hide pane">
        <svg><!-- layout icon --></svg>
    </button>
</div>

<script>
function setReadingPane(position) {
    const layout = document.querySelector('.mail-layout');
    layout.className = `mail-layout reading-pane-${position}`;
    localStorage.setItem('reading_pane', position);
}
</script>

<style>
.mail-layout.reading-pane-right {
    grid-template-columns: 250px 400px 1fr;
}

.mail-layout.reading-pane-bottom {
    grid-template-columns: 250px 1fr;
    grid-template-rows: 1fr 300px;
}

.mail-layout.reading-pane-off .mail-content {
    display: none;
}
</style>

8. Sweep and Clean Up (Outlook)

What: Bulk delete/archive emails from specific senders

Implementation:

<div class="sweep-menu">
    <h4>Clean up emails from: <strong id="sweep-sender"></strong></h4>
    
    <button onclick="sweepDelete('all')">
        Delete all emails from this sender
    </button>
    <button onclick="sweepDelete('older-than-10')">
        Delete emails older than 10 days
    </button>
    <button onclick="sweepArchive('all')">
        Archive all emails from this sender
    </button>
    <button onclick="sweepMove('folder')">
        Move all to folder...
    </button>
</div>

<script>
async function sweepDelete(criteria) {
    const sender = document.getElementById('sweep-sender').textContent;
    
    await fetch('/api/email/sweep', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sender, action: 'delete', criteria })
    });
    
    refreshMailList();
}
</script>

9. Replication Conflicts (Lotus Notes)

What: Show when email was modified in multiple places

Implementation:

<div class="conflict-banner" style="background: #fff3cd;">
    <svg><!-- warning icon --></svg>
    <span>This email has conflicting versions</span>
    <button onclick="resolveConflict()">Resolve</button>
</div>

<div class="conflict-resolver">
    <div class="conflict-version">
        <h4>Version 1 (Local)</h4>
        <div class="version-content">...</div>
        <button onclick="keepVersion(1)">Keep This</button>
    </div>
    
    <div class="conflict-version">
        <h4>Version 2 (Server)</h4>
        <div class="version-content">...</div>
        <button onclick="keepVersion(2)">Keep This</button>
    </div>
</div>

10. Offline Mode (Gmail + Lotus Notes)

What: Read and compose emails offline, sync when online

Implementation:

// Service Worker for offline support
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw-email.js');
}

// Cache emails for offline access
async function cacheEmailsForOffline() {
    const cache = await caches.open('email-cache-v1');
    const emails = await fetch('/api/email/list?folder=inbox');
    const data = await emails.json();
    
    // Cache email list
    await cache.put('/api/email/list?folder=inbox', new Response(JSON.stringify(data)));
    
    // Cache individual emails
    for (const email of data.emails) {
        const response = await fetch(`/api/email/${email.id}`);
        await cache.put(`/api/email/${email.id}`, response.clone());
    }
}

// Queue actions when offline
const offlineQueue = [];

async function sendEmailOffline(emailData) {
    if (navigator.onLine) {
        return await fetch('/api/email/send', {
            method: 'POST',
            body: JSON.stringify(emailData)
        });
    } else {
        offlineQueue.push({ action: 'send', data: emailData });
        localStorage.setItem('email_offline_queue', JSON.stringify(offlineQueue));
        showNotification('Email queued. Will send when online.');
    }
}

// Sync when back online
window.addEventListener('online', async () => {
    const queue = JSON.parse(localStorage.getItem('email_offline_queue') || '[]');
    
    for (const item of queue) {
        if (item.action === 'send') {
            await fetch('/api/email/send', {
                method: 'POST',
                body: JSON.stringify(item.data)
            });
        }
    }
    
    localStorage.removeItem('email_offline_queue');
    showNotification('Offline emails sent!');
});
</script>

🔧 Backend API Requirements

# Gmail Features
POST   /api/email/snooze                - Snooze emails
POST   /api/email/smart-compose         - Get AI suggestions
GET    /api/email/nudges                - Get follow-up reminders

# Outlook Features
GET    /api/email/thread/:id            - Get conversation thread
POST   /api/email/sweep                 - Bulk delete/archive

# Lotus Notes Features
POST   /api/email/flag                  - Flag for follow-up
POST   /api/tasks/from-email            - Convert email to task
GET    /api/email/conflicts             - Get replication conflicts
POST   /api/email/resolve-conflict      - Resolve conflict

# Offline Support
GET    /api/email/offline-sync          - Sync offline changes

📊 Database Schema

-- Snoozed emails
CREATE TABLE email_snooze (
    id UUID PRIMARY KEY,
    email_id UUID REFERENCES emails(id),
    snooze_until TIMESTAMP,
    created_at TIMESTAMP
);

-- Follow-up flags
CREATE TABLE email_flags (
    id UUID PRIMARY KEY,
    email_id UUID REFERENCES emails(id),
    follow_up_date DATE,
    flag_type VARCHAR(50),
    completed BOOLEAN DEFAULT FALSE
);

-- Nudges
CREATE TABLE email_nudges (
    id UUID PRIMARY KEY,
    email_id UUID REFERENCES emails(id),
    last_sent TIMESTAMP,
    dismissed BOOLEAN DEFAULT FALSE
);

-- Offline queue
CREATE TABLE email_offline_queue (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL,
    action VARCHAR(50),
    data JSONB,
    created_at TIMESTAMP
);

-- Threading
ALTER TABLE emails ADD COLUMN thread_id UUID;
ALTER TABLE emails ADD COLUMN in_reply_to UUID;

Implementation Priority

Phase 1: Gmail Features (Week 1)

  1. Snooze emails
  2. Smart compose
  3. Nudges

Phase 2: Outlook Features (Week 2)

  1. Conversation threading
  2. Reading pane options
  3. Sweep and clean up

Phase 3: Lotus Notes Features (Week 3)

  1. Follow-up flags
  2. To-do integration
  3. Offline mode

🧪 Testing Checklist

  • Snooze email until tomorrow
  • Smart compose suggests text
  • Nudge appears for old emails
  • Thread groups related emails
  • Reading pane switches layouts
  • Sweep deletes all from sender
  • Flag email for follow-up
  • Convert email to task
  • Compose email offline
  • Sync offline emails when online