mirror of
https://github.com/jambonz/chrome-extension-dialer.git
synced 2025-12-18 20:37:45 +00:00
fix
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
/dist
|
||||
/lib
|
||||
/components
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-vscode.vscode-typescript-next"
|
||||
]
|
||||
}
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.detectIndentation": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.configPath": ".prettierrc.json",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"typescript.preferences.quoteStyle": "double"
|
||||
}
|
||||
@@ -11,10 +11,8 @@ export interface Call {
|
||||
|
||||
export enum MessageEvent {
|
||||
// Request
|
||||
Login,
|
||||
Call,
|
||||
OpenPhoneWindow,
|
||||
Ping,
|
||||
}
|
||||
|
||||
export interface EmptyData {}
|
||||
|
||||
@@ -1,66 +1,105 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {
|
||||
ChakraProvider,
|
||||
Text,
|
||||
IconButton,
|
||||
Flex,
|
||||
CSSReset,
|
||||
} from "@chakra-ui/react";
|
||||
import { Phone } from "react-feather";
|
||||
import { Call, CallAction, Message, MessageEvent } from "./../common/types";
|
||||
import { openPhonePopup } from "./../utils";
|
||||
|
||||
export const ContentApp = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [position, setPosition] = useState({ left: 0, top: 0 });
|
||||
const [selectedText, setSelectedText] = useState("");
|
||||
const [counting, setCounting] = useState(0);
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
|
||||
// Add event listener for text selection
|
||||
useEffect(() => {
|
||||
function handleMouseUp() {
|
||||
const selectedText = window.getSelection()?.toString();
|
||||
if (selectedText) {
|
||||
setSelectedText(selectedText);
|
||||
const range = window.getSelection()?.getRangeAt(0);
|
||||
const rect = range?.getBoundingClientRect();
|
||||
setIsOpen(true);
|
||||
setPosition({ left: rect?.left || 0, top: rect?.bottom || 0 });
|
||||
function mouseUpListener(e: any) {
|
||||
const text = window.getSelection()?.toString().trim();
|
||||
if (text && text.length > 0) {
|
||||
const number = validateAndFormatNumber(text);
|
||||
setSelectedText(number || "");
|
||||
setPosition({ x: e.pageX, y: e.pageY });
|
||||
} else {
|
||||
setIsOpen(false);
|
||||
setSelectedText("");
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
document.addEventListener("mouseup", mouseUpListener);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
document.removeEventListener("mouseup", mouseUpListener);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const validateAndFormatNumber = (input: string): string | null => {
|
||||
const strippedInput = input.replace(/\s+|\.|-|\+|\(|\)/g, ""); // remove all spaces
|
||||
|
||||
// check if the final string contains only numbers
|
||||
if (/^\d+$/.test(strippedInput)) {
|
||||
return strippedInput;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const onClick = () => {
|
||||
setSelectedText("");
|
||||
const msg: Message<Call> = {
|
||||
event: MessageEvent.Call,
|
||||
data: {
|
||||
action: CallAction.OUTBOUND,
|
||||
number: selectedText || "",
|
||||
},
|
||||
};
|
||||
chrome.runtime.sendMessage(msg);
|
||||
};
|
||||
|
||||
if (!selectedText) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<CSSReset />
|
||||
{isOpen && (
|
||||
<Flex
|
||||
position="fixed"
|
||||
left={position.left}
|
||||
top={position.top}
|
||||
bg="white"
|
||||
p={4}
|
||||
border="1px solid"
|
||||
borderColor="gray.200"
|
||||
boxShadow="md"
|
||||
alignItems="center"
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
backgroundColor: "white",
|
||||
height: "80px",
|
||||
width: "180px",
|
||||
borderRadius: "10px",
|
||||
boxShadow: "0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23)",
|
||||
padding: "10px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
zIndex: "99999999",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<h1
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
color: "#333",
|
||||
display: "inline",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
<Text>{selectedText}</Text>
|
||||
<IconButton
|
||||
colorScheme="blue"
|
||||
ml={2}
|
||||
aria-label="text"
|
||||
icon={<Phone />}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
</ChakraProvider>
|
||||
Number:{" "}
|
||||
</h1>
|
||||
<span style={{ fontSize: "16px", color: "#666" }}>{selectedText}</span>
|
||||
</div>
|
||||
<button
|
||||
style={{
|
||||
padding: "10px",
|
||||
color: "white",
|
||||
background: "#007BFF",
|
||||
border: "none",
|
||||
borderRadius: "5px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
Call
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SipConstants } from "src/lib";
|
||||
import { SipConstants } from "./../lib";
|
||||
import { deleteWindowIdKey, getWindowIdKey, saveWindowIdKey } from "./storage";
|
||||
import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber";
|
||||
|
||||
@@ -13,20 +13,23 @@ export const formatPhoneNumber = (number: string) => {
|
||||
};
|
||||
|
||||
export const openPhonePopup = () => {
|
||||
const runningPhoneWindowId = getWindowIdKey();
|
||||
if (runningPhoneWindowId) {
|
||||
chrome.windows.update(runningPhoneWindowId, { focused: true }, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
deleteWindowIdKey();
|
||||
initiateNewPhonePopup();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
initiateNewPhonePopup();
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const runningPhoneWindowId = getWindowIdKey();
|
||||
if (runningPhoneWindowId) {
|
||||
chrome.windows.update(runningPhoneWindowId, { focused: true }, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
deleteWindowIdKey();
|
||||
initiateNewPhonePopup(resolve);
|
||||
}
|
||||
resolve(1);
|
||||
});
|
||||
} else {
|
||||
initiateNewPhonePopup(resolve);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const initiateNewPhonePopup = () => {
|
||||
const initiateNewPhonePopup = (callback: (v: unknown) => void) => {
|
||||
const cfg: chrome.windows.CreateData = {
|
||||
url: chrome.runtime.getURL("window/index.html"),
|
||||
width: 300,
|
||||
@@ -36,6 +39,7 @@ const initiateNewPhonePopup = () => {
|
||||
state: "normal",
|
||||
};
|
||||
chrome.windows.create(cfg, (w) => {
|
||||
callback(1);
|
||||
if (w && w.id) saveWindowIdKey(w.id);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Delete } from "react-feather";
|
||||
import { DEFAULT_COLOR_SCHEME } from "src/common/constants";
|
||||
import { SipClientStatus } from "src/common/types";
|
||||
import { Call, Message, MessageEvent, SipClientStatus } from "src/common/types";
|
||||
import { SipConstants, SipUA } from "src/lib";
|
||||
import IncommingCall from "./incomming-call";
|
||||
import OutgoingCall from "./outgoing-call";
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
isSipClientAnswered,
|
||||
isSipClientIdle,
|
||||
isSipClientRinging,
|
||||
openPhonePopup,
|
||||
} from "src/utils";
|
||||
|
||||
type PhoneProbs = {
|
||||
@@ -56,6 +57,28 @@ export const Phone = ({
|
||||
}
|
||||
}, [sipDomain, sipUsername, sipPassword, sipServerAddress, sipDisplayName]);
|
||||
|
||||
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;
|
||||
|
||||
if (isSipClientIdle(callStatus)) {
|
||||
setInputNumber(call.number);
|
||||
sipUA.current?.call(call.number);
|
||||
}
|
||||
};
|
||||
|
||||
const createSipClient = (forceOfflineMode = false) => {
|
||||
if (goOffline && !forceOfflineMode) {
|
||||
return;
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = [
|
||||
{
|
||||
entry: "./src/content/index.tsx",
|
||||
target: "web",
|
||||
mode: "production",
|
||||
output: {
|
||||
path: path.join(__dirname, "dist"),
|
||||
filename: "js/content.js",
|
||||
@@ -27,7 +28,7 @@ module.exports = [
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"],
|
||||
extensions: [".ts", ".tsx",".js"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user