add a bunch of number parsing logic with libphonenumber
This commit is contained in:
@@ -171,6 +171,7 @@
|
|||||||
"js-sha256": "^0.9.0",
|
"js-sha256": "^0.9.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"lande": "^1.0.10",
|
"lande": "^1.0.10",
|
||||||
|
"libphonenumber-js": "^1.12.30",
|
||||||
"lodash.chunk": "^4.2.0",
|
"lodash.chunk": "^4.2.0",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {msg} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
type CountryCode,
|
||||||
getDefaultCountry,
|
getDefaultCountry,
|
||||||
INTERNATIONAL_TELEPHONE_CODES,
|
INTERNATIONAL_TELEPHONE_CODES,
|
||||||
} from '#/lib/international-telephone-codes'
|
} from '#/lib/international-telephone-codes'
|
||||||
@@ -23,8 +24,8 @@ export function InternationalPhoneCodeSelect({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
}: {
|
}: {
|
||||||
value?: string
|
value?: CountryCode
|
||||||
onChange: (value: string) => void
|
onChange: (value: CountryCode) => void
|
||||||
}) {
|
}) {
|
||||||
const {_, i18n} = useLingui()
|
const {_, i18n} = useLingui()
|
||||||
const location = useGeolocation()
|
const location = useGeolocation()
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export function SyncContactsFlow() {
|
||||||
|
throw new Error('SyncContactsFlow is not available on web')
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import {t} from '@lingui/macro'
|
||||||
|
import {
|
||||||
|
ParseError,
|
||||||
|
parsePhoneNumber,
|
||||||
|
parsePhoneNumberWithError,
|
||||||
|
type PhoneNumber,
|
||||||
|
} from 'libphonenumber-js/max'
|
||||||
|
|
||||||
|
import {type CountryCode} from '#/lib/international-telephone-codes'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intended for after the user has finished inputting their phone number.
|
||||||
|
*/
|
||||||
|
export function processPhoneNumber(
|
||||||
|
number: string,
|
||||||
|
country: CountryCode,
|
||||||
|
):
|
||||||
|
| {
|
||||||
|
valid: true
|
||||||
|
formatted: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
valid: false
|
||||||
|
reason?: string
|
||||||
|
} {
|
||||||
|
try {
|
||||||
|
const phoneNumber = parsePhoneNumberWithError(number, {
|
||||||
|
defaultCountry: country,
|
||||||
|
})
|
||||||
|
if (!phoneNumber.isValid()) {
|
||||||
|
return {valid: false, reason: t`Invalid phone number`}
|
||||||
|
}
|
||||||
|
const type = phoneNumber.getType()
|
||||||
|
if (
|
||||||
|
type !== 'MOBILE' &&
|
||||||
|
type !== 'FIXED_LINE_OR_MOBILE' &&
|
||||||
|
type !== 'PERSONAL_NUMBER'
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
reason: t`Number should be a mobile number`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (phoneNumber.country !== country) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
reason: t`Country code does not match`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
formatted: formatInternationalWithoutCountryCode(phoneNumber),
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
if (error instanceof ParseError) {
|
||||||
|
return {valid: false, reason: error.message}
|
||||||
|
} else {
|
||||||
|
return {valid: false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatInternationalWithoutCountryCode(phoneNumber: PhoneNumber) {
|
||||||
|
const intl = phoneNumber.formatInternational()
|
||||||
|
const prefix = '+' + phoneNumber.countryCallingCode
|
||||||
|
return intl.replace(prefix, '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCountryCodeFromPastedNumber(
|
||||||
|
text: string,
|
||||||
|
): {countryCode: CountryCode; rest: string} | undefined {
|
||||||
|
try {
|
||||||
|
const phoneNumber = parsePhoneNumber(text)
|
||||||
|
if (!phoneNumber.isValid()) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
const countryCode = phoneNumber.country
|
||||||
|
// we don't have AC and TA in our dropdown - see `#/lib/international-telephone-codes`
|
||||||
|
if (countryCode && countryCode !== 'AC' && countryCode !== 'TA') {
|
||||||
|
return {
|
||||||
|
countryCode,
|
||||||
|
rest: formatInternationalWithoutCountryCode(phoneNumber),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,10 @@ import {msg, Trans} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useMutation} from '@tanstack/react-query'
|
import {useMutation} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {getDefaultCountry} from '#/lib/international-telephone-codes'
|
import {
|
||||||
|
type CountryCode,
|
||||||
|
getDefaultCountry,
|
||||||
|
} from '#/lib/international-telephone-codes'
|
||||||
import {cleanError, isNetworkError} from '#/lib/strings/errors'
|
import {cleanError, isNetworkError} from '#/lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
import {useGeolocationStatus} from '#/state/geolocation'
|
import {useGeolocationStatus} from '#/state/geolocation'
|
||||||
@@ -18,6 +21,10 @@ import * as Layout from '#/components/Layout'
|
|||||||
import {InlineLinkText} from '#/components/Link'
|
import {InlineLinkText} from '#/components/Link'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
import {
|
||||||
|
getCountryCodeFromPastedNumber,
|
||||||
|
processPhoneNumber,
|
||||||
|
} from '../phone-number'
|
||||||
import {type Action, type State} from '../state'
|
import {type Action, type State} from '../state'
|
||||||
|
|
||||||
export function PhoneInput({
|
export function PhoneInput({
|
||||||
@@ -40,24 +47,31 @@ export function PhoneInput({
|
|||||||
const [phoneNumber, setPhoneNumber] = useState(state.phoneNumber ?? '')
|
const [phoneNumber, setPhoneNumber] = useState(state.phoneNumber ?? '')
|
||||||
const gutters = useGutters([0, 'wide'])
|
const gutters = useGutters([0, 'wide'])
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
|
// for API/generic errors
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
// for issues with parsing the number
|
||||||
|
const [formatError, setFormatError] = useState('')
|
||||||
|
|
||||||
const {mutate: submit, isPending} = useMutation({
|
const {mutate: submit, isPending} = useMutation({
|
||||||
mutationFn: async ({}: {countryCode: string; phoneNumber: string}) => {
|
mutationFn: async ({}: {
|
||||||
|
phoneCountryCode: CountryCode
|
||||||
|
phoneNumber: string
|
||||||
|
}) => {
|
||||||
// get otp
|
// get otp
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
setTimeout(resolve, 500)
|
setTimeout(resolve, 500)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onSuccess: (_data, {countryCode, phoneNumber}) => {
|
onSuccess: (_data, {phoneCountryCode, phoneNumber}) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SUBMIT_PHONE_NUMBER',
|
type: 'SUBMIT_PHONE_NUMBER',
|
||||||
payload: {phoneCountryCode: countryCode, phoneNumber},
|
payload: {phoneCountryCode, phoneNumber},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
Keyboard.dismiss()
|
Keyboard.dismiss()
|
||||||
setError('')
|
setError('')
|
||||||
|
setFormatError('')
|
||||||
},
|
},
|
||||||
onError: err => {
|
onError: err => {
|
||||||
if (isNetworkError(err)) {
|
if (isNetworkError(err)) {
|
||||||
@@ -73,6 +87,16 @@ export function PhoneInput({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onSubmitNumber = () => {
|
||||||
|
const result = processPhoneNumber(phoneNumber, countryCode)
|
||||||
|
if (result.valid) {
|
||||||
|
setPhoneNumber(result.formatted)
|
||||||
|
submit({phoneCountryCode: countryCode, phoneNumber: result.formatted})
|
||||||
|
} else {
|
||||||
|
setFormatError(result.reason ?? _(msg`Invalid phone number`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const paddingBottom = Math.max(insets.bottom, tokens.space.xl)
|
const paddingBottom = Math.max(insets.bottom, tokens.space.xl)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -126,32 +150,38 @@ export function PhoneInput({
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={[a.flex_1]}>
|
<View style={[a.flex_1]}>
|
||||||
<TextField.Root>
|
<TextField.Root isInvalid={!!formatError}>
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
label={_(msg`Phone number`)}
|
label={_(msg`Phone number`)}
|
||||||
value={phoneNumber}
|
value={phoneNumber}
|
||||||
onChangeText={setPhoneNumber}
|
onChangeText={text => {
|
||||||
|
if (formatError) setFormatError('')
|
||||||
|
if (Math.abs(text.length - phoneNumber.length) > 1) {
|
||||||
|
// possibly pasted/autocompleted? auto-switch
|
||||||
|
// country code if possible
|
||||||
|
const result = getCountryCodeFromPastedNumber(text)
|
||||||
|
if (result) {
|
||||||
|
setCountryCode(result.countryCode)
|
||||||
|
setPhoneNumber(result.rest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPhoneNumber(text)
|
||||||
|
}}
|
||||||
placeholder={null}
|
placeholder={null}
|
||||||
keyboardType="phone-pad"
|
keyboardType="number-pad" // we don't want people entering +() etc
|
||||||
autoComplete="tel"
|
autoComplete="tel"
|
||||||
returnKeyType={android('next')}
|
returnKeyType={android('next')}
|
||||||
onSubmitEditing={() => submit({countryCode, phoneNumber})}
|
onSubmitEditing={onSubmitNumber}
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{error && (
|
|
||||||
<Text
|
{error && <ErrorText>{error}</ErrorText>}
|
||||||
style={[
|
{formatError && <ErrorText>{formatError}</ErrorText>}
|
||||||
a.text_md,
|
|
||||||
t.atoms.text_contrast_medium,
|
|
||||||
a.leading_snug,
|
|
||||||
a.mt_xl,
|
|
||||||
]}>
|
|
||||||
{error}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
<View style={[a.mt_auto, a.py_xl]}>
|
<View style={[a.mt_auto, a.py_xl]}>
|
||||||
<LegalDisclaimer />
|
<LegalDisclaimer />
|
||||||
</View>
|
</View>
|
||||||
@@ -165,7 +195,7 @@ export function PhoneInput({
|
|||||||
label={_(msg`Next`)}
|
label={_(msg`Next`)}
|
||||||
size="large"
|
size="large"
|
||||||
color="primary"
|
color="primary"
|
||||||
onPress={() => submit({countryCode, phoneNumber})}>
|
onPress={onSubmitNumber}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Next</Trans>
|
<Trans>Next</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
@@ -217,3 +247,18 @@ function LegalDisclaimer() {
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ErrorText({children}: {children: string}) {
|
||||||
|
const t = useTheme()
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.text_md,
|
||||||
|
{color: t.palette.negative_500},
|
||||||
|
a.leading_snug,
|
||||||
|
a.mt_md,
|
||||||
|
]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {useReducer} from 'react'
|
import {useReducer} from 'react'
|
||||||
import {type ExistingContact} from 'expo-contacts'
|
import {type ExistingContact} from 'expo-contacts'
|
||||||
|
|
||||||
|
import {type CountryCode} from '#/lib/international-telephone-codes'
|
||||||
import type * as bsky from '#/types/bsky'
|
import type * as bsky from '#/types/bsky'
|
||||||
|
|
||||||
export type Contact = ExistingContact
|
export type Contact = ExistingContact
|
||||||
@@ -14,12 +15,12 @@ export type Match = {
|
|||||||
export type State =
|
export type State =
|
||||||
| {
|
| {
|
||||||
step: '1: phone input'
|
step: '1: phone input'
|
||||||
phoneCountryCode?: string
|
phoneCountryCode?: CountryCode
|
||||||
phoneNumber?: string
|
phoneNumber?: string
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
step: '2: verify number'
|
step: '2: verify number'
|
||||||
phoneCountryCode: string
|
phoneCountryCode: CountryCode
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
lastSentAt: Date
|
lastSentAt: Date
|
||||||
}
|
}
|
||||||
@@ -41,7 +42,7 @@ export type Action =
|
|||||||
| {
|
| {
|
||||||
type: 'SUBMIT_PHONE_NUMBER'
|
type: 'SUBMIT_PHONE_NUMBER'
|
||||||
payload: {
|
payload: {
|
||||||
phoneCountryCode: string
|
phoneCountryCode: CountryCode
|
||||||
phoneNumber: string
|
phoneNumber: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
import {type CountryCode as LibPhoneNumberJsCountryCode} from 'libphonenumber-js'
|
||||||
|
|
||||||
|
// Exclude Ascension Island and Tristan da Cunha - merged into `SH` in 2009
|
||||||
|
export type CountryCode = Exclude<LibPhoneNumberJsCountryCode, 'AC' | 'TA'>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: data is from Wikipedia, but some have been removed to match `libphonenumber-js`
|
||||||
|
* Mostly tiny British overseas territories + Antarctica, all of which
|
||||||
|
* share codes with a larger country. If you've one of the 10 people from these
|
||||||
|
* places, you probably know what to do.
|
||||||
|
*/
|
||||||
export const INTERNATIONAL_TELEPHONE_CODES = {
|
export const INTERNATIONAL_TELEPHONE_CODES = {
|
||||||
AD: {
|
AD: {
|
||||||
code: '+376',
|
code: '+376',
|
||||||
@@ -34,11 +45,13 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇦🇴',
|
unicodeFlag: '🇦🇴',
|
||||||
svgFlag: require('../../assets/icons/flags/AO.svg'),
|
svgFlag: require('../../assets/icons/flags/AO.svg'),
|
||||||
},
|
},
|
||||||
AQ: {
|
// sorry penguins :(
|
||||||
code: '+672',
|
// same as Norfolk Island
|
||||||
unicodeFlag: '🇦🇶',
|
// AQ: {
|
||||||
svgFlag: require('../../assets/icons/flags/AQ.svg'),
|
// code: '+672',
|
||||||
},
|
// unicodeFlag: '🇦🇶',
|
||||||
|
// svgFlag: require('../../assets/icons/flags/AQ.svg'),
|
||||||
|
// },
|
||||||
AR: {
|
AR: {
|
||||||
code: '+54',
|
code: '+54',
|
||||||
unicodeFlag: '🇦🇷',
|
unicodeFlag: '🇦🇷',
|
||||||
@@ -154,11 +167,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇧🇹',
|
unicodeFlag: '🇧🇹',
|
||||||
svgFlag: require('../../assets/icons/flags/BT.svg'),
|
svgFlag: require('../../assets/icons/flags/BT.svg'),
|
||||||
},
|
},
|
||||||
BV: {
|
// same as Norway
|
||||||
code: '+47',
|
// BV: {
|
||||||
unicodeFlag: '🇧🇻',
|
// code: '+47',
|
||||||
svgFlag: require('../../assets/icons/flags/BV.svg'),
|
// unicodeFlag: '🇧🇻',
|
||||||
},
|
// svgFlag: require('../../assets/icons/flags/BV.svg'),
|
||||||
|
// },
|
||||||
BW: {
|
BW: {
|
||||||
code: '+267',
|
code: '+267',
|
||||||
unicodeFlag: '🇧🇼',
|
unicodeFlag: '🇧🇼',
|
||||||
@@ -379,11 +393,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇬🇷',
|
unicodeFlag: '🇬🇷',
|
||||||
svgFlag: require('../../assets/icons/flags/GR.svg'),
|
svgFlag: require('../../assets/icons/flags/GR.svg'),
|
||||||
},
|
},
|
||||||
GS: {
|
// same as Falkland Islands
|
||||||
code: '+500',
|
// GS: {
|
||||||
unicodeFlag: '🇬🇸',
|
// code: '+500',
|
||||||
svgFlag: require('../../assets/icons/flags/GS.svg'),
|
// unicodeFlag: '🇬🇸',
|
||||||
},
|
// svgFlag: require('../../assets/icons/flags/GS.svg'),
|
||||||
|
// },
|
||||||
GT: {
|
GT: {
|
||||||
code: '+502',
|
code: '+502',
|
||||||
unicodeFlag: '🇬🇹',
|
unicodeFlag: '🇬🇹',
|
||||||
@@ -779,11 +794,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇵🇲',
|
unicodeFlag: '🇵🇲',
|
||||||
svgFlag: require('../../assets/icons/flags/PM.svg'),
|
svgFlag: require('../../assets/icons/flags/PM.svg'),
|
||||||
},
|
},
|
||||||
PN: {
|
// same as New Zealand
|
||||||
code: '+64',
|
// PN: {
|
||||||
unicodeFlag: '🇵🇳',
|
// code: '+64',
|
||||||
svgFlag: require('../../assets/icons/flags/PN.svg'),
|
// unicodeFlag: '🇵🇳',
|
||||||
},
|
// svgFlag: require('../../assets/icons/flags/PN.svg'),
|
||||||
|
// },
|
||||||
PR: {
|
PR: {
|
||||||
code: '+1',
|
code: '+1',
|
||||||
unicodeFlag: '🇵🇷',
|
unicodeFlag: '🇵🇷',
|
||||||
@@ -1199,11 +1215,12 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇫🇴',
|
unicodeFlag: '🇫🇴',
|
||||||
svgFlag: require('../../assets/icons/flags/FO.svg'),
|
svgFlag: require('../../assets/icons/flags/FO.svg'),
|
||||||
},
|
},
|
||||||
HM: {
|
// same as Norfolk Island
|
||||||
code: '+672',
|
// HM: {
|
||||||
unicodeFlag: '🇭🇲',
|
// code: '+672',
|
||||||
svgFlag: require('../../assets/icons/flags/HM.svg'),
|
// unicodeFlag: '🇭🇲',
|
||||||
},
|
// svgFlag: require('../../assets/icons/flags/HM.svg'),
|
||||||
|
// },
|
||||||
KM: {
|
KM: {
|
||||||
code: '+269',
|
code: '+269',
|
||||||
unicodeFlag: '🇰🇲',
|
unicodeFlag: '🇰🇲',
|
||||||
@@ -1229,16 +1246,18 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇹🇨',
|
unicodeFlag: '🇹🇨',
|
||||||
svgFlag: require('../../assets/icons/flags/TC.svg'),
|
svgFlag: require('../../assets/icons/flags/TC.svg'),
|
||||||
},
|
},
|
||||||
TF: {
|
// same as Norfolk Island
|
||||||
code: '+672',
|
// TF: {
|
||||||
unicodeFlag: '🇹🇫',
|
// code: '+672',
|
||||||
svgFlag: require('../../assets/icons/flags/TF.svg'),
|
// unicodeFlag: '🇹🇫',
|
||||||
},
|
// svgFlag: require('../../assets/icons/flags/TF.svg'),
|
||||||
UM: {
|
// },
|
||||||
code: '+1',
|
// same as US mainland
|
||||||
unicodeFlag: '🇺🇲',
|
// UM: {
|
||||||
svgFlag: require('../../assets/icons/flags/UM.svg'),
|
// code: '+1',
|
||||||
},
|
// unicodeFlag: '🇺🇲',
|
||||||
|
// svgFlag: require('../../assets/icons/flags/UM.svg'),
|
||||||
|
// },
|
||||||
VA: {
|
VA: {
|
||||||
code: '+39',
|
code: '+39',
|
||||||
unicodeFlag: '🇻🇦',
|
unicodeFlag: '🇻🇦',
|
||||||
@@ -1249,25 +1268,33 @@ export const INTERNATIONAL_TELEPHONE_CODES = {
|
|||||||
unicodeFlag: '🇽🇰',
|
unicodeFlag: '🇽🇰',
|
||||||
svgFlag: require('../../assets/icons/flags/XK.svg'),
|
svgFlag: require('../../assets/icons/flags/XK.svg'),
|
||||||
},
|
},
|
||||||
}
|
} satisfies Record<
|
||||||
|
CountryCode,
|
||||||
|
{
|
||||||
|
code: string
|
||||||
|
unicodeFlag: string
|
||||||
|
svgFlag: any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
const DEFAULT_PHONE_COUNTRY = 'US'
|
const DEFAULT_PHONE_COUNTRY = 'US' as const
|
||||||
|
|
||||||
export function getDefaultCountry(location?: {countryCode?: string}) {
|
export function getDefaultCountry(location?: {
|
||||||
|
countryCode?: string
|
||||||
|
}): CountryCode {
|
||||||
|
const locationCountryCode = location?.countryCode?.toUpperCase()
|
||||||
if (
|
if (
|
||||||
location?.countryCode &&
|
locationCountryCode &&
|
||||||
location.countryCode.toUpperCase() in INTERNATIONAL_TELEPHONE_CODES
|
locationCountryCode in INTERNATIONAL_TELEPHONE_CODES
|
||||||
) {
|
) {
|
||||||
return location.countryCode.toUpperCase()
|
return locationCountryCode as CountryCode
|
||||||
}
|
}
|
||||||
return DEFAULT_PHONE_COUNTRY
|
return DEFAULT_PHONE_COUNTRY
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPhoneCodeFromCountryCode(countryCode: string) {
|
export function getPhoneCodeFromCountryCode(countryCode: string) {
|
||||||
const country =
|
const country =
|
||||||
INTERNATIONAL_TELEPHONE_CODES[
|
INTERNATIONAL_TELEPHONE_CODES[countryCode.toUpperCase() as CountryCode]
|
||||||
countryCode.toUpperCase() as keyof typeof INTERNATIONAL_TELEPHONE_CODES
|
|
||||||
]
|
|
||||||
if (!country) throw new Error(`Country ${countryCode} not found`)
|
if (!country) throw new Error(`Country ${countryCode} not found`)
|
||||||
return country.code
|
return country.code
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14361,6 +14361,11 @@ levn@^0.4.1:
|
|||||||
prelude-ls "^1.2.1"
|
prelude-ls "^1.2.1"
|
||||||
type-check "~0.4.0"
|
type-check "~0.4.0"
|
||||||
|
|
||||||
|
libphonenumber-js@^1.12.30:
|
||||||
|
version "1.12.30"
|
||||||
|
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.30.tgz#8f37c6d0546e89a399085d329214a583642e611a"
|
||||||
|
integrity sha512-KxH7uIJFD6+cR6nhdh+wY6prFiH26A3W/W1gTMXnng2PXSwVfi5MhYkdq3Z2Y7vhBVa1/5VJgpNtI76UM2njGA==
|
||||||
|
|
||||||
lighthouse-logger@^1.0.0:
|
lighthouse-logger@^1.0.0:
|
||||||
version "1.4.2"
|
version "1.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz#aef90f9e97cd81db367c7634292ee22079280aaa"
|
resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz#aef90f9e97cd81db367c7634292ee22079280aaa"
|
||||||
|
|||||||
Reference in New Issue
Block a user