├── .gitignore
├── .gitattributes
├── preload.js
├── README.md
├── package.json
├── app
├── css
│ ├── register.css
│ ├── main.css
│ ├── call.css
│ ├── message.css
│ └── history.css
├── register.html
├── js
│ ├── register.js
│ ├── message.js
│ ├── history.js
│ └── call.js
├── history.html
├── main.html
├── call.html
└── message.html
└── main.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn.lock
3 | app/js/common.js
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/preload.js:
--------------------------------------------------------------------------------
1 | const { remote } = require('electron');
2 |
3 | let currWindow = remote.BrowserWindow.getFocusedWindow();
4 |
5 | window.closeCurrentWindow = function(){
6 | currWindow.close();
7 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Smart Intercom :office: :house: :phone:
2 |
3 | ### App using Electron, WebRTC and Firebase to develop a desktop application :tada: :tada:
4 |
5 | ### How I can run it?
6 |
7 | - :rocket: Run ***'npm install'*** on terminal in project.
8 | - :rocket: Run ***'npm start'*** on terminal in project.
9 |
10 | #### :pencil: Author: WANTED
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Intercom",
3 | "version": "1.0.0",
4 | "description": "An app calling video and send image !!",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "electron .",
8 | "pack": "electron-builder --dir",
9 | "dist": "electron-builder"
10 | },
11 | "repository": "",
12 | "keywords": [],
13 | "author": "WANTED",
14 | "license": "CC0-1.0",
15 | "devDependencies": {
16 | "electron": "^10.1.6"
17 | },
18 | "dependencies": {
19 | "electron-builder": "^22.9.1",
20 | "firebase": "^8.0.0",
21 | "fs": "0.0.1-security",
22 | "jquery": "^3.5.1",
23 | "materialize-css": "^1.0.0-rc.2",
24 | "path": "^0.12.7",
25 | "process": "^0.11.10",
26 | "url": "^0.11.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/css/register.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, Helvetica, sans-serif;
3 | background-color:white;
4 | max-width: 100%;
5 | padding: 0 20%;
6 | }
7 |
8 | * {
9 | box-sizing: border-box;
10 | }
11 |
12 | /* Add padding to containers */
13 | .container {
14 | padding: 16px;
15 | background-color: white;
16 | }
17 |
18 | /* Full-width input fields */
19 | input[type="text"],
20 | input[type="password"] {
21 | width: 100%;
22 | padding: 15px;
23 | margin: 5px 0 22px 0;
24 | display: inline-block;
25 | border: none;
26 | background: #f1f1f1;
27 | }
28 |
29 | input[type="text"]:focus,
30 | input[type="password"]:focus {
31 | background-color: #ddd;
32 | outline: none;
33 | }
34 |
35 | /* Overwrite default styles of hr */
36 | hr {
37 | border: 1px solid #f1f1f1;
38 | margin-bottom: 25px;
39 | }
40 |
41 | /* Set a style for the submit button */
42 | .registerbtn {
43 | background-color: #4caf50;
44 | color: white;
45 | padding: 16px 20px;
46 | margin: 8px 0;
47 | border: none;
48 | cursor: pointer;
49 | width: 100%;
50 | opacity: 0.9;
51 | font-weight: 600;
52 | font-size: 15px;
53 | }
54 |
55 | .registerbtn:hover {
56 | opacity: 1;
57 | }
58 |
--------------------------------------------------------------------------------
/app/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: Arial, Helvetica, sans-serif;
4 | }
5 |
6 | .topnav {
7 | overflow: hidden;
8 | background-color: #333;
9 | }
10 |
11 | .topnav a {
12 | float: left;
13 | display: block;
14 | color: #f2f2f2;
15 | text-align: center;
16 | padding: 14px 16px;
17 | text-decoration: none;
18 | font-size: 17px;
19 | }
20 |
21 | .topnav a:hover {
22 | background-color: #ddd;
23 | color: black;
24 | }
25 |
26 | .topnav a.active {
27 | background-color: #4caf50;
28 | color: white;
29 | }
30 |
31 | .topnav a.exit {
32 | background-color: #ff0000;
33 | color: white;
34 | }
35 |
36 | .topnav .icon {
37 | display: none;
38 | }
39 |
40 | @media screen and (max-width: 600px) {
41 | .topnav a:not(:first-child) {
42 | display: none;
43 | }
44 | .topnav a.icon {
45 | float: right;
46 | display: block;
47 | }
48 | }
49 |
50 | @media screen and (max-width: 600px) {
51 | .topnav.responsive {
52 | position: relative;
53 | }
54 | .topnav.responsive .icon {
55 | position: absolute;
56 | right: 0;
57 | top: 0;
58 | }
59 | .topnav.responsive a {
60 | float: none;
61 | display: block;
62 | text-align: left;
63 | }
64 | }
65 |
66 | #call,
67 | #history,
68 | #notification,
69 | #statistic,
70 | #signup {
71 | display: none;
72 | }
73 | #call:target {
74 | display: block;
75 | }
76 | #history:target {
77 | display: block;
78 | }
79 | #notification:target {
80 | display: block;
81 | }
82 | #statistic:target {
83 | display: block;
84 | }
85 | #signup:target {
86 | display: block;
87 | }
88 |
--------------------------------------------------------------------------------
/app/register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Đăng Ký Thành Viên
10 |
Hãy điền đầy đủ thông tin dưới đây.
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
33 |
34 |
35 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const electron = require('electron');
2 | const url = require('url');
3 | const path = require('path');
4 |
5 | const { app, BrowserWindow, Menu } = electron;
6 |
7 | let mainWindow;
8 |
9 |
10 | app.on('ready', function () {
11 | mainWindow = new BrowserWindow({
12 | width: 1600,
13 | height: 900,
14 | webPreferences: {
15 | nodeIntegration: true,
16 | allowRunningInsecureContent: true,
17 | enableRemoteModule: true,
18 | preload: path.join(app.getAppPath(), 'preload.js')
19 | }
20 | });
21 |
22 | mainWindow.setMenuBarVisibility(false);
23 |
24 | mainWindow.loadURL(url.format({
25 | pathname: path.join(__dirname, './app/main.html'),
26 | protocol: 'file:',
27 | slashes: true
28 | }));
29 |
30 |
31 |
32 | mainWindow.on('closed', function () {
33 | app.quit();
34 | })
35 |
36 | //build menu from template
37 |
38 | const mainMenu = Menu.buildFromTemplate(mainMenuTemplate);
39 | Menu.setApplicationMenu(mainMenu);
40 | });
41 |
42 | const mainMenuTemplate = [
43 | ]
44 |
45 | if (process.platform == 'darwin') {
46 | mainMenuTemplate.unshift({});
47 | }
48 |
49 | //add developer tools
50 | if (process.env.NODE_ENV != 'production') {
51 | mainMenuTemplate.push({
52 | label: 'Developer Tools',
53 | submenu: [
54 | {
55 | label: 'Toggle DevTools',
56 | accelerator: process.platform == 'darwin' ? 'Command+I' : 'Ctrl+I',
57 | click(item, focusedWindow) {
58 | focusedWindow.toggleDevTools();
59 | }
60 | },
61 | {
62 | role: 'reload'
63 | }
64 | ]
65 | })
66 | }
--------------------------------------------------------------------------------
/app/js/register.js:
--------------------------------------------------------------------------------
1 | firebase.initializeApp(config);
2 | const db = firebase.firestore();
3 |
4 | function validateForm(email, psw, psw_repeat, phone, dept) {
5 | if (
6 | email.length == 0 ||
7 | psw.toString().length < 6 ||
8 | phone.length == 0 ||
9 | dept.length == 0
10 | ) {
11 | alert(
12 | "Nhập thông tin không hợp lệ! Email phải có @, Mật khẩu phải có ít nhất 6 kí tự."
13 | );
14 | return false;
15 | } else if (psw != psw_repeat) {
16 | alert("Mật khẩu không khớp!");
17 | return false;
18 | }
19 |
20 | return true;
21 | }
22 |
23 | async function register() {
24 | var email = document.getElementById("email").value;
25 | var psw = document.getElementById("psw").value;
26 | var psw_repeat = document.getElementById("psw-repeat").value;
27 | var phone = document.getElementById("phone").value;
28 | var dept = document.getElementById("dept").value;
29 |
30 | if (validateForm(email, psw, psw_repeat, phone, dept)) {
31 | firebase
32 | .auth()
33 | .createUserWithEmailAndPassword(email, psw)
34 | .then(async (user) => {
35 | const res = await db.collection("users").doc(user.user.uid).set({
36 | id: user.user.uid,
37 | email: email,
38 | dept: dept,
39 | key: "VINH",
40 | token: "",
41 | notifications: true,
42 | urlToImage:
43 | "https://avatars2.githubusercontent.com/u/60530946?s=460&u=e342f079ed3571122e21b42eedd0ae251a9d91ce&v=4",
44 | username: email.substring(0, email.length - 10),
45 | phone: phone,
46 | });
47 |
48 | alert("Đăng kí thành công!");
49 | window.location.reload();
50 | })
51 | .catch((error) => {
52 | alert(error.message);
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/history.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | History
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
40 |
41 |
42 |
Lọc
43 |
44 |
46 |
47 |
48 |
49 |
50 |
×
51 |
52 |
53 |
![]()
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
58 |
59 |
60 |
61 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/app/css/call.css:
--------------------------------------------------------------------------------
1 | body {
2 | /**/
3 | max-width: 100%;
4 | }
5 |
6 | .my-video {
7 | background-color: #ddd;
8 | border-radius: 4px;
9 | margin: 30px 0px 0px 30px;
10 | width: 440px;
11 | height: 320px;
12 | object-fit: cover;
13 | }
14 |
15 | .friend-video {
16 | background-color: #ddd;
17 | border-radius: 4px;
18 | margin: 30px 0px 0px 30px;
19 | width: 220px;
20 | height: 320px;
21 | object-fit: cover;
22 | }
23 |
24 | .btn-call {
25 | margin: 5px 0px 0px 30px !important;
26 | height: 54px;
27 | width: 770px;
28 | border-radius: 2.5px !important;
29 | font-size: 18px !important;
30 | font-weight: 500;
31 | color: white;
32 | border: 0cm;
33 | background-color: RoyalBlue;
34 | }
35 |
36 | .btn-call:hover {
37 | background-color: DodgerBlue;
38 | }
39 |
40 | .btn-call:after {
41 | background-color: DodgerBlue;
42 | }
43 |
44 | .right-column .display-container .main-container {
45 | margin-left: 20px;
46 | }
47 |
48 | .right-column .picture-container {
49 | justify-content: center;
50 | align-items: center;
51 | padding-left: 240px;
52 | }
53 |
54 | .right-column .display-container {
55 | display: flex;
56 | margin-bottom: 30px;
57 | }
58 |
59 | .right-column .button {
60 | display: inline-block;
61 | }
62 |
63 | .right-column .btn-display {
64 | width: 300px;
65 | padding: 8px 0px 8px 0px;
66 | margin-left: 34px !important;
67 | text-align: center;
68 | }
69 |
70 | .right-column #image_container {
71 | width: 240px;
72 | height: 220px;
73 | background-color: #ddd;
74 | }
75 |
76 | .right-column canvas {
77 | border: 1px solid #ddd;
78 | background-color: #ddd;
79 | width: 320px;
80 | height: 240px;
81 | overflow: auto;
82 | }
83 |
84 | .container-main {
85 | max-width: 100%;
86 | margin: 0 auto;
87 | display: flex;
88 | }
89 |
90 | /* Columns */
91 | .left-column {
92 | height: 100vh;
93 | width: 25%;
94 | position: relative;
95 | border-right: 0.2px solid #ddd;
96 | overflow-y: auto;
97 | }
98 |
99 | .right-column {
100 | width: 75%;
101 | overflow: hidden !important;
102 | }
103 | #userItem {
104 | padding: 10px;
105 | }
106 | .circle {
107 | margin-top: 8px;
108 | height: 60px !important;
109 | width: 60px !important;
110 | }
111 | h5 {
112 | margin-left: 90px !important;
113 | }
114 | p {
115 | margin-left: 90px !important;
116 | }
117 | .collection .collection-item.avatar {
118 | border-right: 0 !important;
119 | padding-top: 4px !important;
120 | vertical-align: middle !important;
121 | padding-left: 0 !important;
122 | margin-left: 0 !important;
123 | }
124 |
125 | .column {
126 | float: left;
127 | }
128 |
129 | /* Clear floats after the columns */
130 | .row:after {
131 | content: "";
132 | display: table;
133 | clear: both;
134 | }
135 |
136 | .image-upload > input
137 | {
138 | display: none;
139 | }
140 |
141 | .btn-container {
142 | background-color: RoyalBlue;
143 | border: none;
144 | color: white;
145 | padding: 20px;
146 | font-size: 16.5px;
147 | font-display: block;
148 | cursor: pointer;
149 | }
150 |
151 | /* Darker background on mouse-over */
152 | .btn-container:hover {
153 | background-color: DodgerBlue;
154 | }
--------------------------------------------------------------------------------
/app/css/message.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100%;
4 | width: 100%;
5 | margin: 0;
6 | font-family: "Roboto", sans-serif;
7 | }
8 |
9 | .container-main {
10 | max-width: 100%;
11 | margin: 0 auto;
12 | display: flex;
13 | }
14 |
15 | /* Columns */
16 | .left-column {
17 | height: 100vh;
18 | width: 25%;
19 | position: relative;
20 | border-right: 0.2px solid #ddd;
21 | overflow-y: auto;
22 | }
23 |
24 | .center {
25 | width: 35%;
26 | overflow: hidden !important;
27 | }
28 |
29 | .right-column {
30 | width: 40%;
31 | overflow: hidden !important;
32 | }
33 | #userItem {
34 | padding: 10px;
35 | }
36 | .circle {
37 | margin-top: 8px;
38 | height: 60px !important;
39 | width: 60px !important;
40 | }
41 | h5 {
42 | margin-left: 80px;
43 | }
44 | p {
45 | margin-left: 80px !important;
46 | font-size: 14px !important;
47 | font-weight: 400;
48 | }
49 | .collection .collection-item.avatar {
50 | border-right: 0 !important;
51 | padding-top: 4px !important;
52 | margin-right: 0 !important;
53 | padding-right: 0 !important;
54 | vertical-align: middle !important;
55 | }
56 | .collection .collection-item.avatar p {
57 | margin: 0;
58 | margin-left: 50px;
59 | }
60 | .select-all {
61 | margin-top: 24px !important;
62 | display: flex;
63 | justify-content: space-between;
64 | max-width: 100%;
65 | background: white;
66 | padding: 0 18px;
67 | }
68 | .font-select-all {
69 | font-size: 15px;
70 | font-weight: 600;
71 | color: dodgerblue;
72 | }
73 |
74 | .active {
75 | color:lightseagreen;
76 | }
77 |
78 | .unactive {
79 | color:gray;
80 | }
81 |
82 | .container-mess {
83 | width: 100%;
84 | padding: 32px 40px;
85 | }
86 | .note {
87 | display: flex;
88 | margin-left: 20px;
89 | }
90 | .input-note {
91 | width: 400px;
92 | }
93 | .btn-update {
94 | margin-left: 30px;
95 | }
96 | #canvas {
97 | border: 1.5px solid #ddd;
98 | width: 360px;
99 | height: 270px;
100 | margin-left: 25px;
101 | background-color: #ddd;
102 | }
103 |
104 | .image-upload > input
105 | {
106 | display: none;
107 | }
108 |
109 | .btn-container {
110 | background-color: RoyalBlue;
111 | border: none;
112 | color: white;
113 | padding: 20px;
114 | font-size: 16.5px;
115 | font-display: block;
116 | cursor: pointer;
117 | margin-top: 8px;
118 | margin-left: 30px;
119 | }
120 |
121 | /* Darker background on mouse-over */
122 | .btn-container:hover {
123 | background-color: DodgerBlue;
124 | }
125 |
126 | .column {
127 | float: left;
128 | padding: 10px;
129 | }
130 |
131 | /* Clear floats after the columns */
132 | .row:after {
133 | content: "";
134 | clear: both;
135 | }
136 |
137 | video {
138 | background-color: #ddd;
139 | height: 120px;
140 | width: 120px;
141 | object-fit: cover;
142 | }
143 |
144 | .btn-noti {
145 | margin-top: 20px !important;
146 | height: 54px;
147 | width: 105%;
148 | border-radius: 2.5px !important;
149 | font-size: 14px !important;
150 | font-weight: 600;
151 | color: white;
152 | border: 0cm;
153 | background-color: RoyalBlue;
154 | }
155 |
156 | .btn-noti:hover {
157 | background-color: DodgerBlue;
158 | }
159 |
160 | .cb-container {
161 | margin-top: 20px !important;
162 | }
163 |
164 | input[type=checkbox] {
165 | margin: 4px 0 0;
166 | margin-top: 20px !important;
167 | padding-top: 20px !important;
168 | line-height: normal;
169 | }
--------------------------------------------------------------------------------
/app/call.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dashboard
7 |
8 |
12 |
13 |
17 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/app/message.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dashboard
7 |
8 |
12 |
13 |
17 |
18 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
57 |
58 |
59 | Gửi Cho Mọi Người
60 |
61 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
80 |
81 |
82 |
90 |
91 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
111 |
112 |
115 |
116 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/app/css/history.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | input {
6 | height: 500px;
7 | }
8 |
9 | .circle {
10 | height: 60px !important;
11 | width: 60px !important;
12 |
13 | }
14 |
15 | .padding-li {
16 | padding-left: 20px !important;
17 | }
18 |
19 | h5 {
20 | padding-left: 30px;
21 | }
22 |
23 | p {
24 | padding-left: 30px;
25 | }
26 |
27 | .li-user {
28 | padding-left: 30px;
29 | }
30 |
31 | .imageSend {
32 | width: 150px;
33 | height: 110px;
34 | margin-right: 10px;
35 | border-radius: 0 !important;
36 | }
37 |
38 | .filter-container {
39 | display: flex;
40 | margin-top: 10px;
41 | }
42 |
43 | .txt-filter {
44 | font-size: 15px;
45 | color: #000;
46 | }
47 |
48 | .input-filter {
49 | width: 150px !important;
50 | }
51 |
52 | .from-filter {
53 | margin-left: 30px;
54 | }
55 |
56 | .to-filter {
57 | margin-left: 30px;
58 | }
59 |
60 | .state-filter {
61 | margin-left: 30px;
62 | }
63 |
64 | .pos-state {
65 | position: relative;
66 | top: 2px;
67 | }
68 | .btn-filter{
69 | position: relative;
70 | left: 80px;
71 | }
72 | .custom-select {
73 | position: relative;
74 | font-family: Arial;
75 | left: 60px;
76 | bottom: 25px;
77 | }
78 |
79 | .custom-select select {
80 | display: none;
81 | /*hide original SELECT element: */
82 |
83 | }
84 |
85 | .select-selected {
86 | background-color: #ddd;
87 | }
88 |
89 | /* Style the arrow inside the select element: */
90 | .select-selected:after {
91 | position: absolute;
92 | content: "";
93 | top: 14px;
94 | right: 10px;
95 | width: 0;
96 | height: 0;
97 | border: 6px solid transparent;
98 | border-color: #000 transparent transparent transparent;
99 | }
100 |
101 | /* Point the arrow upwards when the select box is open (active): */
102 | .select-selected.select-arrow-active:after {
103 | border-color: transparent transparent #000 transparent;
104 | top: 7px;
105 | }
106 |
107 | /* style the items (options), including the selected item: */
108 | .select-items div,
109 | .select-selected {
110 | color: #000;
111 | padding: 8px 16px;
112 | border: 1px solid transparent;
113 | border-color: transparent transparent rgba(0, 0, 0, 0.1) transparent;
114 | cursor: pointer;
115 | }
116 |
117 | /* Style items (options): */
118 | .select-items {
119 | position: absolute;
120 | background-color: #fff;
121 | top: 100%;
122 | left: 0;
123 | right: 0;
124 | z-index: 99;
125 | }
126 |
127 | /* Hide the items when the select box is closed: */
128 | .select-hide {
129 | display: none;
130 | }
131 |
132 | .select-items div:hover,
133 | .same-as-selected {
134 | background-color: rgba(0, 0, 0, 0.1);
135 | }
136 |
137 | #myImg {
138 | border-radius: 5px;
139 | cursor: pointer;
140 | transition: 0.3s;
141 | }
142 |
143 | #myImg:hover {
144 | opacity: 0.7;
145 | }
146 |
147 | /* The Modal (background) */
148 | .modal {
149 | display: none;
150 | /* Hidden by default */
151 | position: fixed;
152 | /* Stay in place */
153 | z-index: 1;
154 | /* Sit on top */
155 | padding-top: 100px;
156 | /* Location of the box */
157 | left: 0;
158 | top: 0;
159 | width: 100%;
160 | /* Full width */
161 | height: 100%;
162 | /* Full height */
163 | overflow: auto;
164 | /* Enable scroll if needed */
165 | background-color: rgb(0, 0, 0);
166 | /* Fallback color */
167 | background-color: rgba(0, 0, 0, 0.9);
168 | /* Black w/ opacity */
169 | }
170 |
171 | /* Modal Content (Image) */
172 | .modal-content {
173 | margin: auto;
174 | display: block;
175 | width: 80%;
176 | max-width: 700px;
177 | }
178 |
179 | /* Caption of Modal Image (Image Text) - Same Width as the Image */
180 | #caption {
181 | margin: auto;
182 | display: block;
183 | width: 80%;
184 | max-width: 700px;
185 | text-align: center;
186 | color: #ccc;
187 | padding: 10px 0;
188 | height: 150px;
189 | }
190 |
191 | /* Add Animation - Zoom in the Modal */
192 | .modal-content,
193 | #caption {
194 | animation-name: zoom;
195 | animation-duration: 0.6s;
196 | }
197 |
198 | @keyframes zoom {
199 | from {
200 | transform: scale(0)
201 | }
202 |
203 | to {
204 | transform: scale(1)
205 | }
206 | }
207 |
208 | /* The Close Button */
209 | .close {
210 | position: absolute;
211 | top: 15px;
212 | right: 35px;
213 | color: #f1f1f1;
214 | font-size: 40px;
215 | font-weight: bold;
216 | transition: 0.3s;
217 | }
218 |
219 | .close:hover,
220 | .close:focus {
221 | color: #bbb;
222 | text-decoration: none;
223 | cursor: pointer;
224 | }
225 |
226 | /* 100% Image Width on Smaller Screens */
227 | @media only screen and (max-width: 700px) {
228 | .modal-content {
229 | width: 100%;
230 | }
231 | }
--------------------------------------------------------------------------------
/app/js/message.js:
--------------------------------------------------------------------------------
1 | firebase.initializeApp(config);
2 | const db = firebase.firestore();
3 | const ref = firebase.storage().ref();
4 |
5 | var users = [];
6 | var file;
7 | var selectAll = false;
8 | var yourVideo = document.getElementById("yourVideo");
9 | var pc = new RTCPeerConnection();
10 |
11 | function showMyFace() {
12 | navigator.mediaDevices
13 | .getUserMedia({ audio: true, video: true })
14 | .then((stream) => (yourVideo.srcObject = stream))
15 | .then((stream) => pc.addStream(stream));
16 | }
17 |
18 | function capture() {
19 | var canvas = document.getElementById("canvas");
20 | var video = document.getElementById("yourVideo");
21 | canvas.width = video.videoWidth;
22 | canvas.height = video.videoHeight;
23 | canvas
24 | .getContext("2d")
25 | .drawImage(video, 0, 0, video.videoWidth, video.videoHeight); // for drawing the video element on the canvas
26 |
27 | if (canvas.getContext) {
28 | var mySrc = canvas.toDataURL("image/png");
29 | file = dataURLtoFile(mySrc, "filename.png");
30 | }
31 | }
32 |
33 | document.getElementById("capture").addEventListener("click", capture);
34 | //db.settings({ timestampsInSnapshots: true});
35 | //get user from database
36 | const getUser = db
37 | .collection("users")
38 | .where("id", "!=", "FnAWubAdtFgYwGIML23oRAtp5u93")
39 | .get()
40 | .then((snapshot) => {
41 | snapshot.docs.forEach((doc) => {
42 | let items = doc.data();
43 |
44 | users.push(items);
45 | });
46 | showUser();
47 | });
48 |
49 | function showUser() {
50 | users.forEach((user) => {
51 | var ul = document.querySelector("ul");
52 | var li = document.createElement("li");
53 | var label = document.createElement("label");
54 | var input = document.createElement("input");
55 | var span = document.createElement("span");
56 | var img = document.createElement("img");
57 | var h5 = document.createElement("h5");
58 | var p = document.createElement("p");
59 | var a1 = document.createElement("a");
60 | var a2 = document.createElement("a");
61 |
62 | li.className = "collection-item avatar";
63 | li.id = "userItem";
64 | img.className = "circle";
65 | h5.className = "title";
66 | a2.className = "secondary-content icon-call";
67 | a2.id = "btn-call";
68 | input.className = "filled-in list-user-item";
69 |
70 | label.htmlFor = user.id;
71 | input.type = "checkbox";
72 | input.id = user.id;
73 | label.onclick = "test()";
74 |
75 | input.name = "user";
76 | input.value = user.id;
77 | img.src =
78 | user.urlToImage == ""
79 | ? "https://avatars2.githubusercontent.com/u/60530946?s=460&u=e342f079ed3571122e21b42eedd0ae251a9d91ce&v=4"
80 | : user.urlToImage;
81 | h5.textContent = user.username;
82 | p.textContent = "Phòng : ";
83 | a1.textContent = user.dept;
84 | a2.href = "#";
85 |
86 | label.appendChild(input);
87 | label.appendChild(span);
88 |
89 | a2.appendChild(label);
90 | p.appendChild(a1);
91 | li.appendChild(img);
92 | li.appendChild(h5);
93 | li.appendChild(p);
94 | li.appendChild(a2);
95 |
96 | ul.appendChild(li);
97 | });
98 | }
99 | function removeDuplicates(array) {
100 | return array.filter((a, b) => array.indexOf(a) === b);
101 | }
102 |
103 | var arrCheckedUser = [];
104 | function selectUser() {
105 | var checks = document.getElementsByClassName("list-user-item");
106 | arrCheckedUser = [];
107 | for (i = 0; i < users.length; i++) {
108 | if (checks[i].checked === true) {
109 | arrCheckedUser.push(checks[i].value);
110 | }
111 | }
112 | removeDuplicates(arrCheckedUser);
113 | }
114 |
115 | // Select all user
116 | document.getElementById("select-all").onclick = function () {
117 | var checkboxes = document.getElementsByName("user");
118 | for (var checkbox of checkboxes) {
119 | checkbox.checked = this.checked;
120 | }
121 | checkSelectAll();
122 | };
123 | var _checkSelectAll = document.getElementById("select-all");
124 | function checkSelectAll() {
125 | if (_checkSelectAll.checked) {
126 | selectAll = true;
127 | var current = document.getElementsByClassName("font-select-all");
128 | current.className = current.className.replace(" active", " unactive");
129 | } else {
130 | selectAll = false;
131 | }
132 | console.log(selectAll);
133 | }
134 |
135 | //Search user
136 | function searchUser() {
137 | var input, filter, ul, li, a, i, txtValue;
138 | input = document.getElementById("search");
139 | filter = input.value.toUpperCase();
140 | ul = document.getElementById("myUL");
141 | li = ul.getElementsByTagName("li");
142 | for (i = 0; i < li.length; i++) {
143 | a = li[i].getElementsByTagName("a")[0];
144 | txtValue = a.textContent || a.innerText;
145 | if (txtValue.toUpperCase().indexOf(filter) > -1) {
146 | li[i].style.display = "";
147 | } else {
148 | li[i].style.display = "none";
149 | }
150 | }
151 | }
152 |
153 | async function addDataToFirestore() {
154 | selectUser();
155 | if (arrCheckedUser.length == 0 && selectAll == false) {
156 | alert("Chưa có người nhận!");
157 | } else {
158 | if (file == null) {
159 | console.log(arrCheckedUser);
160 | const res = await db.collection("notifications").add({
161 | all: selectAll,
162 | body: document.getElementById("note-input").value,
163 | key: "VINH",
164 | members: arrCheckedUser,
165 | publishAt: firebase.firestore.FieldValue.serverTimestamp(),
166 | title: document.getElementById("title-input").value,
167 | urlToImage: "",
168 | });
169 |
170 | document.getElementById("note-input").value = "";
171 | document.getElementById("title-input").value = "";
172 | } else {
173 | const name = +new Date() + "-" + file.name;
174 | const task = ref.child("Photos").child(name).put(file);
175 | task
176 | .then((snapshot) => snapshot.ref.getDownloadURL())
177 | .then(async (url) => {
178 | console.log(url);
179 | const res = await db.collection("notifications").add({
180 | all: selectAll,
181 | body: document.getElementById("note-input").value,
182 | key: "VINH",
183 | members: arrCheckedUser,
184 | publishAt: firebase.firestore.FieldValue.serverTimestamp(),
185 | title: document.getElementById("title-input").value,
186 | urlToImage: url,
187 | });
188 |
189 | document.getElementById("note-input").value = "";
190 | document.getElementById("title-input").value = "";
191 | var canvas = document.getElementById("canvas");
192 | const context = canvas.getContext("2d");
193 | context.clearRect(0, 0, canvas.width, canvas.height);
194 | file = null;
195 | })
196 | .catch(console.error);
197 | }
198 | }
199 | }
200 |
201 | function dataURLtoFile(dataurl, filename) {
202 | var arr = dataurl.split(","),
203 | mime = arr[0].match(/:(.*?);/)[1],
204 | bstr = atob(arr[1]),
205 | n = bstr.length,
206 | u8arr = new Uint8Array(n);
207 | while (n--) {
208 | u8arr[n] = bstr.charCodeAt(n);
209 | }
210 | return new File([u8arr], filename, { type: mime });
211 | }
212 |
213 | function readURL(input) {
214 | if (input.files && input.files[0]) {
215 | var reader = new FileReader();
216 | var image = new Image();
217 |
218 | reader.onload = function (e) {
219 | image.src = e.target.result;
220 | console.log(image.src);
221 |
222 | var canvas = document.getElementById("canvas");
223 | var context = canvas.getContext("2d");
224 |
225 | canvas.getContext("2d").drawImage(image, 0, 0, 300, 300);
226 |
227 | image.onload = function () {
228 | context.drawImage(image, 0, 0, canvas.width, canvas.height);
229 | };
230 | };
231 |
232 | reader.readAsDataURL(input.files[0]);
233 |
234 | file = input.files[0];
235 | console.log(file);
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/app/js/history.js:
--------------------------------------------------------------------------------
1 | //set to input value is today
2 | function getDate() {
3 | var today = new Date();
4 | var dd = today.getDate();
5 | var mm = today.getMonth() + 1; //January is 0!
6 | var yyyy = today.getFullYear();
7 |
8 | if (dd < 10) {
9 | dd = "0" + dd;
10 | }
11 |
12 | if (mm < 10) {
13 | mm = "0" + mm;
14 | }
15 |
16 | today = yyyy + "-" + mm + "-" + dd;
17 | console.log(today);
18 | document.getElementById("end").value = today;
19 | }
20 |
21 | window.onload = function () {
22 | getDate();
23 | };
24 |
25 | firebase.initializeApp(config);
26 |
27 | const db = firebase.firestore();
28 |
29 | //db.settings({ timestampsInSnapshots: true});
30 | //get user from database
31 | const getUser = db
32 | .collection("requests")
33 | .orderBy("responcedTime", "desc")
34 | .get()
35 | .then((snapshot) => {
36 | snapshot.docs.forEach((doc) => {
37 | let items = doc.data();
38 |
39 | db.collection("users")
40 | .where("id", "==", items.receiveID)
41 | .get()
42 | .then((snap) => {
43 | snap.docs.forEach((element) => {
44 | var username = element.data().username;
45 | var urlOfUser = element.data().urlToImage;
46 | var ul = document.querySelector("ul");
47 | var li = document.createElement("li");
48 | var img = document.createElement("img");
49 | var h5 = document.createElement("h5");
50 | var p = document.createElement("p");
51 | var p1 = document.createElement("p");
52 | var p2 = document.createElement("p");
53 | var a1 = document.createElement("a");
54 | var a12 = document.createElement("a");
55 | var a13 = document.createElement("a");
56 | var imgSend = document.createElement("img");
57 | var publishAt = items.publishAt.toDate();
58 | var responceTime = items.responcedTime.toDate();
59 |
60 | const options = {
61 | weekday: "long",
62 | year: "numeric",
63 | month: "long",
64 | day: "numeric",
65 | };
66 | li.className = "collection-item avatar";
67 | li.id = "userItem";
68 | img.className = "circle";
69 | h5.className = "title";
70 | imgSend.className = "imageSend secondary-content";
71 | imgSend.id = "myImg";
72 | imgSend.src = items.urlToImage;
73 |
74 | img.src = urlOfUser;
75 |
76 | //console.log(img.src);
77 | h5.textContent = username;
78 | p.textContent = "Trạng thái: ";
79 | p1.textContent = "Thời gian trả lời: ";
80 | p2.textContent = "Thời gian bắt đầu gọi: ";
81 | a1.textContent = items.responce;
82 | a12.textContent =
83 | responceTime.toLocaleDateString("vi-VN", options) +
84 | " ---- " +
85 | checkValid(responceTime.getHours()) +
86 | ":" +
87 | checkValid(responceTime.getMinutes()) +
88 | ":" +
89 | checkValid(responceTime.getSeconds());
90 | a13.textContent =
91 | publishAt.toLocaleDateString("vi-VN", options) +
92 | " ---- " +
93 | checkValid(publishAt.getHours()) +
94 | ":" +
95 | checkValid(publishAt.getMinutes()) +
96 | ":" +
97 | checkValid(publishAt.getSeconds());
98 |
99 | p.appendChild(a1);
100 | p1.appendChild(a12);
101 | p2.appendChild(a13);
102 | li.appendChild(img);
103 | li.appendChild(h5);
104 | li.appendChild(p);
105 | li.appendChild(p1);
106 | li.appendChild(p2);
107 | li.appendChild(imgSend);
108 |
109 | ul.appendChild(li);
110 | });
111 | });
112 | });
113 | });
114 |
115 | function checkValid(input) {
116 | if (input < 10) {
117 | return "0" + input.toString();
118 | } else {
119 | return input;
120 | }
121 | }
122 |
123 | //Dropdown filter
124 | var x, i, j, l, ll, selElmnt, a, b, c;
125 | /* Look for any elements with the class "custom-select": */
126 | x = document.getElementsByClassName("custom-select");
127 | l = x.length;
128 | for (i = 0; i < l; i++) {
129 | selElmnt = x[i].getElementsByTagName("select")[0];
130 | ll = selElmnt.length;
131 | /* For each element, create a new DIV that will act as the selected item: */
132 | a = document.createElement("DIV");
133 | a.setAttribute("class", "select-selected");
134 | a.innerHTML = selElmnt.options[selElmnt.selectedIndex].innerHTML;
135 | x[i].appendChild(a);
136 | /* For each element, create a new DIV that will contain the option list: */
137 | b = document.createElement("DIV");
138 | b.setAttribute("class", "select-items select-hide");
139 | for (j = 1; j < ll; j++) {
140 | /* For each option in the original select element,
141 | create a new DIV that will act as an option item: */
142 | c = document.createElement("DIV");
143 | c.innerHTML = selElmnt.options[j].innerHTML;
144 | c.addEventListener("click", function (e) {
145 | /* When an item is clicked, update the original select box,
146 | and the selected item: */
147 | var y, i, k, s, h, sl, yl;
148 | s = this.parentNode.parentNode.getElementsByTagName("select")[0];
149 | sl = s.length;
150 | h = this.parentNode.previousSibling;
151 | for (i = 0; i < sl; i++) {
152 | if (s.options[i].innerHTML == this.innerHTML) {
153 | s.selectedIndex = i;
154 | h.innerHTML = this.innerHTML;
155 | y = this.parentNode.getElementsByClassName("same-as-selected");
156 | yl = y.length;
157 | for (k = 0; k < yl; k++) {
158 | y[k].removeAttribute("class");
159 | }
160 | this.setAttribute("class", "same-as-selected");
161 | break;
162 | }
163 | }
164 | h.click();
165 | });
166 | b.appendChild(c);
167 | }
168 | x[i].appendChild(b);
169 | a.addEventListener("click", function (e) {
170 | /* When the select box is clicked, close any other select boxes,
171 | and open/close the current select box: */
172 | e.stopPropagation();
173 | closeAllSelect(this);
174 | this.nextSibling.classList.toggle("select-hide");
175 | this.classList.toggle("select-arrow-active");
176 | });
177 | }
178 |
179 | function closeAllSelect(elmnt) {
180 | /* A function that will close all select boxes in the document,
181 | except the current select box: */
182 | var x,
183 | y,
184 | i,
185 | xl,
186 | yl,
187 | arrNo = [];
188 | x = document.getElementsByClassName("select-items");
189 | y = document.getElementsByClassName("select-selected");
190 | xl = x.length;
191 | yl = y.length;
192 | for (i = 0; i < yl; i++) {
193 | if (elmnt == y[i]) {
194 | arrNo.push(i);
195 | } else {
196 | y[i].classList.remove("select-arrow-active");
197 | }
198 | }
199 | for (i = 0; i < xl; i++) {
200 | if (arrNo.indexOf(i)) {
201 | x[i].classList.add("select-hide");
202 | }
203 | }
204 | }
205 |
206 | /* If the user clicks anywhere outside the select box,
207 | then close all select boxes: */
208 | document.addEventListener("click", closeAllSelect);
209 | ////////////////////////////////
210 | //click zoom img
211 | // Get the modal
212 | var modal = document.getElementById("myModal");
213 |
214 | // Get the image and insert it inside the modal - use its "alt" text as a caption
215 | var img = document.getElementById("myImg");
216 | var modalImg = document.getElementById("img01");
217 | var captionText = document.getElementById("caption");
218 |
219 | if (img) {
220 | img.addEventListener("click", addSrc, false);
221 | }
222 | function addSrc() {
223 | modal.style.display = "block";
224 | modalImg.src = this.src;
225 | captionText.innerHTML = this.alt;
226 | }
227 |
228 | // Get the element that closes the modal
229 | var span = document.getElementsByClassName("close")[0];
230 |
231 | // When the user clicks on (x), close the modal
232 | span.onclick = function () {
233 | modal.style.display = "none";
234 | };
235 | //filter
236 | var dayFrom, dayTo, state;
237 | var yourSelect = document.getElementById("filterByState");
238 | function getFilter() {
239 | dayFrom = new Date(document.getElementById("start").value);
240 | dayTo = document.getElementById("end").value;
241 | state = yourSelect.options[yourSelect.selectedIndex].value;
242 |
243 | console.log(dayFrom);
244 | console.log(dayTo);
245 | console.log(typeof dayFrom);
246 | console.log(state);
247 | filterByState(state);
248 | }
249 | //filter by state
250 |
251 | function filterByState(state) {
252 | filter = state.toUpperCase();
253 | ul = document.getElementById("myUL");
254 | li = ul.getElementsByTagName("li");
255 | for (i = 0; i < li.length; i++) {
256 | a = li[i].getElementsByTagName("a")[0];
257 | response = a.textContent || a.innerText;
258 | if (response.toUpperCase().indexOf(filter) > -1) {
259 | li[i].style.display = "";
260 | } else {
261 | li[i].style.display = "none";
262 | }
263 | }
264 | }
265 | //filter by date
266 |
267 | function filterByDate(dayFrom, dayTo) {
268 | filter = state.toUpperCase();
269 | ul = document.getElementById("myUL");
270 | li = ul.getElementsByTagName("li");
271 | for (i = 0; i < li.length; i++) {
272 | a = li[i].getElementsByTagName("a")[0];
273 | response = a.textContent || a.innerText;
274 | if (response.toUpperCase().indexOf(filter) > -1) {
275 | li[i].style.display = "";
276 | } else {
277 | li[i].style.display = "none";
278 | }
279 | }
280 | }
281 |
--------------------------------------------------------------------------------
/app/js/call.js:
--------------------------------------------------------------------------------
1 | firebase.initializeApp(config);
2 | const ref = firebase.storage().ref();
3 | const db = firebase.firestore();
4 | var database = firebase.database().ref();
5 | var yourVideo = document.getElementById("yourVideo");
6 | var friendsVideo = document.getElementById("friendsVideo");
7 | var yourId = Math.floor(Math.random() * 1000000000);
8 | var ranListenId = makeId(30);
9 | var state = "Call";
10 | var snap;
11 | var file;
12 | var image;
13 | var users = [];
14 | var idUser = "";
15 | var servers = {
16 | iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
17 | };
18 | var pc = new RTCPeerConnection(servers);
19 | var count = 0;
20 | pc.onicecandidate = (event) =>
21 | event.candidate
22 | ? sendMessage(yourId, JSON.stringify({ ice: event.candidate }))
23 | : console.log("Sent All Ice");
24 | pc.onaddstream = (event) => (friendsVideo.srcObject = event.stream);
25 |
26 | document.querySelector("#ShowButton").innerHTML = "Gọi";
27 |
28 | const listenChange = db
29 | .collection("requests")
30 | .where("id", "==", ranListenId)
31 | .onSnapshot((querySnapshot) => {
32 | querySnapshot.docChanges().forEach((snapshot) => {
33 | snap = snapshot;
34 | if (snapshot.doc.data().request == true) {
35 | state = "Connecting";
36 | document.querySelector("#ShowButton").innerHTML = "Hủy Cuộc Gọi";
37 | } else if (snapshot.doc.data().completed == false) {
38 | state = "Disconnect";
39 | document.querySelector("#ShowButton").innerHTML = "Dừng Cuộc Gọi";
40 | } else {
41 | state = "Call";
42 | window.location.reload();
43 | }
44 | });
45 | });
46 |
47 | const getUser = db
48 | .collection("users")
49 | .where("id", "!=", "6mQ06EQZhpay2JFSS2N9N9LCvBj1")
50 | .get()
51 | .then((snapshot) => {
52 | snapshot.docs.forEach((doc) => {
53 | let items = doc.data();
54 |
55 | users.push(items);
56 | });
57 | showUser();
58 | });
59 |
60 | function showUser() {
61 | users.forEach((user) => {
62 | var ul = document.querySelector("ul");
63 | var li = document.createElement("li");
64 | var img = document.createElement("img");
65 | var h5 = document.createElement("h5");
66 | var p = document.createElement("p");
67 | var a1 = document.createElement("a");
68 | var a2 = document.createElement("a");
69 | var i = document.createElement("i");
70 | var id = document.createElement("p");
71 |
72 | li.className = "collection-item avatar";
73 | li.id = "userItem";
74 | img.className = "circle";
75 | h5.className = "title";
76 | a2.className = "secondary-content";
77 | a2.id = "btn-call";
78 | i.className = "material-icons li-user";
79 |
80 | img.src =
81 | user.urlToImage == ""
82 | ? "https://avatars2.githubusercontent.com/u/60530946?s=460&u=e342f079ed3571122e21b42eedd0ae251a9d91ce&v=4"
83 | : user.urlToImage;
84 | //console.log(img.src);
85 | h5.textContent = user.username;
86 | p.textContent = "Phòng : ";
87 | a1.textContent = user.dept;
88 | i.textContent = "phone";
89 | a2.href = "#";
90 | i.id = "call-video";
91 |
92 | i.onclick = () => (idUser = user.id);
93 |
94 | a2.appendChild(i);
95 | p.appendChild(a1);
96 | li.appendChild(img);
97 | li.appendChild(h5);
98 | li.appendChild(p);
99 | li.appendChild(a2);
100 | li.appendChild(id);
101 |
102 | ul.appendChild(li);
103 | });
104 | }
105 |
106 | function sendMessage(senderId, data) {
107 | var msg = database.push({ sender: senderId, message: data });
108 | count++;
109 | msg.remove();
110 | if (count == 1) {
111 | addDataToFirestore(data);
112 | }
113 | }
114 |
115 | async function addDataToFirestore(data) {
116 | const name = +new Date() + "-" + file.name;
117 | const task = ref.child("Photos").child(name).put(file);
118 | task
119 | .then((snapshot) => snapshot.ref.getDownloadURL())
120 | .then(async (url) => {
121 | const res = await db.collection("requests").add({
122 | idSend: "6mQ06EQZhpay2JFSS2N9N9LCvBj1",
123 | receiveID: idUser,
124 | completed: false,
125 | request: true,
126 | publishAt: firebase.firestore.FieldValue.serverTimestamp(),
127 | responcedTime: firebase.firestore.FieldValue.serverTimestamp(),
128 | urlToImage: url,
129 | filePath: "",
130 | responce: "",
131 | sdp: data,
132 | id: ranListenId,
133 | });
134 | })
135 | .catch(console.error);
136 | }
137 | function readMessage(data) {
138 | var msg = JSON.parse(data.val().message);
139 | var sender = data.val().sender;
140 | if (sender != yourId) {
141 | if (msg.ice != undefined) {
142 | pc.addIceCandidate(new RTCIceCandidate(msg.ice));
143 | } else if (msg.sdp.type == "offer") {
144 | var r = confirm("Answer call?");
145 | if (r == true) {
146 | pc.setRemoteDescription(new RTCSessionDescription(msg.sdp))
147 | .then(() => pc.createAnswer())
148 | .then((answer) => pc.setLocalDescription(answer))
149 | .then(() =>
150 | sendMessage(yourId, JSON.stringify({ sdp: pc.localDescription }))
151 | );
152 | } else {
153 | alert("Rejected the call");
154 | }
155 | } else if (msg.sdp.type == "answer") {
156 | msg.sdp.sdp = msg.sdp.sdp.replace(
157 | "useinbandfec=1",
158 | "useinbandfec=1; stereo=1; maxaveragebitrate=2000"
159 | );
160 | pc.setRemoteDescription(new RTCSessionDescription(msg.sdp));
161 | }
162 | }
163 | }
164 | database.on("child_added", readMessage);
165 | function showMyFace() {
166 | navigator.mediaDevices
167 | .getUserMedia({ audio: true, video: true })
168 | .then((stream) => (yourVideo.srcObject = stream))
169 | .then((stream) => pc.addStream(stream));
170 | }
171 | function showFriendsFace() {
172 | console.log(idUser);
173 | if (idUser != "") {
174 | if (state == "Call") {
175 | if (file != null || file == "") {
176 | pc.createOffer()
177 | .then((offer) => {
178 | console.log(offer.sdp);
179 | offer.sdp = offer.sdp.replace(
180 | "useinbandfec=1",
181 | "useinbandfec=1; stereo=1; maxaveragebitrate=2000"
182 | );
183 | pc.setLocalDescription(offer);
184 | })
185 | .then(() => {
186 | sendMessage(yourId, JSON.stringify({ sdp: pc.localDescription }));
187 | });
188 | } else {
189 | alert("Chọn hình!");
190 | }
191 | } else if (state == "Connecting") {
192 | snap.doc.ref.update({
193 | request: false,
194 | completed: true,
195 | responce: "Missing",
196 | });
197 | } else if (state == "Disconnect") {
198 | snap.doc.ref.update({
199 | request: false,
200 | completed: true,
201 | responce: "Not Responding",
202 | });
203 | } else {
204 | }
205 | } else {
206 | alert("Chọn người nhận cuộc gọi!");
207 | }
208 | }
209 |
210 | function makeId(length) {
211 | var result = "";
212 | var characters =
213 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
214 | var charactersLength = characters.length;
215 | for (var i = 0; i < length; i++) {
216 | result += characters.charAt(Math.floor(Math.random() * charactersLength));
217 | }
218 | return result;
219 | }
220 |
221 | function capture() {
222 | var canvas = document.getElementById("canvas");
223 | var video = document.getElementById("yourVideo");
224 | canvas.width = video.videoWidth;
225 | canvas.height = video.videoHeight;
226 | canvas
227 | .getContext("2d")
228 | .drawImage(video, 0, 0, video.videoWidth, video.videoHeight); // for drawing the video element on the canvas
229 |
230 | if (canvas.getContext) {
231 | var mySrc = canvas.toDataURL("image/png");
232 | file = dataURLtoFile(mySrc, "filename.png");
233 | }
234 | }
235 |
236 | function processBase64Image(dataString) {
237 | var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
238 | response = {};
239 |
240 | if (matches.length !== 3) {
241 | return new Error("Invalid input string");
242 | }
243 |
244 | response.type = matches[1];
245 | response.data = new Buffer(matches[2], "base64");
246 |
247 | return response;
248 | }
249 |
250 | function offCamera() {
251 | navigator.mediaDevices
252 | .getUserMedia({ audio: true, video: true })
253 | .then((stream) => (yourVideo.srcObject = stream))
254 | .then((stream) => {
255 | stream.getAudioTracks()[0].stop()
256 | stream.getVideoTracks()[0].stop();
257 | stream.stop();
258 | });
259 | }
260 |
261 | document.getElementById("capture").addEventListener("click", capture);
262 |
263 | function dataURLtoFile(dataurl, filename) {
264 | var arr = dataurl.split(","),
265 | mime = arr[0].match(/:(.*?);/)[1],
266 | bstr = atob(arr[1]),
267 | n = bstr.length,
268 | u8arr = new Uint8Array(n);
269 | while (n--) {
270 | u8arr[n] = bstr.charCodeAt(n);
271 | }
272 | return new File([u8arr], filename, { type: mime });
273 | }
274 |
275 | function readURL(input) {
276 | if (input.files && input.files[0]) {
277 | var reader = new FileReader();
278 | var image = new Image();
279 |
280 | reader.onload = function (e) {
281 | image.src = e.target.result;
282 | console.log(image.src);
283 |
284 | var canvas = document.getElementById("canvas");
285 | var context = canvas.getContext("2d");
286 | context.clearRect(0, 0, canvas.width, canvas.height);
287 | canvas
288 | .getContext("2d")
289 | .drawImage(image, 0, 0, canvas.width, canvas.height);
290 |
291 | image.onload = function () {
292 | context.drawImage(image, 0, 0, canvas.width, canvas.height);
293 | };
294 | };
295 |
296 | reader.readAsDataURL(input.files[0]);
297 |
298 | file = input.files[0];
299 | console.log(file);
300 | }
301 | }
302 |
303 | console.log(idUser);
304 |
--------------------------------------------------------------------------------