Use an intl phone input number field and fix the whole verification flow

- There were some state mismatches in configuring a whatsapp number. This commit fixes those issues and uses an external library for phone number validation
This commit is contained in:
sabaimran 2024-08-01 16:44:17 +05:30
parent 60870a7a3e
commit 84dd1b57fe
4 changed files with 56 additions and 26 deletions

View file

@ -1,6 +1,7 @@
'use client'
import styles from "./settings.module.css";
import "intl-tel-input/styles";
import { Suspense, useEffect, useState } from "react";
import { useToast } from "@/components/ui/use-toast"
@ -74,6 +75,8 @@ import NavMenu from "../components/navMenu/navMenu";
import SidePanel from "../components/sidePanel/chatHistorySidePanel";
import Loading from "../components/loading/loading";
import IntlTelInput from 'intl-tel-input/react';
const ManageFilesModal: React.FC<{ onClose: () => void }> = ({ onClose }) => {
const [syncedFiles, setSyncedFiles] = useState<string[]>([]);
@ -349,7 +352,7 @@ export default function SettingsView() {
const [userConfig, setUserConfig] = useState<UserConfig | null>(null);
const [name, setName] = useState<string | undefined>(undefined);
const [notionToken, setNotionToken] = useState<string | null>(null);
const [number, setNumber] = useState<string | undefined>(undefined);
const [phoneNumber, setPhoneNumber] = useState<string | undefined>(undefined);
const [otp, setOTP] = useState("");
const [numberValidationState, setNumberValidationState] = useState<PhoneNumberValidationState>(PhoneNumberValidationState.Verified);
const [isManageFilesModalOpen, setIsManageFilesModalOpen] = useState(false);
@ -358,7 +361,7 @@ export default function SettingsView() {
useEffect(() => {
setUserConfig(initialUserConfig);
setNumber(initialUserConfig?.phone_number);
setPhoneNumber(initialUserConfig?.phone_number);
setNumberValidationState(
initialUserConfig?.is_phone_number_verified
? PhoneNumberValidationState.Verified
@ -379,7 +382,7 @@ export default function SettingsView() {
const sendOTP = async () => {
try {
const response = await fetch(`/api/phone?phone_number=${number}`, {
const response = await fetch(`/api/phone?phone_number=${phoneNumber}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -942,15 +945,25 @@ export default function SettingsView() {
Connect your number to chat with Khoj on WhatsApp. Learn more about the integration <a href="https://docs.khoj.dev/clients/whatsapp">here</a>.
</p>
<div>
<IntlTelInput
initialValue={phoneNumber || ""}
onChangeNumber={setPhoneNumber}
disabled={numberValidationState === PhoneNumberValidationState.Verified || numberValidationState === PhoneNumberValidationState.VerifyOTP}
initOptions={{
separateDialCode: true,
initialCountry: "us",
utilsScript: "https://assets.khoj.dev/intl-tel-input%4023.8.0_build_js_utils.js",
}}
/>
{numberValidationState === PhoneNumberValidationState.VerifyOTP && (
<>
<p>{`Enter the OTP sent to your WhatsApp number: ${number}`}</p>
<p>{`Enter the OTP sent to your number: ${phoneNumber}`}</p>
<InputOTP
autoFocus={true}
maxLength={6}
value={otp || ""}
onChange={setOTP}
onComplete={() => setNumberValidationState(PhoneNumberValidationState.Verified)}
onComplete={() => setNumberValidationState(PhoneNumberValidationState.VerifyOTP)}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
@ -962,16 +975,6 @@ export default function SettingsView() {
</InputOTPGroup>
</InputOTP>
</>
) || (
<>
<Input
type="tel"
onChange={(e) => setNumber(e.target.value)}
value={number || ""}
placeholder="Enter phone number (e.g. +911234567890)"
className="w-full border border-gray-300 rounded-lg px-4 py-6"
/>
</>
)}
</div>
</CardContent>
@ -986,14 +989,14 @@ export default function SettingsView() {
) || (
<Button
variant="outline"
disabled={!number || number === userConfig.phone_number || !isValidPhoneNumber(number)}
disabled={!phoneNumber || (phoneNumber === userConfig.phone_number && numberValidationState === PhoneNumberValidationState.Verified) || !isValidPhoneNumber(phoneNumber)}
onClick={sendOTP}
>
{!userConfig.phone_number
? (<><Plugs className="inline mr-2" />Setup Whatsapp</>)
: !number || number === userConfig.phone_number || !isValidPhoneNumber(number)
: !phoneNumber || (phoneNumber === userConfig.phone_number && numberValidationState === PhoneNumberValidationState.Verified) || !isValidPhoneNumber(phoneNumber)
? (<><PlugsConnected className="inline mr-2 text-green-400" />Switch Number</>)
: (<>Send OTP to Whatsapp <ArrowRight className="inline ml-2" weight="bold"/></>)
: (<>Send OTP <ArrowRight className="inline ml-2" weight="bold"/></>)
}
</Button>
)}

View file

@ -46,6 +46,7 @@
"eslint": "^8",
"eslint-config-next": "14.2.3",
"input-otp": "^1.2.4",
"intl-tel-input": "^23.8.0",
"katex": "^0.16.10",
"libphonenumber-js": "^1.11.4",
"lucide-react": "^0.397.0",
@ -64,7 +65,13 @@
"zod": "^3.23.8"
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/intl-tel-input": "^18.1.4",
"@types/katex": "^0.16.7",
"@types/markdown-it": "^14.1.1",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.3",
"eslint-config-prettier": "^9.1.0",
@ -73,15 +80,10 @@
"lint-staged": "^15.2.7",
"nodemon": "^3.1.3",
"prettier": "3.3.3",
"tailwindcss-animate": "^1.0.7",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.6",
"typescript": "^5",
"@types/dompurify": "^3.0.5",
"@types/katex": "^0.16.7",
"@types/markdown-it": "^14.1.1",
"@types/react": "^18",
"@types/react-dom": "^18"
"tailwindcss-animate": "^1.0.7",
"typescript": "^5"
},
"prettier": {
"tabWidth": 4

View file

@ -1295,6 +1295,20 @@
dependencies:
"@types/trusted-types" "*"
"@types/intl-tel-input@^18.1.4":
version "18.1.4"
resolved "https://registry.yarnpkg.com/@types/intl-tel-input/-/intl-tel-input-18.1.4.tgz#0eb5211a7490f8a8d7aa940ee594a85138d514c9"
integrity sha512-UT4dQ4dQA0w0uxU161aPROEEwMOQj5Qedm3ImdDNfkxi3JXzBX2FhcUS72pTyZ8ypaUr2e9ruJBiK6bwSGfbew==
dependencies:
"@types/jquery" "*"
"@types/jquery@*":
version "3.5.30"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.30.tgz#888d584cbf844d3df56834b69925085038fd80f7"
integrity sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A==
dependencies:
"@types/sizzle" "*"
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@ -1350,6 +1364,11 @@
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/sizzle@*":
version "2.3.8"
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627"
integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==
"@types/trusted-types@*":
version "2.0.7"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
@ -2982,6 +3001,11 @@ internal-slot@^1.0.4, internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
intl-tel-input@^23.8.0:
version "23.8.0"
resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-23.8.0.tgz#37bec095605516aa72529b3da11335b253b65b2f"
integrity sha512-lx8Sz5LfVyIXyjWbfjno89o4qHfIuWulctGaWbP2RKKnHvagdt9gdibCsv9uEH7izb/yjB6Nst0sRo988/lhpw==
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@ -4442,6 +4466,7 @@ string-argv@~0.3.2:
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==

View file

@ -1298,7 +1298,7 @@ def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False)
"user_photo": user_picture,
"is_active": is_active,
"given_name": given_name,
"phone_number": user.phone_number,
"phone_number": str(user.phone_number),
"is_phone_number_verified": user.verified_phone_number,
# user content settings
"enabled_content_source": enabled_content_sources,