Merge pull request #7 from jambonz/feat/fixbugs

add outgoing page and fix bugs
This commit is contained in:
Dave Horton
2023-10-17 10:55:51 +02:00
committed by GitHub
8 changed files with 134 additions and 55 deletions
+9
View File
@@ -14,6 +14,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"fuse.js": "^6.6.2",
"google-libphonenumber": "^3.2.33", "google-libphonenumber": "^3.2.33",
"jssip": "^3.2.17", "jssip": "^3.2.17",
"react": "^18.2.0", "react": "^18.2.0",
@@ -10153,6 +10154,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/fuse.js": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz",
"integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==",
"engines": {
"node": ">=10"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+1
View File
@@ -9,6 +9,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"fuse.js": "^6.6.2",
"google-libphonenumber": "^3.2.33", "google-libphonenumber": "^3.2.33",
"jssip": "^3.2.17", "jssip": "^3.2.17",
"react": "^18.2.0", "react": "^18.2.0",
+5 -18
View File
@@ -62,24 +62,11 @@ export const WindowApp = () => {
const loadSettings = () => { const loadSettings = () => {
const settings = getSettings(); const settings = getSettings();
if (settings.sipDomain && settings.sipDomain !== sipDomain) { setSipDomain(settings.sipDomain);
setSipDomain(settings.sipDomain); setSipServerAddress(settings.sipServerAddress);
} setSipUsername(settings.sipUsername);
if ( setSipPassword(settings.sipPassword);
settings.sipServerAddress && setSipDisplayName(settings.sipDisplayName);
settings.sipServerAddress !== sipServerAddress
) {
setSipServerAddress(settings.sipServerAddress);
}
if (settings.sipUsername && settings.sipUsername !== sipUsername) {
setSipUsername(settings.sipUsername);
}
if (settings.sipPassword && settings.sipPassword !== sipPassword) {
setSipPassword(settings.sipPassword);
}
if (settings.sipDisplayName && settings.sipDisplayName !== sipDisplayName) {
setSipDisplayName(settings.sipDisplayName);
}
}; };
return ( return (
<Grid h="100vh" templateRows="1fr auto"> <Grid h="100vh" templateRows="1fr auto">
+23 -3
View File
@@ -10,10 +10,11 @@ import {
Spacer, Spacer,
UnorderedList, UnorderedList,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useState } from "react"; import { useEffect, useState } from "react";
import { Search, Sliders } from "react-feather"; import { Search, Sliders } from "react-feather";
import { CallHistory } from "src/common/types"; import { CallHistory } from "src/common/types";
import CallHistoryItem from "./call-history-item"; import CallHistoryItem from "./call-history-item";
import Fuse from "fuse.js";
type CallHistoriesProbs = { type CallHistoriesProbs = {
calls: CallHistory[]; calls: CallHistory[];
@@ -21,6 +22,25 @@ type CallHistoriesProbs = {
export const CallHistories = ({ calls }: CallHistoriesProbs) => { export const CallHistories = ({ calls }: CallHistoriesProbs) => {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [callHistories, setCallHistories] = useState<CallHistory[]>(calls);
useEffect(() => {
if (searchText) {
setCallHistories(
new Fuse(calls, {
keys: ["number"],
})
.search(searchText)
.map(({ item }) => item)
);
} else {
setCallHistories(calls);
}
}, [searchText]);
useEffect(() => {
setCallHistories(calls);
}, [calls]);
return ( return (
<VStack spacing={2}> <VStack spacing={2}>
@@ -46,7 +66,7 @@ export const CallHistories = ({ calls }: CallHistoriesProbs) => {
</Text> </Text>
</HStack> </HStack>
</Grid> </Grid>
{calls.length > 0 ? ( {callHistories.length > 0 ? (
<UnorderedList <UnorderedList
w="full" w="full"
spacing={2} spacing={2}
@@ -54,7 +74,7 @@ export const CallHistories = ({ calls }: CallHistoriesProbs) => {
overflowY="auto" overflowY="auto"
mt={2} mt={2}
> >
{calls.map((c) => ( {callHistories.map((c) => (
<CallHistoryItem call={c} /> <CallHistoryItem call={c} />
))} ))}
</UnorderedList> </UnorderedList>
+3
View File
@@ -27,6 +27,9 @@ export const DialPad = ({ handleDigitPress }: DialPadProbs) => {
height="70px" height="70px"
variant="unstyled" variant="unstyled"
bg="white" bg="white"
_hover={{
bg: "gray.100",
}}
borderRadius={0} borderRadius={0}
> >
{num} {num}
+65 -30
View File
@@ -41,7 +41,7 @@ import {
saveCallHistory, saveCallHistory,
saveCurrentCall, saveCurrentCall,
} from "src/storage"; } from "src/storage";
import dayjs from "dayjs"; import { OutGoingCall } from "./outgoing-call";
type PhoneProbs = { type PhoneProbs = {
sipDomain: string; sipDomain: string;
@@ -75,9 +75,12 @@ export const Phone = ({
const [isCallButtonLoading, setIsCallButtonLoading] = useState(false); const [isCallButtonLoading, setIsCallButtonLoading] = useState(false);
useEffect(() => { useEffect(() => {
if (sipDomain && sipUsername && sipPassword) { if (sipDomain && sipUsername && sipPassword && sipServerAddress) {
createSipClient(); createSipClient();
setIsConfigured(true); setIsConfigured(true);
} else {
setIsConfigured(false);
clientGoOffline();
} }
}, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]); }, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]);
@@ -99,18 +102,18 @@ export const Phone = ({
} }
}, [callStatus]); }, [callStatus]);
useEffect(() => { // useEffect(() => {
chrome.runtime.onMessage.addListener(function (request) { // chrome.runtime.onMessage.addListener(function (request) {
const msg = request as Message<any>; // const msg = request as Message<any>;
switch (msg.event) { // switch (msg.event) {
case MessageEvent.Call: // case MessageEvent.Call:
handleCallEvent(msg.data as Call); // handleCallEvent(msg.data as Call);
break; // break;
default: // default:
break; // break;
} // }
}); // });
}, []); // }, []);
const handleCallEvent = (call: Call) => { const handleCallEvent = (call: Call) => {
if (!call.number) return; if (!call.number) return;
@@ -166,17 +169,24 @@ export const Phone = ({
}); });
// Call Status // Call Status
sipClient.on(SipConstants.SESSION_RINGING, (args) => { sipClient.on(SipConstants.SESSION_RINGING, (args) => {
saveCurrentCall({ if (args.session.direction === "incoming") {
number: args.session.user, saveCurrentCall({
direction: args.session.direction, number: args.session.user,
timeStamp: Date.now(), direction: args.session.direction,
duration: "0", timeStamp: Date.now(),
}); duration: "0",
});
}
setCallStatus(SipConstants.SESSION_RINGING); setCallStatus(SipConstants.SESSION_RINGING);
setSessionDirection(args.session.direction); setSessionDirection(args.session.direction);
setInputNumber(args.session.user); setInputNumber(args.session.user);
}); });
sipClient.on(SipConstants.SESSION_ANSWERED, (args) => { sipClient.on(SipConstants.SESSION_ANSWERED, (args) => {
const currentCall = getCurrentCall();
if (currentCall) {
currentCall.timeStamp = Date.now();
saveCurrentCall(currentCall);
}
setCallStatus(SipConstants.SESSION_ANSWERED); setCallStatus(SipConstants.SESSION_ANSWERED);
startCallDurationCounter(); startCallDurationCounter();
}); });
@@ -203,15 +213,28 @@ export const Phone = ({
saveCallHistory(sipUsername, { saveCallHistory(sipUsername, {
number: call.number, number: call.number,
direction: call.direction, direction: call.direction,
duration: new Date((Date.now() - call.timeStamp) / 1000) duration: transform(Date.now(), call.timeStamp),
.toISOString()
.substr(11, 8),
timeStamp: call.timeStamp, timeStamp: call.timeStamp,
}); });
} }
deleteCurrentCall(); deleteCurrentCall();
}; };
function transform(t1: number, t2: number) {
const diff = Math.abs(t1 - t2) / 1000; // Get the difference in seconds
const hours = Math.floor(diff / 3600);
const minutes = Math.floor((diff % 3600) / 60);
const seconds = Math.floor(diff % 60);
// Pad the values with a leading zero if they are less than 10
const hours1 = hours < 10 ? "0" + hours : hours;
const minutes1 = minutes < 10 ? "0" + minutes : minutes;
const seconds1 = seconds < 10 ? "0" + seconds : seconds;
return `${hours1}:${minutes1}:${seconds1}`;
}
const handleDialPadClick = (value: string) => { const handleDialPadClick = (value: string) => {
if (isSipClientIdle(callStatus)) { if (isSipClientIdle(callStatus)) {
setInputNumber((prev) => prev + value); setInputNumber((prev) => prev + value);
@@ -221,8 +244,16 @@ export const Phone = ({
}; };
const handleCallButtion = () => { const handleCallButtion = () => {
if (sipUA.current) { if (sipUA.current && inputNumber) {
setIsCallButtonLoading(true); setIsCallButtonLoading(true);
setCallStatus(SipConstants.SESSION_RINGING);
setSessionDirection("outgoing");
saveCurrentCall({
number: inputNumber,
direction: "outgoing",
timeStamp: Date.now(),
duration: "0",
});
sipUA.current.call(inputNumber); sipUA.current.call(inputNumber);
} }
}; };
@@ -326,12 +357,16 @@ export const Phone = ({
</Heading> </Heading>
)} )}
{isSipClientRinging(callStatus) && sessionDirection === "incoming" ? ( {isSipClientRinging(callStatus) ? (
<IncommingCall sessionDirection === "incoming" ? (
number={inputNumber} <IncommingCall
answer={handleAnswer} number={inputNumber}
decline={handleDecline} answer={handleAnswer}
/> decline={handleDecline}
/>
) : (
<OutGoingCall number={inputNumber} cancelCall={handleDecline} />
)
) : ( ) : (
<VStack <VStack
spacing={4} spacing={4}
+26
View File
@@ -0,0 +1,26 @@
import { Button, Icon, Text, VStack } from "@chakra-ui/react";
import { PhoneCall } from "react-feather";
import { formatPhoneNumber } from "src/utils";
type OutGoingCallProbs = {
number: string;
cancelCall: () => void;
};
export const OutGoingCall = ({ number, cancelCall }: OutGoingCallProbs) => {
return (
<VStack alignItems="center" spacing={4} mt="130px" w="full">
<Icon as={PhoneCall} color="jambonz.500" w="60px" h="60px" />
<Text fontSize="15px">Dialing</Text>
<Text fontSize="24px" fontWeight="bold">
{formatPhoneNumber(number)}
</Text>
<Button w="full" colorScheme="jambonz" onClick={cancelCall}>
Cancel
</Button>
</VStack>
);
};
export default OutGoingCall;
+2 -4
View File
@@ -23,9 +23,7 @@ import ResetIcon from "src/imgs/icons/Reset.svg";
export const Settings = () => { export const Settings = () => {
const [sipDomain, setSipDomain] = useState(""); const [sipDomain, setSipDomain] = useState("");
const [sipServerAddress, setSipServerAddress] = useState( const [sipServerAddress, setSipServerAddress] = useState("");
"wss://sip.jambonz.cloud:8443/"
);
const [sipUsername, setSipUsername] = useState(""); const [sipUsername, setSipUsername] = useState("");
const [sipPassword, setSipPassword] = useState(""); const [sipPassword, setSipPassword] = useState("");
const [sipDisplayName, setSipDisplayName] = useState(""); const [sipDisplayName, setSipDisplayName] = useState("");
@@ -103,7 +101,7 @@ export const Settings = () => {
<FormLabel>Jambonz Server Address</FormLabel> <FormLabel>Jambonz Server Address</FormLabel>
<Input <Input
type="text" type="text"
placeholder="Server address" placeholder="wss://sip.jambonz.cloud:8443/"
isRequired isRequired
value={sipServerAddress} value={sipServerAddress}
onChange={(e) => setSipServerAddress(e.target.value)} onChange={(e) => setSipServerAddress(e.target.value)}