TelegramPWA/src/util/phoneNumber.ts

99 lines
3.3 KiB
TypeScript

import { ApiCountryCode } from '../api/types';
const PATTERN_PLACEHOLDER = 'X';
const DEFAULT_PATTERN = 'XXX XXX XXX XXX';
export function getCountryCodesByIso(phoneCodeList: ApiCountryCode[], iso: string) {
return phoneCodeList.filter((country) => country.iso2 === iso);
}
export function getCountryFromPhoneNumber(phoneCodeList: ApiCountryCode[], input = '') {
let phoneNumber = input.replace(/[^\d+]+/g, '');
if (phoneNumber.startsWith('+')) {
phoneNumber = phoneNumber.substr(1);
}
const possibleCountries = phoneCodeList
.filter((country) => phoneNumber.startsWith(country.countryCode));
const codesWithPrefix: { code: string; country: ApiCountryCode }[] = possibleCountries
.map((country) => (country.prefixes || ['']).map((prefix) => {
return {
code: `${country.countryCode}${prefix}`,
country,
};
}))
.flat();
const bestMatches = codesWithPrefix
.filter(({ code }) => phoneNumber.startsWith(code))
.sort((a, b) => a.code.length - b.code.length);
return bestMatches[bestMatches.length - 1]?.country;
}
export function formatPhoneNumber(input: string, country?: ApiCountryCode) {
if (!input) {
return '';
}
let phoneNumber = input.replace(/[^\d]+/g, '');
if (country) {
phoneNumber = phoneNumber.substr(country.countryCode.length);
} else if (input.startsWith('+')) {
return input;
}
const pattern = getBestPattern(phoneNumber, country?.patterns);
const result: string[] = []; // Result character array
let j = 0; // Position inside pattern
for (let i = 0; i < phoneNumber.length; i++) {
while (pattern[j] !== PATTERN_PLACEHOLDER && j < pattern.length) {
result.push(pattern[j]);
if (pattern[j] === phoneNumber[i]) { // If pattern contains digits, move input position too
i++;
if (i === phoneNumber.length) break; // But don't overdo it, or it will insert full pattern unexpectedly
}
j++;
}
result.push(phoneNumber[i]); // For placeholder characters, setting current input digit
j++;
}
return result.join('');
}
function getBestPattern(numberWithoutCode: string, patterns?: string[]) {
if (!patterns || patterns.length === 0) return DEFAULT_PATTERN;
if (patterns.length === 1) return patterns[0];
const defaultPattern = patterns.find((pattern) => pattern.startsWith(PATTERN_PLACEHOLDER)) || DEFAULT_PATTERN;
const bestMatches = patterns.filter((pattern) => {
const stripped = pattern.replace(/[^\dX]+/g, '');
if (stripped.startsWith(PATTERN_PLACEHOLDER)) return false; // Don't consider default number format here
for (let i = 0; i < numberWithoutCode.length; i++) {
if (i > stripped.length - 1 || (stripped[i] !== PATTERN_PLACEHOLDER && stripped[i] !== numberWithoutCode[i])) {
return false;
}
}
return true;
});
// Playing it safe: if not sure, use default for that region
return bestMatches.length === 1 ? bestMatches[0] : defaultPattern;
}
export function formatPhoneNumberWithCode(phoneCodeList: ApiCountryCode[], phoneNumber: string) {
if (!phoneNumber) {
return '';
}
const numberWithPlus = phoneNumber.startsWith('+') ? phoneNumber : `+${phoneNumber}`;
const country = getCountryFromPhoneNumber(phoneCodeList, numberWithPlus);
if (!country) {
return numberWithPlus;
}
return `+${country.countryCode} ${formatPhoneNumber(numberWithPlus, country)}`;
}