mirror of
https://github.com/jambonz/chrome-extension-dialer.git
synced 2025-12-19 04:47:45 +00:00
fix switching user status
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
export const DEFAULT_COLOR_SCHEME = "pink";
|
export const DEFAULT_COLOR_SCHEME = "pink";
|
||||||
export const DEFAULT_TOAST_DURATION = 5000;
|
export const DEFAULT_TOAST_DURATION = 3000;
|
||||||
|
|||||||
@@ -79,6 +79,6 @@ export type SipClientStatus =
|
|||||||
| "connecting"
|
| "connecting"
|
||||||
| "connected"
|
| "connected"
|
||||||
| "disconnected"
|
| "disconnected"
|
||||||
| "online"
|
| "registered"
|
||||||
| "offline";
|
| "unregistered";
|
||||||
export type SipCallDirection = "" | "outgoing" | "incoming";
|
export type SipCallDirection = "" | "outgoing" | "incoming";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
IconButton,
|
IconButton,
|
||||||
Menu,
|
Menu,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
@@ -54,11 +55,13 @@ export const IconButtonMenu = ({
|
|||||||
<Spinner color="jambonz.500" size="xs" />
|
<Spinner color="jambonz.500" size="xs" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : items.length > 0 ? (
|
) : items.length > 0 ? (
|
||||||
items.map((i, idx) => (
|
<Box overflowY="auto" maxH="250px">
|
||||||
<MenuItem key={idx} onClick={() => onClick(i.name, i.value)}>
|
{items.map((i, idx) => (
|
||||||
{i.name}
|
<MenuItem key={idx} onClick={() => onClick(i.name, i.value)}>
|
||||||
</MenuItem>
|
{i.name}
|
||||||
))
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem>{noResultLabel}</MenuItem>
|
<MenuItem>{noResultLabel}</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,32 +1,25 @@
|
|||||||
import { Box, Text } from "@chakra-ui/react";
|
import { Box, Text } from "@chakra-ui/react";
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
type JambonzSwitchProbs = {
|
type JambonzSwitchProbs = {
|
||||||
onlabel: string;
|
onlabel: string;
|
||||||
offLabel: string;
|
offLabel: string;
|
||||||
initialCheck: boolean;
|
checked: [boolean, React.Dispatch<React.SetStateAction<boolean>>];
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
onChange: (value: boolean) => void;
|
onChange: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
function JambonzSwitch({
|
function JambonzSwitch({
|
||||||
onlabel,
|
onlabel,
|
||||||
offLabel,
|
offLabel,
|
||||||
initialCheck,
|
checked: [isToggled, setToggled],
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
onChange,
|
onChange,
|
||||||
}: JambonzSwitchProbs) {
|
}: JambonzSwitchProbs) {
|
||||||
const [isToggled, setToggled] = useState(initialCheck);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setToggled(initialCheck);
|
|
||||||
}, [initialCheck]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
position="relative"
|
position="relative"
|
||||||
w="90px"
|
w="90px"
|
||||||
h="30px"
|
h="30px"
|
||||||
bg={isToggled && !isDisabled ? "green.500" : "grey.500"}
|
bg={isToggled ? "green.500" : "grey.500"}
|
||||||
borderRadius="full"
|
borderRadius="full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!isDisabled) {
|
if (!isDisabled) {
|
||||||
|
|||||||
@@ -211,4 +211,8 @@ export default class SipUA extends events.EventEmitter {
|
|||||||
const session: SipSession = this.#sessionManager.getSession(id);
|
const session: SipSession = this.#sessionManager.getSession(id);
|
||||||
session.setActive(true);
|
session.setActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isConnected() {
|
||||||
|
return this.#ua.isConnected();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import GreenAvatar from "src/imgs/icons/Avatar-Green.svg";
|
|||||||
import "./styles.scss";
|
import "./styles.scss";
|
||||||
import {
|
import {
|
||||||
deleteCurrentCall,
|
deleteCurrentCall,
|
||||||
getAdvancedSettings,
|
|
||||||
getCurrentCall,
|
getCurrentCall,
|
||||||
saveCallHistory,
|
saveCallHistory,
|
||||||
saveCurrentCall,
|
saveCurrentCall,
|
||||||
@@ -86,7 +85,7 @@ export const Phone = ({
|
|||||||
const [inputNumber, setInputNumber] = useState("");
|
const [inputNumber, setInputNumber] = useState("");
|
||||||
const [appName, setAppName] = useState("");
|
const [appName, setAppName] = useState("");
|
||||||
const inputNumberRef = useRef(inputNumber);
|
const inputNumberRef = useRef(inputNumber);
|
||||||
const [status, setStatus] = useState<SipClientStatus>("offline");
|
const [status, setStatus] = useState<SipClientStatus>("stop");
|
||||||
const [isConfigured, setIsConfigured] = useState(false);
|
const [isConfigured, setIsConfigured] = useState(false);
|
||||||
const [callStatus, setCallStatus] = useState(SipConstants.SESSION_ENDED);
|
const [callStatus, setCallStatus] = useState(SipConstants.SESSION_ENDED);
|
||||||
const [sessionDirection, setSessionDirection] =
|
const [sessionDirection, setSessionDirection] =
|
||||||
@@ -104,7 +103,9 @@ export const Phone = ({
|
|||||||
const sipPasswordRef = useRef("");
|
const sipPasswordRef = useRef("");
|
||||||
const sipServerAddressRef = useRef("");
|
const sipServerAddressRef = useRef("");
|
||||||
const sipDisplayNameRef = useRef("");
|
const sipDisplayNameRef = useRef("");
|
||||||
const [isForceChangeUaStatus, setIsForceChangeUaStatus] = useState(false);
|
const [isSwitchingUserStatus, setIsSwitchingUserStatus] = useState(true);
|
||||||
|
const [isOnline, setIsOnline] = useState(false);
|
||||||
|
const unregisteredReasonRef = useRef("");
|
||||||
const isInputNumberFocusRef = useRef(false);
|
const isInputNumberFocusRef = useRef(false);
|
||||||
const [registeredUser, setRegisteredUser] = useState<Partial<RegisteredUser>>(
|
const [registeredUser, setRegisteredUser] = useState<Partial<RegisteredUser>>(
|
||||||
{
|
{
|
||||||
@@ -123,8 +124,12 @@ export const Phone = ({
|
|||||||
sipDisplayNameRef.current = sipDisplayName;
|
sipDisplayNameRef.current = sipDisplayName;
|
||||||
if (sipDomain && sipUsername && sipPassword && sipServerAddress) {
|
if (sipDomain && sipUsername && sipPassword && sipServerAddress) {
|
||||||
if (sipUA.current) {
|
if (sipUA.current) {
|
||||||
clientGoOffline();
|
if (sipUA.current.isConnected()) {
|
||||||
isRestartRef.current = true;
|
clientGoOffline();
|
||||||
|
isRestartRef.current = true;
|
||||||
|
} else {
|
||||||
|
createSipClient();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createSipClient();
|
createSipClient();
|
||||||
}
|
}
|
||||||
@@ -133,14 +138,12 @@ export const Phone = ({
|
|||||||
setIsConfigured(false);
|
setIsConfigured(false);
|
||||||
clientGoOffline();
|
clientGoOffline();
|
||||||
}
|
}
|
||||||
getSelfRegisteredUser(sipUsernameRef.current).then(({ json }) => {
|
fetchRegisterUser();
|
||||||
setRegisteredUser(json);
|
|
||||||
});
|
|
||||||
}, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]);
|
}, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const advancedSettings = getAdvancedSettings();
|
|
||||||
setIsAdvancedMode(!!advancedSettings.accountSid);
|
setIsAdvancedMode(!!advancedSettings.accountSid);
|
||||||
|
fetchRegisterUser();
|
||||||
}, [advancedSettings]);
|
}, [advancedSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -173,17 +176,16 @@ export const Phone = ({
|
|||||||
}, [calledANumber]);
|
}, [calledANumber]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === "online" || status === "offline") {
|
if (status === "registered" || status === "disconnected") {
|
||||||
setIsForceChangeUaStatus(false);
|
setIsSwitchingUserStatus(false);
|
||||||
|
setIsOnline(status === "registered");
|
||||||
}
|
}
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
getSelfRegisteredUser(sipUsernameRef.current).then(({ json }) => {
|
fetchRegisterUser();
|
||||||
setRegisteredUser(json);
|
}, 10_000);
|
||||||
});
|
|
||||||
}, 20000);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
@@ -209,6 +211,20 @@ export const Phone = ({
|
|||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
const fetchRegisterUser = () => {
|
||||||
|
getSelfRegisteredUser(sipUsernameRef.current)
|
||||||
|
.then(({ json }) => {
|
||||||
|
setRegisteredUser(json);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setRegisteredUser({
|
||||||
|
allow_direct_app_calling: false,
|
||||||
|
allow_direct_queue_calling: false,
|
||||||
|
allow_direct_user_calling: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const startCallDurationCounter = () => {
|
const startCallDurationCounter = () => {
|
||||||
stopCallDurationCounter();
|
stopCallDurationCounter();
|
||||||
timerRef.current = setInterval(() => {
|
timerRef.current = setInterval(() => {
|
||||||
@@ -225,6 +241,7 @@ export const Phone = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createSipClient = () => {
|
const createSipClient = () => {
|
||||||
|
setIsSwitchingUserStatus(true);
|
||||||
const client = {
|
const client = {
|
||||||
username: `${sipUsernameRef.current}@${sipDomainRef.current}`,
|
username: `${sipUsernameRef.current}@${sipDomainRef.current}`,
|
||||||
password: sipPasswordRef.current,
|
password: sipPasswordRef.current,
|
||||||
@@ -243,26 +260,34 @@ export const Phone = ({
|
|||||||
|
|
||||||
// UA Status
|
// UA Status
|
||||||
sipClient.on(SipConstants.UA_REGISTERED, (args) => {
|
sipClient.on(SipConstants.UA_REGISTERED, (args) => {
|
||||||
setStatus("online");
|
setStatus("registered");
|
||||||
});
|
});
|
||||||
sipClient.on(SipConstants.UA_UNREGISTERED, (args) => {
|
sipClient.on(SipConstants.UA_UNREGISTERED, (args) => {
|
||||||
setStatus("offline");
|
setStatus("unregistered");
|
||||||
if (isRestartRef.current) {
|
if (sipUA.current) {
|
||||||
createSipClient();
|
sipUA.current.stop();
|
||||||
isRestartRef.current = false;
|
|
||||||
} else {
|
|
||||||
clientGoOffline();
|
|
||||||
}
|
}
|
||||||
toast({
|
unregisteredReasonRef.current = `User is not registered${
|
||||||
title: `User is not registered${args.cause ? `, ${args.cause}` : ""}`,
|
args.cause ? `, ${args.cause}` : ""
|
||||||
status: "warning",
|
}`;
|
||||||
duration: DEFAULT_TOAST_DURATION,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
sipClient.on(SipConstants.UA_DISCONNECTED, (args) => {
|
sipClient.on(SipConstants.UA_DISCONNECTED, (args) => {
|
||||||
|
if (unregisteredReasonRef.current) {
|
||||||
|
toast({
|
||||||
|
title: unregisteredReasonRef.current,
|
||||||
|
status: "warning",
|
||||||
|
duration: DEFAULT_TOAST_DURATION,
|
||||||
|
isClosable: true,
|
||||||
|
});
|
||||||
|
unregisteredReasonRef.current = "";
|
||||||
|
}
|
||||||
setStatus("disconnected");
|
setStatus("disconnected");
|
||||||
|
if (isRestartRef.current) {
|
||||||
|
createSipClient();
|
||||||
|
isRestartRef.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.error) {
|
if (args.error) {
|
||||||
toast({
|
toast({
|
||||||
title: `Cannot connect to ${sipServerAddress}, ${args.reason}`,
|
title: `Cannot connect to ${sipServerAddress}, ${args.reason}`,
|
||||||
@@ -392,17 +417,17 @@ export const Phone = ({
|
|||||||
if (s === status) {
|
if (s === status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (s === "offline") {
|
if (s === "unregistered") {
|
||||||
clientGoOffline();
|
if (sipUA.current) {
|
||||||
|
sipUA.current.stop();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createSipClient();
|
if (sipUA.current) {
|
||||||
|
sipUA.current.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isOnline = () => {
|
|
||||||
return status === "online";
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleHangup = () => {
|
const handleHangup = () => {
|
||||||
if (isSipClientAnswered(callStatus) || isSipClientRinging(callStatus)) {
|
if (isSipClientAnswered(callStatus) || isSipClientRinging(callStatus)) {
|
||||||
sipUA.current?.terminate(480, "Call Finished", undefined);
|
sipUA.current?.terminate(480, "Call Finished", undefined);
|
||||||
@@ -441,18 +466,28 @@ export const Phone = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isStatusRegistered = () => {
|
||||||
|
return status === "registered";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center flexDirection="column">
|
<Center flexDirection="column">
|
||||||
{isConfigured ? (
|
{isConfigured ? (
|
||||||
<>
|
<>
|
||||||
<HStack spacing={2} boxShadow="md" w="full" borderRadius={5} p={2}>
|
<HStack spacing={2} boxShadow="md" w="full" borderRadius={5} p={2}>
|
||||||
<Image src={isOnline() ? GreenAvatar : Avatar} boxSize="35px" />
|
<Image
|
||||||
|
src={isStatusRegistered() ? GreenAvatar : Avatar}
|
||||||
|
boxSize="35px"
|
||||||
|
/>
|
||||||
<VStack alignItems="start" w="full" spacing={0}>
|
<VStack alignItems="start" w="full" spacing={0}>
|
||||||
<HStack spacing={2} w="full">
|
<HStack spacing={2} w="full">
|
||||||
<Text fontWeight="bold" fontSize="13px">
|
<Text fontWeight="bold" fontSize="13px">
|
||||||
{sipDisplayName || sipUsername}
|
{sipDisplayName || sipUsername}
|
||||||
</Text>
|
</Text>
|
||||||
<Circle size="8px" bg={isOnline() ? "green.500" : "gray.500"} />
|
<Circle
|
||||||
|
size="8px"
|
||||||
|
bg={isStatusRegistered() ? "green.500" : "gray.500"}
|
||||||
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
<Text fontWeight="bold" w="full">
|
<Text fontWeight="bold" w="full">
|
||||||
{`${sipUsername}@${sipDomain}`}
|
{`${sipUsername}@${sipDomain}`}
|
||||||
@@ -462,13 +497,13 @@ export const Phone = ({
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<VStack h="full" align="center">
|
<VStack h="full" align="center">
|
||||||
<JambonzSwitch
|
<JambonzSwitch
|
||||||
isDisabled={isForceChangeUaStatus}
|
isDisabled={isSwitchingUserStatus}
|
||||||
onlabel="Online"
|
onlabel="Online"
|
||||||
offLabel="Offline"
|
offLabel="Offline"
|
||||||
initialCheck={isOnline() || isForceChangeUaStatus}
|
checked={[isOnline, setIsOnline]}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
setIsForceChangeUaStatus(true);
|
setIsSwitchingUserStatus(true);
|
||||||
handleGoOffline(v ? "online" : "offline");
|
handleGoOffline(v ? "registered" : "unregistered");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -498,7 +533,7 @@ export const Phone = ({
|
|||||||
spacing={2}
|
spacing={2}
|
||||||
w="full"
|
w="full"
|
||||||
mt={5}
|
mt={5}
|
||||||
className={isOnline() ? "" : "blurred"}
|
className={isStatusRegistered() ? "" : "blurred"}
|
||||||
>
|
>
|
||||||
{isAdvanceMode && isSipClientIdle(callStatus) && (
|
{isAdvanceMode && isSipClientIdle(callStatus) && (
|
||||||
<HStack spacing={2} align="start" w="full">
|
<HStack spacing={2} align="start" w="full">
|
||||||
@@ -627,7 +662,7 @@ export const Phone = ({
|
|||||||
<Button
|
<Button
|
||||||
w="full"
|
w="full"
|
||||||
onClick={handleCallButtion}
|
onClick={handleCallButtion}
|
||||||
isDisabled={status === "offline"}
|
isDisabled={!isStatusRegistered()}
|
||||||
colorScheme="jambonz"
|
colorScheme="jambonz"
|
||||||
alignContent="center"
|
alignContent="center"
|
||||||
isLoading={isCallButtonLoading}
|
isLoading={isCallButtonLoading}
|
||||||
|
|||||||
Reference in New Issue
Block a user