
Closing by escape Avatar & new media are now popups Fix deleting message Edit single item from album Fix message text in album Fix overflow in pinned subtitle
246 lines
8.4 KiB
TypeScript
246 lines
8.4 KiB
TypeScript
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
|
import appPeersManager from "../lib/appManagers/appPeersManager";
|
|
import appPollsManager, { Poll } from "../lib/appManagers/appPollsManager";
|
|
import $rootScope from "../lib/rootScope";
|
|
import { findUpTag, whichChild } from "../lib/utils";
|
|
import CheckboxField from "./checkbox";
|
|
import InputField from "./inputField";
|
|
import { PopupElement } from "./popup";
|
|
import RadioField from "./radioField";
|
|
import Scrollable from "./scrollable";
|
|
import { toast } from "./toast";
|
|
|
|
export default class PopupCreatePoll extends PopupElement {
|
|
private questionInput: HTMLInputElement;
|
|
private questions: HTMLElement;
|
|
private scrollable: Scrollable;
|
|
private tempID = 0;
|
|
|
|
private anonymousCheckboxField: ReturnType<typeof CheckboxField>;
|
|
private multipleCheckboxField: PopupCreatePoll['anonymousCheckboxField'];
|
|
private quizCheckboxField: PopupCreatePoll['anonymousCheckboxField'];
|
|
|
|
private correctAnswers: Uint8Array[];
|
|
private quizSolutionInput: HTMLInputElement;
|
|
|
|
constructor() {
|
|
super('popup-create-poll popup-new-media', null, {closable: true, withConfirm: 'CREATE', body: true});
|
|
|
|
this.title.innerText = 'New Poll';
|
|
|
|
const questionField = InputField('Ask a Question', 'Ask a Question', 'question');
|
|
this.questionInput = questionField.firstElementChild as HTMLInputElement;
|
|
|
|
this.header.append(questionField);
|
|
|
|
const hr = document.createElement('hr');
|
|
const d = document.createElement('div');
|
|
d.classList.add('caption');
|
|
d.innerText = 'Options';
|
|
|
|
this.questions = document.createElement('form');
|
|
this.questions.classList.add('poll-create-questions');
|
|
|
|
const dd = document.createElement('div');
|
|
dd.classList.add('poll-create-settings');
|
|
|
|
const settingsCaption = document.createElement('div');
|
|
settingsCaption.classList.add('caption');
|
|
settingsCaption.innerText = 'Settings';
|
|
|
|
const peerID = $rootScope.selectedPeerID;
|
|
|
|
if(!appPeersManager.isBroadcast(peerID)) {
|
|
this.anonymousCheckboxField = CheckboxField('Anonymous Voting', 'anonymous');
|
|
this.anonymousCheckboxField.input.checked = true;
|
|
dd.append(this.anonymousCheckboxField.label);
|
|
}
|
|
|
|
this.multipleCheckboxField = CheckboxField('Multiple Answers', 'multiple');
|
|
this.quizCheckboxField = CheckboxField('Quiz Mode', 'quiz');
|
|
|
|
this.multipleCheckboxField.input.addEventListener('change', () => {
|
|
const checked = this.multipleCheckboxField.input.checked;
|
|
this.quizCheckboxField.input.toggleAttribute('disabled', checked);
|
|
});
|
|
|
|
this.quizCheckboxField.input.addEventListener('change', () => {
|
|
const checked = this.quizCheckboxField.input.checked;
|
|
|
|
(Array.from(this.questions.children) as HTMLElement[]).map(el => {
|
|
el.classList.toggle('radio-field', checked);
|
|
});
|
|
|
|
quizElements.forEach(el => el.classList.toggle('hide', !checked));
|
|
|
|
this.multipleCheckboxField.input.toggleAttribute('disabled', checked);
|
|
});
|
|
|
|
dd.append(this.multipleCheckboxField.label, this.quizCheckboxField.label);
|
|
|
|
const quizElements: HTMLElement[] = [];
|
|
|
|
const quizSolutionCaption = document.createElement('div');
|
|
quizSolutionCaption.classList.add('caption');
|
|
quizSolutionCaption.innerText = 'Explanation';
|
|
|
|
const quizHr = document.createElement('hr');
|
|
|
|
const quizSolutionContainer = document.createElement('div');
|
|
quizSolutionContainer.classList.add('poll-create-questions');
|
|
|
|
const quizSolutionField = InputField('Add a Comment (Optional)', 'Add a Comment (Optional)', 'solution');
|
|
this.quizSolutionInput = quizSolutionField.firstElementChild as HTMLInputElement;
|
|
|
|
const quizSolutionSubtitle = document.createElement('div');
|
|
quizSolutionSubtitle.classList.add('subtitle');
|
|
quizSolutionSubtitle.innerText = 'Users will see this comment after choosing a wrong answer, good for educational purposes.';
|
|
|
|
quizSolutionContainer.append(quizSolutionField, quizSolutionSubtitle);
|
|
|
|
quizElements.push(quizHr, quizSolutionCaption, quizSolutionContainer);
|
|
quizElements.forEach(el => el.classList.add('hide'));
|
|
|
|
this.body.parentElement.insertBefore(hr, this.body);
|
|
this.body.append(d, this.questions, document.createElement('hr'), settingsCaption, dd, ...quizElements);
|
|
|
|
this.confirmBtn.addEventListener('click', this.onSubmitClick);
|
|
|
|
this.scrollable = new Scrollable(this.body);
|
|
this.appendMoreField();
|
|
|
|
this.onEscape = () => {
|
|
return !this.getFilledAnswers().length;
|
|
};
|
|
}
|
|
|
|
private getFilledAnswers() {
|
|
const answers = Array.from(this.questions.children).map((el, idx) => {
|
|
const input = el.querySelector('input[type="text"]') as HTMLInputElement;
|
|
return input.value;
|
|
}).filter(v => !!v.trim());
|
|
|
|
return answers;
|
|
}
|
|
|
|
onSubmitClick = (e: MouseEvent) => {
|
|
const question = this.questionInput.value;
|
|
|
|
if(!question.trim()) {
|
|
toast('Please enter a question.');
|
|
return;
|
|
}
|
|
|
|
if(this.quizCheckboxField.input.checked && !this.correctAnswers?.length) {
|
|
toast('Please choose the correct answer.');
|
|
return;
|
|
}
|
|
|
|
const answers = this.getFilledAnswers();
|
|
|
|
if(answers.length < 2) {
|
|
toast('Please enter at least two options.');
|
|
return;
|
|
}
|
|
|
|
this.closeBtn.click();
|
|
this.confirmBtn.removeEventListener('click', this.onSubmitClick);
|
|
|
|
//const randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
|
|
//const randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
|
|
|
|
const pFlags: Poll['pFlags'] = {};
|
|
|
|
if(this.anonymousCheckboxField && !this.anonymousCheckboxField.input.checked) {
|
|
pFlags.public_voters = true;
|
|
}
|
|
|
|
if(this.multipleCheckboxField.input.checked) {
|
|
pFlags.multiple_choice = true;
|
|
}
|
|
|
|
if(this.quizCheckboxField.input.checked) {
|
|
pFlags.quiz = true;
|
|
}
|
|
|
|
const poll: Poll = {
|
|
_: 'poll',
|
|
pFlags,
|
|
question,
|
|
answers: answers.map((value, idx) => {
|
|
return {
|
|
_: 'pollAnswer',
|
|
text: value,
|
|
option: new Uint8Array([idx])
|
|
};
|
|
}),
|
|
id: undefined
|
|
};
|
|
//poll.id = randomIDS;
|
|
|
|
const inputMediaPoll = appPollsManager.getInputMediaPoll(poll, this.correctAnswers, this.quizSolutionInput.value || undefined);
|
|
|
|
//console.log('Will try to create poll:', inputMediaPoll);
|
|
|
|
appMessagesManager.sendOther($rootScope.selectedPeerID, inputMediaPoll);
|
|
};
|
|
|
|
onInput = (e: Event) => {
|
|
const target = e.target as HTMLInputElement;
|
|
|
|
const radioLabel = findUpTag(target, 'LABEL');
|
|
if(target.value.length) {
|
|
target.parentElement.classList.add('is-filled');
|
|
radioLabel.classList.remove('hidden-widget');
|
|
radioLabel.firstElementChild.removeAttribute('disabled');
|
|
}
|
|
|
|
const isLast = !radioLabel.nextElementSibling;
|
|
if(isLast && target.value.length && this.questions.childElementCount < 10) {
|
|
this.appendMoreField();
|
|
}
|
|
};
|
|
|
|
onDeleteClick = (e: MouseEvent) => {
|
|
const target = e.target as HTMLSpanElement;
|
|
findUpTag(target, 'LABEL').remove();
|
|
|
|
Array.from(this.questions.children).forEach((el, idx) => {
|
|
const label = el.querySelector('label') as HTMLLabelElement;
|
|
label.innerText = 'Option ' + (idx + 1);
|
|
});
|
|
};
|
|
|
|
private appendMoreField() {
|
|
const tempID = this.tempID++;
|
|
const idx = this.questions.childElementCount + 1;
|
|
const questionField = InputField('Add an Option', 'Option ' + idx, 'question-' + tempID);
|
|
(questionField.firstElementChild as HTMLInputElement).addEventListener('input', this.onInput);
|
|
|
|
const radioField = RadioField('', 'question');
|
|
radioField.main.append(questionField);
|
|
radioField.label.classList.add('hidden-widget');
|
|
radioField.input.disabled = true;
|
|
if(!this.quizCheckboxField.input.checked) {
|
|
radioField.label.classList.remove('radio-field');
|
|
}
|
|
radioField.input.addEventListener('change', () => {
|
|
const checked = radioField.input.checked;
|
|
if(checked) {
|
|
const idx = whichChild(radioField.label);
|
|
this.correctAnswers = [new Uint8Array([idx])];
|
|
}
|
|
});
|
|
|
|
const deleteBtn = document.createElement('span');
|
|
deleteBtn.classList.add('btn-icon', 'tgico-close');
|
|
questionField.append(deleteBtn);
|
|
|
|
deleteBtn.addEventListener('click', this.onDeleteClick, {once: true});
|
|
|
|
this.questions.append(radioField.label);
|
|
|
|
this.scrollable.scrollIntoView(this.questions.lastElementChild as HTMLElement, true);
|
|
//this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true);
|
|
}
|
|
} |