diff --git a/package-lock.json b/package-lock.json index 87ebf90..fb59d8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "framer-motion": "^10.16.4", "fuse.js": "^6.6.2", "google-libphonenumber": "^3.2.33", - "jssip": "^3.2.17", + "jssip": "^3.10.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-feather": "^2.0.10", @@ -4900,6 +4900,104 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", + "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "peer": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "peer": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -10706,7 +10804,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true + "devOptional": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -16975,7 +17073,7 @@ "version": "1.68.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz", "integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==", - "dev": true, + "devOptional": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", diff --git a/package.json b/package.json index 4a59b6a..42c4977 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "framer-motion": "^10.16.4", "fuse.js": "^6.6.2", "google-libphonenumber": "^3.2.33", - "jssip": "^3.2.17", + "jssip": "^3.10.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-feather": "^2.0.10", diff --git a/src/common/types.ts b/src/common/types.ts index 3763146..b24570e 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -73,5 +73,12 @@ export interface CallHistory { isSaved?: boolean; } -export type SipClientStatus = "online" | "offline"; +export type SipClientStatus = + | "start" + | "stop" + | "connecting" + | "connected" + | "disconnected" + | "online" + | "offline"; export type SipCallDirection = "" | "outgoing" | "incoming"; diff --git a/src/components/switch/index.tsx b/src/components/switch/index.tsx new file mode 100644 index 0000000..6b19e0a --- /dev/null +++ b/src/components/switch/index.tsx @@ -0,0 +1,62 @@ +import { Box, Text } from "@chakra-ui/react"; +import { useEffect, useState } from "react"; + +type JambonzSwitchProbs = { + onlabel: string; + offLabel: string; + initialCheck: boolean; + onChange: (value: boolean) => void; +}; +function JambonzSwitch({ + onlabel, + offLabel, + initialCheck, + onChange, +}: JambonzSwitchProbs) { + const [isToggled, setToggled] = useState(initialCheck); + + useEffect(() => { + setToggled(initialCheck); + }, [initialCheck]); + + return ( + { + const value = !isToggled; + setToggled(value); + onChange(value); + }} + _hover={{ cursor: "pointer" }} + > + + {isToggled ? onlabel : offLabel} + + + + ); +} + +export default JambonzSwitch; diff --git a/src/window/phone/index.tsx b/src/window/phone/index.tsx index a50b699..81da0f8 100644 --- a/src/window/phone/index.tsx +++ b/src/window/phone/index.tsx @@ -12,6 +12,7 @@ import { Text, Tooltip, VStack, + useToast, } from "@chakra-ui/react"; import { useEffect, useRef, useState } from "react"; import { @@ -52,6 +53,8 @@ import { OutGoingCall } from "./outgoing-call"; import { v4 as uuidv4 } from "uuid"; import IconButtonMenu, { IconButtonMenuItems } from "src/components/menu"; import { getApplications, getQueues, getRegisteredUser } from "src/api"; +import JambonzSwitch from "src/components/switch"; +import { DEFAULT_TOAST_DURATION } from "src/common/constants"; type PhoneProbs = { sipDomain: string; @@ -87,8 +90,6 @@ export const Phone = ({ const timerRef = useRef(null); const [seconds, setSeconds] = useState(0); const secondsRef = useRef(seconds); - const [isStatusDropdownDisabled, setIsStatusDropdownDisabled] = - useState(false); const [isCallButtonLoading, setIsCallButtonLoading] = useState(false); const [isAdvanceMode, setIsAdvancedMode] = useState(false); const isRestartRef = useRef(false); @@ -97,6 +98,8 @@ export const Phone = ({ const sipPasswordRef = useRef(""); const sipServerAddressRef = useRef(""); const sipDisplayNameRef = useRef(""); + const [isForceChangeUaStatus, setIsForceChangeUaStatus] = useState(false); + const toast = useToast(); useEffect(() => { sipDomainRef.current = sipDomain; @@ -129,12 +132,6 @@ export const Phone = ({ secondsRef.current = seconds; }, [inputNumber, seconds, sessionDirection]); - useEffect(() => { - if (isStatusDropdownDisabled) { - setIsStatusDropdownDisabled(false); - } - }, [status]); - useEffect(() => { if (isSipClientIdle(callStatus) && isCallButtonLoading) { setIsCallButtonLoading(false); @@ -158,6 +155,10 @@ export const Phone = ({ } }, [calledANumber]); + useEffect(() => { + setIsForceChangeUaStatus(false); + }, [status]); + // useEffect(() => { // chrome.runtime.onMessage.addListener(function (request) { // const msg = request as Message; @@ -225,6 +226,22 @@ export const Phone = ({ } else { clientGoOffline(); } + toast({ + title: `User is not registered${args.cause ? `, ${args.cause}` : ""}`, + status: "warning", + duration: DEFAULT_TOAST_DURATION, + isClosable: true, + }); + }); + + sipClient.on(SipConstants.UA_DISCONNECTED, (args) => { + setStatus("disconnected"); + toast({ + title: `Cannot connect to ${sipServerAddress}`, + status: "warning", + duration: DEFAULT_TOAST_DURATION, + isClosable: true, + }); }); // Call Status sipClient.on(SipConstants.SESSION_RINGING, (args) => { @@ -405,20 +422,16 @@ export const Phone = ({ {sipDisplayName || sipUsername} - + /> {`${sipUsername}@${sipDomain}`} diff --git a/src/window/settings/basic.tsx b/src/window/settings/basic.tsx index 1d7b71d..671b099 100644 --- a/src/window/settings/basic.tsx +++ b/src/window/settings/basic.tsx @@ -14,7 +14,6 @@ import { import { useEffect, useState } from "react"; import PasswordInput from "src/components/password-input"; import { getSettings, saveSettings } from "src/storage"; -import InfoIcon from "src/imgs/icons/Info.svg"; import ResetIcon from "src/imgs/icons/Reset.svg"; import { AppSettings } from "src/common/types"; import { DEFAULT_TOAST_DURATION } from "src/common/constants";