Skip to main content

max / goingson

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