From b0d742932eaa7afce8b675f226ca71e0245eb1aa Mon Sep 17 00:00:00 2001 From: morethanwords Date: Fri, 26 Mar 2021 21:49:29 +0400 Subject: [PATCH] More translations --- src/components/appSearchSuper..ts | 9 +- src/components/chat/topbar.ts | 4 +- src/components/inputField.ts | 11 +- src/components/popups/createPoll.ts | 118 ++++++++++++------ src/components/sidebarLeft/index.ts | 18 ++- .../sidebarLeft/tabs/chatFolders.ts | 3 - .../sidebarRight/tabs/sharedMedia.ts | 12 +- src/lang.ts | 23 +++- src/lib/appManagers/appMessagesManager.ts | 7 +- src/scss/partials/_audio.scss | 4 + 10 files changed, 142 insertions(+), 67 deletions(-) diff --git a/src/components/appSearchSuper..ts b/src/components/appSearchSuper..ts index 8ebde255..a3174cbf 100644 --- a/src/components/appSearchSuper..ts +++ b/src/components/appSearchSuper..ts @@ -23,6 +23,7 @@ import Scrollable, { ScrollableX } from "./scrollable"; import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers"; import useHeavyAnimationCheck, { getHeavyAnimationPromise } from "../hooks/useHeavyAnimationCheck"; import { isSafari } from "../helpers/userAgent"; +import { LangPackKey, i18n } from "../lib/langPack"; //const testScroll = false; @@ -83,7 +84,7 @@ export default class AppSearchSuper { public goingHard: Partial<{[type in MyInputMessagesFilter]: {scrollTop: number, scrollHeight: number}}> = {}; - constructor(public types: {inputFilter: SearchSuperType, name: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false, public groupByMonth = true) { + constructor(public types: {inputFilter: SearchSuperType, name: LangPackKey, type: string}[], public scrollable: Scrollable, public searchGroups?: {[group in SearchGroupType]: SearchGroup}, public asChatList = false, public groupByMonth = true) { this.container = document.createElement('div'); this.container.classList.add('search-super'); @@ -104,7 +105,7 @@ export default class AppSearchSuper { const span = document.createElement('span'); const i = document.createElement('i'); - span.innerText = type.name; + span.append(i18n(type.name)); span.append(i); menuTab.append(span); @@ -119,10 +120,10 @@ export default class AppSearchSuper { for(const type of types) { const container = document.createElement('div'); - container.classList.add('search-super-container-' + type.name.toLowerCase()/* , 'scrollable', 'scrollable-y' */); + container.classList.add('search-super-container-' + type.type/* , 'scrollable', 'scrollable-y' */); const content = document.createElement('div'); - content.classList.add('search-super-content-' + type.name.toLowerCase()/* , 'scrollable', 'scrollable-y' */); + content.classList.add('search-super-content-' + type.type/* , 'scrollable', 'scrollable-y' */); //content.style.overflowY = 'hidden'; /* container.style.overflow = 'visible'; diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 74f3f3af..603a4fce 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -190,14 +190,14 @@ export default class ChatTopbar { verify: () => mediaSizes.isMobile }, */ { icon: 'mute', - text: 'Mute', + text: 'ChatList.Context.Mute', onClick: () => { this.appMessagesManager.mutePeer(this.peerId); }, verify: () => this.chat.type === 'chat' && rootScope.myId !== this.peerId && !this.appNotificationsManager.isPeerLocalMuted(this.peerId, false) }, { icon: 'unmute', - text: 'Unmute', + text: 'ChatList.Context.Unmute', onClick: () => { this.appMessagesManager.mutePeer(this.peerId); }, diff --git a/src/components/inputField.ts b/src/components/inputField.ts index 0e10d42e..08439b7f 100644 --- a/src/components/inputField.ts +++ b/src/components/inputField.ts @@ -61,6 +61,7 @@ export enum InputState { export type InputFieldOptions = { placeholder?: LangPackKey, label?: LangPackKey, + labelOptions?: any[], name?: string, maxLength?: number, showLengthOn?: number, @@ -147,7 +148,7 @@ class InputField { if(label) { this.label = document.createElement('label'); - this.label.append(i18n(label)); + this.label.append(i18n(label, options.labelOptions)); this.container.append(this.label); } @@ -168,11 +169,11 @@ class InputField { if(isError || diff <= showLengthOn) { labelEl.innerHTML = ''; - labelEl.append(i18n(label), ` (${maxLength - inputLength})`); + labelEl.append(i18n(label, options.labelOptions), ` (${maxLength - inputLength})`); if(!showingLength) showingLength = true; } else if((wasError && !isError) || showingLength) { labelEl.innerHTML = ''; - labelEl.append(i18n(label)); + labelEl.append(i18n(label, options.labelOptions)); showingLength = false; } }; @@ -242,8 +243,8 @@ class InputField { public setState(state: InputState, label?: LangPackKey) { if(label) { - this.label.innerHTML = ''; - this.label.append(i18n(label)); + this.label.textContent = ''; + this.label.append(i18n(label, this.options.labelOptions)); } this.input.classList.toggle('error', !!(state & InputState.Error)); diff --git a/src/components/popups/createPoll.ts b/src/components/popups/createPoll.ts index c3e79b81..411e113b 100644 --- a/src/components/popups/createPoll.ts +++ b/src/components/popups/createPoll.ts @@ -9,6 +9,7 @@ import Scrollable from "../scrollable"; import { toast } from "../toast"; import SendContextMenu from "../chat/sendContextMenu"; import { MessageEntity } from "../../layer"; +import I18n, { _i18n, i18n } from "../../lib/langPack"; const MAX_LENGTH_QUESTION = 255; const MAX_LENGTH_OPTION = 100; @@ -26,19 +27,26 @@ export default class PopupCreatePoll extends PopupElement { 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}); + super('popup-create-poll popup-new-media', null, {closable: true, withConfirm: 'NewPoll.Create', body: true}); - this.title.innerText = 'New Poll'; + _i18n(this.title, 'NewPoll'); this.questionInputField = new InputField({ - placeholder: 'Ask a Question', - label: 'Ask a Question', + placeholder: 'AskAQuestion', + label: 'AskAQuestion', name: 'question', maxLength: MAX_LENGTH_QUESTION }); + this.questionInputField.input.addEventListener('input', () => { + this.handleChange(); + }); + + this.optionInputFields = []; + if(this.chat.type !== 'scheduled') { const sendMenu = new SendContextMenu({ onSilentClick: () => { @@ -64,7 +72,7 @@ export default class PopupCreatePoll extends PopupElement { const hr = document.createElement('hr'); const d = document.createElement('div'); d.classList.add('caption'); - d.innerText = 'Options'; + _i18n(d, 'PollOptions'); this.questions = document.createElement('form'); this.questions.classList.add('poll-create-questions'); @@ -74,11 +82,11 @@ export default class PopupCreatePoll extends PopupElement { const settingsCaption = document.createElement('div'); settingsCaption.classList.add('caption'); - settingsCaption.innerText = 'Settings'; + _i18n(settingsCaption, 'Settings'); if(!this.chat.appPeersManager.isBroadcast(this.chat.peerId)) { this.anonymousCheckboxField = new CheckboxField({ - text: 'Anonymous Voting', + text: 'NewPoll.Anonymous', name: 'anonymous' }); this.anonymousCheckboxField.input.checked = true; @@ -86,11 +94,11 @@ export default class PopupCreatePoll extends PopupElement { } this.multipleCheckboxField = new CheckboxField({ - text: 'Multiple Answers', + text: 'NewPoll.MultipleChoice', name: 'multiple' }); this.quizCheckboxField = new CheckboxField({ - text: 'Quiz Mode', + text: 'NewPoll.Quiz', name: 'quiz' }); @@ -106,9 +114,15 @@ export default class PopupCreatePoll extends PopupElement { 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); @@ -117,7 +131,7 @@ export default class PopupCreatePoll extends PopupElement { const quizSolutionCaption = document.createElement('div'); quizSolutionCaption.classList.add('caption'); - quizSolutionCaption.innerText = 'Explanation'; + _i18n(quizSolutionCaption, 'AccDescrQuizExplanation'); const quizHr = document.createElement('hr'); @@ -125,15 +139,19 @@ export default class PopupCreatePoll extends PopupElement { quizSolutionContainer.classList.add('poll-create-questions'); this.quizSolutionField = new InputField({ - placeholder: 'Add a Comment (Optional)', - label: 'Add a Comment (Optional)', + placeholder: 'NewPoll.Explanation.Placeholder', + label: 'NewPoll.Explanation.Placeholder', name: 'solution', maxLength: MAX_LENGTH_SOLUTION }); + this.questionInputField.input.addEventListener('input', () => { + this.handleChange(); + }); + 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.'; + _i18n(quizSolutionSubtitle, 'AddAnExplanationInfo'); quizSolutionContainer.append(this.quizSolutionField.container, quizSolutionSubtitle); @@ -151,6 +169,8 @@ export default class PopupCreatePoll extends PopupElement { this.onEscape = () => { return !this.getFilledAnswers().length; }; + + this.handleChange(); } private getFilledAnswers() { @@ -166,44 +186,52 @@ export default class PopupCreatePoll extends PopupElement { this.send(); }; - public send(force = false) { + private validate() { const question = this.questionInputField.value; - if(!question) { - toast('Please enter a question.'); - return; + return false; } if(question.length > MAX_LENGTH_QUESTION) { - toast('Question is too long.'); - return; + return false; } if(this.quizCheckboxField.input.checked && !this.correctAnswers?.length) { - toast('Please choose the correct answer.'); - return; + return false; } const answers = this.getFilledAnswers(); - if(answers.length < 2) { - toast('Please enter at least two options.'); - return; + return false; } - + const tooLongOption = answers.find(a => a.length > MAX_LENGTH_OPTION); if(tooLongOption) { - toast('Option is too long.'); - return; + return false; } const quizSolutionEntities: MessageEntity[] = []; const quizSolution = getRichValue(this.quizSolutionField.input, quizSolutionEntities) || undefined; if(quizSolution?.length > MAX_LENGTH_SOLUTION) { - toast('Explanation is too long.'); - return; + 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 quizSolutionEntities: MessageEntity[] = []; + const quizSolution = getRichValue(this.quizSolutionField.input, quizSolutionEntities) || undefined; + if(this.chat.type === 'scheduled' && !force) { this.chat.input.scheduleSending(() => { this.send(true); @@ -280,24 +308,39 @@ export default class PopupCreatePoll extends PopupElement { if(isLast && !isEmpty && this.questions.childElementCount < 10) { this.appendMoreField(); } + + this.handleChange(); }; onDeleteClick = (e: MouseEvent) => { const target = e.target as HTMLSpanElement; - findUpTag(target, 'LABEL').remove(); + const label = findUpTag(target, 'LABEL'); + const idx = whichChild(label); - Array.from(this.questions.children).forEach((el, idx) => { - const label = el.querySelector('label') as HTMLLabelElement; - label.innerText = 'Option ' + (idx + 1); + 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: 'Add an Option', - label: 'Option ' + idx, + placeholder: 'NewPoll.OptionsAddOption', + label: 'NewPoll.OptionLabel', + labelOptions: [idx], name: 'question-' + tempId, maxLength: MAX_LENGTH_OPTION }); @@ -319,6 +362,7 @@ export default class PopupCreatePoll extends PopupElement { if(checked) { const idx = whichChild(radioField.label); this.correctAnswers = [new Uint8Array([idx])]; + this.handleChange(); } }); @@ -332,5 +376,7 @@ export default class PopupCreatePoll extends PopupElement { this.scrollable.scrollIntoViewNew(this.questions.lastElementChild as HTMLElement, 'center'); //this.scrollable.scrollTo(this.scrollable.scrollHeight, 'top', true, true); + + this.optionInputFields.push(questionField); } -} \ No newline at end of file +} diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 7f61b255..d646ccc4 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -181,22 +181,28 @@ export class AppSidebarLeft extends SidebarSlider { const searchSuper = this.searchSuper = new AppSearchSuper([{ inputFilter: 'inputMessagesFilterEmpty', - name: 'Chats' + name: 'FilterChats', + type: 'chats' }, { inputFilter: 'inputMessagesFilterPhotoVideo', - name: 'Media' + name: 'SharedMediaTab2', + type: 'media' }, { inputFilter: 'inputMessagesFilterUrl', - name: 'Links' + name: 'SharedLinksTab2', + type: 'links' }, { inputFilter: 'inputMessagesFilterDocument', - name: 'Files' + name: 'SharedFilesTab2', + type: 'files' }, { inputFilter: 'inputMessagesFilterMusic', - name: 'Music' + name: 'SharedMusicTab2', + type: 'music' }, { inputFilter: 'inputMessagesFilterVoice', - name: 'Voice' + name: 'SharedVoiceTab2', + type: 'voice' }], scrollable, this.searchGroups, true); searchContainer.prepend(searchSuper.nav.parentElement.parentElement); diff --git a/src/components/sidebarLeft/tabs/chatFolders.ts b/src/components/sidebarLeft/tabs/chatFolders.ts index d6c5ea2e..95f5aceb 100644 --- a/src/components/sidebarLeft/tabs/chatFolders.ts +++ b/src/components/sidebarLeft/tabs/chatFolders.ts @@ -49,9 +49,6 @@ export default class AppChatFoldersTab extends SliderSuperTab { else if(pFlags.groups) k = 'FilterAllGroups'; else if(pFlags.broadcasts) k = 'FilterAllChannels'; else if(pFlags.bots) k = 'FilterAllBots'; - else if(pFlags.exclude_muted) k = 'FilterAllUnmuted'; - else if(pFlags.exclude_read) k = 'FilterAllUnread'; - else if(pFlags.exclude_archived) k = 'FilterAllUnarchived'; d.push(i18n(k)); } else { const folder = appMessagesManager.dialogsStorage.getFolder(filter.id); diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index 96bedd51..54cd3552 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -194,16 +194,20 @@ export default class AppSharedMediaTab implements SliderTab { this.searchSuper = new AppSearchSuper([{ inputFilter: 'inputMessagesFilterPhotoVideo', - name: 'Media' + name: 'SharedMediaTab2', + type: 'media' }, { inputFilter: 'inputMessagesFilterDocument', - name: 'Files' + name: 'SharedFilesTab2', + type: 'files' }, { inputFilter: 'inputMessagesFilterUrl', - name: 'Links' + name: 'SharedLinksTab2', + type: 'links' }, { inputFilter: 'inputMessagesFilterMusic', - name: 'Music' + name: 'SharedMusicTab2', + type: 'music' }], this.scroll/* , undefined, undefined, false */); this.profileContentEl.append(this.searchSuper.container); diff --git a/src/lang.ts b/src/lang.ts index 46469f9e..6e4e4b80 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -10,9 +10,6 @@ const lang = { "FilterAllNonContacts": "All Non-Contacts", "FilterAllChannels": "All Channels", "FilterAllBots": "All Bots", - "FilterAllUnmuted": "All Unmuted", - "FilterAllUnread": "All Unread", - "FilterAllUnarchived": "All Unarchived", "WordDelimiter": ", ", "WordDelimiterLast": " and ", "EditProfile.FirstNameLabel": "Name", @@ -48,6 +45,8 @@ const lang = { "Notifications.Sound": "Notification Sound", "Notifications.MessagePreview": "Message preview", "NewPrivateChat": "New Private Chat", + "NewPoll.OptionLabel": "Option %d", + "NewPoll.Create": "CREATE", "Message.Context.Selection.Copy": "Copy selected", "Message.Context.Selection.Clear": "Clear selection", "Message.Context.Selection.Delete": "Delete selected", @@ -222,6 +221,19 @@ const lang = { "Caption": "Caption", "Message": "Message", "Poll": "Poll", + "SharedFilesTab2": "Files", + "SharedMediaTab2": "Media", + //"SharedMediaTabFull2": "Shared Media", + //"SharedGroupsTab2": "Groups", + "SharedLinksTab2": "Links", + "SharedMusicTab2": "Music", + "SharedVoiceTab2": "Voice", + //"SharedGIFsTab2": "GIFs", + "NewPoll": "New Poll", + "PollOptions": "Poll options", + "AskAQuestion": "Ask a Question", + "AddAnExplanationInfo": "Users will see this text after choosing the wrong answer, good for educational purposes.", + "AccDescrQuizExplanation": "Explanation", // * macos "AccountSettings.Filters": "Chat Folders", @@ -332,6 +344,11 @@ const lang = { "Message.Context.Select": "Select", "Message.Context.Pin": "Pin", "Message.Context.Unpin": "Unpin", + "NewPoll.Anonymous": "Anonymous Voting", + "NewPoll.Explanation.Placeholder": "Add a Comment (Optional)", + "NewPoll.OptionsAddOption": "Add an Option", + "NewPoll.MultipleChoice": "Multiple Answers", + "NewPoll.Quiz": "Quiz Mode", }; export default lang; diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index a0d32cf5..f8d8cf1c 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -2665,11 +2665,10 @@ export class AppMessagesManager { // this.log('message action:', action); if((action as MessageAction.messageActionCustomAction).message) { - const richText = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true}); if(plain) { - return richText; + return RichTextProcessor.wrapPlainText(message.message); } else { - element.innerHTML = richText; + element.innerHTML = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true}); return element; } } else { @@ -2723,7 +2722,7 @@ export class AppMessagesManager { args.push(getNameDivHTML(message.fromId, plain)); } - args.push(plain ? action.title : RichTextProcessor.wrapEmojiText(action.title)); + args.push(plain ? action.title : htmlToDocumentFragment(RichTextProcessor.wrapEmojiText(action.title))); break; } diff --git a/src/scss/partials/_audio.scss b/src/scss/partials/_audio.scss index adc7062c..b9a2a25b 100644 --- a/src/scss/partials/_audio.scss +++ b/src/scss/partials/_audio.scss @@ -452,6 +452,10 @@ &__filled { background-color: #0089ff; + + &:not(.progress-line__loaded) { + z-index: 1; + } } &__loaded {