-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate.js
223 lines (197 loc) · 8.08 KB
/
create.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Set the main button to send the data when clicked.
Telegram.WebApp.MainButton.setText('Create poll').show().onClick(pressCreate);
// Contains the HTML element with the list of days that the user has selected.
let dayList;
// Contains the HTML element of the button to add a new day.
let addDayButton;
// Contains the HTML element of the form.
let createForm;
// Whether the closing confirmation is enabled.
let closingConfirmationEnabled = false;
window.addEventListener('load', function () {
// Expand the web app to full screen when loaded.
Telegram.WebApp.expand();
dayList = document.getElementById("selectedDaysList");
addDayButton = document.getElementById("addDayButton");
createForm = document.getElementById("createForm");
// As soon as a title or description is entered, we want to enable the closing confirmation.
document.getElementById("eventTitle").addEventListener("input", enableClosingConfirmation);
document.getElementById("eventDescription").addEventListener("input", enableClosingConfirmation);
});
/**
* Enables the webapp's closing confirmation (if it is not enabled already).
*/
function enableClosingConfirmation() {
if (!closingConfirmationEnabled) {
runOnVersion('6.2', Telegram.WebApp.enableClosingConfirmation);
closingConfirmationEnabled = true;
}
}
/**
* Shows the datepicker for the given input element.
* @param inputElement The input element for which the datepicker should be shown.
*/
function showPicker(inputElement) {
try {
inputElement.showPicker();
} catch {
// showPicker is not completely supported yet by Safari.
inputElement.click();
}
}
/**
* Validates the form. This may cause feedback messages to be shown to the user.
* @returns {boolean} Whether the form is valid.
*/
function validateForm() {
let formValid = true;
// At least two days must be selected.
addDayButton.classList.remove("is-invalid", "is-valid");
if (dayList.children.length < 2) {
addDayButton.classList.add("is-invalid");
formValid = false;
} else {
addDayButton.classList.add("is-valid");
}
const existingDates = new Set();
for (const dayElement of document.getElementsByClassName("day-item")) {
const dateInput = dayElement.getElementsByTagName("input")[0];
dateInput.classList.remove("is-invalid", "is-valid");
if (existingDates.has(dateInput.value)) {
dateInput.classList.add("is-invalid");
formValid = false;
}
existingDates.add(dateInput.value);
}
if (!createForm.classList.contains("was-validated")) {
createForm.classList.add("was-validated");
}
formValid = createForm.checkValidity() && formValid;
return formValid;
}
/**
* Validates the form and shows the popup to ask the user whether they want to save the poll.
*/
function pressCreate() {
if (!validateForm()) {
runOnVersion('6.1', () => Telegram.WebApp.HapticFeedback.notificationOccurred("error"));
return;
}
const confirmText = "Do you want to save the poll? You cannot edit it later.";
runOnVersion("6.2",
() => Telegram.WebApp.showConfirm(confirmText, savePoll),
// If dialogs are not supported, we fall back to a browser confirm dialog.
() => savePoll(confirm(confirmText)));
}
/**
* Sends the data for the newly created poll to the bot and closes the web app.
* @param okay Whether the user wants to save the poll.
*/
async function savePoll(okay) {
if (!okay) {
return;
}
Telegram.WebApp.MainButton.showProgress();
const title = document.getElementById("eventTitle").value;
const description = document.getElementById("eventDescription").value;
const notification = document.getElementById("eventNotification").checked;
const anonymous = document.getElementById("eventAnonymous").checked;
const days = Array.from(dayList.children).map(day => day.getElementsByTagName("input")[0].value);
try {
const response = await fetch("/poll", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title: title,
description: description,
notification: notification,
days: days,
anonymous: anonymous,
initData: Telegram.WebApp.initData,
})
});
if (!response.ok) {
const text = await response.text();
runOnVersion('6.1', () => Telegram.WebApp.HapticFeedback.notificationOccurred("error"));
showAlert("An error occurred while sending the poll: " + text, () => Telegram.WebApp.close());
} else {
Telegram.WebApp.close();
}
} catch (e) {
console.error(e);
Telegram.WebApp.MainButton.hideProgress();
runOnVersion('6.1', () => Telegram.WebApp.HapticFeedback.notificationOccurred("error"));
showAlert("An error occurred while sending the poll: " + e);
}
}
/**
* Adds a new day to the list of selected days.
*/
function addDay() {
// We can remove the validation class because the user has changed the input.
createForm.classList.remove("was-validated");
const day = document.createElement("li");
day.classList.add("list-group-item", "d-flex", "justify-content-between", "align-items-center", "day-item")
// We only want dates after today to be selectable, hence, we set the min attribute to today's date.
const today = new Date().toISOString().split('T')[0];
day.innerHTML = `
<label>
<div class="input-group has-validation">
<!-- The label is only here for accessibility reasons, thus we hide it by default -->
<span class="d-none">Date</span>
<input type="date" value="${today}" min="${today}" required/>
<div class="invalid-feedback">
Please enter a date that is unique and after today.
</div>
</div>
</label>
<button type="button" class="btn-close" aria-label="Delete"></button>`;
const removeButton = day.getElementsByTagName("button")[0];
if (Telegram.WebApp.colorScheme === "dark") {
removeButton.classList.add("btn-close-white");
}
removeButton.addEventListener("click", () => removeDay(day));
// If the value of the input element changes, we want to reorder the day in the list.
day.getElementsByTagName("input")[0].addEventListener("change", () => reorderDay(day));
reorderDay(day); // This also adds the day to the list.
// The setTimeout is needed to wait until the DOM has been updated.
setTimeout(() => {
showPicker(day.getElementsByTagName("input")[0]);
});
}
/**
* Reorders the given day in the list of selected days.
* @param day The day that should be reordered.
*/
function reorderDay(day) {
// We can remove the validation class because the user has changed the input.
createForm.classList.remove("was-validated");
const dayDate = day.getElementsByTagName("input")[0].value;
// If the value has been removed, we can remove the day from the list.
if (dayDate === "") {
removeDay(day);
return;
}
// We want to add the new day before the next-bigger day, hence, we iterate over all days and compare their dates.
for (let child of dayList.children) {
// We know the existing list is sorted already because this function adds the days in the correct order.
// Hence, as soon as we find a day that is bigger than the new day, we insert the new day before it.
const date = child.getElementsByTagName("input")[0].value;
if (date > dayDate) {
dayList.insertBefore(day, child);
return;
}
}
// If there is no day bigger than the new day, we can put it at the end of the list.
dayList.appendChild(day);
}
/**
* Removes the given day from the list of selected days.
* @param day The day that should be removed.
*/
function removeDay(day) {
runOnVersion('6.1', () => Telegram.WebApp.HapticFeedback.impactOccurred("light"));
dayList.removeChild(day);
}