Skip to main content

max / goingson

5.5 KB · 144 lines History Blame Raw
1 /**
2 * GoingsOn - Snooze Module
3 * Snooze functionality for tasks, emails, and events
4 */
5
6 (function() {
7 'use strict';
8 const esc = GoingsOn.utils.escapeHtml;
9 const escAttr = GoingsOn.utils.escapeAttr;
10
11 const ITEM_LABEL = { task: 'Task', email: 'Email', event: 'Event' };
12
13 function apiFor(itemType) {
14 if (itemType === 'task') return GoingsOn.api.tasks;
15 if (itemType === 'email') return GoingsOn.api.emails;
16 return GoingsOn.api.events;
17 }
18
19 function reloadFor(itemType) {
20 if (itemType === 'task') return GoingsOn.tasks.load();
21 if (itemType === 'email') return GoingsOn.emails.load();
22 return GoingsOn.events.load();
23 }
24
25 // ============ Snooze Functions ============
26
27 /**
28 * Open the snooze modal with pre-computed time options.
29 * @param {string} itemType - 'task', 'email', or 'event'
30 * @param {string} id - Item ID to snooze
31 */
32 async function openSnoozeModal(itemType, id) {
33 // Get pre-computed snooze options from backend
34 const response = await GoingsOn.api.snooze.getOptions();
35 const options = response.options;
36
37 let optionsHtml = '<div class="snooze-options">';
38
39 for (const opt of options) {
40 optionsHtml += `
41 <button class="snooze-option" onclick="GoingsOn.snooze.snooze('${escAttr(itemType)}', '${escAttr(id)}', '${escAttr(opt.time)}')">
42 <span class="snooze-option-label">${esc(opt.label)}</span>
43 <span class="snooze-option-time">${esc(opt.formatted)}</span>
44 </button>
45 `;
46 }
47
48 // Custom datetime picker
49 const minCustom = response.minCustom.slice(0, 16);
50 optionsHtml += `
51 <div class="snooze-custom">
52 <label class="form-label" for="snooze-custom-datetime">Custom Date & Time</label>
53 <input type="datetime-local" id="snooze-custom-datetime" class="form-input"
54 min="${minCustom}">
55 <button class="btn btn-primary" style="margin-top: 0.5rem; width: 100%;"
56 onclick="GoingsOn.snooze.snoozeCustom('${escAttr(itemType)}', '${escAttr(id)}')">
57 Snooze Until Custom Time
58 </button>
59 </div>
60 </div>`;
61
62 const label = ITEM_LABEL[itemType] || 'Item';
63 const hintHtml = `<p class="snooze-hint">Hide this ${label.toLowerCase()} and bring it back at the chosen time.</p>`;
64 GoingsOn.ui.openModal(`Snooze ${label}`, hintHtml + optionsHtml);
65 }
66
67 /**
68 * Format an ISO datetime string as a short human-readable snooze time.
69 * @param {string} isoString - ISO 8601 datetime string
70 * @returns {string} Formatted date (e.g., "Mon, Apr 15, 9:00 AM")
71 */
72 function formatSnoozeTime(isoString) {
73 const date = new Date(isoString);
74 const options = { weekday: 'short', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' };
75 return date.toLocaleDateString('en-US', options);
76 }
77
78 /**
79 * Snooze a task, email, or event until the specified time.
80 * @param {string} itemType - 'task', 'email', or 'event'
81 * @param {string} id - Item ID to snooze
82 * @param {string} until - ISO 8601 datetime to snooze until
83 */
84 async function snoozeItem(itemType, id, until) {
85 const label = ITEM_LABEL[itemType] || 'Item';
86 try {
87 await apiFor(itemType).snooze(id, until);
88 reloadFor(itemType);
89 GoingsOn.ui.closeModal();
90 GoingsOn.ui.showToast(`${label} snoozed until ${formatSnoozeTime(until)}`);
91 } catch (err) {
92 GoingsOn.ui.showToast('Failed to snooze: ' + GoingsOn.utils.getErrorMessage(err), 'error');
93 }
94 }
95
96 /**
97 * Snooze using the custom datetime-local picker value.
98 * @param {string} itemType - 'task', 'email', or 'event'
99 * @param {string} id - Item ID to snooze
100 */
101 async function snoozeItemCustom(itemType, id) {
102 const input = document.getElementById('snooze-custom-datetime');
103 if (!input.value) {
104 GoingsOn.ui.showToast('Please select a date and time', 'error');
105 return;
106 }
107 // Parse datetime-local value as local time components to avoid UTC misinterpretation
108 const [datePart, timePart] = input.value.split('T');
109 const [year, month, day] = datePart.split('-').map(Number);
110 const [hours, minutes] = timePart.split(':').map(Number);
111 const dt = new Date(year, month - 1, day, hours, minutes);
112 const until = dt.toISOString();
113 await snoozeItem(itemType, id, until);
114 }
115
116 /**
117 * Remove snooze from a task, email, or event.
118 * @param {string} itemType - 'task', 'email', or 'event'
119 * @param {string} id - Item ID to unsnooze
120 */
121 async function unsnoozeItem(itemType, id) {
122 const label = ITEM_LABEL[itemType] || 'Item';
123 try {
124 await apiFor(itemType).unsnooze(id);
125 reloadFor(itemType);
126 GoingsOn.ui.closeModal();
127 GoingsOn.ui.showToast(`${label} unsnoozed`);
128 } catch (err) {
129 GoingsOn.ui.showToast('Failed to unsnooze: ' + GoingsOn.utils.getErrorMessage(err), 'error');
130 }
131 }
132
133 // ============ Populate GoingsOn.snooze Namespace ============
134
135 GoingsOn.snooze = {
136 openModal: openSnoozeModal,
137 snooze: snoozeItem,
138 snoozeCustom: snoozeItemCustom,
139 unsnooze: unsnoozeItem,
140 formatTime: formatSnoozeTime,
141 };
142
143 })();
144