/** * Mail Calendar Sync - Personal Settings JavaScript */ (function() { 'use strict'; const BASE_URL = OC.generateUrl('/apps/mail_calendar_sync/api'); let currentConfig = {}; let mailAccounts = []; let calendars = []; async function init() { try { const [configData, accountsData, calendarsData] = await Promise.all([ fetchJson(`${BASE_URL}/config`), fetchJson(`${BASE_URL}/mail-accounts`), fetchJson(`${BASE_URL}/calendars`), ]); currentConfig = configData; mailAccounts = accountsData; calendars = calendarsData; renderForm(); loadLog(); } catch (error) { console.error('Failed to load Mail Calendar Sync settings:', error); showStatus('Failed to load settings: ' + error.message, 'error'); } finally { document.getElementById('mcs-loading').style.display = 'none'; document.getElementById('mcs-config').style.display = ''; } } function renderForm() { // Enabled checkbox const enabledCheckbox = document.getElementById('mcs-enabled'); enabledCheckbox.checked = currentConfig.enabled || false; enabledCheckbox.addEventListener('change', onEnabledChange); // Mail accounts dropdown const mailSelect = document.getElementById('mcs-mail-account'); mailSelect.innerHTML = ''; mailAccounts.forEach(account => { const option = document.createElement('option'); option.value = account.id; option.textContent = `${account.name} (${account.email})`; if (currentConfig.mailAccountId == account.id) { option.selected = true; } mailSelect.appendChild(option); }); if (mailAccounts.length === 0) { const option = document.createElement('option'); option.value = ''; option.textContent = 'No mail accounts found — configure one in the Mail app first'; option.disabled = true; mailSelect.appendChild(option); } // Calendars dropdown const calSelect = document.getElementById('mcs-calendar'); calSelect.innerHTML = ''; calendars.forEach(cal => { const option = document.createElement('option'); option.value = cal.uri; option.textContent = cal.name; if (cal.color) { option.style.borderLeft = `4px solid ${cal.color}`; option.style.paddingLeft = '8px'; } if (currentConfig.calendarUri === cal.uri) { option.selected = true; } calSelect.appendChild(option); }); // Sync interval dropdown const intervalSelect = document.getElementById('mcs-sync-interval'); const currentInterval = currentConfig.syncInterval || 600; for (let i = 0; i < intervalSelect.options.length; i++) { if (parseInt(intervalSelect.options[i].value) === currentInterval) { intervalSelect.options[i].selected = true; break; } } // Auto-accept checkbox document.getElementById('mcs-auto-accept').checked = currentConfig.autoAccept || false; // Show/hide sections based on enabled state updateSectionVisibility(currentConfig.enabled || false); // Bind buttons document.getElementById('mcs-save').addEventListener('click', onSave); document.getElementById('mcs-trigger-sync').addEventListener('click', onTriggerSync); } function onEnabledChange(event) { updateSectionVisibility(event.target.checked); } function updateSectionVisibility(enabled) { const sections = [ 'mcs-mail-section', 'mcs-calendar-section', 'mcs-interval-section', 'mcs-auto-accept-section', 'mcs-save-section', ]; sections.forEach(id => { document.getElementById(id).style.display = enabled ? '' : 'none'; }); } async function onSave() { const saveBtn = document.getElementById('mcs-save'); saveBtn.disabled = true; showStatus('Saving…', 'loading'); const config = { enabled: document.getElementById('mcs-enabled').checked, mailAccountId: document.getElementById('mcs-mail-account').value || null, calendarUri: document.getElementById('mcs-calendar').value || null, autoAccept: document.getElementById('mcs-auto-accept').checked, syncInterval: parseInt(document.getElementById('mcs-sync-interval').value) || 600, }; if (config.enabled) { if (!config.mailAccountId) { showStatus('Please select a mail account', 'error'); saveBtn.disabled = false; return; } if (!config.calendarUri) { showStatus('Please select a calendar', 'error'); saveBtn.disabled = false; return; } } try { const result = await fetchJson(`${BASE_URL}/config`, { method: 'PUT', body: JSON.stringify(config), headers: { 'Content-Type': 'application/json', }, }); currentConfig = result; showStatus('Settings saved', 'success'); if (config.enabled) { document.getElementById('mcs-log-section').style.display = ''; loadLog(); } } catch (error) { console.error('Failed to save config:', error); showStatus('Failed to save: ' + error.message, 'error'); } finally { saveBtn.disabled = false; } } async function onTriggerSync() { const btn = document.getElementById('mcs-trigger-sync'); btn.disabled = true; showStatus('Syncing…', 'loading'); // Show the sync details section const detailsSection = document.getElementById('mcs-sync-details'); const syncLog = document.getElementById('mcs-sync-log'); detailsSection.style.display = ''; syncLog.textContent = 'Running sync…'; try { const result = await fetchJson(`${BASE_URL}/trigger-sync`, { method: 'POST', }); const stats = result.stats || {}; const messages = stats.messages || []; // Show detailed log if (messages.length > 0) { syncLog.textContent = messages.join('\n'); } else { syncLog.textContent = 'Sync completed with no messages.'; } const msg = `Sync complete: ${stats.processed || 0} scanned, ${stats.updated || 0} updated, ${stats.errors || 0} errors`; showStatus(msg, stats.errors > 0 ? 'warning' : 'success'); loadLog(); } catch (error) { console.error('Sync failed:', error); syncLog.textContent = 'Sync failed: ' + error.message; showStatus('Sync failed — see details below', 'error'); } finally { btn.disabled = false; } } async function loadLog() { try { const entries = await fetchJson(`${BASE_URL}/log?limit=20`); const tbody = document.getElementById('mcs-log-body'); const emptyMsg = document.getElementById('mcs-log-empty'); const logSection = document.getElementById('mcs-log-section'); if (currentConfig.enabled) { logSection.style.display = ''; } tbody.innerHTML = ''; if (entries.length === 0) { emptyMsg.style.display = ''; return; } emptyMsg.style.display = 'none'; entries.forEach(entry => { const tr = document.createElement('tr'); tr.className = `mcs-log-${entry.action.toLowerCase()}`; const date = entry.createdAt ? new Date(entry.createdAt).toLocaleString() : '—'; tr.innerHTML = ` ${escapeHtml(date)} ${escapeHtml(entry.eventSummary || entry.eventUid || '—')} ${escapeHtml(entry.action)} ${escapeHtml(entry.message || '—')} `; tbody.appendChild(tr); }); } catch (error) { console.error('Failed to load log:', error); } } function showStatus(message, type) { const statusEl = document.getElementById('mcs-status'); statusEl.textContent = message; statusEl.className = `mcs-status mcs-status-${type}`; if (type === 'success') { setTimeout(() => { statusEl.textContent = ''; statusEl.className = 'mcs-status'; }, 5000); } } async function fetchJson(url, options = {}) { const { headers: optHeaders, ...restOptions } = options; const response = await fetch(url, { credentials: 'same-origin', ...restOptions, headers: { 'requesttoken': OC.requestToken, ...(optHeaders || {}), }, }); if (!response.ok) { let errorMsg = `HTTP ${response.status}`; try { const errorData = await response.json(); if (errorData.error) { errorMsg = errorData.error; } } catch (e) { // ignore parse error } throw new Error(errorMsg); } return response.json(); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } document.addEventListener('DOMContentLoaded', init); })();