Browse Source

Temp language commit

master
Eduard Kuzmenko 3 years ago
parent
commit
18734bc5aa
  1. 2
      src/components/appNavigationController.ts
  2. 19
      src/components/chat/input.ts
  3. 14
      src/components/chat/markupTooltip.ts
  4. 15
      src/components/chat/selection.ts
  5. 8
      src/components/checkboxField.ts
  6. 5
      src/components/radioField.ts
  7. 13
      src/components/row.ts
  8. 7
      src/components/sidebarLeft/index.ts
  9. 8
      src/components/sidebarLeft/tabs/background.ts
  10. 55
      src/components/sidebarLeft/tabs/chatFolders.ts
  11. 17
      src/components/sidebarLeft/tabs/editFolder.ts
  12. 16
      src/components/sidebarLeft/tabs/editProfile.ts
  13. 45
      src/components/sidebarLeft/tabs/generalSettings.ts
  14. 12
      src/components/sidebarLeft/tabs/includedChats.ts
  15. 26
      src/components/sidebarLeft/tabs/notifications.ts
  16. 52
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  17. 140
      src/lang.ts
  18. 20
      src/lib/appManagers/appImManager.ts
  19. 96
      src/lib/langPack.ts

2
src/components/appNavigationController.ts

@ -5,7 +5,7 @@ import { logger } from "../lib/logger";
import { doubleRaf } from "../helpers/schedulers"; import { doubleRaf } from "../helpers/schedulers";
export type NavigationItem = { export type NavigationItem = {
type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | 'esg', type: 'left' | 'right' | 'im' | 'chat' | 'popup' | 'media' | 'menu' | 'esg' | 'multiselect' | 'input-helper' | 'markup',
onPop: (canAnimate: boolean) => boolean | void, onPop: (canAnimate: boolean) => boolean | void,
onEscape?: () => boolean, onEscape?: () => boolean,
noHistory?: boolean, noHistory?: boolean,

19
src/components/chat/input.ts

@ -36,6 +36,8 @@ import rootScope from '../../lib/rootScope';
import PopupPinMessage from '../popups/unpinMessage'; import PopupPinMessage from '../popups/unpinMessage';
import { debounce } from '../../helpers/schedulers'; import { debounce } from '../../helpers/schedulers';
import { tsNow } from '../../helpers/date'; import { tsNow } from '../../helpers/date';
import appNavigationController from '../appNavigationController';
import { isMobile } from '../../helpers/userAgent';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
@ -1159,8 +1161,10 @@ export default class ChatInput {
} }
}; };
private onHelperCancel = (e: Event) => { private onHelperCancel = (e?: Event) => {
if(e) {
cancelEvent(e); cancelEvent(e);
}
if(this.willSendWebPage) { if(this.willSendWebPage) {
const lastUrl = this.lastUrl; const lastUrl = this.lastUrl;
@ -1458,8 +1462,12 @@ export default class ChatInput {
this.forwardingFromPeerId = 0; this.forwardingFromPeerId = 0;
this.editMsgId = undefined; this.editMsgId = undefined;
this.helperType = this.helperFunc = undefined; this.helperType = this.helperFunc = undefined;
if(this.chat.container.classList.contains('is-helper-active')) {
appNavigationController.removeByType('input-helper');
this.chat.container.classList.remove('is-helper-active'); this.chat.container.classList.remove('is-helper-active');
} }
}
public setInputValue(value: string, clear = true, focus = true) { public setInputValue(value: string, clear = true, focus = true) {
clear && this.clearInput(); clear && this.clearInput();
@ -1488,6 +1496,15 @@ export default class ChatInput {
scroll.scrollTo(scroll.scrollHeight, 'top', true, true, 200); scroll.scrollTo(scroll.scrollHeight, 'top', true, true, 200);
} */ } */
if(!isMobile) {
appNavigationController.pushItem({
type: 'input-helper',
onPop: () => {
this.onHelperCancel();
}
});
}
if(input !== undefined) { if(input !== undefined) {
this.setInputValue(input); this.setInputValue(input);
} }

14
src/components/chat/markupTooltip.ts

@ -4,7 +4,8 @@ import RichTextProcessor from "../../lib/richtextprocessor";
import ButtonIcon from "../buttonIcon"; import ButtonIcon from "../buttonIcon";
import { clamp } from "../../helpers/number"; import { clamp } from "../../helpers/number";
import { isTouchSupported } from "../../helpers/touchSupport"; import { isTouchSupported } from "../../helpers/touchSupport";
import { isApple } from "../../helpers/userAgent"; import { isApple, isMobile } from "../../helpers/userAgent";
import appNavigationController from "../appNavigationController";
//import { logger } from "../../lib/logger"; //import { logger } from "../../lib/logger";
export default class MarkupTooltip { export default class MarkupTooltip {
@ -185,6 +186,8 @@ export default class MarkupTooltip {
document.removeEventListener('mouseup', this.onMouseUpSingle); document.removeEventListener('mouseup', this.onMouseUpSingle);
this.waitingForMouseUp = false; this.waitingForMouseUp = false;
appNavigationController.removeByType('markup');
if(this.hideTimeout) clearTimeout(this.hideTimeout); if(this.hideTimeout) clearTimeout(this.hideTimeout);
this.hideTimeout = window.setTimeout(() => { this.hideTimeout = window.setTimeout(() => {
this.hideTimeout = undefined; this.hideTimeout = undefined;
@ -295,6 +298,15 @@ export default class MarkupTooltip {
this.container.classList.add('is-visible'); this.container.classList.add('is-visible');
if(!isMobile) {
appNavigationController.pushItem({
type: 'markup',
onPop: () => {
this.hide();
}
});
}
//this.log('selection', selectionRect, activeButton); //this.log('selection', selectionRect, activeButton);
} }

15
src/components/chat/selection.ts

@ -13,6 +13,8 @@ import { toast } from "../toast";
import SetTransition from "../singleTransition"; import SetTransition from "../singleTransition";
import ListenerSetter from "../../helpers/listenerSetter"; import ListenerSetter from "../../helpers/listenerSetter";
import PopupSendNow from "../popups/sendNow"; import PopupSendNow from "../popups/sendNow";
import appNavigationController from "../appNavigationController";
import { isMobileSafari } from "../../helpers/userAgent";
const MAX_SELECTION_LENGTH = 100; const MAX_SELECTION_LENGTH = 100;
//const MIN_CLICK_MOVE = 32; // minimum bubble height //const MIN_CLICK_MOVE = 32; // minimum bubble height
@ -315,6 +317,19 @@ export default class ChatSelection {
}); });
}); });
if(!isMobileSafari) {
if(forwards) {
appNavigationController.pushItem({
type: 'multiselect',
onPop: () => {
this.cancelSelection();
}
});
} else {
appNavigationController.removeByType('multiselect');
}
}
//const chatInput = this.appImManager.chatInput; //const chatInput = this.appImManager.chatInput;
if(this.isSelecting) { if(this.isSelecting) {

8
src/components/checkboxField.ts

@ -1,6 +1,7 @@
import appStateManager from "../lib/appManagers/appStateManager"; import appStateManager from "../lib/appManagers/appStateManager";
import { getDeepProperty } from "../helpers/object"; import { getDeepProperty } from "../helpers/object";
import { ripple } from "./ripple"; import { ripple } from "./ripple";
import { LangPackKey, _i18n } from "../lib/langPack";
export default class CheckboxField { export default class CheckboxField {
public input: HTMLInputElement; public input: HTMLInputElement;
@ -8,7 +9,7 @@ export default class CheckboxField {
public span: HTMLSpanElement; public span: HTMLSpanElement;
constructor(options: { constructor(options: {
text?: string, text?: LangPackKey,
name?: string, name?: string,
round?: boolean, round?: boolean,
stateKey?: string, stateKey?: string,
@ -56,10 +57,7 @@ export default class CheckboxField {
if(options.text) { if(options.text) {
span = this.span = document.createElement('span'); span = this.span = document.createElement('span');
span.classList.add('checkbox-caption'); span.classList.add('checkbox-caption');
_i18n(span, options.text);
if(options.text) {
span.innerText = options.text;
}
} else { } else {
label.classList.add('checkbox-without-caption'); label.classList.add('checkbox-without-caption');
} }

5
src/components/radioField.ts

@ -1,5 +1,6 @@
import appStateManager from "../lib/appManagers/appStateManager"; import appStateManager from "../lib/appManagers/appStateManager";
import { getDeepProperty } from "../helpers/object"; import { getDeepProperty } from "../helpers/object";
import { LangPackKey, _i18n } from "../lib/langPack";
export default class RadioField { export default class RadioField {
public input: HTMLInputElement; public input: HTMLInputElement;
@ -7,7 +8,7 @@ export default class RadioField {
public main: HTMLElement; public main: HTMLElement;
constructor(options: { constructor(options: {
text?: string, text?: LangPackKey,
name: string, name: string,
value?: string, value?: string,
stateKey?: string stateKey?: string
@ -37,7 +38,7 @@ export default class RadioField {
main.classList.add('radio-field-main'); main.classList.add('radio-field-main');
if(options.text) { if(options.text) {
main.innerHTML = options.text; _i18n(main, options.text);
/* const caption = document.createElement('div'); /* const caption = document.createElement('div');
caption.classList.add('radio-field-main-caption'); caption.classList.add('radio-field-main-caption');
caption.innerHTML = text; caption.innerHTML = text;

13
src/components/row.ts

@ -3,6 +3,7 @@ import RadioField from "./radioField";
import { ripple } from "./ripple"; import { ripple } from "./ripple";
import { SliderSuperTab } from "./slider"; import { SliderSuperTab } from "./slider";
import RadioForm from "./radioForm"; import RadioForm from "./radioForm";
import { LangPackKey, _i18n } from "../lib/langPack";
export default class Row { export default class Row {
public container: HTMLElement; public container: HTMLElement;
@ -17,10 +18,12 @@ export default class Row {
constructor(options: Partial<{ constructor(options: Partial<{
icon: string, icon: string,
subtitle: string, subtitle: string,
subtitleLangKey: LangPackKey
radioField: Row['radioField'], radioField: Row['radioField'],
checkboxField: Row['checkboxField'], checkboxField: Row['checkboxField'],
noCheckboxSubtitle: boolean, noCheckboxSubtitle: boolean,
title: string, title: string,
titleLangKey: LangPackKey,
titleRight: string, titleRight: string,
clickable: boolean | ((e: Event) => void), clickable: boolean | ((e: Event) => void),
navigationTab: SliderSuperTab navigationTab: SliderSuperTab
@ -32,6 +35,8 @@ export default class Row {
this.subtitle.classList.add('row-subtitle'); this.subtitle.classList.add('row-subtitle');
if(options.subtitle) { if(options.subtitle) {
this.subtitle.innerHTML = options.subtitle; this.subtitle.innerHTML = options.subtitle;
} else if(options.subtitleLangKey) {
_i18n(this.subtitle, options.subtitleLangKey);
} }
let havePadding = false; let havePadding = false;
@ -48,7 +53,7 @@ export default class Row {
if(!options.noCheckboxSubtitle) { if(!options.noCheckboxSubtitle) {
this.checkboxField.input.addEventListener('change', () => { this.checkboxField.input.addEventListener('change', () => {
this.subtitle.innerHTML = this.checkboxField.input.checked ? 'Enabled' : 'Disabled'; _i18n(this.subtitle, this.checkboxField.input.checked ? 'Checkbox.Enabled' : 'Checkbox.Disabled');
}); });
} }
} }
@ -56,7 +61,7 @@ export default class Row {
const i = options.radioField || options.checkboxField; const i = options.radioField || options.checkboxField;
i.label.classList.add('disable-hover'); i.label.classList.add('disable-hover');
} else { } else {
if(options.title) { if(options.title || options.titleLangKey) {
let c: HTMLElement; let c: HTMLElement;
if(options.titleRight) { if(options.titleRight) {
c = document.createElement('div'); c = document.createElement('div');
@ -68,7 +73,11 @@ export default class Row {
this.title = document.createElement('div'); this.title = document.createElement('div');
this.title.classList.add('row-title'); this.title.classList.add('row-title');
if(options.title) {
this.title.innerHTML = options.title; this.title.innerHTML = options.title;
} else {
_i18n(this.title, options.titleLangKey);
}
c.append(this.title); c.append(this.title);
if(options.titleRight) { if(options.titleRight) {

7
src/components/sidebarLeft/index.ts

@ -24,7 +24,6 @@ import AppContactsTab from "./tabs/contacts";
import AppArchivedTab from "./tabs/archivedTab"; import AppArchivedTab from "./tabs/archivedTab";
import AppAddMembersTab from "./tabs/addMembers"; import AppAddMembersTab from "./tabs/addMembers";
import { i18n_, LangPackKey } from "../../lib/langPack"; import { i18n_, LangPackKey } from "../../lib/langPack";
import ButtonMenuToggle from "../buttonMenuToggle";
import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu"; import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -84,7 +83,7 @@ export class AppSidebarLeft extends SidebarSlider {
const btnArchive: ButtonMenuItemOptions = { const btnArchive: ButtonMenuItemOptions = {
icon: 'archive', icon: 'archive',
text: 'Archived', text: 'ChatList.Menu.Archived',
onClick: () => { onClick: () => {
new AppArchivedTab(this).open(); new AppArchivedTab(this).open();
} }
@ -92,7 +91,7 @@ export class AppSidebarLeft extends SidebarSlider {
const btnMenu = ButtonMenu([{ const btnMenu = ButtonMenu([{
icon: 'newgroup', icon: 'newgroup',
text: 'New Group', text: 'NewGroup',
onClick: onNewGroupClick onClick: onNewGroupClick
}, { }, {
icon: 'user', icon: 'user',
@ -114,7 +113,7 @@ export class AppSidebarLeft extends SidebarSlider {
} }
}, { }, {
icon: 'help btn-disabled', icon: 'help btn-disabled',
text: 'Help', text: 'SettingsHelp',
onClick: () => { onClick: () => {
} }

8
src/components/sidebarLeft/tabs/background.ts

@ -20,16 +20,16 @@ import { wrapPhoto } from "../../wrappers";
export default class AppBackgroundTab extends SliderSuperTab { export default class AppBackgroundTab extends SliderSuperTab {
init() { init() {
this.container.classList.add('background-container'); this.container.classList.add('background-container');
this.title.innerText = 'Chat Background'; this.setTitle('ChatBackground');
{ {
const container = generateSection(this.scrollable); const container = generateSection(this.scrollable);
const uploadButton = Button('btn-primary btn-transparent', {icon: 'cameraadd', text: 'Upload Wallpaper', disabled: true}); const uploadButton = Button('btn-primary btn-transparent', {icon: 'cameraadd', text: 'ChatBackground.UploadWallpaper', disabled: true});
const colorButton = Button('btn-primary btn-transparent', {icon: 'colorize', text: 'Set a Color', disabled: true}); const colorButton = Button('btn-primary btn-transparent', {icon: 'colorize', text: 'ChatBackground.SetColor', disabled: true});
const blurCheckboxField = new CheckboxField({ const blurCheckboxField = new CheckboxField({
text: 'Blur Wallpaper Image', text: 'ChatBackground.Blur',
name: 'blur', name: 'blur',
stateKey: 'settings.background.blur', stateKey: 'settings.background.blur',
withRipple: true withRipple: true

55
src/components/sidebarLeft/tabs/chatFolders.ts

@ -2,7 +2,6 @@ import { SliderSuperTab } from "../../slider";
import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { attachClickEvent, cancelEvent, positionElementByIndex } from "../../../helpers/dom"; import { attachClickEvent, cancelEvent, positionElementByIndex } from "../../../helpers/dom";
import { ripple } from "../../ripple";
import { toast } from "../../toast"; import { toast } from "../../toast";
import type { MyDialogFilter } from "../../../lib/storages/filters"; import type { MyDialogFilter } from "../../../lib/storages/filters";
import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; import type { DialogFilterSuggested, DialogFilter } from "../../../layer";
@ -16,7 +15,7 @@ import rootScope from "../../../lib/rootScope";
import AppEditFolderTab from "./editFolder"; import AppEditFolderTab from "./editFolder";
import Row from "../../row"; import Row from "../../row";
import { SettingSection } from ".."; import { SettingSection } from "..";
import { i18n_ } from "../../../lib/langPack"; import { i18n, i18n_, LangPackKey } from "../../../lib/langPack";
export default class AppChatFoldersTab extends SliderSuperTab { export default class AppChatFoldersTab extends SliderSuperTab {
private createFolderBtn: HTMLElement; private createFolderBtn: HTMLElement;
@ -30,13 +29,12 @@ export default class AppChatFoldersTab extends SliderSuperTab {
private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div?: HTMLElement) { private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div?: HTMLElement) {
let filter: DialogFilter | MyDialogFilter; let filter: DialogFilter | MyDialogFilter;
let description = ''; let description = '';
let d: string[] = []; let d: HTMLElement[] = [];
if(dialogFilter._ === 'dialogFilterSuggested') { if(dialogFilter._ === 'dialogFilterSuggested') {
filter = dialogFilter.filter; filter = dialogFilter.filter;
description = dialogFilter.description; description = dialogFilter.description;
} else { } else {
filter = dialogFilter; filter = dialogFilter;
description = '';
let enabledFilters = Object.keys(filter.pFlags).length; let enabledFilters = Object.keys(filter.pFlags).length;
/* (['include_peers', 'exclude_peers'] as ['include_peers', 'exclude_peers']).forEach(key => { /* (['include_peers', 'exclude_peers'] as ['include_peers', 'exclude_peers']).forEach(key => {
@ -44,18 +42,17 @@ export default class AppChatFoldersTab extends SliderSuperTab {
}); */ }); */
if(enabledFilters === 1) { if(enabledFilters === 1) {
description = 'All ';
const pFlags = filter.pFlags; const pFlags = filter.pFlags;
if(pFlags.contacts) description += 'Contacts'; let k: LangPackKey;
else if(pFlags.non_contacts) description += 'Non-Contacts'; if(pFlags.contacts) k = 'FilterAllContacts';
else if(pFlags.groups) description += 'Groups'; else if(pFlags.non_contacts) k = 'FilterAllNonContacts';
else if(pFlags.broadcasts) description += 'Channels'; else if(pFlags.groups) k = 'FilterAllGroups';
else if(pFlags.bots) description += 'Bots'; else if(pFlags.broadcasts) k = 'FilterAllChannels';
else if(pFlags.exclude_muted) description += 'Unmuted'; else if(pFlags.bots) k = 'FilterAllBots';
else if(pFlags.exclude_read) description += 'Unread'; else if(pFlags.exclude_muted) k = 'FilterAllUnmuted';
else if(pFlags.exclude_archived) description += 'Unarchived'; else if(pFlags.exclude_read) k = 'FilterAllUnread';
d.push(description); else if(pFlags.exclude_archived) k = 'FilterAllUnarchived';
d.push(i18n(k));
} else { } else {
const folder = appMessagesManager.dialogsStorage.getFolder(filter.id); const folder = appMessagesManager.dialogsStorage.getFolder(filter.id);
let chats = 0, channels = 0, groups = 0; let chats = 0, channels = 0, groups = 0;
@ -65,19 +62,33 @@ export default class AppChatFoldersTab extends SliderSuperTab {
else chats++; else chats++;
} }
if(chats) d.push(chats + ' chats'); if(chats) d.push(i18n('Chats', [chats]));
if(channels) d.push(channels + ' channels'); if(channels) d.push(i18n('Channels', [channels]));
if(groups) d.push(groups + ' groups'); if(groups) d.push(i18n('Groups', [groups]));
} }
} }
if(!div) { if(!div) {
const row = new Row({ const row = new Row({
title: RichTextProcessor.wrapEmojiText(filter.title), title: RichTextProcessor.wrapEmojiText(filter.title),
subtitle: d.length ? d.join(', ') : description, subtitle: description,
clickable: true clickable: true
}); });
if(d.length) {
let arr: HTMLElement[] = d.slice(0, 1);
for(let i = 1; i < d.length; ++i) {
const isLast = (d.length - 1) === i;
const delimiterKey: LangPackKey = isLast ? 'WordDelimiterLast' : 'WordDelimiter';
arr.push(i18n(delimiterKey));
arr.push(d[i]);
}
arr.forEach(el => {
row.subtitle.append(el);
});
}
div = row.container; div = row.container;
if(dialogFilter._ === 'dialogFilter') { if(dialogFilter._ === 'dialogFilter') {
@ -119,12 +130,12 @@ export default class AppChatFoldersTab extends SliderSuperTab {
}); });
this.foldersSection = new SettingSection({ this.foldersSection = new SettingSection({
name: 'ChatList.Filter.List.Header' name: 'Filters'
}); });
this.foldersSection.container.style.display = 'none'; this.foldersSection.container.style.display = 'none';
this.suggestedSection = new SettingSection({ this.suggestedSection = new SettingSection({
name: 'ChatList.Filter.Recommended.Header' name: 'FilterRecommended'
}); });
this.suggestedSection.container.style.display = 'none'; this.suggestedSection.container.style.display = 'none';
@ -208,7 +219,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
suggestedFilters.forEach(filter => { suggestedFilters.forEach(filter => {
const div = this.renderFolder(filter); const div = this.renderFolder(filter);
const button = Button('btn-primary btn-color-primary', {text: 'ChatList.Filter.Recommended.Add'}); const button = Button('btn-primary btn-color-primary', {text: 'Add'});
div.append(button); div.append(button);
this.suggestedSection.content.append(div); this.suggestedSection.content.append(div);

17
src/components/sidebarLeft/tabs/editFolder.ts

@ -39,15 +39,14 @@ export default class AppEditFolderTab extends SliderSuperTab {
this.container.classList.add('edit-folder-container'); this.container.classList.add('edit-folder-container');
this.caption = document.createElement('div'); this.caption = document.createElement('div');
this.caption.classList.add('caption'); this.caption.classList.add('caption');
this.caption.append(i18n(`Choose chats and types of chats that will this.caption.append(i18n('FilterIncludeExcludeInfo'));
appear and never appear in this folder.`));
this.stickerContainer = document.createElement('div'); this.stickerContainer = document.createElement('div');
this.stickerContainer.classList.add('sticker-container'); this.stickerContainer.classList.add('sticker-container');
this.confirmBtn = ButtonIcon('check btn-confirm hide blue'); this.confirmBtn = ButtonIcon('check btn-confirm hide blue');
const deleteFolderButton: ButtonMenuItemOptions = { const deleteFolderButton: ButtonMenuItemOptions = {
icon: 'delete danger', icon: 'delete danger',
text: 'Delete Folder', text: 'FilterMenuDelete',
onClick: () => { onClick: () => {
deleteFolderButton.element.setAttribute('disabled', 'true'); deleteFolderButton.element.setAttribute('disabled', 'true');
appMessagesManager.filtersStorage.updateDialogFilter(this.filter, true).then(bool => { appMessagesManager.filtersStorage.updateDialogFilter(this.filter, true).then(bool => {
@ -68,13 +67,13 @@ appear and never appear in this folder.`));
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.nameInputField = new InputField({ this.nameInputField = new InputField({
label: 'Folder Name', label: 'FilterNameInputLabel',
maxLength: MAX_FOLDER_NAME_LENGTH maxLength: MAX_FOLDER_NAME_LENGTH
}); });
inputWrapper.append(this.nameInputField.container); inputWrapper.append(this.nameInputField.container);
const generateList = (className: string, h2Text: LangPackKey, buttons: {icon: string, name?: string, withRipple?: true, text: string}[], to: any) => { const generateList = (className: string, h2Text: LangPackKey, buttons: {icon: string, name?: string, withRipple?: true, text: LangPackKey}[], to: any) => {
const container = document.createElement('div'); const container = document.createElement('div');
container.classList.add('folder-list', className); container.classList.add('folder-list', className);
@ -104,7 +103,7 @@ appear and never appear in this folder.`));
return container; return container;
}; };
this.include_peers = generateList('folder-list-included', 'ChatList.Filter.Include.Header', [{ this.include_peers = generateList('folder-list-included', 'FilterInclude', [{
icon: 'add primary', icon: 'add primary',
text: 'ChatList.Filter.Include.AddChat', text: 'ChatList.Filter.Include.AddChat',
withRipple: true withRipple: true
@ -130,7 +129,7 @@ appear and never appear in this folder.`));
name: 'bots' name: 'bots'
}], this.flags); }], this.flags);
this.exclude_peers = generateList('folder-list-excluded', 'ChatList.Filter.Exclude.Header', [{ this.exclude_peers = generateList('folder-list-excluded', 'FilterExclude', [{
icon: 'minus primary', icon: 'minus primary',
text: 'ChatList.Filter.Exclude.AddChat', text: 'ChatList.Filter.Exclude.AddChat',
withRipple: true withRipple: true
@ -227,7 +226,7 @@ appear and never appear in this folder.`));
private onCreateOpen() { private onCreateOpen() {
this.caption.style.display = ''; this.caption.style.display = '';
this.setTitle('New Folder'); this.setTitle('FilterNew');
this.menuBtn.classList.add('hide'); this.menuBtn.classList.add('hide');
this.confirmBtn.classList.remove('hide'); this.confirmBtn.classList.remove('hide');
this.nameInputField.value = ''; this.nameInputField.value = '';
@ -240,7 +239,7 @@ appear and never appear in this folder.`));
private onEditOpen() { private onEditOpen() {
this.caption.style.display = 'none'; this.caption.style.display = 'none';
this.setTitle(this.type === 'create' ? 'New Folder' : 'Edit Folder'); this.setTitle(this.type === 'create' ? 'FilterNew' : 'FilterHeaderEdit');
if(this.type === 'edit') { if(this.type === 'edit') {
this.menuBtn.classList.remove('hide'); this.menuBtn.classList.remove('hide');

16
src/components/sidebarLeft/tabs/editProfile.ts

@ -31,7 +31,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.firstNameInputField = new InputField({ this.firstNameInputField = new InputField({
label: 'Login.Register.FirstName.Placeholder', label: 'EditProfile.FirstNameLabel',
name: 'first-name', name: 'first-name',
maxLength: 70 maxLength: 70
}); });
@ -41,7 +41,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
maxLength: 64 maxLength: 64
}); });
this.bioInputField = new InputField({ this.bioInputField = new InputField({
label: 'AccountSettings.Bio', label: 'EditProfile.BioLabel',
name: 'bio', name: 'bio',
maxLength: 70 maxLength: 70
}); });
@ -76,7 +76,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
this.usernameInputField = new UsernameInputField({ this.usernameInputField = new UsernameInputField({
peerId: 0, peerId: 0,
label: 'EditAccount.Username', label: 'EditProfile.Username.Label',
name: 'username', name: 'username',
plainText: true, plainText: true,
listenerSetter: this.listenerSetter, listenerSetter: this.listenerSetter,
@ -84,21 +84,21 @@ export default class AppEditProfileTab extends SliderSuperTab {
this.editPeer.handleChange(); this.editPeer.handleChange();
this.setProfileUrl(); this.setProfileUrl();
}, },
availableText: 'Username is available', availableText: 'EditProfile.Username.Available',
takenText: 'Username is already taken', takenText: 'EditProfile.Username.Taken',
invalidText: 'Username is invalid' invalidText: 'EditProfile.Username.Invalid'
}); });
inputWrapper.append(this.usernameInputField.container); inputWrapper.append(this.usernameInputField.container);
const caption = document.createElement('div'); const caption = document.createElement('div');
caption.classList.add('caption'); caption.classList.add('caption');
caption.append(i18n('UsernameSettings.ChangeDescription')); caption.append(i18n('EditProfile.Username.Help'));
caption.append(document.createElement('br'), document.createElement('br')); caption.append(document.createElement('br'), document.createElement('br'));
const profileUrlContainer = this.profileUrlContainer = document.createElement('div'); const profileUrlContainer = this.profileUrlContainer = document.createElement('div');
profileUrlContainer.classList.add('profile-url-container'); profileUrlContainer.classList.add('profile-url-container');
profileUrlContainer.append(i18n('This link opens a chat with you:')); profileUrlContainer.append(i18n('UsernameHelpLink', ['']));
const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a'); const profileUrlAnchor = this.profileUrlAnchor = document.createElement('a');
profileUrlAnchor.classList.add('profile-url'); profileUrlAnchor.classList.add('profile-url');

45
src/components/sidebarLeft/tabs/generalSettings.ts

@ -10,6 +10,7 @@ import { isApple } from "../../../helpers/userAgent";
import Row from "../../row"; import Row from "../../row";
import { attachClickEvent } from "../../../helpers/dom"; import { attachClickEvent } from "../../../helpers/dom";
import AppBackgroundTab from "./background"; import AppBackgroundTab from "./background";
import { LangPackKey, _i18n } from "../../../lib/langPack";
export class RangeSettingSelector { export class RangeSettingSelector {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -17,7 +18,7 @@ export class RangeSettingSelector {
public onChange: (value: number) => void; public onChange: (value: number) => void;
constructor(name: string, step: number, initialValue: number, minValue: number, maxValue: number) { constructor(name: LangPackKey, step: number, initialValue: number, minValue: number, maxValue: number) {
const BASE_CLASS = 'range-setting-selector'; const BASE_CLASS = 'range-setting-selector';
this.container = document.createElement('div'); this.container = document.createElement('div');
this.container.classList.add(BASE_CLASS); this.container.classList.add(BASE_CLASS);
@ -27,7 +28,7 @@ export class RangeSettingSelector {
const nameDiv = document.createElement('div'); const nameDiv = document.createElement('div');
nameDiv.classList.add(BASE_CLASS + '-name'); nameDiv.classList.add(BASE_CLASS + '-name');
nameDiv.innerHTML = name; _i18n(nameDiv, name);
const valueDiv = document.createElement('div'); const valueDiv = document.createElement('div');
valueDiv.classList.add(BASE_CLASS + '-value'); valueDiv.classList.add(BASE_CLASS + '-value');
@ -55,26 +56,26 @@ export class RangeSettingSelector {
export default class AppGeneralSettingsTab extends SliderSuperTab { export default class AppGeneralSettingsTab extends SliderSuperTab {
init() { init() {
this.container.classList.add('general-settings-container'); this.container.classList.add('general-settings-container');
this.title.innerText = 'General'; this.setTitle('General');
const section = generateSection.bind(null, this.scrollable); const section = generateSection.bind(null, this.scrollable);
{ {
const container = section('Settings'); const container = section('Settings');
const range = new RangeSettingSelector('Message Text Size', 1, rootScope.settings.messagesTextSize, 12, 20); const range = new RangeSettingSelector('TextSize', 1, rootScope.settings.messagesTextSize, 12, 20);
range.onChange = (value) => { range.onChange = (value) => {
appStateManager.setByKey('settings.messagesTextSize', value); appStateManager.setByKey('settings.messagesTextSize', value);
}; };
const chatBackgroundButton = Button('btn-primary btn-transparent', {icon: 'photo', text: 'Chat Background'}); const chatBackgroundButton = Button('btn-primary btn-transparent', {icon: 'photo', text: 'ChatBackground'});
attachClickEvent(chatBackgroundButton, () => { attachClickEvent(chatBackgroundButton, () => {
new AppBackgroundTab(this.slider).open(); new AppBackgroundTab(this.slider).open();
}); });
const animationsCheckboxField = new CheckboxField({ const animationsCheckboxField = new CheckboxField({
text: 'Enable Animations', text: 'EnableAnimations',
name: 'animations', name: 'animations',
stateKey: 'settings.animationsEnabled', stateKey: 'settings.animationsEnabled',
withRipple: true withRipple: true
@ -84,58 +85,58 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
} }
{ {
const container = section('Keyboard'); const container = section('General.Keyboard');
const form = document.createElement('form'); const form = document.createElement('form');
const enterRow = new Row({ const enterRow = new Row({
radioField: new RadioField({ radioField: new RadioField({
text: 'Send by Enter', text: 'General.SendShortcut.Enter',
name: 'send-shortcut', name: 'send-shortcut',
value: 'enter', value: 'enter',
stateKey: 'settings.sendShortcut' stateKey: 'settings.sendShortcut'
}), }),
subtitle: 'New line by Shift + Enter', subtitleLangKey: 'General.SendShortcut.NewLine.ShiftEnter'
}); });
const ctrlEnterRow = new Row({ const ctrlEnterRow = new Row({
radioField: new RadioField({ radioField: new RadioField({
text: `Send by ${isApple ? '⌘' : 'Ctrl'} + Enter`,
name: 'send-shortcut', name: 'send-shortcut',
value: 'ctrlEnter', value: 'ctrlEnter',
stateKey: 'settings.sendShortcut' stateKey: 'settings.sendShortcut'
}), }),
subtitle: 'New line by Enter', subtitleLangKey: 'General.SendShortcut.NewLine.Enter'
}); });
_i18n(ctrlEnterRow.radioField.main, 'General.SendShortcut.CtrlEnter', [isApple ? '⌘' : 'Ctrl']);
form.append(enterRow.container, ctrlEnterRow.container); form.append(enterRow.container, ctrlEnterRow.container);
container.append(form); container.append(form);
} }
{ {
const container = section('Auto-Download Media'); const container = section('AutoDownloadMedia');
//container.classList.add('sidebar-left-section-disabled'); //container.classList.add('sidebar-left-section-disabled');
const contactsCheckboxField = new CheckboxField({ const contactsCheckboxField = new CheckboxField({
text: 'Contacts', text: 'AutodownloadContacts',
name: 'contacts', name: 'contacts',
stateKey: 'settings.autoDownload.contacts', stateKey: 'settings.autoDownload.contacts',
withRipple: true withRipple: true
}); });
const privateCheckboxField = new CheckboxField({ const privateCheckboxField = new CheckboxField({
text: 'Private Chats', text: 'AutodownloadPrivateChats',
name: 'private', name: 'private',
stateKey: 'settings.autoDownload.private', stateKey: 'settings.autoDownload.private',
withRipple: true withRipple: true
}); });
const groupsCheckboxField = new CheckboxField({ const groupsCheckboxField = new CheckboxField({
text: 'Group Chats', text: 'AutodownloadGroupChats',
name: 'groups', name: 'groups',
stateKey: 'settings.autoDownload.groups', stateKey: 'settings.autoDownload.groups',
withRipple: true withRipple: true
}); });
const channelsCheckboxField = new CheckboxField({ const channelsCheckboxField = new CheckboxField({
text: 'Channels', text: 'AutodownloadChannels',
name: 'channels', name: 'channels',
stateKey: 'settings.autoDownload.channels', stateKey: 'settings.autoDownload.channels',
withRipple: true withRipple: true
@ -145,17 +146,17 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
} }
{ {
const container = section('Auto-Play Media'); const container = section('General.AutoplayMedia');
//container.classList.add('sidebar-left-section-disabled'); //container.classList.add('sidebar-left-section-disabled');
const gifsCheckboxField = new CheckboxField({ const gifsCheckboxField = new CheckboxField({
text: 'GIFs', text: 'AutoplayGIF',
name: 'gifs', name: 'gifs',
stateKey: 'settings.autoPlay.gifs', stateKey: 'settings.autoPlay.gifs',
withRipple: true withRipple: true
}); });
const videosCheckboxField = new CheckboxField({ const videosCheckboxField = new CheckboxField({
text: 'Videos', text: 'AutoplayVideo',
name: 'videos', name: 'videos',
stateKey: 'settings.autoPlay.videos', stateKey: 'settings.autoPlay.videos',
withRipple: true withRipple: true
@ -165,16 +166,16 @@ export default class AppGeneralSettingsTab extends SliderSuperTab {
} }
{ {
const container = section('Stickers'); const container = section('Telegram.InstalledStickerPacksController');
const suggestCheckboxField = new CheckboxField({ const suggestCheckboxField = new CheckboxField({
text: 'Suggest Stickers by Emoji', text: 'Stickers.SuggestStickers',
name: 'suggest', name: 'suggest',
stateKey: 'settings.stickers.suggest', stateKey: 'settings.stickers.suggest',
withRipple: true withRipple: true
}); });
const loopCheckboxField = new CheckboxField({ const loopCheckboxField = new CheckboxField({
text: 'Loop Animated Stickers', text: 'InstalledStickers.LoopAnimated',
name: 'loop', name: 'loop',
stateKey: 'settings.stickers.loop', stateKey: 'settings.stickers.loop',
withRipple: true withRipple: true

12
src/components/sidebarLeft/tabs/includedChats.ts

@ -124,7 +124,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
dom.containerEl.append(this.checkbox(selected)); dom.containerEl.append(this.checkbox(selected));
if(selected) dom.listEl.classList.add('active'); if(selected) dom.listEl.classList.add('active');
let subtitle: LangPackKey; /* let subtitle: LangPackKey;
if(peerId > 0) { if(peerId > 0) {
if(peerId === rootScope.myId) { if(peerId === rootScope.myId) {
@ -138,7 +138,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
subtitle = appPeersManager.isBroadcast(peerId) ? 'Channel' : 'Group'; subtitle = appPeersManager.isBroadcast(peerId) ? 'Channel' : 'Group';
} }
_i18n(dom.lastMessageSpan, subtitle); _i18n(dom.lastMessageSpan, subtitle); */
}); });
}; };
@ -149,19 +149,19 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
} }
this.confirmBtn.style.display = this.type === 'excluded' ? '' : 'none'; this.confirmBtn.style.display = this.type === 'excluded' ? '' : 'none';
this.setTitle(this.type === 'included' ? 'Included Chats' : 'Excluded Chats'); this.setTitle(this.type === 'included' ? 'FilterAlwaysShow' : 'FilterNeverShow');
const filter = this.filter; const filter = this.filter;
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();
const dd = document.createElement('div'); const dd = document.createElement('div');
dd.classList.add('sidebar-left-h2'); dd.classList.add('sidebar-left-h2');
_i18n(dd, 'ChatList.Add.TopSeparator'); _i18n(dd, 'FilterChatTypes');
const categories = document.createElement('div'); const categories = document.createElement('div');
categories.classList.add('folder-categories'); categories.classList.add('folder-categories');
let details: {[flag: string]: {ico: string, text: string}}; let details: {[flag: string]: {ico: string, text: LangPackKey}};
if(this.type === 'excluded') { if(this.type === 'excluded') {
details = { details = {
exclude_muted: {ico: 'mute', text: 'ChatList.Filter.MutedChats'}, exclude_muted: {ico: 'mute', text: 'ChatList.Filter.MutedChats'},
@ -192,7 +192,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
const d = document.createElement('div'); const d = document.createElement('div');
d.classList.add('sidebar-left-h2'); d.classList.add('sidebar-left-h2');
_i18n(d, 'ChatList.Add.BottomSeparator'); _i18n(d, 'FilterChats');
fragment.append(dd, categories, hr, d); fragment.append(dd, categories, hr, d);

26
src/components/sidebarLeft/tabs/notifications.ts

@ -18,7 +18,7 @@ export default class AppNotificationsTab extends SliderSuperTabEventable {
const NotifySection = (options: { const NotifySection = (options: {
name: LangPackKey, name: LangPackKey,
typeText: string, typeText: LangPackKey,
inputKey: InputNotifyKey, inputKey: InputNotifyKey,
}) => { }) => {
const section = new SettingSection({ const section = new SettingSection({
@ -27,12 +27,12 @@ export default class AppNotificationsTab extends SliderSuperTabEventable {
const enabledRow = new Row({ const enabledRow = new Row({
checkboxField: new CheckboxField({text: options.typeText, checked: true}), checkboxField: new CheckboxField({text: options.typeText, checked: true}),
subtitle: 'Loading...', subtitleLangKey: 'Loading',
}); });
const previewEnabledRow = new Row({ const previewEnabledRow = new Row({
checkboxField: new CheckboxField({text: 'Message preview', checked: true}), checkboxField: new CheckboxField({text: 'Notifications.MessagePreview', checked: true}),
subtitle: 'Loading...', subtitleLangKey: 'Loading',
}); });
section.content.append(enabledRow.container, previewEnabledRow.container); section.content.append(enabledRow.container, previewEnabledRow.container);
@ -80,35 +80,35 @@ export default class AppNotificationsTab extends SliderSuperTabEventable {
NotifySection({ NotifySection({
name: 'AutoDownloadSettings.TypePrivateChats', name: 'AutoDownloadSettings.TypePrivateChats',
typeText: 'Notifications for private chats', typeText: 'NotificationsForPrivateChats',
inputKey: 'inputNotifyUsers' inputKey: 'inputNotifyUsers'
}); });
NotifySection({ NotifySection({
name: 'DataAndStorage.CategorySettings.GroupChats', name: 'AutoDownloadSettings.TypeGroupChats',
typeText: 'Notifications for groups', typeText: 'NotificationsForGroups',
inputKey: 'inputNotifyChats' inputKey: 'inputNotifyChats'
}); });
NotifySection({ NotifySection({
name: 'AutoDownloadSettings.TypeChannels', name: 'AutoDownloadSettings.TypeChannels',
typeText: 'Notifications for channels', typeText: 'NotificationsForChannels',
inputKey: 'inputNotifyBroadcasts' inputKey: 'inputNotifyBroadcasts'
}); });
{ {
const section = new SettingSection({ const section = new SettingSection({
name: 'Suggest.Localization.Other' name: 'NotificationsOther'
}); });
const contactsSignUpRow = new Row({ const contactsSignUpRow = new Row({
checkboxField: new CheckboxField({text: 'Contacts joined Telegram', checked: true}), checkboxField: new CheckboxField({text: 'ContactJoined', checked: true}),
subtitle: 'Loading...', subtitleLangKey: 'Loading',
}); });
const soundRow = new Row({ const soundRow = new Row({
checkboxField: new CheckboxField({text: 'Notification sound', checked: true, stateKey: 'settings.notifications.sound'}), checkboxField: new CheckboxField({text: 'Notifications.Sound', checked: true, stateKey: 'settings.notifications.sound'}),
subtitle: 'Enabled', subtitleLangKey: 'Checkbox.Enabled',
}); });
section.content.append(contactsSignUpRow.container, soundRow.container); section.content.append(contactsSignUpRow.container, soundRow.container);

52
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -19,6 +19,7 @@ import AppBlockedUsersTab from "./blockedUsers";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import rootScope from "../../../lib/rootScope"; import rootScope from "../../../lib/rootScope";
import { convertKeyToInputKey } from "../../../helpers/string"; import { convertKeyToInputKey } from "../../../helpers/string";
import { LangPackKey, _i18n } from "../../../lib/langPack";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab { export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
private activeSessionsRow: Row; private activeSessionsRow: Row;
@ -26,11 +27,11 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
protected init() { protected init() {
this.container.classList.add('privacy-container'); this.container.classList.add('privacy-container');
this.title.innerText = 'Privacy and Security'; this.setTitle('PrivacySettings');
const section = generateSection.bind(null, this.scrollable); const section = generateSection.bind(null, this.scrollable);
const SUBTITLE = 'Loading...'; const SUBTITLE: LangPackKey = 'Loading';
{ {
const section = new SettingSection({noDelimiter: true}); const section = new SettingSection({noDelimiter: true});
@ -38,8 +39,8 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
let blockedPeerIds: number[]; let blockedPeerIds: number[];
const blockedUsersRow = new Row({ const blockedUsersRow = new Row({
icon: 'deleteuser', icon: 'deleteuser',
title: 'Blocked Users', titleLangKey: 'BlockedUsers',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
const tab = new AppBlockedUsersTab(this.slider); const tab = new AppBlockedUsersTab(this.slider);
tab.peerIds = blockedPeerIds; tab.peerIds = blockedPeerIds;
@ -51,8 +52,8 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
let passwordState: AccountPassword; let passwordState: AccountPassword;
const twoFactorRowOptions = { const twoFactorRowOptions = {
icon: 'lock', icon: 'lock',
title: 'Two-Step Verification', titleLangKey: 'TwoStepVerification' as LangPackKey,
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: (e: Event) => { clickable: (e: Event) => {
let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab; let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab;
if(passwordState.pFlags.has_password) { if(passwordState.pFlags.has_password) {
@ -77,8 +78,8 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const activeSessionsRow = this.activeSessionsRow = new Row({ const activeSessionsRow = this.activeSessionsRow = new Row({
icon: 'activesessions', icon: 'activesessions',
title: 'Active Sessions', titleLangKey: 'SessionsTitle',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
const tab = new AppActiveSessionsTab(this.slider); const tab = new AppActiveSessionsTab(this.slider);
tab.privacyTab = this; tab.privacyTab = this;
@ -94,7 +95,12 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
let blockedCount: number; let blockedCount: number;
const setBlockedCount = (count: number) => { const setBlockedCount = (count: number) => {
blockedCount = count; blockedCount = count;
blockedUsersRow.subtitle.innerText = count ? (count + ' ' + (count > 1 ? 'users' : 'user')) : 'None';
if(count) {
_i18n(blockedUsersRow.subtitle, 'Privacy.BlockedUsers', [count]);
} else {
_i18n(blockedUsersRow.subtitle, 'Privacy.BlockedUsers.None');
}
}; };
this.listenerSetter.add(rootScope, 'peer_block', () => { this.listenerSetter.add(rootScope, 'peer_block', () => {
@ -128,7 +134,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
} }
{ {
const container = section('Privacy'); const container = section('PrivacyTitle');
container.classList.add('privacy-navigation-container'); container.classList.add('privacy-navigation-container');
@ -137,48 +143,48 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
}> = {}; }> = {};
const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({ const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({
title: 'Who can see my phone number?', titleLangKey: 'PrivacyPhoneTitle',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyPhoneNumberTab(this.slider).open() new AppPrivacyPhoneNumberTab(this.slider).open()
} }
}); });
const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({ const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({
title: 'Who can see your Last Seen time?', titleLangKey: 'LastSeenTitle',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyLastSeenTab(this.slider).open() new AppPrivacyLastSeenTab(this.slider).open()
} }
}); });
const photoVisibilityRow = rowsByKeys['inputPrivacyKeyProfilePhoto'] = new Row({ const photoVisibilityRow = rowsByKeys['inputPrivacyKeyProfilePhoto'] = new Row({
title: 'Who can see my profile photo?', titleLangKey: 'PrivacyProfilePhotoTitle',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyProfilePhotoTab(this.slider).open(); new AppPrivacyProfilePhotoTab(this.slider).open();
} }
}); });
const callRow = rowsByKeys['inputPrivacyKeyPhoneCall'] = new Row({ const callRow = rowsByKeys['inputPrivacyKeyPhoneCall'] = new Row({
title: 'Who can call me?', titleLangKey: 'WhoCanCallMe',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyCallsTab(this.slider).open(); new AppPrivacyCallsTab(this.slider).open();
} }
}); });
const linkAccountRow = rowsByKeys['inputPrivacyKeyForwards'] = new Row({ const linkAccountRow = rowsByKeys['inputPrivacyKeyForwards'] = new Row({
title: 'Who can add a link to my account when forwarding my messages?', titleLangKey: 'PrivacyForwardsTitle',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyForwardMessagesTab(this.slider).open(); new AppPrivacyForwardMessagesTab(this.slider).open();
} }
}); });
const groupChatsAddRow = rowsByKeys['inputPrivacyKeyChatInvite'] = new Row({ const groupChatsAddRow = rowsByKeys['inputPrivacyKeyChatInvite'] = new Row({
title: 'Who can add me to group chats?', titleLangKey: 'WhoCanAddMe',
subtitle: SUBTITLE, subtitleLangKey: SUBTITLE,
clickable: () => { clickable: () => {
new AppPrivacyAddToGroupsTab(this.slider).open(); new AppPrivacyAddToGroupsTab(this.slider).open();
} }
@ -216,7 +222,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
apiManager.invokeApi('account.getAuthorizations').then(auths => { apiManager.invokeApi('account.getAuthorizations').then(auths => {
this.activeSessionsRow.freezed = false; this.activeSessionsRow.freezed = false;
this.authorizations = auths.authorizations; this.authorizations = auths.authorizations;
this.activeSessionsRow.subtitle.innerText = this.authorizations.length + ' ' + (this.authorizations.length !== 1 ? 'devices' : 'device'); _i18n(this.activeSessionsRow.subtitle, 'Privacy.Devices', [this.authorizations.length]);
//console.log('auths', auths); //console.log('auths', auths);
}); });
} }

140
src/lang.ts

@ -0,0 +1,140 @@
const lang = {
FilterIncludeExcludeInfo: 'Choose chats and types of chats that will\nappear and never appear in this folder.',
FilterNameInputLabel: 'Folder Name',
FilterMenuDelete: 'Delete Folder',
FilterHeaderEdit: 'Edit Folder',
FilterAllGroups: 'All Groups',
FilterAllContacts: 'All Contacts',
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',
"EditProfile.BioLabel": 'Bio (optional)',
"EditProfile.Username.Label": 'Username (optional)',
"EditProfile.Username.Available": 'Username is available',
"EditProfile.Username.Taken": 'Username is already taken',
"EditProfile.Username.Invalid": 'Username is invalid',
"EditProfile.Username.Help": "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\nYou can use a–z, 0–9 and underscores. Minimum length is 5 characters.",
"ChatList.Menu.Archived": "Archived",
Saved: "Saved",
"General.Keyboard": "Keyboard",
"General.SendShortcut.Enter": "Send by Enter",
"General.SendShortcut.CtrlEnter": "Send by %s + Enter",
"General.SendShortcut.NewLine.ShiftEnter": "New line by Shift + Enter",
"General.SendShortcut.NewLine.Enter": "New line by Enter",
"General.AutoplayMedia": "Auto-Play Media",
"ChatBackground.UploadWallpaper": "Upload Wallpaper",
"ChatBackground.SetColor": "Upload Wallpaper",
"ChatBackground.Blur": "Upload Wallpaper",
"Notifications.Sound": "Notification Sound",
"Notifications.MessagePreview": "Message preview",
"Checkbox.Enabled": "Enabled",
"Checkbox.Disabled": "Disabled",
"Privacy.Devices": {
one_value: '%1$d device',
other_value: '%1$d devices'
},
"Privacy.BlockedUsers": {
one_value: '%1$d user',
other_value: '%1$d users',
},
"Privacy.BlockedUsers.None": 'None',
// * android
FilterAlwaysShow: 'Include Chats',
FilterNeverShow: 'Exclude Chats',
FilterInclude: 'Included Chats',
FilterExclude: 'Excluded Chats',
FilterChatTypes: 'Chat types',
FilterChats: 'Chats',
FilterNew: 'New Folder',
Filters: 'Folders',
FilterRecommended: 'Recommended Folders',
Add: 'Add',
Chats: {
one_value: '%1$d chat',
other_value: '%1$d chats'
},
Channels: {
one_value: '%1$d channel',
other_value: '%1$d channels'
},
Groups: {
one_value: '%1$d group',
other_value: '%1$d groups'
},
UsernameHelpLink: "This link opens a chat with you:\n%1$s",
NewGroup: "New Group",
Contacts: "Contacts",
SavedMessages: "Saved Messages",
Settings: "Settings",
SettingsHelp: "Help",
General: "General",
TextSize: "Message Text Size",
ChatBackground: "Chat Background",
EnableAnimations: "Enable Animations",
AutoDownloadMedia: "Auto-Download Media",
AutodownloadContacts: 'Contacts',
AutodownloadPrivateChats: 'Private Chats',
AutodownloadGroupChats: 'Group Chats',
AutodownloadChannels: 'Channels',
AutoplayGIF: 'GIFs',
AutoplayVideo: 'Videos',
NotificationsForGroups: 'Notifications for groups',
NotificationsForPrivateChats: 'Notifications for private chats',
NotificationsForChannels: 'Notifications for channels',
NotificationsOther: 'Other',
ContactJoined: 'Contact joined Telegram',
Loading: "Loading...",
BlockedUsers: "Blocked Users",
TwoStepVerification: "Two-Step Verification",
PrivacySettings: "Privacy and Security",
PrivacyTitle: "Privacy",
PrivacyPhoneTitle: "Who can see my phone number?",
PrivacyProfilePhotoTitle: "Who can see my profile photos & videos?",
PrivacyForwardsTitle: "Who can add a link to my account when forwarding my messages?",
LastSeenTitle: "Who can see your Last Seen time?",
SessionsTitle: "Active Sessions",
WhoCanCallMe: "Who can call me?",
WhoCanAddMe: "Who can add me to group chats?",
// * macos
"ChatList.Filter.Header": "Create folders for different groups of chats and quickly switch between them.",
"ChatList.Filter.NewTitle": "Create Folder",
"ChatList.Filter.List.Title": "Chat Folders",
"ChatList.Filter.Include.AddChat": "Add Chats",
"ChatList.Filter.Exclude.AddChat": "Add Chats",
"ChatList.Filter.All": "All",
"ChatList.Filter.Contacts": "Contacts",
"ChatList.Filter.NonContacts": "Non-Contacts",
"ChatList.Filter.Groups": "Groups",
"ChatList.Filter.Channels": "Channels",
"ChatList.Filter.Bots": "Bots",
"ChatList.Filter.MutedChats": "Muted",
"ChatList.Filter.ReadChats": "Read",
"ChatList.Filter.Archive": "Archived",
"Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco",
"EditAccount.Username": "Username",
"EditAccount.Title": "Edit Profile",
"EditAccount.Logout": "Log Out",
"Login.Register.LastName.Placeholder": "Last Name",
"AccountSettings.Filters": "Chat Folders",
"AccountSettings.Notifications": "Notifications and Sounds",
"AccountSettings.PrivacyAndSecurity": "Privacy and Security",
"AccountSettings.Language": "Language",
"Telegram.GeneralSettingsViewController": "General Settings",
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"AutoDownloadSettings.TypePrivateChats": "Private Chats",
"AutoDownloadSettings.TypeGroupChats": "Groups",
"AutoDownloadSettings.TypeChannels": "Channels",
};
export default lang;

20
src/lib/appManagers/appImManager.ts

@ -318,25 +318,7 @@ export class AppImManager {
const chat = this.chat; const chat = this.chat;
if(e.key === 'Escape') { if(e.key === 'Meta' || e.key === 'Control') {
let cancel = true;
if(this.markupTooltip?.container?.classList.contains('is-visible')) {
this.markupTooltip.hide();
} else if(chat.selection.isSelecting) {
chat.selection.cancelSelection();
} else if(chat.container.classList.contains('is-helper-active')) {
chat.input.replyElements.cancelBtn.click();
} else if(chat.peerId !== 0) { // hide current dialog
this.setPeer(0);
} else {
cancel = false;
}
// * cancel event for safari, because if application is in fullscreen, browser will try to exit fullscreen
if(cancel) {
cancelEvent(e);
}
} else if(e.key === 'Meta' || e.key === 'Control') {
return; return;
} else if(e.code === "KeyC" && (e.ctrlKey || e.metaKey) && target.tagName !== 'INPUT') { } else if(e.code === "KeyC" && (e.ctrlKey || e.metaKey) && target.tagName !== 'INPUT') {
return; return;

96
src/lib/langPack.ts

@ -1,5 +1,6 @@
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import { safeAssign } from "../helpers/object"; import { safeAssign } from "../helpers/object";
import type lang from "../lang";
import { LangPackDifference, LangPackString } from "../layer"; import { LangPackDifference, LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker"; import apiManager from "./mtproto/mtprotoworker";
import sessionStorage from "./sessionStorage"; import sessionStorage from "./sessionStorage";
@ -34,37 +35,7 @@ export const langPack: {[actionType: string]: string} = {
"messageActionBotAllowed": "You allowed this bot to message you when logged in {}" "messageActionBotAllowed": "You allowed this bot to message you when logged in {}"
}; };
namespace Strings { export type LangPackKey = string | keyof typeof lang;
export type Bio = 'Bio.Description';
export type LoginRegister = 'Login.Register.FirstName.Placeholder' | 'Login.Register.LastName.Placeholder';
export type EditAccount = 'EditAccount.Logout' | 'EditAccount.Title' | 'EditAccount.Title' | 'EditAccount.Username';
export type AccountSettings = 'AccountSettings.Filters' | 'AccountSettings.Notifications' | 'AccountSettings.PrivacyAndSecurity' | 'AccountSettings.Language' | 'AccountSettings.Bio';
export type Telegram = 'Telegram.GeneralSettingsViewController' | 'Telegram.NotificationSettingsViewController' | 'Telegram.LanguageViewController';
export type ChatList = ChatListFilter;
export type ChatListAdd = 'ChatList.Add.TopSeparator' | 'ChatList.Add.BottomSeparator';
export type ChatListFilterIncluded = 'ChatList.Filter.Include.Header' | 'ChatList.Filter.Include.AddChat';
export type ChatListFilterExcluded = 'ChatList.Filter.Exclude.Header' | 'ChatList.Filter.Exclude.AddChat';
export type ChatListFilterList = 'ChatList.Filter.List.Header' | 'ChatList.Filter.List.Title';
export type ChatListFilterRecommended = 'ChatList.Filter.Recommended.Header' | 'ChatList.Filter.Recommended.Add';
export type ChatListFilter = ChatListAdd | ChatListFilterIncluded | ChatListFilterExcluded | ChatListFilterList | ChatListFilterRecommended | 'ChatList.Filter.Header' | 'ChatList.Filter.NewTitle' | 'ChatList.Filter.NonContacts' | 'ChatList.Filter.Contacts' | 'ChatList.Filter.Groups' | 'ChatList.Filter.Channels' | 'ChatList.Filter.Bots';
export type AutoDownloadSettings = 'AutoDownloadSettings.TypePrivateChats' | 'AutoDownloadSettings.TypeChannels';
export type DataAndStorage = 'DataAndStorage.CategorySettings.GroupChats';
export type Suggest = 'Suggest.Localization.Other';
export type UsernameSettings = 'UsernameSettings.ChangeDescription';
export type LangPackKey = string | AccountSettings | EditAccount | Telegram | ChatList | LoginRegister | Bio | AutoDownloadSettings | DataAndStorage | Suggest | UsernameSettings;
}
export type LangPackKey = Strings.LangPackKey;
namespace I18n { namespace I18n {
export const strings: Map<LangPackKey, LangPackString> = new Map(); export const strings: Map<LangPackKey, LangPackString> = new Map();
@ -76,7 +47,7 @@ namespace I18n {
sessionStorage.get('langPack'), sessionStorage.get('langPack'),
polyfillPromise polyfillPromise
]).then(([langPack]) => { ]).then(([langPack]) => {
if(!langPack) { if(!langPack || true) {
return getLangPack('en'); return getLangPack('en');
} }
@ -96,8 +67,40 @@ namespace I18n {
lang_code: langCode, lang_code: langCode,
lang_pack: 'macos' lang_pack: 'macos'
}), }),
apiManager.invokeApi('langpack.getLangPack', {
lang_code: langCode,
lang_pack: 'android'
}),
import('../lang'),
polyfillPromise polyfillPromise
]).then(([langPack, _]) => { ]).then(([langPack, _langPack, __langPack, _]) => {
let strings: LangPackString[] = [];
for(const i in __langPack.default) {
// @ts-ignore
const v = __langPack.default[i];
if(typeof(v) === 'string') {
strings.push({
_: 'langPackString',
key: i,
value: v
});
} else {
strings.push({
_: 'langPackStringPluralized',
key: i,
...v
});
}
}
strings = strings.concat(langPack.strings);
for(const string of _langPack.strings) {
strings.push(string);
}
langPack.strings = strings;
return sessionStorage.set({langPack}).then(() => { return sessionStorage.set({langPack}).then(() => {
applyLangPack(langPack); applyLangPack(langPack);
return langPack; return langPack;
@ -106,7 +109,7 @@ namespace I18n {
} }
export const polyfillPromise = (function checkIfPolyfillNeeded() { export const polyfillPromise = (function checkIfPolyfillNeeded() {
if(typeof(Intl) !== 'undefined' && typeof(Intl.PluralRules) !== 'undefined'/* && false */) { if(typeof(Intl) !== 'undefined' && typeof(Intl.PluralRules) !== 'undefined' && false) {
return Promise.resolve(); return Promise.resolve();
} else { } else {
return import('./pluralPolyfill').then(_Intl => { return import('./pluralPolyfill').then(_Intl => {
@ -143,24 +146,28 @@ namespace I18n {
let out = ''; let out = '';
if(str) { if(str) {
if(str._ === 'langPackStringPluralized') { if(str._ === 'langPackStringPluralized' && args?.length) {
const v = args[0] as number; const v = args[0] as number;
const s = pluralRules.select(v); const s = pluralRules.select(v);
// @ts-ignore // @ts-ignore
out = str[s + '_value']; out = str[s + '_value'] || str['other_value'];
} else if(str._ === 'langPackString') { } else if(str._ === 'langPackString') {
out = str.value; out = str.value;
} else { } else {
//out = '[' + key + ']'; out = '[' + key + ']';
out = key; //out = key;
} }
} else { } else {
//out = '[' + key + ']'; out = '[' + key + ']';
out = key; //out = key;
} }
out = out
.replace(/\n/g, '<br>')
.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
if(args?.length) { if(args?.length) {
out = out.replace(/%./g, (match, offset, string) => { out = out.replace(/%\d\$.|%./g, (match, offset, string) => {
return '' + args.shift(); return '' + args.shift();
}); });
} }
@ -172,7 +179,7 @@ namespace I18n {
export type IntlElementOptions = { export type IntlElementOptions = {
element?: HTMLElement, element?: HTMLElement,
property?: 'innerText' | 'innerHTML' | 'placeholder' property?: /* 'innerText' | */'innerHTML' | 'placeholder'
key: LangPackKey, key: LangPackKey,
args?: any[] args?: any[]
}; };
@ -180,7 +187,7 @@ namespace I18n {
public element: IntlElementOptions['element']; public element: IntlElementOptions['element'];
public key: IntlElementOptions['key']; public key: IntlElementOptions['key'];
public args: IntlElementOptions['args']; public args: IntlElementOptions['args'];
public property: IntlElementOptions['property'] = 'innerText'; public property: IntlElementOptions['property'] = 'innerHTML';
constructor(options: IntlElementOptions) { constructor(options: IntlElementOptions) {
this.element = options.element || document.createElement('span'); this.element = options.element || document.createElement('span');
@ -193,7 +200,8 @@ namespace I18n {
public update(options?: IntlElementOptions) { public update(options?: IntlElementOptions) {
safeAssign(this, options); safeAssign(this, options);
(this.element as any)[this.property] = getString(this.key, this.args); const str = getString(this.key, this.args);
(this.element as any)[this.property] = str;
} }
} }

Loading…
Cancel
Save