Language improvements

This commit is contained in:
Eduard Kuzmenko 2021-04-04 19:39:17 +04:00
parent b4b0ced131
commit fc8861027b
71 changed files with 424 additions and 293 deletions

View File

@ -11,7 +11,7 @@ import { logger } from "../lib/logger";
import VideoPlayer from "../lib/mediaPlayer";
import { RichTextProcessor } from "../lib/richtextprocessor";
import rootScope from "../lib/rootScope";
import { cancelEvent, fillPropertyValue, findUpClassName, generatePathData } from "../helpers/dom";
import { cancelEvent, fillPropertyValue, generatePathData } from "../helpers/dom";
import animationIntersector from "./animationIntersector";
import appMediaPlaybackController from "./appMediaPlaybackController";
import AvatarElement from "./avatar";
@ -19,7 +19,6 @@ import ButtonIcon from "./buttonIcon";
import { ButtonMenuItemOptions } from "./buttonMenu";
import ButtonMenuToggle from "./buttonMenuToggle";
import { LazyLoadQueueBase } from "./lazyLoadQueue";
import { renderImageFromUrl } from "./misc";
import PopupForward from "./popups/forward";
import ProgressivePreloader from "./preloader";
import Scrollable from "./scrollable";
@ -32,6 +31,8 @@ import appNavigationController from "./appNavigationController";
import { Message } from "../layer";
import { forEachReverse } from "../helpers/array";
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
import findUpClassName from "../helpers/dom/findUpClassName";
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1199,7 +1200,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
onClick: this.onForwardClick
}, {
icon: 'download',
text: 'Download',
text: 'MediaViewer.Context.Download',
onClick: this.onDownloadClick
}, {
icon: 'delete danger btn-disabled',
@ -1436,7 +1437,7 @@ export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMe
this.setBtnMenuToggle([{
icon: 'download',
text: 'Download',
text: 'MediaViewer.Context.Download',
onClick: this.onDownloadClick
}, {
icon: 'delete danger btn-disabled',

View File

@ -11,7 +11,7 @@ import { logger } from "../lib/logger";
import VideoPlayer from "../lib/mediaPlayer";
import { RichTextProcessor } from "../lib/richtextprocessor";
import rootScope from "../lib/rootScope";
import { cancelEvent, fillPropertyValue, findUpClassName, generatePathData } from "../helpers/dom";
import { cancelEvent, fillPropertyValue, generatePathData } from "../helpers/dom";
import animationIntersector from "./animationIntersector";
import appMediaPlaybackController from "./appMediaPlaybackController";
import AvatarElement from "./avatar";
@ -19,7 +19,6 @@ import ButtonIcon from "./buttonIcon";
import { ButtonMenuItemOptions } from "./buttonMenu";
import ButtonMenuToggle from "./buttonMenuToggle";
import { LazyLoadQueueBase } from "./lazyLoadQueue";
import { renderImageFromUrl } from "./misc";
import PopupForward from "./popups/forward";
import ProgressivePreloader from "./preloader";
import Scrollable from "./scrollable";
@ -30,6 +29,8 @@ import { SearchSuperContext } from "./appSearchSuper.";
import { Message, PhotoSize } from "../layer";
import { forEachReverse } from "../helpers/array";
import AppSharedMediaTab from "./sidebarRight/tabs/sharedMedia";
import findUpClassName from "../helpers/dom/findUpClassName";
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
// TODO: масштабирование картинок (не SVG) при ресайзе, и правильный возврат на исходную позицию
// TODO: картинки "обрезаются" если возвращаются или появляются с места, где есть их перекрытие (топбар, поле ввода)
@ -1101,7 +1102,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
onClick: this.onForwardClick
}, {
icon: 'download',
text: 'Download',
text: 'MediaViewer.Context.Download',
onClick: this.onDownloadClick
}, {
icon: 'delete danger btn-disabled',
@ -1338,7 +1339,7 @@ export class AppMediaViewerAvatar extends AppMediaViewerBase<'', 'delete', AppMe
this.setBtnMenuToggle([{
icon: 'download',
text: 'Download',
text: 'MediaViewer.Context.Download',
onClick: this.onDownloadClick
}, {
icon: 'delete danger btn-disabled',

View File

@ -1,5 +1,5 @@
import { formatDateAccordingToToday, months } from "../helpers/date";
import { findUpClassName, positionElementByIndex } from "../helpers/dom";
import { positionElementByIndex } from "../helpers/dom";
import { copy, getObjectKeysAndSort } from "../helpers/object";
import { escapeRegExp, limitSymbols } from "../helpers/string";
import appChatsManager from "../lib/appManagers/appChatsManager";
@ -17,13 +17,15 @@ import AppMediaViewer from "./appMediaViewer";
import { SearchGroup, SearchGroupType } from "./appSearch";
import { horizontalMenu } from "./horizontalMenu";
import LazyLoadQueue from "./lazyLoadQueue";
import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc";
import { putPreloader, formatPhoneNumber } from "./misc";
import { ripple } from "./ripple";
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";
import findUpClassName from "../helpers/dom/findUpClassName";
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
//const testScroll = false;

View File

@ -5,13 +5,15 @@ import appPeersManager from "../lib/appManagers/appPeersManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager";
import appUsersManager from "../lib/appManagers/appUsersManager";
import rootScope from "../lib/rootScope";
import { cancelEvent, findUpAttribute, findUpClassName } from "../helpers/dom";
import { cancelEvent } from "../helpers/dom";
import Scrollable from "./scrollable";
import { FocusDirection } from "../helpers/fastSmoothScroll";
import CheckboxField from "./checkboxField";
import appProfileManager from "../lib/appManagers/appProfileManager";
import { safeAssign } from "../helpers/object";
import { i18n, LangPackKey, _i18n } from "../lib/langPack";
import findUpAttribute from "../helpers/dom/findUpAttribute";
import findUpClassName from "../helpers/dom/findUpClassName";
type PeerType = 'contacts' | 'dialogs' | 'channelParticipants';

View File

@ -8,7 +8,7 @@ import type { AppDocsManager } from "../../lib/appManagers/appDocsManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type sessionStorage from '../../lib/sessionStorage';
import type Chat from "./chat";
import { findUpClassName, cancelEvent, findUpTag, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex, reflowScrollableElement } from "../../helpers/dom";
import { cancelEvent, whichChild, getElementByPoint, attachClickEvent, positionElementByIndex, reflowScrollableElement } from "../../helpers/dom";
import { getObjectKeysAndSort } from "../../helpers/object";
import { isTouchSupported } from "../../helpers/touchSupport";
import { logger } from "../../lib/logger";
@ -49,6 +49,8 @@ import { SliceEnd } from "../../helpers/slicedArray";
import serverTimeManager from "../../lib/mtproto/serverTimeManager";
import PeerTitle from "../peerTitle";
import { forEachReverse } from "../../helpers/array";
import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag";
const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Message.messageService['action']['_'][] = [/* 'messageActionHistoryClear' */];

View File

@ -18,18 +18,18 @@ import type sessionStorage from '../../lib/sessionStorage';
import EventListenerBase from "../../helpers/eventListenerBase";
import { logger, LogLevels } from "../../lib/logger";
import rootScope from "../../lib/rootScope";
import appSidebarRight, { AppSidebarRight } from "../sidebarRight";
import appSidebarRight from "../sidebarRight";
import ChatBubbles from "./bubbles";
import ChatContextMenu from "./contextMenu";
import ChatInput from "./input";
import ChatSelection from "./selection";
import ChatTopbar from "./topbar";
import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
import { renderImageFromUrl } from "../misc";
import SetTransition from "../singleTransition";
import { fastRaf } from "../../helpers/schedulers";
import AppPrivateSearchTab from "../sidebarRight/tabs/search";
import type { State } from "../../lib/appManagers/appStateManager";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled';

View File

@ -4,7 +4,7 @@ import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type { AppPollsManager, Poll } from "../../lib/appManagers/appPollsManager";
import type Chat from "./chat";
import { isTouchSupported } from "../../helpers/touchSupport";
import { attachClickEvent, cancelEvent, cancelSelection, findUpClassName, isSelectionEmpty } from "../../helpers/dom";
import { attachClickEvent, cancelEvent, cancelSelection, isSelectionEmpty } from "../../helpers/dom";
import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../misc";
import PopupDeleteMessages from "../popups/deleteMessages";
@ -14,6 +14,7 @@ import { copyTextToClipboard } from "../../helpers/clipboard";
import PopupSendNow from "../popups/sendNow";
import { toast } from "../toast";
import I18n, { LangPackKey } from "../../lib/langPack";
import findUpClassName from "../../helpers/dom/findUpClassName";
export default class ChatContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[];

View File

@ -14,7 +14,7 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
//import Recorder from '../opus-recorder/dist/recorder.min';
import opusDecodeController from "../../lib/opusDecodeController";
import RichTextProcessor from "../../lib/richtextprocessor";
import { attachClickEvent, blurActiveElement, cancelEvent, cancelSelection, findUpClassName, getRichValue, isInputEmpty, markdownTags, MarkdownType, placeCaretAtEnd, isSendShortcutPressed } from "../../helpers/dom";
import { attachClickEvent, blurActiveElement, cancelEvent, cancelSelection, getRichValue, isInputEmpty, markdownTags, MarkdownType, placeCaretAtEnd, isSendShortcutPressed } from "../../helpers/dom";
import { ButtonMenuItemOptions } from '../buttonMenu';
import emoticonsDropdown from "../emoticonsDropdown";
import PopupCreatePoll from "../popups/createPoll";
@ -40,6 +40,7 @@ import appNavigationController from '../appNavigationController';
import { isMobile } from '../../helpers/userAgent';
import { i18n } from '../../lib/langPack';
import { generateTail } from './bubbles';
import findUpClassName from '../../helpers/dom/findUpClassName';
const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';

View File

@ -1,5 +1,5 @@
import type { AppImManager } from "../../lib/appManagers/appImManager";
import { MarkdownType, cancelEvent, getSelectedNodes, markdownTags, findUpClassName, attachClickEvent, cancelSelection, isSelectionEmpty } from "../../helpers/dom";
import { MarkdownType, cancelEvent, getSelectedNodes, markdownTags, attachClickEvent, isSelectionEmpty } from "../../helpers/dom";
import RichTextProcessor from "../../lib/richtextprocessor";
import ButtonIcon from "../buttonIcon";
import { clamp } from "../../helpers/number";

View File

@ -1,10 +1,10 @@
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
import { limitSymbols } from "../../helpers/string";
import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import appPhotosManager from "../../lib/appManagers/appPhotosManager";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import DivAndCaption from "../divAndCaption";
import { renderImageFromUrl } from "../misc";
import { wrapSticker } from "../wrappers";
export function wrapReplyDivAndCaption(options: {

View File

@ -1,10 +1,11 @@
import type ChatTopbar from "./topbar";
import { cancelEvent, whichChild, findUpTag } from "../../helpers/dom";
import { cancelEvent, whichChild } from "../../helpers/dom";
import AppSearch, { SearchGroup } from "../appSearch";
import PopupDatePicker from "../popups/datePicker";
import { ripple } from "../ripple";
import InputSearch from "../inputSearch";
import type Chat from "./chat";
import findUpTag from "../../helpers/dom/findUpTag";
export default class ChatSearch {
private element: HTMLElement;

View File

@ -3,7 +3,7 @@ import type ChatBubbles from "./bubbles";
import type ChatInput from "./input";
import type Chat from "./chat";
import { isTouchSupported } from "../../helpers/touchSupport";
import { blurActiveElement, cancelEvent, cancelSelection, findUpClassName, getSelectedText } from "../../helpers/dom";
import { blurActiveElement, cancelEvent, cancelSelection, getSelectedText } from "../../helpers/dom";
import Button from "../button";
import ButtonIcon from "../buttonIcon";
import CheckboxField from "../checkboxField";
@ -16,6 +16,7 @@ import PopupSendNow from "../popups/sendNow";
import appNavigationController from "../appNavigationController";
import { isMobileSafari } from "../../helpers/userAgent";
import I18n, { i18n, _i18n } from "../../lib/langPack";
import findUpClassName from "../../helpers/dom/findUpClassName";
const MAX_SELECTION_LENGTH = 100;
//const MIN_CLICK_MOVE = 32; // minimum bubble height

View File

@ -1,6 +1,6 @@
import { findUpClassName } from "../../helpers/dom";
import findUpClassName from "../../helpers/dom/findUpClassName";
import { MyDocument } from "../../lib/appManagers/appDocsManager";
import appImManager, { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import appStickersManager from "../../lib/appManagers/appStickersManager";
import { EmoticonsDropdown } from "../emoticonsDropdown";
import { SuperStickerRenderer } from "../emoticonsDropdown/tabs/stickers";

View File

@ -4,7 +4,7 @@ import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManage
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type { AppSidebarRight } from "../sidebarRight";
import type Chat from "./chat";
import { findUpClassName, cancelEvent, attachClickEvent, blurActiveElement } from "../../helpers/dom";
import { cancelEvent, attachClickEvent, blurActiveElement } from "../../helpers/dom";
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
import { isSafari } from "../../helpers/userAgent";
import rootScope from "../../lib/rootScope";
@ -24,6 +24,7 @@ import { LEFT_COLUMN_ACTIVE_CLASSNAME } from "../sidebarLeft";
import AppPrivateSearchTab from "../sidebarRight/tabs/search";
import PeerTitle from "../peerTitle";
import { i18n } from "../../lib/langPack";
import findUpClassName from "../../helpers/dom/findUpClassName";
export default class ChatTopbar {
container: HTMLDivElement;

View File

@ -1,9 +1,7 @@
import InputField from "./inputField";
import InputField, { InputFieldOptions } from "./inputField";
export default class CodeInputField extends InputField {
constructor(options: {
label?: string,
name?: string,
constructor(options: InputFieldOptions & {
length: number,
onFill: (code: number) => void
}) {

View File

@ -2,11 +2,11 @@ import appDialogsManager from "../lib/appManagers/appDialogsManager";
import appMessagesManager, {Dialog} from "../lib/appManagers/appMessagesManager";
import appPeersManager from "../lib/appManagers/appPeersManager";
import rootScope from "../lib/rootScope";
import { findUpTag } from "../helpers/dom";
import { positionMenu, openBtnMenu } from "./misc";
import ButtonMenu, { ButtonMenuItemOptions } from "./buttonMenu";
import PopupDeleteDialog from "./popups/deleteDialog";
import { i18n } from "../lib/langPack";
import findUpTag from "../helpers/dom/findUpTag";
export default class DialogsContextMenu {
private element: HTMLElement;

View File

@ -2,7 +2,7 @@ import { isTouchSupported } from "../../helpers/touchSupport";
import appChatsManager from "../../lib/appManagers/appChatsManager";
import appImManager from "../../lib/appManagers/appImManager";
import rootScope from "../../lib/rootScope";
import { blurActiveElement, findUpClassName, findUpTag, whichChild } from "../../helpers/dom";
import { blurActiveElement, whichChild } from "../../helpers/dom";
import animationIntersector from "../animationIntersector";
import { horizontalMenu } from "../horizontalMenu";
import LazyLoadQueue, { LazyLoadQueueIntersector } from "../lazyLoadQueue";
@ -16,6 +16,8 @@ import { pause } from "../../helpers/schedulers";
import { MOUNT_CLASS_TO } from "../../config/debug";
import AppGifsTab from "../sidebarRight/tabs/gifs";
import AppStickersTab from "../sidebarRight/tabs/stickers";
import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';

View File

@ -1,5 +1,6 @@
import emoticonsDropdown, { EmoticonsDropdown, EMOTICONSSTICKERGROUP, EmoticonsTab } from "..";
import { readBlobAsText } from "../../../helpers/blob";
import renderImageFromUrl from "../../../helpers/dom/renderImageFromUrl";
import mediaSizes from "../../../helpers/mediaSizes";
import { MessagesAllStickers, StickerSet } from "../../../layer";
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
@ -10,7 +11,7 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor";
import rootScope from "../../../lib/rootScope";
import animationIntersector from "../../animationIntersector";
import LazyLoadQueue, { LazyLoadQueueRepeat } from "../../lazyLoadQueue";
import { putPreloader, renderImageFromUrl } from "../../misc";
import { putPreloader } from "../../misc";
import Scrollable, { ScrollableX } from "../../scrollable";
import StickyIntersector from "../../stickyIntersector";
import { wrapSticker } from "../../wrappers";

View File

@ -1,11 +1,11 @@
import { calcImageInBox } from "../helpers/dom";
import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { wrapVideo } from "./wrappers";
import { renderImageFromUrl } from "./misc";
import { LazyLoadQueueRepeat2 } from "./lazyLoadQueue";
import animationIntersector from "./animationIntersector";
import Scrollable from "./scrollable";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import renderImageFromUrl from "../helpers/dom/renderImageFromUrl";
const width = 400;
const maxSingleWidth = width - 100;

View File

@ -1,9 +1,10 @@
import { whichChild, findUpAsChild } from "../helpers/dom";
import { whichChild } from "../helpers/dom";
import { TransitionSlider } from "./transition";
import { ScrollableX } from "./scrollable";
import rootScope from "../lib/rootScope";
import { fastRaf } from "../helpers/schedulers";
import { FocusDirection } from "../helpers/fastSmoothScroll";
import findUpAsChild from "../helpers/dom/findUpAsChild";
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 250, scrollableX?: ScrollableX) {
const selectTab = TransitionSlider(content, tabs || content.dataset.animation === 'tabs' ? 'tabs' : 'navigation', transitionTime, onTransitionEnd);

View File

@ -7,54 +7,6 @@ import { isTouchSupported } from "../helpers/touchSupport";
import { isApple, isMobileSafari } from "../helpers/userAgent";
import appNavigationController from "./appNavigationController";
export const loadedURLs: {[url: string]: boolean} = {};
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);
else elem.style.backgroundImage = 'url(' + url + ')';
};
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = false): boolean {
if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {
if(elem) {
set(elem, url);
}
callback && callback();
return true;
} else {
const isImage = elem instanceof HTMLImageElement;
const loader = isImage ? elem as HTMLImageElement : new Image();
//const loader = new Image();
loader.src = url;
//let perf = performance.now();
loader.addEventListener('load', () => {
if(!isImage && elem) {
set(elem, url);
}
loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf);
if(callback) {
// TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются
/* getHeavyAnimationPromise().then(() => {
callback();
}); */
callback();
}
//callback && callback();
});
if(callback) {
loader.addEventListener('error', callback);
}
return false;
}
}
export function putPreloader(elem: Element, returnDiv = false): HTMLElement {
const html = `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">

View File

@ -1,16 +1,12 @@
import { cancelEvent } from "../helpers/dom";
import InputField from "./inputField";
import InputField, { InputFieldOptions } from "./inputField";
export default class PasswordInputField extends InputField {
public passwordVisible = false;
public toggleVisible: HTMLElement;
public onVisibilityClickAdditional: () => void;
constructor(options: {
label?: string,
name?: string,
labelText?: string,
} = {}) {
constructor(options: InputFieldOptions = {}) {
super({
plainText: true,
...options

View File

@ -5,13 +5,14 @@ import appPollsManager, { Poll, PollResults } from "../lib/appManagers/appPollsM
import serverTimeManager from "../lib/mtproto/serverTimeManager";
import { RichTextProcessor } from "../lib/richtextprocessor";
import rootScope from "../lib/rootScope";
import { attachClickEvent, cancelEvent, detachClickEvent, findUpClassName, replaceContent } from "../helpers/dom";
import { attachClickEvent, cancelEvent, detachClickEvent, replaceContent } from "../helpers/dom";
import { ripple } from "./ripple";
import appSidebarRight from "./sidebarRight";
import AppPollResultsTab from "./sidebarRight/tabs/pollResults";
import { i18n, LangPackKey } from "../lib/langPack";
import { fastRaf } from "../helpers/schedulers";
import SetTransition from "./singleTransition";
import findUpClassName from "../helpers/dom/findUpClassName";
let lineTotalLength = 0;
const tailLength = 9;
@ -203,7 +204,7 @@ export default class PollElement extends HTMLElement {
//console.log('pollElement poll:', poll, results);
let descKey: LangPackKey = '';
let descKey: LangPackKey;
if(poll.pFlags) {
this.isPublic = !!poll.pFlags.public_voters;
this.isQuiz = !!poll.pFlags.quiz;

View File

@ -1,15 +1,15 @@
import type { Poll } from "../../lib/appManagers/appPollsManager";
import type Chat from "../chat/chat";
import PopupElement from ".";
import { cancelEvent, findUpTag, getRichValue, isInputEmpty, whichChild } from "../../helpers/dom";
import { cancelEvent, getRichValue, isInputEmpty, whichChild } from "../../helpers/dom";
import CheckboxField from "../checkboxField";
import InputField from "../inputField";
import RadioField from "../radioField";
import Scrollable from "../scrollable";
import { toast } from "../toast";
import SendContextMenu from "../chat/sendContextMenu";
import { MessageEntity } from "../../layer";
import I18n, { _i18n, i18n } from "../../lib/langPack";
import findUpTag from "../../helpers/dom/findUpTag";
const MAX_LENGTH_QUESTION = 255;
const MAX_LENGTH_OPTION = 100;

View File

@ -1,9 +1,10 @@
import rootScope from "../../lib/rootScope";
import { blurActiveElement, findUpClassName } from "../../helpers/dom";
import { blurActiveElement } from "../../helpers/dom";
import { ripple } from "../ripple";
import animationIntersector from "../animationIntersector";
import appNavigationController, { NavigationItem } from "../appNavigationController";
import { i18n, LangPackKey } from "../../lib/langPack";
import findUpClassName from "../../helpers/dom/findUpClassName";
export type PopupButton = {
text?: string,
@ -17,7 +18,7 @@ export type PopupButton = {
export type PopupOptions = Partial<{
closable: true,
overlayClosable: true,
withConfirm: LangPackKey,
withConfirm: LangPackKey | true,
body: true
}>;
@ -69,7 +70,9 @@ export default class PopupElement {
if(options.withConfirm) {
this.btnConfirm = document.createElement('button');
this.btnConfirm.classList.add('btn-primary', 'btn-color-primary');
this.btnConfirm.append(i18n(options.withConfirm));
if(options.withConfirm !== true) {
this.btnConfirm.append(i18n(options.withConfirm));
}
this.header.append(this.btnConfirm);
ripple(this.btnConfirm);
}

View File

@ -3,7 +3,7 @@ import PopupElement, { addCancelButton, PopupButton, PopupOptions } from ".";
import { i18n, LangPackKey } from "../../lib/langPack";
import CheckboxField, { CheckboxFieldOptions } from "../checkboxField";
export type PopupPeerButtonCallbackCheckboxes = {[text in LangPackKey]: boolean};
export type PopupPeerButtonCallbackCheckboxes = Partial<{[text in LangPackKey]: boolean}>;
export type PopupPeerButtonCallback = (checkboxes?: PopupPeerButtonCallbackCheckboxes) => void;
export type PopupPeerOptions = PopupOptions & Partial<{

View File

@ -13,7 +13,7 @@ export default class PopupSchedule extends PopupDatePicker {
noButtons: true,
noTitle: true,
closable: true,
withConfirm: 'Send Today',
withConfirm: true,
minDate: getMinDate(),
maxDate: (() => {
const date = new Date();

View File

@ -6,12 +6,13 @@ import { wrapSticker } from "../wrappers";
import LazyLoadQueue from "../lazyLoadQueue";
import { putPreloader } from "../misc";
import animationIntersector from "../animationIntersector";
import { findUpClassName, toggleDisability } from "../../helpers/dom";
import { toggleDisability } from "../../helpers/dom";
import appImManager from "../../lib/appManagers/appImManager";
import { StickerSet } from "../../layer";
import mediaSizes from "../../helpers/mediaSizes";
import { i18n } from "../../lib/langPack";
import Button from "../button";
import findUpClassName from "../../helpers/dom/findUpClassName";
const ANIMATION_GROUP = 'STICKERS-POPUP';

View File

@ -9,7 +9,7 @@ import PeerTitle from "../peerTitle";
export default class PopupPinMessage {
constructor(peerId: number, mid: number, unpin?: true, onConfirm?: () => void) {
let title: LangPackKey, description: string, buttons: PopupButton[] = [];
let title: LangPackKey, description: LangPackKey, buttons: PopupButton[] = [];
const canUnpin = appPeersManager.canPinMessage(peerId);

View File

@ -1,5 +1,5 @@
import findUpClassName from "../helpers/dom/findUpClassName";
import {isTouchSupported} from "../helpers/touchSupport";
import { findUpClassName } from "../helpers/dom";
import rootScope from "../lib/rootScope";
let rippleClickId = 0;

View File

@ -5,7 +5,6 @@ import appPeersManager from "../../lib/appManagers/appPeersManager";
import appStateManager from "../../lib/appManagers/appStateManager";
import appUsersManager from "../../lib/appManagers/appUsersManager";
import rootScope from "../../lib/rootScope";
import { attachClickEvent, findUpClassName, findUpTag } from "../../helpers/dom";
import { SearchGroup } from "../appSearch";
import "../avatar";
import Scrollable, { ScrollableX } from "../scrollable";
@ -27,6 +26,8 @@ import ButtonMenu, { ButtonMenuItemOptions } from "../buttonMenu";
import CheckboxField from "../checkboxField";
import { isMobileSafari } from "../../helpers/userAgent";
import appNavigationController from "../appNavigationController";
import findUpClassName from "../../helpers/dom/findUpClassName";
import findUpTag from "../../helpers/dom/findUpTag";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';

View File

@ -26,7 +26,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
this.setTitle('RecoveryEmailTitle');
const section = new SettingSection({
caption: '',
caption: true,
noDelimiter: true
});

View File

@ -21,7 +21,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
this.setTitle('TwoStepVerificationTitle');
const section = new SettingSection({
caption: ' ',
caption: true,
noDelimiter: true
});

View File

@ -5,13 +5,14 @@ import Row from "../../row";
import { Authorization } from "../../../layer";
import { formatDateAccordingToToday } from "../../../helpers/date";
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc";
import { attachClickEvent, findUpClassName, toggleDisability } from "../../../helpers/dom";
import { attachClickEvent, toggleDisability } from "../../../helpers/dom";
import ButtonMenu from "../../buttonMenu";
import apiManager from "../../../lib/mtproto/mtprotoworker";
import { toast } from "../../toast";
import AppPrivacyAndSecurityTab from "./privacyAndSecurity";
import I18n from "../../../lib/langPack";
import PopupPeer from "../../popups/peer";
import findUpClassName from "../../../helpers/dom/findUpClassName";
export default class AppActiveSessionsTab extends SliderSuperTab {
public privacyTab: AppPrivacyAndSecurityTab;

View File

@ -2,8 +2,9 @@ import { generateSection } from "..";
import { averageColor } from "../../../helpers/averageColor";
import blur from "../../../helpers/blur";
import { deferredPromise } from "../../../helpers/cancellablePromise";
import { highlightningColor, rgbToHsl } from "../../../helpers/color";
import { attachClickEvent, findUpClassName } from "../../../helpers/dom";
import { highlightningColor } from "../../../helpers/color";
import { attachClickEvent } from "../../../helpers/dom";
import findUpClassName from "../../../helpers/dom/findUpClassName";
import { AccountWallPapers, WallPaper } from "../../../layer";
import appDocsManager, { MyDocument } from "../../../lib/appManagers/appDocsManager";
import appDownloadManager from "../../../lib/appManagers/appDownloadManager";

View File

@ -1,13 +1,14 @@
import { SliderSuperTab } from "../../slider";
import { SettingSection } from "..";
import { attachContextMenuListener, openBtnMenu, positionMenu } from "../../misc";
import { attachClickEvent, findUpTag } from "../../../helpers/dom";
import { attachClickEvent } from "../../../helpers/dom";
import ButtonMenu from "../../buttonMenu";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager";
import Button from "../../button";
import PopupPickUser from "../../popups/pickUser";
import rootScope from "../../../lib/rootScope";
import findUpTag from "../../../helpers/dom/findUpTag";
export default class AppBlockedUsersTab extends SliderSuperTab {
public peerIds: number[];

View File

@ -167,7 +167,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
}, {listenerSetter: this.listenerSetter});
}
{
/* {
const section = new SettingSection({
});
@ -195,7 +195,7 @@ export default class AppEditGroupTab extends SliderSuperTab {
}
this.scrollable.append(section.container);
}
} */
if(appChatsManager.hasRights(this.chatId, 'delete_chat')) {
const section = new SettingSection({});

View File

@ -5,10 +5,11 @@ import appSidebarRight from "..";
import appUsersManager from "../../../lib/appManagers/appUsersManager";
import appInlineBotsManager, { AppInlineBotsManager } from "../../../lib/appManagers/appInlineBotsManager";
import GifsMasonry from "../../gifsMasonry";
import { findUpClassName, attachClickEvent } from "../../../helpers/dom";
import { attachClickEvent } from "../../../helpers/dom";
import appImManager from "../../../lib/appManagers/appImManager";
import type { MyDocument } from "../../../lib/appManagers/appDocsManager";
import mediaSizes from "../../../helpers/mediaSizes";
import findUpClassName from "../../../helpers/dom/findUpClassName";
const ANIMATIONGROUP = 'GIFS-SEARCH';

View File

@ -1,11 +1,12 @@
import { attachClickEvent, findUpTag } from "../../../helpers/dom";
import { attachClickEvent } from "../../../helpers/dom";
import findUpTag from "../../../helpers/dom/findUpTag";
import ListenerSetter from "../../../helpers/listenerSetter";
import ScrollableLoader from "../../../helpers/listLoader";
import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../layer";
import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appProfileManager from "../../../lib/appManagers/appProfileManager";
import I18n, { i18n } from "../../../lib/langPack";
import I18n, { i18n, LangPackKey } from "../../../lib/langPack";
import rootScope from "../../../lib/rootScope";
import CheckboxField from "../../checkboxField";
import PopupPickUser from "../../popups/pickUser";
@ -18,7 +19,7 @@ import AppUserPermissionsTab from "./userPermissions";
export class ChatPermissions {
public v: Array<{
flags: ChatRights[],
text: string,
text: LangPackKey,
checkboxField?: CheckboxField
}>;
private toggleWith: Partial<{[chatRight in ChatRights]: ChatRights[]}>;

View File

@ -116,7 +116,7 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable {
plainText: true,
listenerSetter: this.listenerSetter,
availableText: 'Link.Available',
invalidText: 'Link is invalid',
invalidText: 'Link.Invalid',
takenText: 'Link.Taken',
onChange: onChange,
peerId: this.peerId,

View File

@ -92,9 +92,9 @@ export default class AppSharedMediaTab extends SliderSuperTab {
this.title.append(i18n('Telegram.PeerInfoController'));
this.editBtn = ButtonIcon('edit');
const moreBtn = ButtonIcon('more');
//const moreBtn = ButtonIcon('more');
transitionFirstItem.append(this.title, this.editBtn, moreBtn);
transitionFirstItem.append(this.title, this.editBtn/* , moreBtn */);
const transitionLastItem = document.createElement('div');
transitionLastItem.classList.add('transition-item');

View File

@ -1,7 +1,7 @@
import { SliderSuperTab } from "../../slider";
import InputSearch from "../../inputSearch";
import LazyLoadQueue from "../../lazyLoadQueue";
import { findUpClassName, attachClickEvent } from "../../../helpers/dom";
import { attachClickEvent } from "../../../helpers/dom";
import appImManager from "../../../lib/appManagers/appImManager";
import appStickersManager from "../../../lib/appManagers/appStickersManager";
import PopupStickers from "../../popups/stickers";
@ -12,6 +12,7 @@ import appSidebarRight from "..";
import { StickerSet, StickerSetCovered } from "../../../layer";
import { forEachReverse } from "../../../helpers/array";
import { i18n } from "../../../lib/langPack";
import findUpClassName from "../../../helpers/dom/findUpClassName";
export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch;

View File

@ -19,7 +19,6 @@ import AudioElement from './audio';
import ReplyContainer from './chat/replyContainer';
import { Layouter, RectPart } from './groupedLayout';
import LazyLoadQueue from './lazyLoadQueue';
import { renderImageFromUrl } from './misc';
import PollElement from './poll';
import ProgressivePreloader from './preloader';
import './middleEllipsis';
@ -30,6 +29,7 @@ import { SearchSuperContext } from './appSearchSuper.';
import rootScope from '../lib/rootScope';
import { onVideoLoad } from '../helpers/files';
import { animateSingle } from '../helpers/animation';
import renderImageFromUrl from '../helpers/dom/renderImageFromUrl';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB

View File

@ -2,7 +2,7 @@ const App = {
id: 1025907,
hash: '452b0359b988148995f22ff0f4229750',
version: '0.4.0',
langPackVersion: '0.0.5',
langPackVersion: '0.0.8',
langPack: 'macos',
langPackCode: 'en',
domains: [] as string[],

View File

@ -1,4 +1,4 @@
import { renderImageFromUrl } from "../components/misc";
import renderImageFromUrl from "./dom/renderImageFromUrl";
export const averageColor = (imageUrl: string): Promise<Uint8ClampedArray> => {
const img = document.createElement('img');

View File

@ -338,56 +338,6 @@ export function generatePathData(x: number, y: number, width: number, height: nu
MOUNT_CLASS_TO.generatePathData = generatePathData;
//export function findUpClassName<T>(el: any, className: string): T;
export function findUpClassName(el: any, className: string): HTMLElement {
return el.closest('.' + className);
/* if(el.classList.contains(className)) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.classList.contains(className))
return el;
}
return null; */
}
export function findUpTag(el: any, tag: string): HTMLElement {
return el.closest(tag);
/* if(el.tagName === tag) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.tagName === tag)
return el;
}
return null; */
}
export function findUpAttribute(el: any, attribute: string): HTMLElement {
return el.closest(`[${attribute}]`);
/* if(el.getAttribute(attribute) !== null) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.getAttribute(attribute) !== null)
return el;
}
return null; */
}
export function findUpAsChild(el: any, parent: any) {
if(el.parentElement === parent) return el;
while(el.parentElement) {
el = el.parentElement;
if(el.parentElement === parent) {
return el;
}
}
return null;
}
export function whichChild(elem: Node) {
if(!elem.parentNode) {
return -1;
@ -499,26 +449,6 @@ export function blurActiveElement() {
return false;
}
export function fixSafariStickyInput(input: HTMLElement) {
input.style.transform = 'translateY(-99999px)';
/* input.style.position = 'fixed';
input.style.top = '-99999px';
input.style.left = '0'; */
input.focus();
setTimeout(() => {
//fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4, undefined, FocusDirection.Static);
/* input.style.position = '';
input.style.top = ''; */
input.style.transform = '';
//fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4, undefined, FocusDirection.Static);
/* setTimeout(() => {
fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4);
}, 50); */
}, 0);
}
export const CLICK_EVENT_NAME: 'mousedown' | 'touchend' | 'click' = (isTouchSupported ? 'mousedown' : 'click') as any;
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
export const attachClickEvent = (elem: HTMLElement, callback: (e: TouchEvent | MouseEvent) => void, options: AttachClickOptions = {}) => {

View File

@ -0,0 +1,12 @@
export default function findUpAsChild(el: any, parent: any) {
if(el.parentElement === parent) return el;
while(el.parentElement) {
el = el.parentElement;
if(el.parentElement === parent) {
return el;
}
}
return null;
}

View File

@ -0,0 +1,11 @@
export default function findUpAttribute(el: any, attribute: string): HTMLElement {
return el.closest(`[${attribute}]`);
/* if(el.getAttribute(attribute) !== null) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.getAttribute(attribute) !== null)
return el;
}
return null; */
}

View File

@ -0,0 +1,12 @@
//export function findUpClassName<T>(el: any, className: string): T;
export default function findUpClassName(el: any, className: string): HTMLElement {
return el.closest('.' + className);
/* if(el.classList.contains(className)) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.classList.contains(className))
return el;
}
return null; */
}

View File

@ -0,0 +1,11 @@
export default function findUpTag(el: any, tag: string): HTMLElement {
return el.closest(tag);
/* if(el.tagName === tag) return el; // 03.02.2020
while(el.parentElement) {
el = el.parentElement;
if(el.tagName === tag)
return el;
}
return null; */
}

View File

@ -0,0 +1,19 @@
export default function fixSafariStickyInput(input: HTMLElement) {
input.style.transform = 'translateY(-99999px)';
/* input.style.position = 'fixed';
input.style.top = '-99999px';
input.style.left = '0'; */
input.focus();
setTimeout(() => {
//fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4, undefined, FocusDirection.Static);
/* input.style.position = '';
input.style.top = ''; */
input.style.transform = '';
//fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4, undefined, FocusDirection.Static);
/* setTimeout(() => {
fastSmoothScroll(findUpClassName(input, 'scrollable-y') || window as any, document.activeElement as HTMLElement, 'start', 4);
}, 50); */
}, 0);
}

View File

@ -0,0 +1,47 @@
export const loadedURLs: {[url: string]: boolean} = {};
const set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {
if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;
else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);
else elem.style.backgroundImage = 'url(' + url + ')';
};
// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.
export default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string, callback?: (err?: Event) => void, useCache = false): boolean {
if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {
if(elem) {
set(elem, url);
}
callback && callback();
return true;
} else {
const isImage = elem instanceof HTMLImageElement;
const loader = isImage ? elem as HTMLImageElement : new Image();
//const loader = new Image();
loader.src = url;
//let perf = performance.now();
loader.addEventListener('load', () => {
if(!isImage && elem) {
set(elem, url);
}
loadedURLs[url] = true;
//console.log('onload:', url, performance.now() - perf);
if(callback) {
// TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются
/* getHeavyAnimationPromise().then(() => {
callback();
}); */
callback();
}
//callback && callback();
});
if(callback) {
loader.addEventListener('error', callback);
}
return false;
}
}

View File

@ -55,24 +55,8 @@
<div class="input-wrapper"></div>
</div>
</div>
<div class="page-password">
<div class="container center-align">
<div class="auth-image"></div>
<h4 class="phone">Enter Your Password</h4>
<p class="subtitle">Your account is protected with<br>an additional password</p>
<div class="input-wrapper"></div>
</div>
</div>
<div class="page-signUp">
<div class="container center-align">
<div class="auth-image avatar-edit">
<canvas class="avatar-edit-canvas" id="canvas-avatar"></canvas>
<span class="tgico tgico-cameraadd"></span>
</div>
<h4 class="fullName">Your Name</h4>
<p class="subtitle">Enter your name and add<br>a profile picture</p>
</div>
</div>
<div class="page-password"></div>
<div class="page-signUp"></div>
</div>
</div>
</div>

View File

@ -87,8 +87,8 @@ const lang = {
"Popup.Unpin.Hide": "Hide",
"TwoStepAuth.InvalidPassword": "Invalid password",
"TwoStepAuth.EmailCodeChangeEmail": "Change Email",
"PleaseWait": "Please wait...",
"MarkupTooltip.LinkPlaceholder": "Enter URL...",
"MediaViewer.Context.Download": "Download",
// * android
"ActionCreateChannel": "Channel created",
@ -358,7 +358,6 @@ const lang = {
"PleaseEnterCurrentPassword": "Enter your password",
"PleaseEnterFirstPassword": "Enter a password",
"PleaseReEnterPassword": "Re-enter your password",
"LoginPassword": "Password",
"Continue": "Continue",
"YourEmailSkip": "Skip",
"YourEmailSkipWarning": "Warning",
@ -391,6 +390,8 @@ const lang = {
"Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.",
"Chat.CopySelectedText": "Copy Selected Text",
"Chat.Confirm.Unpin": "Would you like to unpin this message?",
"Chat.Context.Scheduled.SendNow": "Send Now",
"Chat.Context.Scheduled.Reschedule": "Re-schedule",
"Chat.Date.ScheduledFor": "Scheduled for %@",
//"Chat.Date.ScheduledUntilOnline": "Scheduled until online",
"Chat.Date.ScheduledForToday": "Scheduled for today",
@ -483,6 +484,7 @@ const lang = {
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Telegram.PeerInfoController": "Info",
"Telegram.LanguageViewController": "Language",
"Stickers.SearchAdd": "Add",
"Stickers.SearchAdded": "Added",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
@ -596,8 +598,7 @@ const lang = {
"TwoStepAuth.RecoveryCodeInvalid": "Invalid code. Please try again.",
"TwoStepAuth.RecoveryCodeExpired": "Code Expired",
"TwoStepAuth.SetupHintTitle": "Password Hint",
"TwoStepAuth.SetupHintPlaceholder": "Hint",
"PHONE_CODE_INVALID": "Invalid code",
"TwoStepAuth.SetupHintPlaceholder": "Hint"
};
export default lang;

View File

@ -5,9 +5,22 @@ const lang = {
"Login.PhoneLabelInvalid": "Phone Number Invalid",
"Login.KeepSigned": "Keep me signed in",
"Login.StartText": "Please confirm your country and\nenter your phone number.",
"Login.Code.SentSms": "We have sent you an SMS\nwith the code.",
"Login.Code.SentInApp": "We have sent you a message in Telegram\nwith the code.",
"Login.Code.SentCall": "We will call you and voice\nthe code.",
"Login.Code.SentUnknown": "Please check everything\nfor a code (type: %s)",
"Login.Password.Title": "Enter Your Password",
"Login.Password.Subtitle": "Your account is protected with\nan additional password",
"Login.Register.Subtitle": "Enter your name and add\na profile picture",
"PleaseWait": "Please wait...",
// * android
"Code": "Code",
"LoginPassword": "Password",
"YourName": "Your Name",
"FirstName": "First name (required)",
"LastName": "Last name (optional)",
"StartMessaging": "Start Messaging",
// * macos
"Login.Next": "Next",
@ -18,6 +31,9 @@ const lang = {
"Login.QR.Help3": "Point your phone at this screen to confirm login",
"Login.QR.Cancel": "Log in by phone Number",
"Login.QR.Login": "Log in by QR Code",
"PHONE_CODE_INVALID": "Invalid code",
"PHONE_CODE_EXPIRED": "Code expired",
"PASSWORD_HASH_INVALID": "Incorrect password"
};
export default lang;

4
src/layer.d.ts vendored
View File

@ -6043,7 +6043,9 @@ export namespace LangPackDifference {
lang_code: string,
from_version: number,
version: number,
strings: Array<LangPackString>
strings: Array<LangPackString>,
local?: boolean,
appVersion?: string
};
}

View File

@ -11,7 +11,7 @@ import { isSafari } from "../../helpers/userAgent";
import { logger, LogLevels } from "../logger";
import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope";
import { findUpTag, positionElementByIndex } from "../../helpers/dom";
import { positionElementByIndex } from "../../helpers/dom";
import appImManager from "./appImManager";
import appMessagesManager, { Dialog } from "./appMessagesManager";
import {MyDialogFilter as DialogFilter} from "../storages/filters";
@ -30,6 +30,7 @@ import appNotificationsManager from "./appNotificationsManager";
import { InputNotifyPeer } from "../../layer";
import PeerTitle from "../../components/peerTitle";
import { i18n } from "../langPack";
import findUpTag from "../../helpers/dom/findUpTag";
type DialogDom = {
avatarEl: AvatarElement,

View File

@ -2690,7 +2690,7 @@ export class AppMessagesManager {
} else {
let _ = action._;
//let suffix = '';
let langPackKey: LangPackKey = '';
let langPackKey: LangPackKey;
let args: any[];
const getNameDivHTML = (peerId: number, plain: boolean) => {
@ -2789,14 +2789,14 @@ export class AppMessagesManager {
}
default:
langPackKey = langPack[_] || `[${action._}]`;
langPackKey = (langPack[_] || `[${action._}]`) as any;
break;
}
if(!langPackKey) {
langPackKey = langPack[_];
if(langPackKey === undefined) {
langPackKey = '[' + _ + ']';
langPackKey = '[' + _ + ']' as any;
}
}

View File

@ -11,8 +11,8 @@ import { MyDocument } from "./appDocsManager";
import appDownloadManager from "./appDownloadManager";
import appUsersManager from "./appUsersManager";
import blur from "../../helpers/blur";
import { renderImageFromUrl } from "../../components/misc";
import { MOUNT_CLASS_TO } from "../../config/debug";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
export type MyPhoto = Photo.photo;

View File

@ -406,7 +406,7 @@ export class AppUsersManager {
const user = this.getUser(userId);
if(!user) {
key = '';
key = '' as any;
break;
}
@ -432,8 +432,6 @@ export class AppUsersManager {
}
case 'userStatusOffline': {
key = 'last seen ';
const date = user.status.was_online;
const now = Date.now() / 1000;

View File

@ -42,7 +42,7 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionBotAllowed": "Chat.Service.BotPermissionAllowed"
};
export type LangPackKey = string | keyof typeof lang | keyof typeof langSign;
export type LangPackKey = /* string | */keyof typeof lang | keyof typeof langSign;
namespace I18n {
export const strings: Map<LangPackKey, LangPackString> = new Map();
@ -50,10 +50,11 @@ namespace I18n {
let cacheLangPackPromise: Promise<LangPackDifference>;
export let lastRequestedLangCode: string;
export let requestedServerLanguage = false;
export function getCacheLangPack(): Promise<LangPackDifference> {
if(cacheLangPackPromise) return cacheLangPackPromise;
return cacheLangPackPromise = Promise.all([
sessionStorage.get('langPack'),
sessionStorage.get('langPack') as Promise<LangPackDifference>,
polyfillPromise
]).then(([langPack]) => {
if(!langPack/* || true */) {
@ -91,13 +92,15 @@ namespace I18n {
from_version: 0,
lang_code: defaultCode,
strings,
version: 0
version: 0,
local: true
};
return saveLangPack(langPack);
});
}
export function loadLangPack(langCode: string) {
requestedServerLanguage = true;
return Promise.all([
apiManager.invokeApiCacheable('langpack.getLangPack', {
lang_code: langCode,
@ -164,7 +167,6 @@ namespace I18n {
}
export function saveLangPack(langPack: LangPackDifference) {
// @ts-ignore
langPack.appVersion = App.langPackVersion;
return sessionStorage.set({langPack}).then(() => {

48
src/pages/loginPage.ts Normal file
View File

@ -0,0 +1,48 @@
import { LangPackKey, i18n } from "../lib/langPack";
export default class LoginPage {
public element: HTMLElement;
public container: HTMLElement;
public imageDiv: HTMLElement;
public inputWrapper: HTMLElement;
public title: HTMLElement;
public subtitle: HTMLParagraphElement;
constructor(options: {
className: string,
withInputWrapper?: boolean,
titleLangKey?: LangPackKey,
subtitleLangKey?: LangPackKey,
}) {
this.element = document.body.querySelector('.' + options.className) as HTMLDivElement;
//this.element = document.createElement('div');
//this.element.className = 'page-' + options.className;
this.container = document.createElement('div');
this.container.className = 'container center-align';
this.imageDiv = document.createElement('div');
this.imageDiv.className = 'auth-image';
this.title = document.createElement('h4');
if(options.titleLangKey) {
this.title.append(i18n(options.titleLangKey));
}
this.subtitle = document.createElement('p');
this.subtitle.className = 'subtitle';
if(options.subtitleLangKey) {
this.subtitle.append(i18n(options.subtitleLangKey));
}
this.container.append(this.imageDiv, this.title, this.subtitle);
if(options.withInputWrapper) {
this.inputWrapper = document.createElement('div');
this.inputWrapper.className = 'input-wrapper';
this.container.append(this.inputWrapper);
}
this.element.append(this.container);
}
}

View File

@ -9,6 +9,8 @@ import pageSignIn from './pageSignIn';
import pageSignUp from './pageSignUp';
import TrackingMonkey from '../components/monkeys/tracking';
import CodeInputField from '../components/codeInputField';
import { replaceContent } from '../helpers/dom';
import { i18n, LangPackKey } from '../lib/langPack';
let authCode: AuthSentCode.authSentCode = null;
@ -89,12 +91,12 @@ let onFirstMount = (): Promise<any> => {
break;
case 'PHONE_CODE_EXPIRED':
codeInput.classList.add('error');
codeInputField.label.innerText = 'Code expired';
replaceContent(codeInputField.label, i18n('PHONE_CODE_EXPIRED'));
break;
case 'PHONE_CODE_EMPTY':
case 'PHONE_CODE_INVALID':
codeInput.classList.add('error');
codeInputField.label.innerText = 'Invalid Code';
replaceContent(codeInputField.label, i18n('PHONE_CODE_INVALID'));
break;
default:
codeInputField.label.innerText = err.type;
@ -127,21 +129,25 @@ const page = new Page('page-authCode', true, onFirstMount, (_authCode: typeof au
}
headerElement.innerText = authCode.phone_number;
let key: LangPackKey, args: any[];
switch(authCode.type._) {
case 'auth.sentCodeTypeSms':
sentTypeElement.innerHTML = 'We have sent you an SMS<br>with the code.';
key = 'Login.Code.SentSms';
break;
case 'auth.sentCodeTypeApp':
sentTypeElement.innerHTML = 'We have sent you a message in Telegram<br>with the code.';
key = 'Login.Code.SentInApp';
break;
case 'auth.sentCodeTypeCall':
sentTypeElement.innerHTML = 'We will call you and voice<br>the code.';
key = 'Login.Code.SentCall';
break;
default:
sentTypeElement.innerHTML = `Please check everything<br>for a code (type: ${authCode.type._})`;
key = 'Login.Code.SentUnknown';
args = [authCode.type._];
break;
}
replaceContent(sentTypeElement, i18n(key, args));
appStateManager.pushToState('authState', {_: 'authStateAuthCode', sentCode: _authCode});
appStateManager.saveState();
}, () => {

View File

@ -2,6 +2,7 @@
//import appStateManager from "../lib/appManagers/appStateManager";
import { blurActiveElement } from "../helpers/dom";
import appStateManager from "../lib/appManagers/appStateManager";
import I18n from "../lib/langPack";
import Page from "./page";
let onFirstMount = () => {
@ -14,6 +15,14 @@ let onFirstMount = () => {
m.default.broadcast('im_mount');
});
if(!I18n.requestedServerLanguage) {
I18n.getCacheLangPack().then(langPack => {
if(langPack.local) {
I18n.getLangPack(langPack.lang_code);
}
});
}
blurActiveElement();
return new Promise<void>((resolve) => {
window.requestAnimationFrame(() => {

View File

@ -8,23 +8,35 @@ import pageIm from './pageIm';
import Button from '../components/button';
import PasswordInputField from '../components/passwordInputField';
import PasswordMonkey from '../components/monkeys/password';
import { ripple } from '../components/ripple';
import RichTextProcessor from '../lib/richtextprocessor';
import I18n from '../lib/langPack';
import LoginPage from './loginPage';
import { replaceContent } from '../helpers/dom';
const TEST = false;
let passwordInput: HTMLInputElement;
let onFirstMount = (): Promise<any> => {
const btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'});
const page = new LoginPage({
className: 'page-password',
withInputWrapper: true,
titleLangKey: 'Login.Password.Title',
subtitleLangKey: 'Login.Password.Subtitle'
});
const btnNext = Button('btn-primary btn-color-primary');
const btnNextI18n = new I18n.IntlElement({key: 'Login.Next'});
btnNext.append(btnNextI18n.element);
const passwordInputField = new PasswordInputField({
label: 'Password',
label: 'LoginPassword',
name: 'password'
});
passwordInput = passwordInputField.input as HTMLInputElement;
page.pageEl.querySelector('.input-wrapper').append(passwordInputField.container, btnNext);
page.inputWrapper.append(passwordInputField.container, btnNext);
let getStateInterval: number;
@ -37,24 +49,14 @@ let onFirstMount = (): Promise<any> => {
return !TEST && passwordManager.getState().then(_state => {
state = _state;
passwordInputField.label.innerHTML = state.hint ? RichTextProcessor.wrapEmojiText(state.hint) : 'Password';
if(state.hint) {
replaceContent(passwordInputField.label, RichTextProcessor.wrapEmojiText(state.hint));
} else {
passwordInputField.setLabel();
}
});
};
let handleError = (err: any) => {
btnNext.removeAttribute('disabled');
passwordInputField.input.classList.add('error');
switch(err.type) {
default:
//btnNext.innerText = err.type;
btnNext.innerText = 'INVALID PASSWORD';
break;
}
getState();
};
let state: AccountPassword;
btnNext.addEventListener('click', function(this, e) {
@ -66,8 +68,8 @@ let onFirstMount = (): Promise<any> => {
this.setAttribute('disabled', 'true');
let value = passwordInput.value;
this.textContent = 'PLEASE WAIT...';
putPreloader(this);
btnNextI18n.update({key: 'PleaseWait'});
const preloader = putPreloader(this);
passwordManager.check(value, state).then((response) => {
//console.log('passwordManager response:', response);
@ -80,17 +82,30 @@ let onFirstMount = (): Promise<any> => {
break;
default:
btnNext.removeAttribute('disabled');
btnNext.innerText = response._;
ripple(btnNext);
btnNextI18n.update({key: response._ as any});
preloader.remove();
break;
}
}).catch(handleError);
}).catch((err: any) => {
btnNext.removeAttribute('disabled');
passwordInputField.input.classList.add('error');
switch(err.type) {
default:
//btnNext.innerText = err.type;
btnNextI18n.update({key: 'PASSWORD_HASH_INVALID'});
break;
}
preloader.remove();
getState();
});
});
passwordInput.addEventListener('keypress', function(this, e) {
this.classList.remove('error');
btnNext.innerText = 'NEXT';
ripple(btnNext);
btnNextI18n.update({key: 'Login.Next'});
if(e.key === 'Enter') {
return btnNext.click();
@ -99,7 +114,7 @@ let onFirstMount = (): Promise<any> => {
const size = mediaSizes.isMobile ? 100 : 166;
const monkey = new PasswordMonkey(passwordInputField, size);
page.pageEl.querySelector('.auth-image').append(monkey.container);
page.imageDiv.append(monkey.container);
return Promise.all([
monkey.load(),
getState()

View File

@ -4,7 +4,7 @@ import Countries, { Country as _Country } from "../countries";
import appStateManager from "../lib/appManagers/appStateManager";
import apiManager from "../lib/mtproto/mtprotoworker";
import { RichTextProcessor } from '../lib/richtextprocessor';
import { findUpTag, attachClickEvent, cancelEvent, replaceContent } from "../helpers/dom";
import { attachClickEvent, cancelEvent, replaceContent } from "../helpers/dom";
import Page from "./page";
import pageAuthCode from "./pageAuthCode";
import InputField from "../components/inputField";
@ -15,9 +15,11 @@ import fastSmoothScroll from "../helpers/fastSmoothScroll";
import { isTouchSupported } from "../helpers/touchSupport";
import App from "../config/app";
import Modes from "../config/modes";
import I18n, { _i18n, i18n } from "../lib/langPack";
import I18n, { _i18n, i18n, LangPackKey } from "../lib/langPack";
import { LangPackString } from "../layer";
import lottieLoader from "../lib/lottieLoader";
import { ripple } from "../components/ripple";
import findUpTag from "../helpers/dom/findUpTag";
type Country = _Country & {
li?: HTMLLIElement[]
@ -370,7 +372,9 @@ let onFirstMount = () => {
setTimeout(() => {
btnQr.removeAttribute('disabled');
preloaderDiv.remove();
if(preloaderDiv) {
preloaderDiv.remove();
}
}, 200);
});
});
@ -442,20 +446,20 @@ let onFirstMount = () => {
]).then(res => {
const backup: LangPackString[] = [];
res[0].forEach(string => {
const backupString = I18n.strings.get(string.key);
const backupString = I18n.strings.get(string.key as LangPackKey);
if(!backupString) {
return;
}
backup.push(backupString);
I18n.strings.set(string.key, string);
I18n.strings.set(string.key as LangPackKey, string);
});
const btnChangeLanguage = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'Login.ContinueOnLanguage'});
inputWrapper.append(btnChangeLanguage);
backup.forEach(string => {
I18n.strings.set(string.key, string);
I18n.strings.set(string.key as LangPackKey, string);
});
attachClickEvent(btnChangeLanguage, (e) => {
@ -478,6 +482,7 @@ let onFirstMount = () => {
const page = new Page('page-sign', true, onFirstMount, () => {
if(btnNext) {
replaceContent(btnNext, i18n('Login.Next'));
ripple(btnNext, undefined, undefined, true);
btnNext.removeAttribute('disabled');
}

View File

@ -10,7 +10,7 @@ import { bytesCmp, bytesToBase64 } from '../helpers/bytes';
import { pause } from '../helpers/schedulers';
import App from '../config/app';
import Button from '../components/button';
import { _i18n, i18n } from '../lib/langPack';
import { _i18n, i18n, LangPackKey } from '../lib/langPack';
let onFirstMount = async() => {
const pageElement = page.pageEl;
@ -29,7 +29,7 @@ let onFirstMount = async() => {
const helpList = document.createElement('ol');
helpList.classList.add('qr-description');
['Login.QR.Help1', 'Login.QR.Help2', 'Login.QR.Help3'].forEach((key) => {
(['Login.QR.Help1', 'Login.QR.Help2', 'Login.QR.Help3'] as LangPackKey[]).forEach((key) => {
const li = document.createElement('li');
li.append(i18n(key));
helpList.append(li);

View File

@ -1,40 +1,62 @@
import type { CancellablePromise } from '../helpers/cancellablePromise';
import type { InputFile } from '../layer';
import type { AuthState } from '../types';
import Button from '../components/button';
import InputField from '../components/inputField';
import { putPreloader } from '../components/misc';
import PopupAvatar from '../components/popups/avatar';
import { replaceContent } from '../helpers/dom';
import appStateManager from '../lib/appManagers/appStateManager';
import I18n, { i18n } from '../lib/langPack';
//import apiManager from '../lib/mtproto/apiManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import RichTextProcessor from '../lib/richtextprocessor';
import { AuthState } from '../types';
import LoginPage from './loginPage';
import Page from './page';
import pageIm from './pageIm';
let authCode: AuthState.signUp['authCode'] = null;
const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(imported => {
const pageElement = page.pageEl;
const avatarPreview = pageElement.querySelector('#canvas-avatar') as HTMLCanvasElement;
const page = new LoginPage({
className: 'page-signUp',
withInputWrapper: true,
titleLangKey: 'YourName',
subtitleLangKey: 'Login.Register.Subtitle'
});
page.imageDiv.classList.add('avatar-edit');
page.title.classList.add('fullName');
const avatarPreview = document.createElement('canvas');
avatarPreview.id = 'canvas-avatar';
avatarPreview.className = 'avatar-edit-canvas';
const addIco = document.createElement('span');
addIco.className = 'tgico tgico-cameraadd';
page.imageDiv.append(avatarPreview, addIco);
const appProfileManager = imported.default;
let uploadAvatar: () => Promise<any>;
pageElement.querySelector('.auth-image').addEventListener('click', () => {
let uploadAvatar: () => CancellablePromise<InputFile>;
page.imageDiv.addEventListener('click', () => {
new PopupAvatar().open(avatarPreview, (_uploadAvatar) => {
uploadAvatar = _uploadAvatar;
});
});
const headerName = pageElement.getElementsByClassName('fullName')[0] as HTMLHeadingElement;
const handleInput = (e: Event) => {
const name = nameInputField.value || '';
const lastName = lastNameInputField.value || '';
const fullName = name || lastName
? (name + ' ' + lastName).trim()
: 'Your Name';
: '';
if(headerName.innerHTML !== fullName) headerName.innerHTML = RichTextProcessor.wrapEmojiText(fullName);
if(fullName) replaceContent(page.title, RichTextProcessor.wrapEmojiText(fullName));
else replaceContent(page.title, i18n('YourName'));
};
let sendAvatar = () => new Promise<void>((resolve, reject) => {
@ -44,32 +66,28 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
}
//console.log('invoking uploadFile...');
uploadAvatar().then((inputFile: any) => {
uploadAvatar().then((inputFile) => {
//console.log('uploaded smthn', inputFile);
appProfileManager.uploadProfilePhoto(inputFile).then(resolve, reject);
}, reject);
});
const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper');
const nameInputField = new InputField({
label: 'Name',
label: 'FirstName',
maxLength: 70
});
const lastNameInputField = new InputField({
label: 'Last Name (optional)',
label: 'LastName',
maxLength: 64
});
const btnSignUp = Button('btn-primary btn-color-primary');
btnSignUp.append('START MESSAGING');
const btnI18n = new I18n.IntlElement({key: 'StartMessaging'});
btnSignUp.append(btnI18n.element);
inputWrapper.append(nameInputField.container, lastNameInputField.container, btnSignUp);
headerName.parentElement.append(inputWrapper);
page.inputWrapper.append(nameInputField.container, lastNameInputField.container, btnSignUp);
nameInputField.input.addEventListener('input', handleInput);
lastNameInputField.input.addEventListener('input', handleInput);
@ -84,7 +102,7 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
return false;
}
this.setAttribute('disabled', 'true');
this.disabled = true;
const name = nameInputField.value.trim();
const lastName = lastNameInputField.value.trim();
@ -98,8 +116,8 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
//console.log('invoking auth.signUp with params:', params);
this.textContent = 'PLEASE WAIT...';
putPreloader(this);
btnI18n.update({key: 'PleaseWait'});
const preloader = putPreloader(this);
apiManager.invokeApi('auth.signUp', params)
.then((response) => {
@ -115,7 +133,9 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
break;
default:
this.innerText = response._;
btnI18n.update({key: response._ as any});
this.removeAttribute('disabled');
preloader.remove();
break;
}
@ -123,10 +143,11 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
pageAuthCode(Object.assign(code, {phoneNumber})); */
}).catch(err => {
this.removeAttribute('disabled');
preloader.remove();
switch(err.type) {
default:
this.innerText = err.type;
btnI18n.update({key: err.type});
break;
}
});

View File

@ -208,4 +208,10 @@
{"name": "users", "type": "Array<number>"}
],
"type": "MessageAction"
}, {
"predicate": "langPackDifference",
"params": [
{"name": "local", "type": "boolean"},
{"name": "appVersion", "type": "string"}
]
}]

View File

@ -102,7 +102,7 @@ $bubble-margin: .25rem;
margin-left: -50%;
margin-right: -50%;
text-align: center;
color: var(--primary-text-color);
color: var(--primary-color);
line-height: 2.1;
font-weight: 500;
font-size: 15px;