Import contact popup
This commit is contained in:
parent
733c0e4762
commit
ac1865f678
@ -24,18 +24,31 @@ export default class EditPeer {
|
|||||||
|
|
||||||
private peerId: number;
|
private peerId: number;
|
||||||
|
|
||||||
|
private _disabled = false;
|
||||||
|
private avatarSize = 120;
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
peerId: number,
|
peerId?: number,
|
||||||
inputFields: EditPeer['inputFields'],
|
inputFields: EditPeer['inputFields'],
|
||||||
listenerSetter: ListenerSetter,
|
listenerSetter: ListenerSetter,
|
||||||
doNotEditAvatar?: boolean,
|
doNotEditAvatar?: boolean,
|
||||||
|
withoutAvatar?: boolean,
|
||||||
|
nextBtn?: HTMLButtonElement,
|
||||||
|
avatarSize?: number
|
||||||
}) {
|
}) {
|
||||||
safeAssign(this, options);
|
safeAssign(this, options);
|
||||||
|
|
||||||
|
if(!this.nextBtn) {
|
||||||
this.nextBtn = ButtonCorner({icon: 'check'});
|
this.nextBtn = ButtonCorner({icon: 'check'});
|
||||||
|
} else if(!this.nextBtn.classList.contains('btn-corner')) {
|
||||||
|
this.handleChange = () => {
|
||||||
|
this.nextBtn.toggleAttribute('disabled', !this.isChanged() || this.disabled);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!options.withoutAvatar) {
|
||||||
this.avatarElem = document.createElement('avatar-element') as AvatarElement;
|
this.avatarElem = document.createElement('avatar-element') as AvatarElement;
|
||||||
this.avatarElem.classList.add('avatar-placeholder', 'avatar-120');
|
this.avatarElem.classList.add('avatar-placeholder', 'avatar-' + this.avatarSize);
|
||||||
this.avatarElem.setAttribute('peer', '' + this.peerId);
|
this.avatarElem.setAttribute('peer', '' + this.peerId);
|
||||||
|
|
||||||
if(!options.doNotEditAvatar) {
|
if(!options.doNotEditAvatar) {
|
||||||
@ -47,14 +60,58 @@ export default class EditPeer {
|
|||||||
|
|
||||||
this.avatarEdit.container.append(this.avatarElem);
|
this.avatarEdit.container.append(this.avatarElem);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.inputFields.forEach(inputField => {
|
this.inputFields.forEach(inputField => {
|
||||||
this.listenerSetter.add(inputField.input)('input', this.handleChange);
|
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<any>, unlockOnSuccess = false) {
|
||||||
|
this.disabled = true;
|
||||||
|
promise.then(() => {
|
||||||
|
if(unlockOnSuccess) {
|
||||||
|
this.disabled = false;
|
||||||
|
}
|
||||||
|
}, () => {
|
||||||
|
this.disabled = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public isChanged = () => {
|
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 = () => {
|
public handleChange = () => {
|
||||||
|
@ -75,7 +75,9 @@ export type InputFieldOptions = {
|
|||||||
maxLength?: number,
|
maxLength?: number,
|
||||||
showLengthOn?: number,
|
showLengthOn?: number,
|
||||||
plainText?: true,
|
plainText?: true,
|
||||||
animate?: true
|
animate?: true,
|
||||||
|
required?: boolean,
|
||||||
|
validate?: () => boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
class InputField {
|
class InputField {
|
||||||
@ -86,6 +88,9 @@ class InputField {
|
|||||||
|
|
||||||
public originalValue: string;
|
public originalValue: string;
|
||||||
|
|
||||||
|
public required: boolean;
|
||||||
|
public validate: () => boolean;
|
||||||
|
|
||||||
//public onLengthChange: (length: number, isOverflow: boolean) => void;
|
//public onLengthChange: (length: number, isOverflow: boolean) => void;
|
||||||
protected wasInputFakeClientHeight: number;
|
protected wasInputFakeClientHeight: number;
|
||||||
// protected showScrollDebounced: () => void;
|
// protected showScrollDebounced: () => void;
|
||||||
@ -94,6 +99,13 @@ class InputField {
|
|||||||
this.container = document.createElement('div');
|
this.container = document.createElement('div');
|
||||||
this.container.classList.add('input-field');
|
this.container.classList.add('input-field');
|
||||||
|
|
||||||
|
this.required = options.required;
|
||||||
|
this.validate = options.validate;
|
||||||
|
|
||||||
|
if(this.required) {
|
||||||
|
this.originalValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
if(options.maxLength) {
|
if(options.maxLength) {
|
||||||
options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3));
|
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() {
|
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) {
|
public setOriginalValue(value: InputField['originalValue'] = '', silent = false) {
|
||||||
|
88
src/components/popups/createContact.ts
Normal file
88
src/components/popups/createContact.ts
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import appNavigationController, { NavigationItem } from "../appNavigationControl
|
|||||||
import { i18n, LangPackKey } from "../../lib/langPack";
|
import { i18n, LangPackKey } from "../../lib/langPack";
|
||||||
import findUpClassName from "../../helpers/dom/findUpClassName";
|
import findUpClassName from "../../helpers/dom/findUpClassName";
|
||||||
import blurActiveElement from "../../helpers/dom/blurActiveElement";
|
import blurActiveElement from "../../helpers/dom/blurActiveElement";
|
||||||
|
import ListenerSetter from "../../helpers/listenerSetter";
|
||||||
|
|
||||||
export type PopupButton = {
|
export type PopupButton = {
|
||||||
text?: string,
|
text?: string,
|
||||||
@ -34,7 +35,7 @@ export default class PopupElement {
|
|||||||
protected header = document.createElement('div');
|
protected header = document.createElement('div');
|
||||||
protected title = document.createElement('div');
|
protected title = document.createElement('div');
|
||||||
protected btnClose: HTMLElement;
|
protected btnClose: HTMLElement;
|
||||||
protected btnConfirm: HTMLElement;
|
protected btnConfirm: HTMLButtonElement;
|
||||||
protected body: HTMLElement;
|
protected body: HTMLElement;
|
||||||
protected buttons: HTMLElement;
|
protected buttons: HTMLElement;
|
||||||
|
|
||||||
@ -44,6 +45,8 @@ export default class PopupElement {
|
|||||||
|
|
||||||
protected navigationItem: NavigationItem;
|
protected navigationItem: NavigationItem;
|
||||||
|
|
||||||
|
protected listenerSetter: ListenerSetter;
|
||||||
|
|
||||||
constructor(className: string, buttons?: Array<PopupButton>, options: PopupOptions = {}) {
|
constructor(className: string, buttons?: Array<PopupButton>, options: PopupOptions = {}) {
|
||||||
this.element.classList.add('popup');
|
this.element.classList.add('popup');
|
||||||
this.element.className = 'popup' + (className ? ' ' + className : '');
|
this.element.className = 'popup' + (className ? ' ' + className : '');
|
||||||
@ -54,6 +57,8 @@ export default class PopupElement {
|
|||||||
|
|
||||||
this.header.append(this.title);
|
this.header.append(this.title);
|
||||||
|
|
||||||
|
this.listenerSetter = new ListenerSetter();
|
||||||
|
|
||||||
if(options.closable) {
|
if(options.closable) {
|
||||||
this.btnClose = document.createElement('span');
|
this.btnClose = document.createElement('span');
|
||||||
this.btnClose.classList.add('btn-icon', 'popup-close', 'tgico-close');
|
this.btnClose.classList.add('btn-icon', 'popup-close', 'tgico-close');
|
||||||
@ -157,6 +162,7 @@ export default class PopupElement {
|
|||||||
this.onClose && this.onClose();
|
this.onClose && this.onClose();
|
||||||
this.element.classList.add('hiding');
|
this.element.classList.add('hiding');
|
||||||
this.element.classList.remove('active');
|
this.element.classList.remove('active');
|
||||||
|
this.listenerSetter.removeAll();
|
||||||
|
|
||||||
if(this.btnClose) this.btnClose.removeEventListener('click', this.hide);
|
if(this.btnClose) this.btnClose.removeEventListener('click', this.hide);
|
||||||
rootScope.isOverlayActive = false;
|
rootScope.isOverlayActive = false;
|
||||||
|
@ -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');
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,11 +7,13 @@
|
|||||||
import { SliderSuperTab } from "../../slider";
|
import { SliderSuperTab } from "../../slider";
|
||||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||||
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
|
|
||||||
import InputSearch from "../../inputSearch";
|
import InputSearch from "../../inputSearch";
|
||||||
import { isMobile } from "../../../helpers/userAgent";
|
import { isMobile } from "../../../helpers/userAgent";
|
||||||
import { canFocus } from "../../../helpers/dom/canFocus";
|
import { canFocus } from "../../../helpers/dom/canFocus";
|
||||||
import windowSize from "../../../helpers/windowSize";
|
import windowSize from "../../../helpers/windowSize";
|
||||||
|
import ButtonCorner from "../../buttonCorner";
|
||||||
|
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
|
||||||
|
import PopupCreateContact from "../../popups/createContact";
|
||||||
|
|
||||||
// TODO: поиск по людям глобальный, если не нашло в контактах никого
|
// TODO: поиск по людям глобальный, если не нашло в контактах никого
|
||||||
|
|
||||||
@ -29,8 +31,15 @@ export default class AppContactsTab extends SliderSuperTab {
|
|||||||
this.list.id = 'contacts';
|
this.list.id = 'contacts';
|
||||||
this.list.classList.add('contacts-container');
|
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, () => {
|
appDialogsManager.setListClickListener(this.list, () => {
|
||||||
(this.container.querySelector('.sidebar-close-button') as HTMLElement).click();
|
this.close();
|
||||||
}, undefined, true);
|
}, undefined, true);
|
||||||
|
|
||||||
this.inputSearch = new InputSearch('Search', (value) => {
|
this.inputSearch = new InputSearch('Search', (value) => {
|
||||||
|
@ -31,7 +31,7 @@ export default class AppEditContactTab extends SliderSuperTab {
|
|||||||
|
|
||||||
protected init() {
|
protected init() {
|
||||||
this.container.classList.add('edit-peer-container', 'edit-contact-container');
|
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});
|
const section = new SettingSection({noDelimiter: true});
|
||||||
@ -51,13 +51,14 @@ export default class AppEditContactTab extends SliderSuperTab {
|
|||||||
maxLength: 70
|
maxLength: 70
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(this.peerId) {
|
||||||
const user = appUsersManager.getUser(this.peerId);
|
const user = appUsersManager.getUser(this.peerId);
|
||||||
|
|
||||||
this.nameInputField.setOriginalValue(user.first_name);
|
this.nameInputField.setOriginalValue(user.first_name);
|
||||||
this.lastNameInputField.setOriginalValue(user.last_name);
|
this.lastNameInputField.setOriginalValue(user.last_name);
|
||||||
|
}
|
||||||
|
|
||||||
inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container);
|
inputWrapper.append(this.nameInputField.container, this.lastNameInputField.container);
|
||||||
|
|
||||||
inputFields.push(this.nameInputField, this.lastNameInputField);
|
inputFields.push(this.nameInputField, this.lastNameInputField);
|
||||||
|
|
||||||
this.editPeer = new EditPeer({
|
this.editPeer = new EditPeer({
|
||||||
@ -68,6 +69,7 @@ export default class AppEditContactTab extends SliderSuperTab {
|
|||||||
});
|
});
|
||||||
this.content.append(this.editPeer.nextBtn);
|
this.content.append(this.editPeer.nextBtn);
|
||||||
|
|
||||||
|
if(this.peerId) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.classList.add('avatar-edit');
|
div.classList.add('avatar-edit');
|
||||||
div.append(this.editPeer.avatarElem);
|
div.append(this.editPeer.avatarElem);
|
||||||
@ -114,6 +116,9 @@ export default class AppEditContactTab extends SliderSuperTab {
|
|||||||
profileSubtitleDiv.append(i18n('EditContact.OriginalName'));
|
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);
|
this.scrollable.append(section.container);
|
||||||
|
|
||||||
@ -128,7 +133,7 @@ export default class AppEditContactTab extends SliderSuperTab {
|
|||||||
}, {listenerSetter: this.listenerSetter});
|
}, {listenerSetter: this.listenerSetter});
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if(this.peerId) {
|
||||||
const section = new SettingSection({
|
const section = new SettingSection({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
110
src/components/telInputField.ts
Normal file
110
src/components/telInputField.ts
Normal file
@ -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<typeof formatPhoneNumber>) => 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<typeof formatPhoneNumber>;
|
||||||
|
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
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
}
|
@ -570,6 +570,7 @@ const lang = {
|
|||||||
"Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.",
|
"Alert.UserDoesntExists": "Sorry, this user doesn't seem to exist.",
|
||||||
"Appearance.Reset": "Reset to Defaults",
|
"Appearance.Reset": "Reset to Defaults",
|
||||||
"Bio.Description": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco",
|
"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.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.",
|
"Channel.UsernameAboutGroup": "People can share this link with others and find your group using Telegram search.",
|
||||||
"Chat.CopySelectedText": "Copy Selected Text",
|
"Chat.CopySelectedText": "Copy Selected Text",
|
||||||
|
@ -23,6 +23,7 @@ const lang = {
|
|||||||
"StartMessaging": "Start Messaging",
|
"StartMessaging": "Start Messaging",
|
||||||
|
|
||||||
// * macos
|
// * macos
|
||||||
|
"Contacts.PhoneNumber.Placeholder": "Phone Number",
|
||||||
"Login.Next": "Next",
|
"Login.Next": "Next",
|
||||||
"Login.ContinueOnLanguage": "Continue in English",
|
"Login.ContinueOnLanguage": "Continue in English",
|
||||||
"Login.QR.Title": "Log in to Telegram by QR Code",
|
"Login.QR.Title": "Log in to Telegram by QR Code",
|
||||||
|
@ -17,7 +17,7 @@ import cleanUsername from "../../helpers/cleanUsername";
|
|||||||
import { tsNow } from "../../helpers/date";
|
import { tsNow } from "../../helpers/date";
|
||||||
import { formatPhoneNumber } from "../../helpers/formatPhoneNumber";
|
import { formatPhoneNumber } from "../../helpers/formatPhoneNumber";
|
||||||
import { safeReplaceObject, isObject } from "../../helpers/object";
|
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 I18n, { i18n, LangPackKey } from "../langPack";
|
||||||
//import apiManager from '../mtproto/apiManager';
|
//import apiManager from '../mtproto/apiManager';
|
||||||
import apiManager from '../mtproto/mtprotoworker';
|
import apiManager from '../mtproto/mtprotoworker';
|
||||||
@ -680,75 +680,50 @@ export class AppUsersManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* function importContact (phone, firstName, lastName) {
|
public importContact(first_name: string, last_name: string, phone: string) {
|
||||||
return MtpApiManager.invokeApi('contacts.importContacts', {
|
return this.importContacts([{
|
||||||
contacts: [{
|
first_name,
|
||||||
_: 'inputPhoneContact',
|
last_name,
|
||||||
client_id: '1',
|
phones: [phone]
|
||||||
phone: phone,
|
}]).then(userIds => {
|
||||||
first_name: firstName,
|
if(!userIds.length) {
|
||||||
last_name: lastName
|
const error = new Error();
|
||||||
}],
|
(error as any).type = 'NO_USER';
|
||||||
replace: false
|
throw error;
|
||||||
}).then(function (importedContactsResult) {
|
|
||||||
saveApiUsers(importedContactsResult.users)
|
|
||||||
|
|
||||||
var foundUserID = false
|
|
||||||
angular.forEach(importedContactsResult.imported, function (importedContact) {
|
|
||||||
onContactUpdated(foundUserID = importedContact.user_id, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
return foundUserID || false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function importContacts (contacts) {
|
return userIds[0];
|
||||||
var inputContacts = [],
|
});
|
||||||
i
|
}
|
||||||
var j
|
|
||||||
|
|
||||||
for (i = 0; i < contacts.length; i++) {
|
public importContacts(contacts: {phones: string[], first_name: string, last_name: string}[]) {
|
||||||
for (j = 0; j < contacts[i].phones.length; j++) {
|
const inputContacts: InputContact[] = [];
|
||||||
|
|
||||||
|
for(let i = 0; i < contacts.length; ++i) {
|
||||||
|
for(let j = 0; j < contacts[i].phones.length; ++j) {
|
||||||
inputContacts.push({
|
inputContacts.push({
|
||||||
_: 'inputPhoneContact',
|
_: 'inputPhoneContact',
|
||||||
client_id: (i << 16 | j).toString(10),
|
client_id: (i << 16 | j).toString(10),
|
||||||
phone: contacts[i].phones[j],
|
phone: contacts[i].phones[j],
|
||||||
first_name: contacts[i].first_name,
|
first_name: contacts[i].first_name,
|
||||||
last_name: contacts[i].last_name
|
last_name: contacts[i].last_name
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MtpApiManager.invokeApi('contacts.importContacts', {
|
return apiManager.invokeApi('contacts.importContacts', {
|
||||||
contacts: inputContacts,
|
contacts: inputContacts
|
||||||
replace: false
|
}).then((importedContactsResult) => {
|
||||||
}).then(function (importedContactsResult) {
|
this.saveApiUsers(importedContactsResult.users);
|
||||||
saveApiUsers(importedContactsResult.users)
|
|
||||||
|
|
||||||
var result = []
|
const userIds = importedContactsResult.imported.map((importedContact) => {
|
||||||
angular.forEach(importedContactsResult.imported, function (importedContact) {
|
this.onContactUpdated(importedContact.user_id, true);
|
||||||
onContactUpdated(importedContact.user_id, true)
|
return importedContact.user_id;
|
||||||
result.push(importedContact.user_id)
|
|
||||||
})
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return userIds;
|
||||||
});
|
});
|
||||||
} */
|
}
|
||||||
|
|
||||||
public getTopPeers(type: TopPeerType) {
|
public getTopPeers(type: TopPeerType) {
|
||||||
if(this.getTopPeersPromises[type]) return this.getTopPeersPromises[type];
|
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) {
|
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', {
|
return apiManager.invokeApi('contacts.addContact', {
|
||||||
id: this.getUserInput(userId),
|
id: this.getUserInput(userId),
|
||||||
first_name,
|
first_name,
|
||||||
|
@ -40,6 +40,7 @@ import { getCountryEmoji } from "../vendor/emoji";
|
|||||||
import simulateEvent from "../helpers/dom/dispatchEvent";
|
import simulateEvent from "../helpers/dom/dispatchEvent";
|
||||||
import stateStorage from "../lib/stateStorage";
|
import stateStorage from "../lib/stateStorage";
|
||||||
import rootScope from "../lib/rootScope";
|
import rootScope from "../lib/rootScope";
|
||||||
|
import TelInputField from "../components/telInputField";
|
||||||
|
|
||||||
//import _countries from '../countries_pretty.json';
|
//import _countries from '../countries_pretty.json';
|
||||||
let btnNext: HTMLButtonElement = null, btnQr: HTMLButtonElement;
|
let btnNext: HTMLButtonElement = null, btnQr: HTMLButtonElement;
|
||||||
@ -163,7 +164,7 @@ let onFirstMount = () => {
|
|||||||
lastCountrySelected = countries.find(c => c.default_name === defaultName);
|
lastCountrySelected = countries.find(c => c.default_name === defaultName);
|
||||||
lastCountryCodeSelected = lastCountrySelected.country_codes.find(_countryCode => _countryCode.country_code === countryCode);
|
lastCountryCodeSelected = lastCountrySelected.country_codes.find(_countryCode => _countryCode.country_code === countryCode);
|
||||||
|
|
||||||
telInputField.value = lastValue = phoneCode;
|
telInputField.value = telInputField.lastValue = phoneCode;
|
||||||
hidePicker();
|
hidePicker();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
telEl.focus();
|
telEl.focus();
|
||||||
@ -269,88 +270,24 @@ let onFirstMount = () => {
|
|||||||
else countryInput.focus();
|
else countryInput.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
let pasted = false;
|
const telInputField = new TelInputField({
|
||||||
let lastValue = '';
|
onInput: (formatted) => {
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
lottieLoader.loadLottieWorkers();
|
||||||
|
|
||||||
const value = telInputField.value;
|
const {country, code} = formatted;
|
||||||
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' */;
|
let countryName = country ? country.name || country.default_name : ''/* 'Unknown' */;
|
||||||
if(countryName !== countryInputField.value && (
|
if(countryName !== countryInputField.value && (
|
||||||
!lastCountrySelected ||
|
!lastCountrySelected ||
|
||||||
!country ||
|
!country ||
|
||||||
!countryCode || (
|
!code || (
|
||||||
lastCountrySelected !== country &&
|
lastCountrySelected !== country &&
|
||||||
lastCountryCodeSelected.country_code !== countryCode.country_code
|
lastCountryCodeSelected.country_code !== code.country_code
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
replaceContent(countryInput, country ? i18n(country.default_name as any) : countryName);
|
replaceContent(countryInput, country ? i18n(country.default_name as any) : countryName);
|
||||||
lastCountrySelected = country;
|
lastCountrySelected = country;
|
||||||
lastCountryCodeSelected = countryCode;
|
lastCountryCodeSelected = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if(country && (telInputField.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) {
|
//if(country && (telInputField.value.length - 1) >= (country.pattern ? country.pattern.length : 9)) {
|
||||||
@ -359,24 +296,15 @@ let onFirstMount = () => {
|
|||||||
} else {
|
} else {
|
||||||
btnNext.style.visibility = 'hidden';
|
btnNext.style.visibility = 'hidden';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
telEl.addEventListener('paste', () => {
|
const telEl = telInputField.input;
|
||||||
pasted = true;
|
|
||||||
//console.log('paste', telEl.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* telEl.addEventListener('change', (e) => {
|
|
||||||
console.log('change', telEl.value);
|
|
||||||
}); */
|
|
||||||
|
|
||||||
telEl.addEventListener('keypress', (e) => {
|
telEl.addEventListener('keypress', (e) => {
|
||||||
//console.log('keypress', this.value);
|
//console.log('keypress', this.value);
|
||||||
if(!btnNext.style.visibility &&/* this.value.length >= 9 && */ e.key === 'Enter') {
|
if(!btnNext.style.visibility &&/* this.value.length >= 9 && */ e.key === 'Enter') {
|
||||||
return onSubmit();
|
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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
border-radius: $border-radius-medium;
|
border-radius: $border-radius-medium;
|
||||||
|
|
||||||
&:not(:focus):empty ~ label {
|
&[data-placeholder]:not(:focus):empty ~ label {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,3 +189,24 @@
|
|||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user