Commit 30f0b4dd authored by Muhammadali's avatar Muhammadali

Initial commit

parents
Pipeline #224 canceled with stages
.DS_STORE
node_modules
scripts/flow/*/.flowconfig
.flowconfig
*~
*.pyc
.grunt
_SpecRunner.html
__benchmarks__
build/
remote-repo/
coverage/
.module-cache
fixtures/dom/public/react-dom.js
fixtures/dom/public/react.js
test/the-files-to-test.generated.js
*.log*
chrome-user-data
*.sublime-project
*.sublime-workspace
.idea
*.iml
.vscode
*.swp
*.swo
packages/react-devtools-core/dist
packages/react-devtools-extensions/chrome/build
packages/react-devtools-extensions/chrome/*.crx
packages/react-devtools-extensions/chrome/*.pem
packages/react-devtools-extensions/firefox/build
packages/react-devtools-extensions/firefox/*.xpi
packages/react-devtools-extensions/firefox/*.pem
packages/react-devtools-extensions/shared/build
packages/react-devtools-extensions/.tempUserDataDir
packages/react-devtools-inline/dist
packages/react-devtools-shell/dist
packages/react-devtools-timeline/dist
\ No newline at end of file
<!-- @format -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
<script defer src="main.js"></script></head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "boxdialer",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack serve --mode=development",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@headlessui/react": "^1.7.17",
"@heroicons/react": "^2.0.18",
"@mui/icons-material": "^5.14.3",
"@mui/material": "^5.14.5",
"date-fns": "^2.30.0",
"ecmascript-webrtc-sipml": "git+https://git.4u.uz/kulya/ecmascript-webrtc-sipml.git",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
"styled-components": "^6.0.7"
},
"devDependencies": {
"@babel/core": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"@babel/preset-react": "^7.22.5",
"autoprefixer": "^9.8.8",
"babel-loader": "^9.1.3",
"css-loader": "^6.8.1",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.3",
"postcss": "^7.0.39",
"postcss-loader": "^7.3.3",
"style-loader": "^3.3.3",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
}
}
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
/** @format */
import WidjetButton from './components/WidjetButton';
import { BoxDialer } from './lib/boxDIaler';
window.BoxDialer = new BoxDialer();
export default WidjetButton;
/** @format */
import React, { useEffect, useState } from 'react';
import MainBlock, { ModalContainer } from './style';
import VerticalAlignTopIcon from '@mui/icons-material/VerticalAlignTop';
const Modal = ({
children,
hidden,
hangupButton,
phoneNumber,
callType,
callAnswerType,
}) => {
const [rollUp, setRollUp] = useState(false);
const [modalHidden, setModalHidden] = useState(false);
useEffect(() => {
setModalHidden(hidden);
}, [hidden]);
if (!modalHidden) return;
const boxDialer = window.BoxDialer;
return (
<MainBlock hidden={modalHidden || modalHidden} rollUp={rollUp}>
<ModalContainer onClick={(e) => e.stopPropagation()} rollUp={rollUp}>
{children}
<ModalContainer.Top className={'top'} rollUp={rollUp}>
{callType === 'outcoming' ? (
<p>Звонок на {phoneNumber}</p>
) : (
<p>Звонок от {phoneNumber}</p>
)}
<p className='rollUpButton' onClick={() => setRollUp(true)}>
Свернуть
</p>
</ModalContainer.Top>
<hr />
<ModalContainer.Head rollUp={rollUp}>
<div className='round'></div>
<p className='name'>No name</p>
</ModalContainer.Head>
<hr />
<ModalContainer.Body rollUp={rollUp}></ModalContainer.Body>
<hr />
<ModalContainer.Bottom rollUp={rollUp}></ModalContainer.Bottom>
<hr />
{callType === 'outcoming' ? (
<ModalContainer.Footer rollUp={rollUp} onClick={() => hangupButton()}>
<button className='redButton'>ЗАКРЫТЬ</button>
</ModalContainer.Footer>
) : (
<ModalContainer.Footer rollUp={rollUp}>
<button
className='redButton'
onClick={() => boxDialer.skipButtonClick()}>
ЗАКРЫТЬ
</button>
{callAnswerType !== 'answered' && (
<button
className='greenButton'
onClick={() => boxDialer.answerButtonClick()}>
ОТВЕТИТЬ
</button>
)}
</ModalContainer.Footer>
)}
</ModalContainer>
<div className='rollUpVesion'>
<button
className='close'
onClick={() => {
hangupButton();
setRollUp(false);
setModalHidden(false);
}}>
ЗАКРЫТЬ
</button>
<button className='rollUpUn' onClick={() => setRollUp(false)}>
<VerticalAlignTopIcon />
</button>
</div>
</MainBlock>
);
};
export default Modal;
/** @format */
import styled from 'styled-components';
const MainBlock = styled.div`
z-index: 999999999999;
display: ${({ hidden }) => (hidden ? 'flex' : 'none')};
transition: 0.3s;
${({ rollUp }) =>
rollUp
? {
position: 'fixed',
transition: '0.3s',
width: '175px',
height: '45px',
bottom: '170px',
left: '50px',
borderRadius: '50px',
display: 'flex',
backgroundColor: '#37404bad',
alignItems: 'center',
padding: '10px',
}
: {
transition: '0.3s',
width: '100%',
height: '100%',
position: 'fixed',
bottom: '0',
left: '0',
backgroundColor: '#00000061',
justifyContent: 'center',
alignItems: 'center',
}}
.rollUpVesion {
display: ${({ rollUp }) => (!rollUp ? 'none' : 'flex')};
justify-content: space-between;
width: 100%;
}
.close {
background-color: #9d0f23;
border-radius: 50px;
padding: 7px 17px;
font-size: 12px;
color: #fff;
font-weight: bold;
}
.rollUpUn {
color: #d0d0d1;
}
`;
const ModalContainer = styled.div`
display: ${({ rollUp }) => (rollUp ? 'none' : 'block')};
background-color: #29303a;
border-radius: 3px;
width: 550px;
hr {
display: ${({ rollUp }) => (rollUp ? 'none' : 'block')};
height: 1px;
border-top: 1px solid #4c515a;
width: 93%;
margin: auto;
}
`;
ModalContainer.Top = styled.div`
display: ${({ rollUp }) => (rollUp ? 'none' : 'flex')};
padding: 15px;
align-items: center;
justify-content: space-between;
color: #bdc0c3;
.rollUpButton {
cursor: pointer;
}
`;
ModalContainer.Head = styled.div`
display: ${({ rollUp }) => (rollUp ? 'none' : 'flex')};
padding: 15px;
gap: 10px;
.round {
width: 54px;
height: 54px;
border-radius: 250px;
background-color: #ccc;
}
.name {
color: #fff;
font-size: 18px;
}
`;
ModalContainer.Body = styled.div`
display: ${({ rollUp }) => (rollUp ? 'none' : 'block')};
padding: 15px;
margin: 15px;
background-color: #232932;
padding: 10px 12px;
.title {
}
.description {
}
`;
ModalContainer.Bottom = styled.div`
margin: 15px;
display: ${({ rollUp }) => (rollUp ? 'none' : 'block')};
`;
ModalContainer.Footer = styled.div`
justify-content: center;
align-items: center;
padding: 15px;
gap: 20px;
.redButton {
background-color: #9d0f23;
border-radius: 50px;
padding: 10px 24px;
font-size: 12;
color: #fff;
font-weight: bold;
}
.greenButton {
background-color: green;
border-radius: 50px;
padding: 10px 24px;
font-size: 12;
color: #000;
font-weight: bold;
}
display: ${({ rollUp }) => (rollUp ? 'none' : 'flex')};
`;
export { ModalContainer };
export default MainBlock;
/** @format */
import React, { createElement } from 'react';
import * as MuiIcons from '@mui/icons-material';
export const MuiIcon = ({ iconName }) => {
try {
return <>{createElement(MuiIcons[iconName])}</>;
} catch {
return <>{createElement(MuiIcons['BubbleChart'])}</>;
}
};
export default MuiIcon;
export { MuiIcon as default } from './MuiIcon'
export * from './MuiIcon'
\ No newline at end of file
/** @format */
import { Menu, Transition } from '@headlessui/react';
import React, { Fragment } from 'react';
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
}
const DropDownTailwind = ({ reasons, reason, selectFunc }) => {
return (
<Menu as='div' className='text-left w-full h-full'>
<div className='w-full'>
<Menu.Button
style={{
backgroundColor: '#5e5e5e',
width: '100%',
height: '30px',
color: 'white',
display: 'flex',
alignItems: 'center',
}}
className='inline-flex justify-center rounded-md font-semibold text-gray-900 shadow-sm hover:bg-gray-50 gap-1 border-0 text-sm'>
{reason ? reason : 'Nothing'}
</Menu.Button>
</div>
{reasons && reasons.length && (
<Transition
as={Fragment}
enter='transition ease-out duration-100'
enterFrom='transform opacity-0 scale-95'
enterTo='transform opacity-100 scale-100'
leave='transition ease-in duration-75'
leaveFrom='transform opacity-100 scale-100'
leaveTo='transform opacity-0 scale-95'>
<Menu.Items className='absolute left-1 z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none'>
<div className='py-3 flex flex-col'>
{reasons.map((value) => (
<Menu.Item onClick={() => selectFunc(value)}>
{({ active }) => (
<p
className={classNames(
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700',
'block px-5 py-1 text-lg',
)}>
{value}
</p>
)}
</Menu.Item>
))}
</div>
</Menu.Items>
</Transition>
)}
</Menu>
);
};
export default DropDownTailwind;
/** @format */
import React, { useEffect, useState } from 'react';
import { intervalToDuration } from 'date-fns';
const TimerUi = ({ reason, time }) => {
console.log(reason, time, 'dsahfhfghgf');
const boxDialer = window.BoxDialer;
const [dndTimer, setDndTimer] = useState(time);
useEffect(() => {
if (reason !== 'free') {
var intervalId = setInterval(() => {
console.log('ticking');
setDndTimer((prevCount) => prevCount + 1);
}, 1000);
return () => clearInterval(intervalId);
}
}, [reason]);
useEffect(() => {
setDndTimer(time);
}, [reason]);
const dataFormatter = (secund) => {
let ts = intervalToDuration({
start: 0,
end: secund * 1000,
});
return secund > 3599
? `${ts.hours}:${ts.minutes}:${ts.seconds}`
: `${ts.minutes}:${ts.seconds}`;
};
if (reason === 'free') {
return <></>;
}
return (
<span>
{boxDialer?.timer.type !== 'free' && (
<span>{dataFormatter(dndTimer)}</span>
)}
</span>
);
};
export default TimerUi;
/** @format */
import React, { useCallback, useEffect, useState } from 'react';
import WidjetBlock, {
DialPadIcon,
Input,
ModalPhoneDropDowm,
NumberButton,
} from './style';
import numbers from './numbers';
import Modal from '../Modal';
import CallIcon from '@mui/icons-material/Call';
import { toast } from 'react-hot-toast';
import DropDownTailwind from '../TailwindComponents/DropDown';
import TimerUi from '../TimerUi';
const WidjetButton = ({ data, onSetDnd }) => {
data = {
iscloud: true,
number: '4003',
wsurl: 'cld.alovoice.uz:61040',
server_id: 40,
secret: '26a830',
reason: 'free',
lefttime: 231461,
reasons: ['free', 'outcalls', 'home', 'totop'],
};
if (window?.alovoice_wsphone) {
data = window.alovoice_wsphone;
onSetDnd = window.alovoice_onsetdnd;
}
const [phoneModal, setPhoneModal] = useState(false);
const [inputValue, setInputValue] = useState('');
const [dynamicValue, setDynamicValue] = useState('pending');
const [modalState, setModalState] = useState(false);
const boxDialer = window.BoxDialer;
useEffect(() => {
if (data && data?.number) {
boxDialer.setSipStateChangeCallback(setDynamicValue, 'dynamicValue');
boxDialer.setSipStateChangeCallback(setModalState, 'modalState');
boxDialer.sipRegister(data);
}
}, []);
console.log(data, 'data');
useEffect(() => {
if (!modalState.bool) setInputValue('');
}, [modalState.bool]);
const inputController = ({ value, type, from }) => {
boxDialer.onSoundType({ type: 'pick', bool: true });
if (modalState.callType === 'outcoming' && modalState.bool) {
boxDialer.sipSendDTMF(value);
}
switch (type) {
case 'add':
if (
value.match(/^[a-zA-Z0-9!@#\$%\^\&*\)\(+=._-]+$/g).join('').length
) {
if (from === 'input') return setInputValue(value);
if (from === 'button') setInputValue(inputValue + value);
}
break;
case 'delete':
if (from === 'button') {
setInputValue(
inputValue
.split('')
.slice(0, inputValue.length - 1)
.join(''),
);
}
if (from === 'input') {
setInputValue(value);
}
break;
default:
break;
}
};
document.onkeydown = function (e) {
if (phoneModal) {
e = e || window.event;
if (e.ctrlKey && e.keyCode === 86) {
navigator.clipboard
.readText()
.then((text) => {
inputController({ type: 'add', value: text, from: 'button' });
})
.catch((err) => {
console.log('Something went wrong', err);
});
}
return true;
}
};
const gameState = {
toggleCP: (e) => {
if (phoneModal && e.key === 'Enter') {
boxDialer.initialized(inputValue, data.number);
} else if (phoneModal && e.key === 'Backspace') {
inputController({ type: 'delete', from: 'button', value: e.value });
} else if (phoneModal && !isNaN(+e.key)) {
inputController({ type: 'add', value: e.key, from: 'button' });
}
},
};
const handleKeyUp = useCallback(
(event) => {
console.log(event);
gameState.toggleCP(event);
},
[gameState.toggleCP],
);
useEffect(() => {
document.addEventListener('keyup', handleKeyUp);
return () => {
document.removeEventListener('keyup', handleKeyUp);
};
}, [handleKeyUp]);
const hangupButton = () => {
boxDialer.stopCall();
};
const callButton = () => {
if (dynamicValue === 'connected') {
boxDialer.initialized(inputValue, data.number);
} else {
toast.error('Please wait', {
style: {
zIndex: '99999999999999999',
},
});
}
};
if (!data || !data?.number) return;
return (
<>
<WidjetBlock
onClick={(e) => {
phoneModal ? setPhoneModal(false) : setPhoneModal(true);
}}>
<WidjetBlock.AccauntInfo status={dynamicValue}>
<div className='left'>
<p className='title'>{data?.number}</p>
<p>
<span className='status'>{dynamicValue}</span>
<TimerUi reason={data?.reason} time={data.lefttime} />
</p>
</div>
<div className='right'>
<DialPadIcon />
</div>
<div className='bottom'>
<WidjetBlock.StatusLine status={dynamicValue} />
</div>
</WidjetBlock.AccauntInfo>
<ModalPhoneDropDowm
hidden={!phoneModal}
onClick={(e) => e.stopPropagation()}>
<ModalPhoneDropDowm.InputArea>
<Input disabled type='text' value={inputValue} />
{inputValue.length ? (
<Input.BackSpaceIcon
onClick={() =>
inputController({ type: 'delete', from: 'button' })
}
/>
) : (
''
)}
</ModalPhoneDropDowm.InputArea>
<ModalPhoneDropDowm.ButtonArea>
{numbers?.map((mapItem) => (
<NumberButton
key={mapItem.id}
onClick={() => {
mapItem.type === 'number' &&
inputController({
value: mapItem.value,
type: 'add',
from: 'button',
});
}}>
{mapItem.nameNumber}
<NumberButton.Icon>{mapItem.icon}</NumberButton.Icon>
</NumberButton>
))}
{!modalState.bool ? (
<>
<NumberButton style={{ gridColumn: '1/3' }}>
<DropDownTailwind
selectFunc={onSetDnd}
reasons={data.reasons}
reason={data.reason}
/>
</NumberButton>
<NumberButton
onClick={() => callButton()}
// onClick={() => boxDialer.stopTimer()}
status={dynamicValue}
type={'call_button'}>
<CallIcon />
</NumberButton>
</>
) : (
<>
<div style={{ height: '33px' }}></div>
<div style={{ height: '33px' }}></div>
</>
)}
</ModalPhoneDropDowm.ButtonArea>
</ModalPhoneDropDowm>
</WidjetBlock>
<Modal
callAnswerType={modalState.callType}
hidden={modalState.bool}
callType={modalState.callType}
hangupButton={hangupButton}
phoneNumber={
modalState.callType === 'outcoming'
? inputValue
: modalState.callFromName
}
/>
</>
);
};
export default WidjetButton;
/** @format */
const numbers = [
{
id: 8,
nameNumber: '7',
value: '7',
type: 'number',
style: {},
icon: null,
},
{
id: 9,
nameNumber: '8',
value: '8',
type: 'number',
style: {},
icon: null,
},
{
id: 14,
nameNumber: '9',
value: '9',
type: 'number',
style: {},
icon: null,
},
{
id: 5,
nameNumber: '4',
value: '4',
type: 'number',
style: {},
icon: null,
},
{
id: 6,
nameNumber: '5',
value: '5',
type: 'number',
style: {},
icon: null,
},
{
id: 7,
nameNumber: '6',
value: '6',
type: 'number',
style: {},
icon: null,
},
{
id: 2,
nameNumber: '1',
value: '1',
type: 'number',
style: {},
icon: null,
},
{
id: 3,
nameNumber: '2',
value: '2',
type: 'number',
style: {},
icon: null,
},
{
id: 4,
nameNumber: '3',
value: '3',
type: 'number',
style: {},
icon: null,
},
{
id: 10,
nameNumber: '*',
value: '*',
type: 'number',
style: {},
icon: null,
},
{
id: 1,
nameNumber: '0',
value: '0',
type: 'number',
style: {},
icon: null,
},
{
id: 11,
nameNumber: '#',
value: '#',
type: 'number',
style: {},
icon: null,
},
// {
// id: 12,
// nameNumber: (
// <>
// <DropDown />
// </>
// ),
// type: 'DND_BUTTON',
// value: '#',
// style: { gridColumn: '1/3' },
// icon: null,
// },
// {
// id: 13,
// nameNumber: '',
// type: 'CALL_BUTTON',
// value: '#',
// style: { backgroundColor: '#bbed21', color: '#535c69' },
// icon: (
// <div
// style={{
// display: 'flex',
// alignItems: 'center',
// justifyContent: 'center',
// }}>
// <CallIcon />
// </div>
// ),
// },
];
export default numbers;
/** @format */
import styled from 'styled-components';
import CallIcon from '@mui/icons-material/Dialpad';
import KeyboardBackspaceIcon from '@mui/icons-material/KeyboardBackspace';
const WidjetBlock = styled.div`
position: fixed;
bottom: 30px;
left: 50px;
border-radius: 13px;
background-color: #164e63e6;
cursor: pointer;
color: white;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
height: 70px;
min-width: 180px;
max-width: 200px;
z-index: 9999999999999999999;
`;
WidjetBlock.AccauntInfo = styled.div`
height: 100%;
display: grid;
grid-template-columns: 1fr 30px;
grid-template-rows: 1fr 10px;
align-items: center;
.left {
display: flex;
flex-direction: column;
align-items: center;
}
.right {
}
.title {
font-size: 22px;
font-weight: 600;
}
.bottom {
height: 100%;
grid-column: 1/3;
grid-row: 2/3;
}
.status {
border-radius: 10px;
padding: 0 5px;
margin: 0 5px;
background-color: ${({ status }) =>
status === 'pending'
? 'orange'
: status === 'connected'
? '#a0c13d'
: '#ccc'};
color: ${({ status }) =>
status === 'pending' ? '#fff' : status === 'connected' ? '#333' : '#ccc'};
}
`;
WidjetBlock.StatusLine = styled.div`
height: 100%;
background-color: ${({ backColor }) => backColor};
border-bottom-left-radius: 30px;
border-bottom-right-radius: 30px;
background-color: ${({ status }) =>
status === 'pending'
? 'orange'
: status === 'connected'
? '#a0c13d'
: '#ccc'};
`;
const DialPadIcon = styled(CallIcon)`
font-size: 26px;
`;
const ModalPhoneDropDowm = styled.div`
width: 230px;
position: absolute;
bottom: 135px;
transition: 0.2s;
z-index: 99999999999999;
transform: translate(${({ hidden }) => (hidden ? '-200%' : '-50%')}, 0%);
left: 50%;
background-color: #c9d5d9;
border-radius: 20px;
padding: 15px;
display: grid;
grid-template-rows: 40px 1fr;
grid-gap: 15px;
cursor: default;
`;
ModalPhoneDropDowm.InputArea = styled.div`
position: relative;
`;
ModalPhoneDropDowm.ButtonArea = styled.div`
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 8px;
`;
const NumberButton = styled.div`
background-color: #5e5e5e;
color: #fff;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
border-radius: 7px;
font-size: 22px;
cursor: pointer;
height: 33px;
background-color: ${({ status, type }) =>
type === 'call_button' && status === 'pending'
? 'orange'
: status === 'connected' && '#a0c13d'};
`;
NumberButton.Icon = styled.div`
font-size: 20px;
`;
const Input = styled.input`
position: absolute;
width: 100%;
height: 100%;
border-radius: 3px;
background-color: #fff;
border: 0 !important;
padding: 0px 35px 0 10px;
font-size: 22px;
color: #000;
:focus {
border-color: inherit;
-webkit-box-shadow: none;
box-shadow: none;
padding: 0 10px;
font-size: 24px;
outline: 0 none;
}
position: relative;
display: inline-block;
margin: 0;
outline: none;
border: 0;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
&[type='number'] {
-moz-appearance: textfield;
}
`;
Input.BackSpaceIcon = styled(KeyboardBackspaceIcon)`
color: black;
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
top: 50%;
transform: translate(0, -50%);
`;
const Select = styled.select`
width: 100%;
height: 100%;
:focus {
border-color: inherit;
-webkit-box-shadow: none;
box-shadow: none;
padding: 0 10px;
font-size: 24px;
outline: 0 none;
}
position: relative;
display: inline-block;
margin: 0;
outline: none;
border: 0;
background-color: #5e5e5e;
color: #fff;
font-size: 18px;
border-radius: 7px;
padding: 5px;
`;
export { DialPadIcon, ModalPhoneDropDowm, NumberButton, Input, Select };
export default WidjetBlock;
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
\ No newline at end of file
<!-- @format -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
</body>
</html>
/** @format */
import React from 'react';
import ReactDOM from 'react-dom';
import { Toaster } from 'react-hot-toast';
import WidjetButton from './components/WidjetButton/index';
import { BoxDialer } from './lib/boxDIaler';
import './index.css';
window.BoxDialer = new BoxDialer();
ReactDOM.render(
<React.StrictMode>
<Toaster />
<WidjetButton />
</React.StrictMode>,
document.getElementById('app'),
);
/** @format */
import SIPml from 'ecmascript-webrtc-sipml';
import { toast } from 'react-hot-toast';
import outcomingringTone from '../sounds/ringtone.wav';
import waitRingTone from '../sounds/ringbacktone.wav';
import pickSound from '../sounds/dtmf.wav';
export class BoxDialer {
sTransferNumber;
oRingTone;
oRingbackTone;
oSipStack;
oSipSessionRegister;
oSipSessionCall;
oSipSessionTransferCall;
videoRemote;
videoLocal;
audioRemote;
bFullScreen = false;
oNotifICall;
bDisableVideo = false;
viewVideoLocal;
viewVideoRemote;
viewLocalScreencast; // <video> (webrtc) or <div> (webrtc4all)
oConfigCall;
oReadyStateTimer;
ringtone;
ringbacktone;
sipState;
soundBool;
onSipStateChange = {};
timer = {
time: 0,
type: null,
};
testTimer;
constructor() {
this.played = false;
document.body.addEventListener('click', () => {
this.played = true;
});
this.videoRemote = document.createElement('video');
this.videoLocal = document.createElement('video');
this.audioRemote = document.createElement('audio');
this.audioRemote.autoplay = true;
document.head.appendChild(this.videoRemote);
document.head.appendChild(this.videoLocal);
document.head.appendChild(this.audioRemote);
this.ringtone = new Audio(outcomingringTone);
this.waitRingTone = new Audio(waitRingTone);
this.pickSound = new Audio(pickSound);
this.ringtone.loop = true;
this.waitRingTone.loop = true;
this.oConfigCall = {
audio_remote: this.audioRemote,
video_local: this.viewVideoLocal, //this.viewVideoLocal
video_remote: this.viewVideoRemote, // this.viewVideoRemote
screencast_window_id: 0x00000000, // entire desktop
bandwidth: { audio: undefined, video: undefined },
video_size: {
minWidth: undefined,
minHeight: undefined,
maxWidth: undefined,
maxHeight: undefined,
},
events_listener: {
events: '*',
listener: (event) => this.onSessionEvent({ type: 'call', event }),
},
sip_caps: [
{ name: '+g.oma.sip-im' },
{ name: 'language', value: '"en,fr"' },
],
};
}
startTimer() {
console.log('start timer');
this.testTimer = setInterval(() => {
this.timer.time = this.timer.time + 1;
this.onSipStateChange.setDndTimer(this.timer.time);
}, 1000);
}
stopTimer() {
clearInterval(this.testTimer);
this.timer.time = 0;
this.onSipStateChange.setDndTimer(this.timer.time);
}
onSoundType({ type, bool }) {
if (this.played) {
switch (type) {
case 'ring':
if (bool) {
this.ringtone.pause();
this.ringtone.remove();
this.ringtone.play();
} else {
this.ringtone.pause();
this.ringtone.remove();
}
break;
case 'wait':
if (bool) {
this.waitRingTone.pause();
this.waitRingTone.remove();
this.waitRingTone.play();
} else {
this.waitRingTone.pause();
this.waitRingTone.remove();
}
break;
case 'pick':
if (bool) {
this.pickSound.remove();
this.pickSound.pause();
this.pickSound.play();
} else {
this.pickSound.pause();
this.pickSound.remove();
}
break;
default:
console.error('onSoundType: Such a sound does not exist');
break;
}
}
}
sipRegister = ({ number, secret, wsurl }) => {
try {
Notification.requestPermission();
SIPml.setDebugLevel(
window.localStorage &&
window.localStorage.getItem('org.doubango.expert.disable_debug') ===
'true'
? 'error'
: 'info',
);
// create SIP stack
this.oSipStack = new SIPml.Stack({
realm: wsurl, // 'cld.alovoice.uz:61040',
impi: number, //'3003',
impu: `sip:${number}@${wsurl}`, //'sip:3003@cld.alovoice.uz:61040',
password: secret, //'944ad5',
display_name: number, //'3003',
websocket_proxy_url: `wss://${wsurl}/ws`, //'wss://cld.alovoice.uz:61040/ws',
// outbound_proxy_url: '', //(window.localStorage ? window.localStorage.getItem('org.doubango.expert.sip_outboundproxy_url') : null),
ice_servers: [{ url: 'stun:stun.l.google.com:19302' }], //(window.localStorage ? window.localStorage.getItem('org.doubango.expert.ice_servers') : null),
enable_rtcweb_breaker: true, //(window.localStorage ? window.localStorage.getItem('org.doubango.expert.enable_rtcweb_breaker') == "true" : false),
events_listener: {
events: '*',
listener: (event) => this.onSessionEvent({ type: 'stack', event }),
},
enable_early_ims: true, //(window.localStorage ? window.localStorage.getItem('org.doubango.expert.disable_early_ims') != "true" : true), // Must be true unless you're using a real IMS network
enable_media_stream_cache: true, //(window.localStorage ? window.localStorage.getItem('org.doubango.expert.enable_media_caching') == "true" : false),
//bandwidth: (window.localStorage ? tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.bandwidth')) : null), // could be redefined a session-level
//video_size: (window.localStorage ? tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.video_size')) : null), // could be redefined a session-level
sip_headers: [
{
name: 'User-Agent',
value: 'Alovoice-client/v1.0-2023.08.08',
},
{ name: 'Organization', value: 'Technounit' },
],
});
if (this.oSipStack.start() !== 0) {
console.log('<b>Failed to start the SIP stack</b>');
} else return;
} catch (e) {
console.log('<b>2:' + e + '</b>');
}
};
register() {
if (this.oSipSessionRegister) this.oSipSessionRegister.unregister();
this.oSipSessionRegister = this.oSipStack.newSession('register', {
expires: 200,
events_listener: {
events: '*',
listener: (event) => {
this.onSessionEvent({ type: 'register', event });
return event;
},
},
});
this.oSipSessionRegister.register();
}
setGuiPhoneState({ stateCode, funcName, funcParam }) {
if (typeof window?.BX24 !== 'undefined') {
let bxStateCode = '';
switch (stateCode) {
case 'i_new_call':
bxStateCode = 'incoming';
break;
case 'connected':
bxStateCode = '';
break;
default:
break;
}
window?.BX24.placement.call(
'CallCardSetUiState',
{ uiState: bxStateCode },
(e) => {
this.bxLog('CallCardSetUiState', e);
},
);
} else {
if (this.onSipStateChange[funcName] !== 'undefined') {
this.onSipStateChange[funcName](funcParam);
}
}
}
bxLog(...datas) {
console.log(`%c BXLOG:`, datas, 'background-color: red;');
}
onSessionEvent({ type, event }) {
this.sipState = type + '_' + event?.type;
console.log('---onSessionEvent EVENT [' + this.sipState + ']', event);
switch (this.sipState) {
case 'register_stopping':
case 'register_stoped':
case 'register_failed_to_start':
case 'register_failed_to_stop':
this.oSipStack = null;
this.oSipSessionRegister = null;
this.oSipSessionCall = null;
break;
case 'register_terminated':
// оторвался
break;
case 'register_connected':
this.onSipStateChange.dynamicValue('connected');
break;
case 'register_connecting':
this.onSipStateChange.dynamicValue('pending');
break;
case 'stack_started':
this.register();
break;
case 'call_connecting':
this.setGuiPhoneState({
funcName: 'modalState',
funcParam: {
bool: true,
callType: 'outcoming',
callAnswerType: null,
},
});
break;
case 'call_connected':
// QORA EKRANDA ULANYAPTI.. SOZINI VAHTGA OZGARTIRISH KERE ( SEKKUNDAMER )
break;
case 'hangup_terminating':
// QORA EKRANNI OCHIRISH KERE
this.setGuiPhoneState({
funcName: 'modalState',
funcParam: {
bool: false,
callType: 'outcoming',
callAnswerType: null,
},
});
break;
case 'call_terminated':
console.log('%c call_terminated', 'background-color: yellow;');
this.onSoundType({ type: 'ring', bool: false });
this.setGuiPhoneState({
funcName: 'modalState',
funcParam: {
bool: false,
callType: 'outcoming',
callAnswerType: null,
callFromName: null,
},
});
break;
case 'stack_i_new_call':
this.oSipSessionCall = event.newSession;
this.oSipSessionCall.setConfiguration(this.oConfigCall);
this.onSoundType({ type: 'ring', bool: true });
this.setGuiPhoneState({
funcName: 'modalState',
funcParam: {
bool: true,
callType: '',
callAnswerType: null,
callFromName: event?.o_event?.o_session?.o_uri_from?.s_user_name,
},
});
break;
case 'stack_stopped':
this.register();
break;
default:
break;
}
}
// call_connected;
setSipStateChangeCallback(callback, funcName) {
this.onSipStateChange[funcName] = callback.bind(this);
}
sipHangUp() {
console.log('sipHangUp');
}
sipUnRegister() {
console.log('sipUnRegister');
}
sipTransfer() {
console.log('sipTransfer');
}
initialized(phoneNumber, phone) {
if (phoneNumber == phone) {
toast.error('Please enter another number');
return;
}
let phoneNumberString = phoneNumber.toString();
if (
phoneNumberString.length &&
phoneNumberString.replace(/\D/g, '').length === phoneNumberString.length
) {
this.oSipSessionCall = this.oSipStack.newSession(
'call-audio',
this.oConfigCall,
);
this.oSipSessionCall.call(phoneNumberString);
} else {
toast.error('Do not correct number', {
style: {
zIndex: '99999999999999999',
},
});
}
}
stopCall() {
this.onSoundType({ type: 'ring', bool: false });
this.onSipStateChange.modalState({
bool: false,
callType: 'answered',
callAnswerType: 'oncoming',
callFromName: null,
});
if (this.oSipSessionCall) {
this.oSipSessionCall.hangup({
events_listener: {
events: '*',
listener: (event) => this.onSessionEvent({ type: 'hangup', event }),
},
});
}
}
skipButtonClick() {
this.onSoundType({ type: 'ring', bool: false });
this.stopCall();
}
answerButtonClick() {
this.onSipStateChange.modalState({
bool: true,
callType: 'answered',
callAnswerType: 'oncoming',
});
this.oSipSessionCall.accept(this.oConfigCall);
this.onSoundType({ type: 'ring', bool: false });
}
sipSendDTMF(c) {
if (this.oSipSessionCall && c) {
this.oSipSessionCall.dtmf(c);
}
}
addCommentButtonClick() {
console.log('addCommentButtonClick');
}
muteButtonClick() {
console.log('muteButtonClick');
}
holdButtonClick() {
console.log('holdButtonClick');
}
closeButtonClick() {
console.log('closeButtonClick');
}
transferButtonClick() {
console.log('transferButtonClick');
}
cancelTransferButtonClick() {
console.log('cancelTransferButtonClick');
}
completeTransferButtonClick() {
console.log('completeTransferButtonClick');
}
nextButtonClick() {
console.log('nextButtonClick');
}
entityChanged() {
console.log('entityChanged');
}
qualityMeterClick() {
console.log('qualityMeterClick');
}
dialpadButtonClick() {
console.log('dialpadButtonClick');
}
notifyAdminButtonClick() {
console.log('notifyAdminButtonClick');
}
}
/**
* @format
* @type {import('tailwindcss').Config}
*/
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
// Or if using `src` directory:
'./src/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};
/** @format */
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.join(__dirname, 'src', 'index.js'),
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: {
loader: 'babel-loader',
options: {
presets: [['@babel/env', { loose: true }], '@babel/preset-react'],
},
},
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader'],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: ['file-loader'],
},
{
test: /\.svg$/,
use: ['@svgr/webpack'],
},
{
test: /\.(mp3|wav)$/i,
loader: 'file-loader',
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html'),
}),
],
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment