Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
12 KiB
388 lines
12 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import type { Poll } from "../../lib/appManagers/appPollsManager"; |
|
import type Chat from "../chat/chat"; |
|
import PopupElement from "."; |
|
import CheckboxField from "../checkboxField"; |
|
import InputField from "../inputField"; |
|
import RadioField from "../radioField"; |
|
import Scrollable from "../scrollable"; |
|
import SendContextMenu from "../chat/sendContextMenu"; |
|
import I18n, { _i18n } from "../../lib/langPack"; |
|
import findUpTag from "../../helpers/dom/findUpTag"; |
|
import { cancelEvent } from "../../helpers/dom/cancelEvent"; |
|
import getRichValue from "../../helpers/dom/getRichValue"; |
|
import isInputEmpty from "../../helpers/dom/isInputEmpty"; |
|
import whichChild from "../../helpers/dom/whichChild"; |
|
import { attachClickEvent } from "../../helpers/dom/clickEvent"; |
|
|
|
const MAX_LENGTH_QUESTION = 255; |
|
const MAX_LENGTH_OPTION = 100; |
|
const MAX_LENGTH_SOLUTION = 200; |
|
|
|
export default class PopupCreatePoll extends PopupElement { |
|
private questionInputField: InputField; |
|
private questions: HTMLElement; |
|
private scrollable: Scrollable; |
|
private tempId = 0; |
|
|
|
private anonymousCheckboxField: CheckboxField; |
|
private multipleCheckboxField: PopupCreatePoll['anonymousCheckboxField']; |
|
private quizCheckboxField: PopupCreatePoll['anonymousCheckboxField']; |
|
|
|
private correctAnswers: Uint8Array[]; |
|
private quizSolutionField: InputField; |
|
private optionInputFields: InputField[]; |
|
|
|
constructor(private chat: Chat) { |
|
super('popup-create-poll popup-new-media', null, {closable: true, withConfirm: 'Create', body: true}); |
|
|
|
_i18n(this.title, 'NewPoll'); |
|
|
|
this.questionInputField = new InputField({ |
|
placeholder: 'AskAQuestion', |
|
label: 'AskAQuestion', |
|
name: 'question', |
|
maxLength: MAX_LENGTH_QUESTION |
|
}); |
|
|
|
this.listenerSetter.add(this.questionInputField.input)('input', () => { |
|
this.handleChange(); |
|
}); |
|
|
|
this.optionInputFields = []; |
|
|
|
if(this.chat.type !== 'scheduled') { |
|
const sendMenu = new SendContextMenu({ |
|
onSilentClick: () => { |
|
this.chat.input.sendSilent = true; |
|
this.send(); |
|
}, |
|
onScheduleClick: () => { |
|
this.chat.input.scheduleSending(() => { |
|
this.send(); |
|
}); |
|
}, |
|
openSide: 'bottom-left', |
|
onContextElement: this.btnConfirm, |
|
}); |
|
|
|
sendMenu.setPeerId(this.chat.peerId); |
|
|
|
this.header.append(sendMenu.sendMenu); |
|
} |
|
|
|
this.header.append(this.questionInputField.container); |
|
|
|
const hr = document.createElement('hr'); |
|
const d = document.createElement('div'); |
|
d.classList.add('caption'); |
|
_i18n(d, 'PollOptions'); |
|
|
|
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'); |
|
_i18n(settingsCaption, 'Settings'); |
|
|
|
if(!this.chat.appPeersManager.isBroadcast(this.chat.peerId)) { |
|
this.anonymousCheckboxField = new CheckboxField({ |
|
text: 'NewPoll.Anonymous', |
|
name: 'anonymous' |
|
}); |
|
this.anonymousCheckboxField.input.checked = true; |
|
dd.append(this.anonymousCheckboxField.label); |
|
} |
|
|
|
this.multipleCheckboxField = new CheckboxField({ |
|
text: 'NewPoll.MultipleChoice', |
|
name: 'multiple' |
|
}); |
|
this.quizCheckboxField = new CheckboxField({ |
|
text: 'NewPoll.Quiz', |
|
name: 'quiz' |
|
}); |
|
|
|
this.listenerSetter.add(this.multipleCheckboxField.input)('change', () => { |
|
const checked = this.multipleCheckboxField.input.checked; |
|
this.quizCheckboxField.input.toggleAttribute('disabled', checked); |
|
}); |
|
|
|
this.listenerSetter.add(this.quizCheckboxField.input)('change', () => { |
|
const checked = this.quizCheckboxField.input.checked; |
|
|
|
(Array.from(this.questions.children) as HTMLElement[]).map(el => { |
|
el.classList.toggle('radio-field', checked); |
|
}); |
|
|
|
if(!checked) { |
|
this.correctAnswers = undefined; |
|
this.quizSolutionField.setValueSilently(''); |
|
} |
|
|
|
quizElements.forEach(el => el.classList.toggle('hide', !checked)); |
|
|
|
this.multipleCheckboxField.input.toggleAttribute('disabled', checked); |
|
this.handleChange(); |
|
}); |
|
|
|
dd.append(this.multipleCheckboxField.label, this.quizCheckboxField.label); |
|
|
|
const quizElements: HTMLElement[] = []; |
|
|
|
const quizSolutionCaption = document.createElement('div'); |
|
quizSolutionCaption.classList.add('caption'); |
|
_i18n(quizSolutionCaption, 'AccDescrQuizExplanation'); |
|
|
|
const quizHr = document.createElement('hr'); |
|
|
|
const quizSolutionContainer = document.createElement('div'); |
|
quizSolutionContainer.classList.add('poll-create-questions'); |
|
|
|
this.quizSolutionField = new InputField({ |
|
placeholder: 'NewPoll.Explanation.Placeholder', |
|
label: 'NewPoll.Explanation.Placeholder', |
|
name: 'solution', |
|
maxLength: MAX_LENGTH_SOLUTION |
|
}); |
|
|
|
this.listenerSetter.add(this.questionInputField.input)('input', () => { |
|
this.handleChange(); |
|
}); |
|
|
|
const quizSolutionSubtitle = document.createElement('div'); |
|
quizSolutionSubtitle.classList.add('subtitle'); |
|
_i18n(quizSolutionSubtitle, 'AddAnExplanationInfo'); |
|
|
|
quizSolutionContainer.append(this.quizSolutionField.container, 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); |
|
|
|
attachClickEvent(this.btnConfirm, this.onSubmitClick, {listenerSetter: this.listenerSetter}); |
|
|
|
this.scrollable = new Scrollable(this.body); |
|
this.appendMoreField(); |
|
|
|
this.onEscape = () => { |
|
return !this.getFilledAnswers().length; |
|
}; |
|
|
|
this.handleChange(); |
|
} |
|
|
|
private getFilledAnswers() { |
|
const answers = Array.from(this.questions.children).map((el, idx) => { |
|
const input = el.querySelector('.input-field-input') as HTMLElement; |
|
return input instanceof HTMLInputElement ? input.value : getRichValue(input, false).value; |
|
}).filter(v => !!v.trim()); |
|
|
|
return answers; |
|
} |
|
|
|
private onSubmitClick = () => { |
|
this.send(); |
|
}; |
|
|
|
private validate() { |
|
const question = this.questionInputField.value; |
|
if(!question) { |
|
return false; |
|
} |
|
|
|
if(question.length > MAX_LENGTH_QUESTION) { |
|
return false; |
|
} |
|
|
|
if(this.quizCheckboxField.input.checked && !this.correctAnswers?.length) { |
|
return false; |
|
} |
|
|
|
const answers = this.getFilledAnswers(); |
|
if(answers.length < 2) { |
|
return false; |
|
} |
|
|
|
const tooLongOption = answers.find(a => a.length > MAX_LENGTH_OPTION); |
|
if(tooLongOption) { |
|
return false; |
|
} |
|
|
|
const {value: quizSolution} = getRichValue(this.quizSolutionField.input, false); |
|
if(quizSolution.length > MAX_LENGTH_SOLUTION) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
private handleChange() { |
|
const valid = this.validate(); |
|
this.btnConfirm.toggleAttribute('disabled', !valid); |
|
} |
|
|
|
public send(force = false) { |
|
const question = this.questionInputField.value; |
|
|
|
const answers = this.getFilledAnswers(); |
|
|
|
const {value: quizSolution, entities: quizSolutionEntities} = getRichValue(this.quizSolutionField.input); |
|
|
|
if(this.chat.type === 'scheduled' && !force) { |
|
this.chat.input.scheduleSending(() => { |
|
this.send(true); |
|
}); |
|
|
|
return; |
|
} |
|
|
|
this.hide(); |
|
|
|
//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 = this.chat.appPollsManager.getInputMediaPoll(poll, this.correctAnswers, quizSolution, quizSolutionEntities); |
|
|
|
//console.log('Will try to create poll:', inputMediaPoll); |
|
|
|
this.chat.appMessagesManager.sendOther(this.chat.peerId, inputMediaPoll, { |
|
threadId: this.chat.threadId, |
|
replyToMsgId: this.chat.input.replyToMsgId, |
|
scheduleDate: this.chat.input.scheduleDate, |
|
silent: this.chat.input.sendSilent |
|
}); |
|
|
|
if(this.chat.input.helperType === 'reply') { |
|
this.chat.input.clearHelper(); |
|
} |
|
|
|
this.chat.input.onMessageSent(false, false); |
|
} |
|
|
|
onInput = (e: Event) => { |
|
const target = e.target as HTMLInputElement; |
|
|
|
const radioLabel = findUpTag(target, 'LABEL'); |
|
const isEmpty = isInputEmpty(target); |
|
if(!isEmpty) { |
|
target.parentElement.classList.add('is-filled'); |
|
radioLabel.classList.remove('hidden-widget'); |
|
radioLabel.firstElementChild.removeAttribute('disabled'); |
|
} |
|
|
|
const isLast = !radioLabel.nextElementSibling; |
|
if(isLast && !isEmpty && this.questions.childElementCount < 10) { |
|
this.appendMoreField(); |
|
} |
|
|
|
this.handleChange(); |
|
}; |
|
|
|
onDeleteClick = (e: MouseEvent) => { |
|
const target = e.target as HTMLSpanElement; |
|
const label = findUpTag(target, 'LABEL'); |
|
const idx = whichChild(label); |
|
|
|
if(this.correctAnswers && this.correctAnswers[0][0] === idx) { |
|
this.correctAnswers = undefined; |
|
} |
|
|
|
label.remove(); |
|
this.optionInputFields.splice(idx, 1); |
|
|
|
this.optionInputFields.forEach((inputField, idx) => { |
|
inputField.options.labelOptions.length = 0; |
|
inputField.options.labelOptions.push(idx + 1); |
|
const i18nElement = I18n.weakMap.get(inputField.label.firstElementChild as HTMLElement); |
|
i18nElement.update(); |
|
}); |
|
|
|
this.handleChange(); |
|
}; |
|
|
|
private appendMoreField() { |
|
const tempId = this.tempId++; |
|
const idx = this.questions.childElementCount + 1; |
|
const questionField = new InputField({ |
|
placeholder: 'NewPoll.OptionsAddOption', |
|
label: 'NewPoll.OptionLabel', |
|
labelOptions: [idx], |
|
name: 'question-' + tempId, |
|
maxLength: MAX_LENGTH_OPTION |
|
}); |
|
this.listenerSetter.add(questionField.input)('input', this.onInput); |
|
|
|
const radioField = new RadioField({ |
|
text: '', |
|
name: 'question' |
|
}); |
|
radioField.main.append(questionField.container); |
|
attachClickEvent(questionField.input, cancelEvent, {listenerSetter: this.listenerSetter}); |
|
radioField.label.classList.add('hidden-widget'); |
|
radioField.input.disabled = true; |
|
if(!this.quizCheckboxField.input.checked) { |
|
radioField.label.classList.remove('radio-field'); |
|
} |
|
this.listenerSetter.add(radioField.input)('change', () => { |
|
const checked = radioField.input.checked; |
|
if(checked) { |
|
const idx = whichChild(radioField.label); |
|
this.correctAnswers = [new Uint8Array([idx])]; |
|
this.handleChange(); |
|
} |
|
}); |
|
|
|
const deleteBtn = document.createElement('span'); |
|
deleteBtn.classList.add('btn-icon', 'tgico-close'); |
|
questionField.container.append(deleteBtn); |
|
|
|
attachClickEvent(deleteBtn, this.onDeleteClick, {listenerSetter: this.listenerSetter, once: true}); |
|
|
|
this.questions.append(radioField.label); |
|
|
|
this.scrollable.scrollIntoViewNew(this.questions.lastElementChild as HTMLElement, 'center'); |
|
//this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true); |
|
|
|
this.optionInputFields.push(questionField); |
|
} |
|
}
|
|
|