Browse Source

Import contact popup

master
morethanwords 3 years ago
parent
commit
ac1865f678
  1. 87
      src/components/editPeer.ts
  2. 20
      src/components/inputField.ts
  3. 88
      src/components/popups/createContact.ts
  4. 8
      src/components/popups/index.ts
  5. 14
      src/components/sidebarLeft/tabs/addContact.ts
  6. 13
      src/components/sidebarLeft/tabs/contacts.ts
  7. 107
      src/components/sidebarRight/tabs/editContact.ts
  8. 110
      src/components/telInputField.ts
  9. 1
      src/lang.ts
  10. 1
      src/langSign.ts
  11. 93
      src/lib/appManagers/appUsersManager.ts
  12. 126
      src/pages/pageSignIn.ts
  13. 25
      src/scss/partials/popups/_mediaAttacher.scss

87
src/components/editPeer.ts

@ -24,37 +24,94 @@ export default class EditPeer { @@ -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<any>, 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 = () => {

20
src/components/inputField.ts

@ -75,7 +75,9 @@ export type InputFieldOptions = { @@ -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 { @@ -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 { @@ -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 { @@ -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) {

88
src/components/popups/createContact.ts

@ -0,0 +1,88 @@ @@ -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();
}
}

8
src/components/popups/index.ts

@ -11,6 +11,7 @@ import appNavigationController, { NavigationItem } from "../appNavigationControl @@ -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 { @@ -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 { @@ -44,6 +45,8 @@ export default class PopupElement {
protected navigationItem: NavigationItem;
protected listenerSetter: ListenerSetter;
constructor(className: string, buttons?: Array<PopupButton>, options: PopupOptions = {}) {
this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : '');
@ -54,6 +57,8 @@ export default class PopupElement { @@ -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 { @@ -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;

14
src/components/sidebarLeft/tabs/addContact.ts

@ -1,14 +0,0 @@ @@ -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');
}
}

13
src/components/sidebarLeft/tabs/contacts.ts

@ -7,11 +7,13 @@ @@ -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 { @@ -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) => {

107
src/components/sidebarRight/tabs/editContact.ts

@ -31,7 +31,7 @@ export default class AppEditContactTab extends SliderSuperTab { @@ -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 { @@ -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 { @@ -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 { @@ -128,7 +133,7 @@ export default class AppEditContactTab extends SliderSuperTab {
}, {listenerSetter: this.listenerSetter});
}
{
if(this.peerId) {
const section = new SettingSection({
});

110
src/components/telInputField.ts

@ -0,0 +1,110 @@ @@ -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
});*/
}
}

1
src/lang.ts

@ -570,6 +570,7 @@ const lang = { @@ -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",

1
src/langSign.ts

@ -23,6 +23,7 @@ const lang = { @@ -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",

93
src/lib/appManagers/appUsersManager.ts

@ -17,7 +17,7 @@ import cleanUsername from "../../helpers/cleanUsername"; @@ -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 { @@ -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 { @@ -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,

126
src/pages/pageSignIn.ts

@ -40,6 +40,7 @@ import { getCountryEmoji } from "../vendor/emoji"; @@ -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 = () => { @@ -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 = () => { @@ -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;
}
});

25
src/scss/partials/popups/_mediaAttacher.scss

@ -147,7 +147,7 @@ @@ -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 @@ @@ -188,4 +188,25 @@
.popup-container:not(.is-album) .popup-item-media + .popup-item-media {
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…
Cancel
Save