mirror of
https://github.com/jambonz/chrome-extension-dialer.git
synced 2025-12-19 04:47:45 +00:00
Merge pull request #7 from jambonz/feat/fixbugs
add outgoing page and fix bugs
This commit is contained in:
9
package-lock.json
generated
9
package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"fuse.js": "^6.6.2",
|
||||
"google-libphonenumber": "^3.2.33",
|
||||
"jssip": "^3.2.17",
|
||||
"react": "^18.2.0",
|
||||
@@ -10153,6 +10154,14 @@
|
||||
"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": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"fuse.js": "^6.6.2",
|
||||
"google-libphonenumber": "^3.2.33",
|
||||
"jssip": "^3.2.17",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -62,24 +62,11 @@ export const WindowApp = () => {
|
||||
|
||||
const loadSettings = () => {
|
||||
const settings = getSettings();
|
||||
if (settings.sipDomain && settings.sipDomain !== sipDomain) {
|
||||
setSipDomain(settings.sipDomain);
|
||||
}
|
||||
if (
|
||||
settings.sipServerAddress &&
|
||||
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);
|
||||
}
|
||||
setSipDomain(settings.sipDomain);
|
||||
setSipServerAddress(settings.sipServerAddress);
|
||||
setSipUsername(settings.sipUsername);
|
||||
setSipPassword(settings.sipPassword);
|
||||
setSipDisplayName(settings.sipDisplayName);
|
||||
};
|
||||
return (
|
||||
<Grid h="100vh" templateRows="1fr auto">
|
||||
|
||||
@@ -10,10 +10,11 @@ import {
|
||||
Spacer,
|
||||
UnorderedList,
|
||||
} from "@chakra-ui/react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Search, Sliders } from "react-feather";
|
||||
import { CallHistory } from "src/common/types";
|
||||
import CallHistoryItem from "./call-history-item";
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
type CallHistoriesProbs = {
|
||||
calls: CallHistory[];
|
||||
@@ -21,6 +22,25 @@ type CallHistoriesProbs = {
|
||||
|
||||
export const CallHistories = ({ calls }: CallHistoriesProbs) => {
|
||||
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 (
|
||||
<VStack spacing={2}>
|
||||
@@ -46,7 +66,7 @@ export const CallHistories = ({ calls }: CallHistoriesProbs) => {
|
||||
</Text>
|
||||
</HStack>
|
||||
</Grid>
|
||||
{calls.length > 0 ? (
|
||||
{callHistories.length > 0 ? (
|
||||
<UnorderedList
|
||||
w="full"
|
||||
spacing={2}
|
||||
@@ -54,7 +74,7 @@ export const CallHistories = ({ calls }: CallHistoriesProbs) => {
|
||||
overflowY="auto"
|
||||
mt={2}
|
||||
>
|
||||
{calls.map((c) => (
|
||||
{callHistories.map((c) => (
|
||||
<CallHistoryItem call={c} />
|
||||
))}
|
||||
</UnorderedList>
|
||||
|
||||
@@ -27,6 +27,9 @@ export const DialPad = ({ handleDigitPress }: DialPadProbs) => {
|
||||
height="70px"
|
||||
variant="unstyled"
|
||||
bg="white"
|
||||
_hover={{
|
||||
bg: "gray.100",
|
||||
}}
|
||||
borderRadius={0}
|
||||
>
|
||||
{num}
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
saveCallHistory,
|
||||
saveCurrentCall,
|
||||
} from "src/storage";
|
||||
import dayjs from "dayjs";
|
||||
import { OutGoingCall } from "./outgoing-call";
|
||||
|
||||
type PhoneProbs = {
|
||||
sipDomain: string;
|
||||
@@ -75,9 +75,12 @@ export const Phone = ({
|
||||
const [isCallButtonLoading, setIsCallButtonLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (sipDomain && sipUsername && sipPassword) {
|
||||
if (sipDomain && sipUsername && sipPassword && sipServerAddress) {
|
||||
createSipClient();
|
||||
setIsConfigured(true);
|
||||
} else {
|
||||
setIsConfigured(false);
|
||||
clientGoOffline();
|
||||
}
|
||||
}, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]);
|
||||
|
||||
@@ -99,18 +102,18 @@ export const Phone = ({
|
||||
}
|
||||
}, [callStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
chrome.runtime.onMessage.addListener(function (request) {
|
||||
const msg = request as Message<any>;
|
||||
switch (msg.event) {
|
||||
case MessageEvent.Call:
|
||||
handleCallEvent(msg.data as Call);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// chrome.runtime.onMessage.addListener(function (request) {
|
||||
// const msg = request as Message<any>;
|
||||
// switch (msg.event) {
|
||||
// case MessageEvent.Call:
|
||||
// handleCallEvent(msg.data as Call);
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
// }, []);
|
||||
|
||||
const handleCallEvent = (call: Call) => {
|
||||
if (!call.number) return;
|
||||
@@ -166,17 +169,24 @@ export const Phone = ({
|
||||
});
|
||||
// Call Status
|
||||
sipClient.on(SipConstants.SESSION_RINGING, (args) => {
|
||||
saveCurrentCall({
|
||||
number: args.session.user,
|
||||
direction: args.session.direction,
|
||||
timeStamp: Date.now(),
|
||||
duration: "0",
|
||||
});
|
||||
if (args.session.direction === "incoming") {
|
||||
saveCurrentCall({
|
||||
number: args.session.user,
|
||||
direction: args.session.direction,
|
||||
timeStamp: Date.now(),
|
||||
duration: "0",
|
||||
});
|
||||
}
|
||||
setCallStatus(SipConstants.SESSION_RINGING);
|
||||
setSessionDirection(args.session.direction);
|
||||
setInputNumber(args.session.user);
|
||||
});
|
||||
sipClient.on(SipConstants.SESSION_ANSWERED, (args) => {
|
||||
const currentCall = getCurrentCall();
|
||||
if (currentCall) {
|
||||
currentCall.timeStamp = Date.now();
|
||||
saveCurrentCall(currentCall);
|
||||
}
|
||||
setCallStatus(SipConstants.SESSION_ANSWERED);
|
||||
startCallDurationCounter();
|
||||
});
|
||||
@@ -203,15 +213,28 @@ export const Phone = ({
|
||||
saveCallHistory(sipUsername, {
|
||||
number: call.number,
|
||||
direction: call.direction,
|
||||
duration: new Date((Date.now() - call.timeStamp) / 1000)
|
||||
.toISOString()
|
||||
.substr(11, 8),
|
||||
duration: transform(Date.now(), call.timeStamp),
|
||||
timeStamp: call.timeStamp,
|
||||
});
|
||||
}
|
||||
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) => {
|
||||
if (isSipClientIdle(callStatus)) {
|
||||
setInputNumber((prev) => prev + value);
|
||||
@@ -221,8 +244,16 @@ export const Phone = ({
|
||||
};
|
||||
|
||||
const handleCallButtion = () => {
|
||||
if (sipUA.current) {
|
||||
if (sipUA.current && inputNumber) {
|
||||
setIsCallButtonLoading(true);
|
||||
setCallStatus(SipConstants.SESSION_RINGING);
|
||||
setSessionDirection("outgoing");
|
||||
saveCurrentCall({
|
||||
number: inputNumber,
|
||||
direction: "outgoing",
|
||||
timeStamp: Date.now(),
|
||||
duration: "0",
|
||||
});
|
||||
sipUA.current.call(inputNumber);
|
||||
}
|
||||
};
|
||||
@@ -326,12 +357,16 @@ export const Phone = ({
|
||||
</Heading>
|
||||
)}
|
||||
|
||||
{isSipClientRinging(callStatus) && sessionDirection === "incoming" ? (
|
||||
<IncommingCall
|
||||
number={inputNumber}
|
||||
answer={handleAnswer}
|
||||
decline={handleDecline}
|
||||
/>
|
||||
{isSipClientRinging(callStatus) ? (
|
||||
sessionDirection === "incoming" ? (
|
||||
<IncommingCall
|
||||
number={inputNumber}
|
||||
answer={handleAnswer}
|
||||
decline={handleDecline}
|
||||
/>
|
||||
) : (
|
||||
<OutGoingCall number={inputNumber} cancelCall={handleDecline} />
|
||||
)
|
||||
) : (
|
||||
<VStack
|
||||
spacing={4}
|
||||
|
||||
26
src/window/phone/outgoing-call.tsx
Normal file
26
src/window/phone/outgoing-call.tsx
Normal 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;
|
||||
@@ -23,9 +23,7 @@ import ResetIcon from "src/imgs/icons/Reset.svg";
|
||||
|
||||
export const Settings = () => {
|
||||
const [sipDomain, setSipDomain] = useState("");
|
||||
const [sipServerAddress, setSipServerAddress] = useState(
|
||||
"wss://sip.jambonz.cloud:8443/"
|
||||
);
|
||||
const [sipServerAddress, setSipServerAddress] = useState("");
|
||||
const [sipUsername, setSipUsername] = useState("");
|
||||
const [sipPassword, setSipPassword] = useState("");
|
||||
const [sipDisplayName, setSipDisplayName] = useState("");
|
||||
@@ -103,7 +101,7 @@ export const Settings = () => {
|
||||
<FormLabel>Jambonz Server Address</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Server address"
|
||||
placeholder="wss://sip.jambonz.cloud:8443/"
|
||||
isRequired
|
||||
value={sipServerAddress}
|
||||
onChange={(e) => setSipServerAddress(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user