diff --git a/src/components/editPeer.ts b/src/components/editPeer.ts index 819e14ff..8b2a1068 100644 --- a/src/components/editPeer.ts +++ b/src/components/editPeer.ts @@ -24,37 +24,94 @@ export default class EditPeer { private peerId: number; + private _disabled = false; + private avatarSize = 120; + constructor(options: { - peerId: number, + peerId?: number, inputFields: EditPeer['inputFields'], listenerSetter: ListenerSetter, doNotEditAvatar?: boolean, + withoutAvatar?: boolean, + nextBtn?: HTMLButtonElement, + avatarSize?: number }) { safeAssign(this, options); - this.nextBtn = ButtonCorner({icon: 'check'}); - - this.avatarElem = document.createElement('avatar-element') as AvatarElement; - this.avatarElem.classList.add('avatar-placeholder', 'avatar-120'); - this.avatarElem.setAttribute('peer', '' + this.peerId); - - if(!options.doNotEditAvatar) { - this.avatarEdit = new AvatarEdit((_upload) => { - this.uploadAvatar = _upload; - this.handleChange(); - this.avatarElem.remove(); - }); + if(!this.nextBtn) { + this.nextBtn = ButtonCorner({icon: 'check'}); + } else if(!this.nextBtn.classList.contains('btn-corner')) { + this.handleChange = () => { + this.nextBtn.toggleAttribute('disabled', !this.isChanged() || this.disabled); + }; + } - this.avatarEdit.container.append(this.avatarElem); + if(!options.withoutAvatar) { + this.avatarElem = document.createElement('avatar-element') as AvatarElement; + this.avatarElem.classList.add('avatar-placeholder', 'avatar-' + this.avatarSize); + this.avatarElem.setAttribute('peer', '' + this.peerId); + + if(!options.doNotEditAvatar) { + this.avatarEdit = new AvatarEdit((_upload) => { + this.uploadAvatar = _upload; + this.handleChange(); + this.avatarElem.remove(); + }); + + this.avatarEdit.container.append(this.avatarElem); + } } this.inputFields.forEach(inputField => { this.listenerSetter.add(inputField.input)('input', this.handleChange); }); + + this.handleChange(); + } + + public get disabled() { + return this._disabled; + } + + public set disabled(value) { + this._disabled = value; + this.inputFields.forEach(inputField => inputField.input.toggleAttribute('disabled', value)); + this.handleChange(); + } + + public lockWithPromise(promise: Promise, unlockOnSuccess = false) { + this.disabled = true; + promise.then(() => { + if(unlockOnSuccess) { + this.disabled = false; + } + }, () => { + this.disabled = false; + }); } public isChanged = () => { - return !!this.uploadAvatar || !!this.inputFields.find(inputField => inputField.isValid()); + if(this.uploadAvatar) { + return true; + } + + let validLength = 0, requiredLength = 0, requiredValidLength = 0; + this.inputFields.forEach(inputField => { + const isValid = inputField.isValid(); + if(isValid) { + ++validLength; + + if(inputField.required) { + ++requiredValidLength; + } + } + + if(inputField.required) { + ++requiredLength; + } + }); + + return requiredLength === requiredValidLength && validLength > 0; }; public handleChange = () => { diff --git a/src/components/inputField.ts b/src/components/inputField.ts index e23995ed..a60301f2 100644 --- a/src/components/inputField.ts +++ b/src/components/inputField.ts @@ -75,7 +75,9 @@ export type InputFieldOptions = { maxLength?: number, showLengthOn?: number, plainText?: true, - animate?: true + animate?: true, + required?: boolean, + validate?: () => boolean }; class InputField { @@ -86,6 +88,9 @@ class InputField { public originalValue: string; + public required: boolean; + public validate: () => boolean; + //public onLengthChange: (length: number, isOverflow: boolean) => void; protected wasInputFakeClientHeight: number; // protected showScrollDebounced: () => void; @@ -94,6 +99,13 @@ class InputField { this.container = document.createElement('div'); this.container.classList.add('input-field'); + this.required = options.required; + this.validate = options.validate; + + if(this.required) { + this.originalValue = ''; + } + if(options.maxLength) { options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3)); } @@ -259,8 +271,12 @@ class InputField { } } + public isChanged() { + return this.value !== this.originalValue; + } + public isValid() { - return !this.input.classList.contains('error') && this.value !== this.originalValue; + return !this.input.classList.contains('error') && this.isChanged() && (!this.validate || this.validate()); } public setOriginalValue(value: InputField['originalValue'] = '', silent = false) { diff --git a/src/components/popups/createContact.ts b/src/components/popups/createContact.ts new file mode 100644 index 00000000..66d2fff5 --- /dev/null +++ b/src/components/popups/createContact.ts @@ -0,0 +1,88 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import InputField from "../inputField"; +import PopupElement from "."; +import { attachClickEvent } from "../../helpers/dom/clickEvent"; +import EditPeer from "../editPeer"; +import { _i18n } from "../../lib/langPack"; +import TelInputField from "../telInputField"; +import appUsersManager from "../../lib/appManagers/appUsersManager"; +import { formatPhoneNumber } from "../../helpers/formatPhoneNumber"; +import { toastNew } from "../toast"; + +export default class PopupCreateContact extends PopupElement { + constructor() { + super('popup-create-contact popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Add'}); + + _i18n(this.title, 'AddContactTitle'); + + attachClickEvent(this.btnConfirm, () => { + const promise = appUsersManager.importContact(nameInputField.value, lastNameInputField.value, telInputField.value); + + promise.then(() => { + this.hide(); + }, (err) => { + if(err.type === 'NO_USER') { + toastNew({langPackKey: 'Contacts.PhoneNumber.NotRegistred'}); + editPeer.disabled = false; + } + }); + + editPeer.lockWithPromise(promise); + }, {listenerSetter: this.listenerSetter}); + + const inputFields: InputField[] = []; + const div = document.createElement('div'); + div.classList.add('name-fields'); + const nameInputField = new InputField({ + label: 'FirstName', + name: 'create-contact-name', + maxLength: 70, + required: true + }); + const lastNameInputField = new InputField({ + label: 'LastName', + name: 'create-contact-lastname', + maxLength: 70 + }); + const telInputField = new TelInputField({required: true}); + inputFields.push(nameInputField, lastNameInputField, telInputField); + + const onInput = () => { + const name = nameInputField.value + ' ' + lastNameInputField.value; + // const abbr = RichTextProcessor.getAbbreviation(name); + editPeer.avatarElem.setAttribute('peer-title', name); + editPeer.avatarElem.update(); + }; + + this.listenerSetter.add(nameInputField.input)('input', onInput); + this.listenerSetter.add(lastNameInputField.input)('input', onInput); + + const user = appUsersManager.getSelf(); + const formatted = formatPhoneNumber(user.phone); + if(formatted) { + telInputField.validate = () => { + return !!telInputField.value.match(/\d/); + }; + + telInputField.value = '+' + formatted.code.country_code; + } + + const editPeer = new EditPeer({ + inputFields, + listenerSetter: this.listenerSetter, + doNotEditAvatar: true, + nextBtn: this.btnConfirm, + avatarSize: 100 + }); + + div.append(nameInputField.container, lastNameInputField.container, editPeer.avatarElem); + this.container.append(div, telInputField.container); + + this.show(); + } +} diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index 71d9dc36..6d867493 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -11,6 +11,7 @@ import appNavigationController, { NavigationItem } from "../appNavigationControl import { i18n, LangPackKey } from "../../lib/langPack"; import findUpClassName from "../../helpers/dom/findUpClassName"; import blurActiveElement from "../../helpers/dom/blurActiveElement"; +import ListenerSetter from "../../helpers/listenerSetter"; export type PopupButton = { text?: string, @@ -34,7 +35,7 @@ export default class PopupElement { protected header = document.createElement('div'); protected title = document.createElement('div'); protected btnClose: HTMLElement; - protected btnConfirm: HTMLElement; + protected btnConfirm: HTMLButtonElement; protected body: HTMLElement; protected buttons: HTMLElement; @@ -44,6 +45,8 @@ export default class PopupElement { protected navigationItem: NavigationItem; + protected listenerSetter: ListenerSetter; + constructor(className: string, buttons?: Array, options: PopupOptions = {}) { this.element.classList.add('popup'); this.element.className = 'popup' + (className ? ' ' + className : ''); @@ -54,6 +57,8 @@ export default class PopupElement { this.header.append(this.title); + this.listenerSetter = new ListenerSetter(); + if(options.closable) { this.btnClose = document.createElement('span'); this.btnClose.classList.add('btn-icon', 'popup-close', 'tgico-close'); @@ -157,6 +162,7 @@ export default class PopupElement { this.onClose && this.onClose(); this.element.classList.add('hiding'); this.element.classList.remove('active'); + this.listenerSetter.removeAll(); if(this.btnClose) this.btnClose.removeEventListener('click', this.hide); rootScope.isOverlayActive = false; diff --git a/src/components/sidebarLeft/tabs/addContact.ts b/src/components/sidebarLeft/tabs/addContact.ts deleted file mode 100644 index f3e68b2e..00000000 --- a/src/components/sidebarLeft/tabs/addContact.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * https://github.com/morethanwords/tweb - * Copyright (C) 2019-2021 Eduard Kuzmenko - * https://github.com/morethanwords/tweb/blob/master/LICENSE - */ - -import { SliderSuperTab } from "../../slider"; - -export default class AppAddContactTab extends SliderSuperTab { - protected init() { - this.container.classList.add('add-contact-container'); - this.setTitle('AddContactTitle'); - } -} diff --git a/src/components/sidebarLeft/tabs/contacts.ts b/src/components/sidebarLeft/tabs/contacts.ts index 14e49928..b2fb2d0e 100644 --- a/src/components/sidebarLeft/tabs/contacts.ts +++ b/src/components/sidebarLeft/tabs/contacts.ts @@ -7,11 +7,13 @@ import { SliderSuperTab } from "../../slider"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; -import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; import InputSearch from "../../inputSearch"; import { isMobile } from "../../../helpers/userAgent"; import { canFocus } from "../../../helpers/dom/canFocus"; import windowSize from "../../../helpers/windowSize"; +import ButtonCorner from "../../buttonCorner"; +import { attachClickEvent } from "../../../helpers/dom/clickEvent"; +import PopupCreateContact from "../../popups/createContact"; // TODO: поиск по людям глобальный, если не нашло в контактах никого @@ -29,8 +31,15 @@ export default class AppContactsTab extends SliderSuperTab { this.list.id = 'contacts'; this.list.classList.add('contacts-container'); + const btnAdd = ButtonCorner({icon: 'add', className: 'is-visible'}); + this.content.append(btnAdd); + + attachClickEvent(btnAdd, () => { + new PopupCreateContact(); + }, {listenerSetter: this.listenerSetter}); + appDialogsManager.setListClickListener(this.list, () => { - (this.container.querySelector('.sidebar-close-button') as HTMLElement).click(); + this.close(); }, undefined, true); this.inputSearch = new InputSearch('Search', (value) => { diff --git a/src/components/sidebarRight/tabs/editContact.ts b/src/components/sidebarRight/tabs/editContact.ts index 116a4b12..666f6cd6 100644 --- a/src/components/sidebarRight/tabs/editContact.ts +++ b/src/components/sidebarRight/tabs/editContact.ts @@ -31,7 +31,7 @@ export default class AppEditContactTab extends SliderSuperTab { protected init() { this.container.classList.add('edit-peer-container', 'edit-contact-container'); - this.setTitle('Edit'); + this.setTitle(this.peerId ? 'Edit' : 'AddContactTitle'); { const section = new SettingSection({noDelimiter: true}); @@ -50,14 +50,15 @@ export default class AppEditContactTab extends SliderSuperTab { name: 'contact-lastname', maxLength: 70 }); - - const user = appUsersManager.getUser(this.peerId); - this.nameInputField.setOriginalValue(user.first_name); - this.lastNameInputField.setOriginalValue(user.last_name); + if(this.peerId) { + const user = appUsersManager.getUser(this.peerId); - inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container); + this.nameInputField.setOriginalValue(user.first_name); + this.lastNameInputField.setOriginalValue(user.last_name); + } + inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container); inputFields.push(this.nameInputField, this.lastNameInputField); this.editPeer = new EditPeer({ @@ -68,52 +69,56 @@ export default class AppEditContactTab extends SliderSuperTab { }); this.content.append(this.editPeer.nextBtn); - const div = document.createElement('div'); - div.classList.add('avatar-edit'); - div.append(this.editPeer.avatarElem); - - const notificationsCheckboxField = new CheckboxField({ - text: 'Notifications' - }); - - notificationsCheckboxField.input.addEventListener('change', (e) => { - if(!e.isTrusted) { - return; - } - - appMessagesManager.mutePeer(this.peerId); - }); - - this.listenerSetter.add(rootScope)('notify_settings', (update) => { - if(update.peer._ !== 'notifyPeer') return; - const peerId = appPeersManager.getPeerId(update.peer.peer); - if(this.peerId === peerId) { - const enabled = !appNotificationsManager.isMuted(update.notify_settings); - if(enabled !== notificationsCheckboxField.checked) { - notificationsCheckboxField.checked = enabled; + if(this.peerId) { + const div = document.createElement('div'); + div.classList.add('avatar-edit'); + div.append(this.editPeer.avatarElem); + + const notificationsCheckboxField = new CheckboxField({ + text: 'Notifications' + }); + + notificationsCheckboxField.input.addEventListener('change', (e) => { + if(!e.isTrusted) { + return; } - } - }); - - const notificationsRow = new Row({ - checkboxField: notificationsCheckboxField - }); - - const enabled = !appNotificationsManager.isPeerLocalMuted(this.peerId, false); - notificationsCheckboxField.checked = enabled; - - const profileNameDiv = document.createElement('div'); - profileNameDiv.classList.add('profile-name'); - profileNameDiv.append(new PeerTitle({ - peerId: this.peerId - }).element); - //profileNameDiv.innerHTML = 'Karen Stanford'; - - const profileSubtitleDiv = document.createElement('div'); - profileSubtitleDiv.classList.add('profile-subtitle'); - profileSubtitleDiv.append(i18n('EditContact.OriginalName')); + + appMessagesManager.mutePeer(this.peerId); + }); + + this.listenerSetter.add(rootScope)('notify_settings', (update) => { + if(update.peer._ !== 'notifyPeer') return; + const peerId = appPeersManager.getPeerId(update.peer.peer); + if(this.peerId === peerId) { + const enabled = !appNotificationsManager.isMuted(update.notify_settings); + if(enabled !== notificationsCheckboxField.checked) { + notificationsCheckboxField.checked = enabled; + } + } + }); + + const notificationsRow = new Row({ + checkboxField: notificationsCheckboxField + }); + + const enabled = !appNotificationsManager.isPeerLocalMuted(this.peerId, false); + notificationsCheckboxField.checked = enabled; + + const profileNameDiv = document.createElement('div'); + profileNameDiv.classList.add('profile-name'); + profileNameDiv.append(new PeerTitle({ + peerId: this.peerId + }).element); + //profileNameDiv.innerHTML = 'Karen Stanford'; + + const profileSubtitleDiv = document.createElement('div'); + profileSubtitleDiv.classList.add('profile-subtitle'); + profileSubtitleDiv.append(i18n('EditContact.OriginalName')); - section.content.append(div, profileNameDiv, profileSubtitleDiv, inputWrapper, notificationsRow.container); + section.content.append(div, profileNameDiv, profileSubtitleDiv, inputWrapper, notificationsRow.container); + } else { + section.content.append(inputWrapper); + } this.scrollable.append(section.container); @@ -128,7 +133,7 @@ export default class AppEditContactTab extends SliderSuperTab { }, {listenerSetter: this.listenerSetter}); } - { + if(this.peerId) { const section = new SettingSection({ }); diff --git a/src/components/telInputField.ts b/src/components/telInputField.ts new file mode 100644 index 00000000..87787467 --- /dev/null +++ b/src/components/telInputField.ts @@ -0,0 +1,110 @@ +/* + * https://github.com/morethanwords/tweb + * Copyright (C) 2019-2021 Eduard Kuzmenko + * https://github.com/morethanwords/tweb/blob/master/LICENSE + */ + +import placeCaretAtEnd from "../helpers/dom/placeCaretAtEnd"; +import { formatPhoneNumber } from "../helpers/formatPhoneNumber"; +import { isApple, isAndroid, isAppleMobile } from "../helpers/userAgent"; +import { HelpCountry, HelpCountryCode } from "../layer"; +import InputField, { InputFieldOptions } from "./inputField"; + +export default class TelInputField extends InputField { + private pasted = false; + public lastValue = ''; + + constructor(options: InputFieldOptions & { + onInput?: (formatted: ReturnType) => void + } = {}) { + super({ + label: 'Contacts.PhoneNumber.Placeholder', + //plainText: true, + name: 'phone', + ...options + }); + + this.container.classList.add('input-field-phone'); + + let telEl = this.input; + if(telEl instanceof HTMLInputElement) { + telEl.type = 'tel'; + telEl.autocomplete = 'rr55RandomRR55'; + } else { + telEl.inputMode = 'decimal'; + + const pixelRatio = window.devicePixelRatio; + if(pixelRatio > 1) { + let letterSpacing: number; + if(isApple) { + letterSpacing = pixelRatio * -.16; + } else if(isAndroid) { + letterSpacing = 0; + } + + telEl.style.setProperty('--letter-spacing', letterSpacing + 'px'); + } + + const originalFunc = this.setValueSilently.bind(this); + this.setValueSilently = (value) => { + originalFunc(value); + placeCaretAtEnd(this.input, true); + }; + } + + telEl.addEventListener('input', () => { + //console.log('input', this.value); + telEl.classList.remove('error'); + + const value = this.value; + const diff = Math.abs(value.length - this.lastValue.length); + if(diff > 1 && !this.pasted && isAppleMobile) { + this.setValueSilently(this.lastValue + value); + } + + this.pasted = false; + + this.setLabel(); + + let formattedPhoneNumber: ReturnType; + let formatted: string, country: HelpCountry, countryCode: HelpCountryCode, leftPattern = ''; + if(this.value.replace(/\++/, '+') === '+') { + this.setValueSilently('+'); + } else { + formattedPhoneNumber = formatPhoneNumber(this.value); + formatted = formattedPhoneNumber.formatted; + country = formattedPhoneNumber.country; + leftPattern = formattedPhoneNumber.leftPattern; + countryCode = formattedPhoneNumber.code; + this.setValueSilently(this.lastValue = formatted ? '+' + formatted : ''); + } + + telEl.dataset.leftPattern = leftPattern/* .replace(/X/g, '0') */; + + //console.log(formatted, country); + + options.onInput && options.onInput(formattedPhoneNumber); + }); + + telEl.addEventListener('paste', () => { + this.pasted = true; + //console.log('paste', telEl.value); + }); + + /* telEl.addEventListener('change', (e) => { + console.log('change', telEl.value); + }); */ + + telEl.addEventListener('keypress', (e) => { + //console.log('keypress', this.value); + if(/\D/.test(e.key) && !(e.metaKey || e.ctrlKey) && e.key !== 'Backspace' && !(e.key === '+' && e.shiftKey/* && !this.value */)) { + e.preventDefault(); + return false; + } + }); + + /* telEl.addEventListener('focus', function(this: typeof telEl, e) { + this.removeAttribute('readonly'); // fix autocomplete + });*/ + } +} diff --git a/src/lang.ts b/src/lang.ts index 7d772b2f..a3cae116 100644 --- a/src/lang.ts +++ b/src/lang.ts @@ -570,6 +570,7 @@ const lang = { "Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.", "Appearance.Reset": "Reset to Defaults", "Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco", + "Contacts.PhoneNumber.NotRegistred": "The person with this phone number is not registered on Telegram yet.", "Channel.UsernameAboutChannel": "People can share this link with others and can find your channel using Telegram search.", "Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.", "Chat.CopySelectedText": "Copy Selected Text", diff --git a/src/langSign.ts b/src/langSign.ts index 067bc084..06d91fba 100644 --- a/src/langSign.ts +++ b/src/langSign.ts @@ -23,6 +23,7 @@ const lang = { "StartMessaging": "Start Messaging", // * macos + "Contacts.PhoneNumber.Placeholder": "Phone Number", "Login.Next": "Next", "Login.ContinueOnLanguage": "Continue in English", "Login.QR.Title": "Log in to Telegram by QR Code", diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index 990df65f..3bf19fc3 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -17,7 +17,7 @@ import cleanUsername from "../../helpers/cleanUsername"; import { tsNow } from "../../helpers/date"; import { formatPhoneNumber } from "../../helpers/formatPhoneNumber"; import { safeReplaceObject, isObject } from "../../helpers/object"; -import { Chat, InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer"; +import { Chat, InputContact, InputUser, User as MTUser, UserProfilePhoto, UserStatus } from "../../layer"; import I18n, { i18n, LangPackKey } from "../langPack"; //import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/mtprotoworker'; @@ -680,75 +680,50 @@ export class AppUsersManager { } } - /* function importContact (phone, firstName, lastName) { - return MtpApiManager.invokeApi('contacts.importContacts', { - contacts: [{ - _: 'inputPhoneContact', - client_id: '1', - phone: phone, - first_name: firstName, - last_name: lastName - }], - replace: false - }).then(function (importedContactsResult) { - saveApiUsers(importedContactsResult.users) - - var foundUserID = false - angular.forEach(importedContactsResult.imported, function (importedContact) { - onContactUpdated(foundUserID = importedContact.user_id, true) - }) - - return foundUserID || false - }) + public importContact(first_name: string, last_name: string, phone: string) { + return this.importContacts([{ + first_name, + last_name, + phones: [phone] + }]).then(userIds => { + if(!userIds.length) { + const error = new Error(); + (error as any).type = 'NO_USER'; + throw error; + } + + return userIds[0]; + }); } - function importContacts (contacts) { - var inputContacts = [], - i - var j + public importContacts(contacts: {phones: string[], first_name: string, last_name: string}[]) { + const inputContacts: InputContact[] = []; - for (i = 0; i < contacts.length; i++) { - for (j = 0; j < contacts[i].phones.length; j++) { + for(let i = 0; i < contacts.length; ++i) { + for(let j = 0; j < contacts[i].phones.length; ++j) { inputContacts.push({ _: 'inputPhoneContact', client_id: (i << 16 | j).toString(10), phone: contacts[i].phones[j], first_name: contacts[i].first_name, last_name: contacts[i].last_name - }) + }); } } - return MtpApiManager.invokeApi('contacts.importContacts', { - contacts: inputContacts, - replace: false - }).then(function (importedContactsResult) { - saveApiUsers(importedContactsResult.users) - - var result = [] - angular.forEach(importedContactsResult.imported, function (importedContact) { - onContactUpdated(importedContact.user_id, true) - result.push(importedContact.user_id) - }) + return apiManager.invokeApi('contacts.importContacts', { + contacts: inputContacts + }).then((importedContactsResult) => { + this.saveApiUsers(importedContactsResult.users); - return result - }) - } */ - - /* public deleteContacts(userIds: number[]) { - var ids: any[] = []; - userIds.forEach((userId) => { - ids.push(this.getUserInput(userId)); - }) - - return apiManager.invokeApi('contacts.deleteContacts', { - id: ids - }).then(() => { - userIds.forEach((userId) => { - this.onContactUpdated(userId, false); + const userIds = importedContactsResult.imported.map((importedContact) => { + this.onContactUpdated(importedContact.user_id, true); + return importedContact.user_id; }); + + return userIds; }); - } */ + } public getTopPeers(type: TopPeerType) { if(this.getTopPeersPromises[type]) return this.getTopPeersPromises[type]; @@ -889,6 +864,14 @@ export class AppUsersManager { } public addContact(userId: number, first_name: string, last_name: string, phone: string, showPhone?: true) { + /* if(!userId) { + return this.importContacts([{ + first_name, + last_name, + phones: [phone] + }]); + } */ + return apiManager.invokeApi('contacts.addContact', { id: this.getUserInput(userId), first_name, diff --git a/src/pages/pageSignIn.ts b/src/pages/pageSignIn.ts index c261e7bb..ef2bffff 100644 --- a/src/pages/pageSignIn.ts +++ b/src/pages/pageSignIn.ts @@ -40,6 +40,7 @@ import { getCountryEmoji } from "../vendor/emoji"; import simulateEvent from "../helpers/dom/dispatchEvent"; import stateStorage from "../lib/stateStorage"; import rootScope from "../lib/rootScope"; +import TelInputField from "../components/telInputField"; //import _countries from '../countries_pretty.json'; let btnNext: HTMLButtonElement = null, btnQr: HTMLButtonElement; @@ -163,7 +164,7 @@ let onFirstMount = () => { lastCountrySelected = countries.find(c => c.default_name === defaultName); lastCountryCodeSelected = lastCountrySelected.country_codes.find(_countryCode => _countryCode.country_code === countryCode); - telInputField.value = lastValue = phoneCode; + telInputField.value = telInputField.lastValue = phoneCode; hidePicker(); setTimeout(() => { telEl.focus(); @@ -269,114 +270,41 @@ let onFirstMount = () => { else countryInput.focus(); }); - let pasted = false; - let lastValue = ''; - - const telInputField = new InputField({ - label: 'Login.PhoneLabel', - //plainText: true, - name: 'phone' - }); - - telInputField.container.classList.add('input-field-phone'); - - let telEl = telInputField.input; - if(telEl instanceof HTMLInputElement) { - telEl.type = 'tel'; - telEl.autocomplete = 'rr55RandomRR55'; - } else { - telEl.inputMode = 'decimal'; - - const pixelRatio = window.devicePixelRatio; - if(pixelRatio > 1) { - let letterSpacing: number; - if(isApple) { - letterSpacing = pixelRatio * -.16; - } else if(isAndroid) { - letterSpacing = 0; + const telInputField = new TelInputField({ + onInput: (formatted) => { + lottieLoader.loadLottieWorkers(); + + const {country, code} = formatted; + let countryName = country ? country.name || country.default_name : ''/* 'Unknown' */; + if(countryName !== countryInputField.value && ( + !lastCountrySelected || + !country || + !code || ( + lastCountrySelected !== country && + lastCountryCodeSelected.country_code !== code.country_code + ) + ) + ) { + replaceContent(countryInput, country ? i18n(country.default_name as any) : countryName); + lastCountrySelected = country; + lastCountryCodeSelected = code; } - - telEl.style.setProperty('--letter-spacing', letterSpacing + 'px'); - } - - const originalFunc = telInputField.setValueSilently.bind(telInputField); - telInputField.setValueSilently = (value) => { - originalFunc(value); - placeCaretAtEnd(telInputField.input, true); - }; - } - telEl.addEventListener('input', () => { - //console.log('input', this.value); - telEl.classList.remove('error'); - - lottieLoader.loadLottieWorkers(); - - const value = telInputField.value; - const diff = Math.abs(value.length - lastValue.length); - if(diff > 1 && !pasted && isAppleMobile) { - telInputField.setValueSilently(lastValue + value); - } - - pasted = false; - - telInputField.setLabel(); - - let formatted: string, country: HelpCountry, countryCode: HelpCountryCode, leftPattern = ''; - if(telInputField.value.replace(/\++/, '+') === '+') { - telInputField.setValueSilently('+'); - } else { - const o = formatPhoneNumber(telInputField.value); - formatted = o.formatted; - country = o.country; - leftPattern = o.leftPattern; - countryCode = o.code; - telInputField.setValueSilently(lastValue = formatted ? '+' + formatted : ''); - } - - telEl.dataset.leftPattern = leftPattern/* .replace(/X/g, '0') */; - - //console.log(formatted, country); - - let countryName = country ? country.name || country.default_name : ''/* 'Unknown' */; - if(countryName !== countryInputField.value && ( - !lastCountrySelected || - !country || - !countryCode || ( - lastCountrySelected !== country && - lastCountryCodeSelected.country_code !== countryCode.country_code - ) - ) - ) { - replaceContent(countryInput, country ? i18n(country.default_name as any) : countryName); - lastCountrySelected = country; - lastCountryCodeSelected = countryCode; - } - - //if(country && (telInputField.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) { - if(country || (telInputField.value.length - 1) > 1) { - btnNext.style.visibility = ''; - } else { - btnNext.style.visibility = 'hidden'; + //if(country && (telInputField.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) { + if(country || (telInputField.value.length - 1) > 1) { + btnNext.style.visibility = ''; + } else { + btnNext.style.visibility = 'hidden'; + } } }); - telEl.addEventListener('paste', () => { - pasted = true; - //console.log('paste', telEl.value); - }); - - /* telEl.addEventListener('change', (e) => { - console.log('change', telEl.value); - }); */ + const telEl = telInputField.input; telEl.addEventListener('keypress', (e) => { //console.log('keypress', this.value); if(!btnNext.style.visibility &&/* this.value.length >= 9 && */ e.key === 'Enter') { return onSubmit(); - } else if(/\D/.test(e.key) && !(e.metaKey || e.ctrlKey) && e.key !== 'Backspace' && !(e.key === '+' && e.shiftKey/* && !this.value */)) { - e.preventDefault(); - return false; } }); diff --git a/src/scss/partials/popups/_mediaAttacher.scss b/src/scss/partials/popups/_mediaAttacher.scss index d60489b5..6d837796 100644 --- a/src/scss/partials/popups/_mediaAttacher.scss +++ b/src/scss/partials/popups/_mediaAttacher.scss @@ -147,7 +147,7 @@ font-size: 1rem; border-radius: $border-radius-medium; - &:not(:focus):empty ~ label { + &[data-placeholder]:not(:focus):empty ~ label { opacity: 0; } } @@ -188,4 +188,25 @@ .popup-container:not(.is-album) .popup-item-media + .popup-item-media { margin-top: .5rem; } -} \ No newline at end of file +} + +.popup-create-contact { + .name-fields { + display: flex; + flex-direction: column; + position: relative; + padding-left: 116px; + margin-top: 1rem; + + .input-field:first-child { + margin-top: 0; + } + } + + .avatar-placeholder { + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + } +}