mirror of
https://github.com/jambonz/chrome-extension-dialer.git
synced 2026-10-04 02:04:28 +00:00
add outgoing page and fix bugs
This commit is contained in:
Generated
+9
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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}
|
||||||
|
|||||||
@@ -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 = () => {
|
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)}
|
||||||
|
|||||||
Reference in New Issue
Block a user