From 318578d535da7901c573195eaaae33f1809ebe27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:13:07 -0300
Subject: [PATCH 01/45] Add chat-message component
---
.../chat/message/chat-message.component.js | 35 +++++++++++++++++++
.../components/chat/message/chat-message.css | 28 +++++++++++++++
.../components/chat/message/chat-message.html | 4 +++
3 files changed, 67 insertions(+)
create mode 100644 webchat/components/chat/message/chat-message.component.js
create mode 100644 webchat/components/chat/message/chat-message.css
create mode 100644 webchat/components/chat/message/chat-message.html
diff --git a/webchat/components/chat/message/chat-message.component.js b/webchat/components/chat/message/chat-message.component.js
new file mode 100644
index 000000000..84cc30538
--- /dev/null
+++ b/webchat/components/chat/message/chat-message.component.js
@@ -0,0 +1,35 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("chatMessage", {
+ templateUrl: "app/components/chat/message/chat-message.html",
+ controller: [
+ 'AuthService',
+ chatMessageController,
+ ],
+ controllerAs: "chatMessageCtrl",
+ bindings: {
+ messageObj: '<',
+ },
+ });
+
+ function chatMessageController(AuthService) {
+ const chatMessageCtrl = this;
+
+
+ chatMessageCtrl.formatTimestamp = (timestamp) => {
+ return timestamp.getHours() + ":" + timestamp.getMinutes();
+ };
+
+ chatMessageCtrl.$onInit = () => {
+ const timestamp = new Date(chatMessageCtrl.messageObj.timestamp);
+ chatMessageCtrl.formattedTimestamp = chatMessageCtrl.formatTimestamp(timestamp);
+ };
+
+ chatMessageCtrl.messageSent = () => {
+ const result = chatMessageCtrl.messageObj.sender === AuthService.getCurrentUser().key;
+ return result;
+ };
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/message/chat-message.css b/webchat/components/chat/message/chat-message.css
new file mode 100644
index 000000000..0c244fb3e
--- /dev/null
+++ b/webchat/components/chat/message/chat-message.css
@@ -0,0 +1,28 @@
+.chat-message__container {
+ width: fit-content;
+ max-width: 60%;
+ padding: 5px 10px;
+ background: #FFFFFF;
+ margin: 2px 5px;
+ border: 1px solid black;
+ border-radius: 5px;
+ display: grid;
+ grid-template-columns: auto auto;
+ grid-template-areas:
+ 'text timestamp';
+}
+
+.chat-message__text {
+ grid-area: text;
+}
+
+.chat-message__time {
+ grid-area: timestamp;
+ font-size: 0.8em;
+ align-self: flex-end;
+ margin: 0 10px;
+}
+
+.chat-message--sent {
+ margin-left: auto;
+}
\ No newline at end of file
diff --git a/webchat/components/chat/message/chat-message.html b/webchat/components/chat/message/chat-message.html
new file mode 100644
index 000000000..9bec975f3
--- /dev/null
+++ b/webchat/components/chat/message/chat-message.html
@@ -0,0 +1,4 @@
+
+ {{ chatMessageCtrl.messageObj.msg }}
+ {{ chatMessageCtrl.formattedTimestamp }}
+
\ No newline at end of file
From 52cbc129f350aeb46a5e248a2301607c8f24ec35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:15:28 -0300
Subject: [PATCH 02/45] Add chat-buttons component
---
.../chat/buttons/chat-buttons.component.js | 18 ++++++++++++++++++
.../components/chat/buttons/chat-buttons.css | 0
.../components/chat/buttons/chat-buttons.html | 1 +
3 files changed, 19 insertions(+)
create mode 100644 webchat/components/chat/buttons/chat-buttons.component.js
create mode 100644 webchat/components/chat/buttons/chat-buttons.css
create mode 100644 webchat/components/chat/buttons/chat-buttons.html
diff --git a/webchat/components/chat/buttons/chat-buttons.component.js b/webchat/components/chat/buttons/chat-buttons.component.js
new file mode 100644
index 000000000..08bf5d7ea
--- /dev/null
+++ b/webchat/components/chat/buttons/chat-buttons.component.js
@@ -0,0 +1,18 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("chatButtons", {
+ templateUrl: "app/components/chat/buttons/chat-buttons.html",
+ controller: chatButtonsController,
+ controllerAs: "chatButtonsCtrl",
+ bindings: {
+ callFunc: "<",
+ },
+ });
+
+ function chatButtonsController() {
+ const chatButtonsCtrl = this;
+
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/buttons/chat-buttons.css b/webchat/components/chat/buttons/chat-buttons.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/webchat/components/chat/buttons/chat-buttons.html b/webchat/components/chat/buttons/chat-buttons.html
new file mode 100644
index 000000000..7240fd019
--- /dev/null
+++ b/webchat/components/chat/buttons/chat-buttons.html
@@ -0,0 +1 @@
+
\ No newline at end of file
From 22689ecc85ddba8973efce18b5a501114dd4202f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:15:58 -0300
Subject: [PATCH 03/45] Add chat-input component
---
.../chat/input/chat-input.component.js | 39 +++++++++++++++++++
webchat/components/chat/input/chat-input.css | 22 +++++++++++
webchat/components/chat/input/chat-input.html | 14 +++++++
3 files changed, 75 insertions(+)
create mode 100644 webchat/components/chat/input/chat-input.component.js
create mode 100644 webchat/components/chat/input/chat-input.css
create mode 100644 webchat/components/chat/input/chat-input.html
diff --git a/webchat/components/chat/input/chat-input.component.js b/webchat/components/chat/input/chat-input.component.js
new file mode 100644
index 000000000..768511a44
--- /dev/null
+++ b/webchat/components/chat/input/chat-input.component.js
@@ -0,0 +1,39 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("chatInput", {
+ templateUrl: "app/components/chat/input/chat-input.html",
+ controller: chatInputController,
+ controllerAs: "chatInputCtrl",
+ bindings: {
+ sendMessageFunc: "<",
+ state: "<"
+ },
+ });
+
+ function chatInputController() {
+ const chatInputCtrl = this;
+
+ chatInputCtrl.getState = () => chatInputCtrl.state;
+
+ chatInputCtrl.getStateStyle = () => {
+ const state = chatInputCtrl.getState();
+ if (_.includes(['connected', 'complete'], state)) {
+ return 'lawngreen';
+ } else if (_.includes(['failed', 'disconnected', 'closed'], state)) {
+ return 'red';
+ } else if (_.includes(['new', 'checking'], state)) {
+ return 'goldenrod';
+ }
+ return 'lightgray';
+ };
+
+ chatInputCtrl.sendMessage = () => {
+ if (chatInputCtrl.msg) {
+ chatInputCtrl.sendMessageFunc(chatInputCtrl.msg);
+ chatInputCtrl.msg = '';
+ }
+ };
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/input/chat-input.css b/webchat/components/chat/input/chat-input.css
new file mode 100644
index 000000000..1f9764359
--- /dev/null
+++ b/webchat/components/chat/input/chat-input.css
@@ -0,0 +1,22 @@
+.chat-input__container {
+ height: 100%;
+ width: 100%;
+ display: flex;
+ align-content: center;
+ align-items: center;
+}
+
+.chat-input__connection-state {
+ margin-left: 10px;
+}
+
+.chat-input__text {
+ width: 100%;
+ height: 70%;
+ margin: 0 0 0 10px;
+ background: #FFFFFF;
+}
+
+.chat-input__text:disabled {
+ background: #dddddd;
+}
\ No newline at end of file
diff --git a/webchat/components/chat/input/chat-input.html b/webchat/components/chat/input/chat-input.html
new file mode 100644
index 000000000..d62da383d
--- /dev/null
+++ b/webchat/components/chat/input/chat-input.html
@@ -0,0 +1,14 @@
+
From f3e940cf021b2870465fe7c9f0eb414a675cec94 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:16:25 -0300
Subject: [PATCH 04/45] Add chat-body component
---
.../components/chat/body/chat-body.component.js | 17 +++++++++++++++++
webchat/components/chat/body/chat-body.css | 8 ++++++++
webchat/components/chat/body/chat-body.html | 7 +++++++
3 files changed, 32 insertions(+)
create mode 100644 webchat/components/chat/body/chat-body.component.js
create mode 100644 webchat/components/chat/body/chat-body.css
create mode 100644 webchat/components/chat/body/chat-body.html
diff --git a/webchat/components/chat/body/chat-body.component.js b/webchat/components/chat/body/chat-body.component.js
new file mode 100644
index 000000000..cc3c4f4bc
--- /dev/null
+++ b/webchat/components/chat/body/chat-body.component.js
@@ -0,0 +1,17 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("chatBody", {
+ templateUrl: "app/components/chat/body/chat-body.html",
+ controller: chatBodyController,
+ controllerAs: "chatBodyCtrl",
+ bindings: {
+ messages: '<',
+ },
+ });
+
+ function chatBodyController() {
+ const chatBodyCtrl = this;
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/body/chat-body.css b/webchat/components/chat/body/chat-body.css
new file mode 100644
index 000000000..161d82c13
--- /dev/null
+++ b/webchat/components/chat/body/chat-body.css
@@ -0,0 +1,8 @@
+.chat-body__container {
+ height: 100%;
+ max-height: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow-y: scroll;
+ justify-content: flex-end;
+}
diff --git a/webchat/components/chat/body/chat-body.html b/webchat/components/chat/body/chat-body.html
new file mode 100644
index 000000000..48bd48557
--- /dev/null
+++ b/webchat/components/chat/body/chat-body.html
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
From 33470526108c8f4d4032aafcd98f139bd8c8da77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:16:40 -0300
Subject: [PATCH 05/45] Add chat-header component
---
.../chat/header/chat-header.component.js | 19 +++++++++++++++++++
.../components/chat/header/chat-header.css | 17 +++++++++++++++++
.../components/chat/header/chat-header.html | 8 ++++++++
3 files changed, 44 insertions(+)
create mode 100644 webchat/components/chat/header/chat-header.component.js
create mode 100644 webchat/components/chat/header/chat-header.css
create mode 100644 webchat/components/chat/header/chat-header.html
diff --git a/webchat/components/chat/header/chat-header.component.js b/webchat/components/chat/header/chat-header.component.js
new file mode 100644
index 000000000..f8949a503
--- /dev/null
+++ b/webchat/components/chat/header/chat-header.component.js
@@ -0,0 +1,19 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("chatHeader", {
+ templateUrl: "app/components/chat/header/chat-header.html",
+ controller: chatHeaderController,
+ controllerAs: "chatHeaderCtrl",
+ bindings: {
+ user: "<",
+ chat: "<",
+ callFunc: "<",
+ },
+ });
+
+ function chatHeaderController() {
+ const chatHeaderCtrl = this;
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/header/chat-header.css b/webchat/components/chat/header/chat-header.css
new file mode 100644
index 000000000..b5f28f0bd
--- /dev/null
+++ b/webchat/components/chat/header/chat-header.css
@@ -0,0 +1,17 @@
+.chat-header__container {
+ display: grid;
+ grid-template-columns: auto max-content;
+ grid-template-areas:
+ 'current-user video-buttons';
+ align-items: center;
+}
+
+.chat-header__current-user {
+ grid-area: current-user;
+ margin: 0 10px;
+ color: #EEEEEE;
+}
+
+.chat-header__buttons {
+ grid-area: video-buttons;
+}
\ No newline at end of file
diff --git a/webchat/components/chat/header/chat-header.html b/webchat/components/chat/header/chat-header.html
new file mode 100644
index 000000000..c357f885f
--- /dev/null
+++ b/webchat/components/chat/header/chat-header.html
@@ -0,0 +1,8 @@
+
From bd755364f3adb0f7aa333c852e676e1c0a893dbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:16:55 -0300
Subject: [PATCH 06/45] Add ecis-chat-component
---
.../components/chat/ecis-chat.component.js | 28 +++++++++++++++++++
webchat/components/chat/ecis-chat.css | 25 +++++++++++++++++
webchat/components/chat/ecis-chat.html | 16 +++++++++++
3 files changed, 69 insertions(+)
create mode 100644 webchat/components/chat/ecis-chat.component.js
create mode 100644 webchat/components/chat/ecis-chat.css
create mode 100644 webchat/components/chat/ecis-chat.html
diff --git a/webchat/components/chat/ecis-chat.component.js b/webchat/components/chat/ecis-chat.component.js
new file mode 100644
index 000000000..a80eb9cab
--- /dev/null
+++ b/webchat/components/chat/ecis-chat.component.js
@@ -0,0 +1,28 @@
+(function () {
+ 'use strict';
+
+ angular.module("webchat").component("ecisChat", {
+ templateUrl: "app/components/chat/ecis-chat.html",
+ controller: ecisChatController,
+ controllerAs: "ecisChatCtrl",
+ bindings: {
+ chat: '<',
+ user: '<',
+ callFunc: '<',
+ state: '<',
+ },
+ });
+
+ function ecisChatController() {
+ const ecisChatCtrl = this;
+
+ ecisChatCtrl.sendMessage = (msg) => {
+ ecisChatCtrl.chat.sendMessage(msg);
+ };
+
+ ecisChatCtrl.call = () => {
+ ecisChatCtrl.callFunc(ecisChatCtrl.user);
+ };
+ }
+
+})();
\ No newline at end of file
diff --git a/webchat/components/chat/ecis-chat.css b/webchat/components/chat/ecis-chat.css
new file mode 100644
index 000000000..ee8a080f5
--- /dev/null
+++ b/webchat/components/chat/ecis-chat.css
@@ -0,0 +1,25 @@
+.chat__container {
+ height: 100%;
+ background: #EEEEEE;
+ display: grid;
+ grid-template-rows: 64px calc(100% - 64px - 48px) 48px;
+ grid-template-areas:
+ 'header'
+ 'body'
+ 'input';
+}
+
+.chat__header {
+ grid-area: header;
+ background: #009688;
+}
+
+.chat__body {
+ grid-area: body;
+ background: #E5DDD3;
+}
+
+.chat__input {
+ grid-area: input;
+ background: #009688;
+}
\ No newline at end of file
diff --git a/webchat/components/chat/ecis-chat.html b/webchat/components/chat/ecis-chat.html
new file mode 100644
index 000000000..b46d04748
--- /dev/null
+++ b/webchat/components/chat/ecis-chat.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
From bc43450001a952d8e0167d9965de51d9797bd555 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:17:38 -0300
Subject: [PATCH 07/45] Add chat components imports
---
webchat/index.html | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/webchat/index.html b/webchat/index.html
index 5d91c4f0e..5308c018d 100644
--- a/webchat/index.html
+++ b/webchat/index.html
@@ -58,6 +58,14 @@
+
+
+
+
+
+
+
+
@@ -125,6 +133,13 @@
+
+
+
+
+
+
+
From ffd092c6039d52b5205d0a117bc713c411c6545f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:18:27 -0300
Subject: [PATCH 08/45] Add chat component implementation
---
.../toggle-button/toggle-button.component.js | 2 -
webchat/home/home.html | 10 ++-
webchat/home/homeController.js | 69 ++++++++++++++++++-
3 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/webchat/components/toggle-button/toggle-button.component.js b/webchat/components/toggle-button/toggle-button.component.js
index c3575f400..94c0874d4 100644
--- a/webchat/components/toggle-button/toggle-button.component.js
+++ b/webchat/components/toggle-button/toggle-button.component.js
@@ -36,14 +36,12 @@
const toggleButtonCtrl = this;
toggleButtonCtrl.$onInit = () => {
- console.log(toggleButtonCtrl);
_.defaults(toggleButtonCtrl, {
active: true,
iconColorOn: "#EEE",
iconColorOff: "#EEE",
action: () => {}
});
- console.log(toggleButtonCtrl);
};
toggleButtonCtrl.toggle = () => {
diff --git a/webchat/home/home.html b/webchat/home/home.html
index 2f19105a3..c4e19a178 100644
--- a/webchat/home/home.html
+++ b/webchat/home/home.html
@@ -1,3 +1,11 @@
-
+
+
+
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index fa8beabbb..b69a562bf 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -3,11 +3,74 @@
const webchat = angular.module('webchat');
- webchat.controller('HomeController', ['WebchatService', function HomeController (WebchatService) {
+ webchat.controller('HomeController', ['UserService', 'AuthService', 'WebchatService', 'MessageService', '$scope', function HomeController (UserService, AuthService, WebchatService, MessageService, $scope) {
const homeCtrl = this;
- homeCtrl.contacts = WebchatService.getContacts();
+ homeCtrl.$onInit = () => {
+ homeCtrl.client = AuthService.chatClient;
+ homeCtrl.cachedUsers = {};
- }]);
+ homeCtrl.getUserList(homeCtrl.client.users);
+ homeCtrl.client.on('user-list-updated', homeCtrl.getUserList);
+ homeCtrl.client.on('call-requested', homeCtrl.promptCall);
+ homeCtrl.client.on('chat-created', homeCtrl.chatCreated);
+ };
+
+ homeCtrl.getUserList = (users) => {
+ const parsedUsers = [];
+
+ if (users.forEach) {
+ users.forEach(userKey => {
+ if (userKey !== homeCtrl.client.id) {
+ if (!_.has(homeCtrl.cachedUsers, userKey)) {
+ UserService.getUser(userKey).then(user => {
+ homeCtrl.cachedUsers[userKey] = user;
+ parsedUsers.push(homeCtrl.cachedUsers[userKey]);
+ });
+ } else {
+ parsedUsers.push(homeCtrl.cachedUsers[userKey]);
+ }
+ }
+ });
+ }
+ homeCtrl.contacts = parsedUsers;
+ };
+
+ homeCtrl.openChat = (user) => {
+ homeCtrl.currentUser = user;
+ console.log(homeCtrl.client.chats[user.key]);
+ };
+ homeCtrl.chatCreated = (e) => {
+ UserService.getUser(e.id).then(user => {
+ homeCtrl.currentChat = e.chat;
+ homeCtrl.currentUser = user;
+ homeCtrl.currentChat.on('ice-connection-changed', homeCtrl.stateChange);
+ homeCtrl.currentChat.on('msg-list-updated', list => {
+ $scope.$apply();
+ });
+ });
+ };
+
+ homeCtrl.stateChange = (state) => {
+ homeCtrl.state = state;
+ console.log(state);
+ $scope.$apply();
+ };
+
+ homeCtrl.call = (user) => {
+ homeCtrl.client.requestCall(user.key);
+ homeCtrl.openChat(user);
+ };
+
+ homeCtrl.promptCall = (id) => {
+ UserService.getUser(id).then(user => {
+ MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
+ if (answer) {
+ homeCtrl.client.acceptCall(id);
+ }
+ });
+ });
+ };
+ }]);
})();
From 52b38e8ce7d517df2a2d5b5f9b21d08362df341b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 1 Mar 2019 13:21:20 -0300
Subject: [PATCH 09/45] Fix ecis-header user description to receive email
---
webchat/components/header/ecis-header.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webchat/components/header/ecis-header.html b/webchat/components/header/ecis-header.html
index f5d410fb5..a1eb40357 100644
--- a/webchat/components/header/ecis-header.html
+++ b/webchat/components/header/ecis-header.html
@@ -11,7 +11,7 @@
class="ecis-header__current-user"
name="{{headerCtrl.user.name}}"
avatar="{{headerCtrl.user.photo_url}}"
- text="{{headerCtrl.user.current_institution.name}}">
+ text="{{headerCtrl.user.email[0]}}">
+ ng-model="chatInputCtrl.msg"
+ ng-keydown="$event.keyCode === 13 && chatInputCtrl.sendMessage()"/>
Date: Fri, 8 Mar 2019 10:57:42 -0300
Subject: [PATCH 17/45] Add autoclose sidenav when a chat is open and is on
mobile
---
webchat/home/homeController.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index 4de3f6d31..43a3d92d5 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -3,7 +3,8 @@
const webchat = angular.module('webchat');
- webchat.controller('HomeController', ['UserService', 'AuthService', 'MessageService', '$scope', function HomeController (UserService, AuthService, MessageService, $scope) {
+ webchat.controller('HomeController', ['UserService', 'AuthService', 'MessageService', '$scope', 'NavbarManagementService',
+ function HomeController (UserService, AuthService, MessageService, $scope, NavbarManagementService) {
const homeCtrl = this;
homeCtrl.$onInit = () => {
@@ -36,6 +37,9 @@
homeCtrl.openChat = (user) => {
homeCtrl.currentUser = user;
+ if (Utils.isMobileScreen()) {
+ NavbarManagementService.toggleSidenav('left');
+ }
};
homeCtrl.chatCreated = (e) => {
From 5f288d380bfef9686b64034bb0c7c0a8cfef07e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 8 Mar 2019 12:33:55 -0300
Subject: [PATCH 18/45] Add disabled input when state is not connected
---
webchat/components/chat/input/chat-input.component.js | 5 +++++
webchat/components/chat/input/chat-input.html | 6 ++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/webchat/components/chat/input/chat-input.component.js b/webchat/components/chat/input/chat-input.component.js
index 768511a44..c7e90699a 100644
--- a/webchat/components/chat/input/chat-input.component.js
+++ b/webchat/components/chat/input/chat-input.component.js
@@ -34,6 +34,11 @@
chatInputCtrl.msg = '';
}
};
+
+ chatInputCtrl.inputDisabled = () => {
+ return !_.includes(['connected', 'complete'], chatInputCtrl.state);
+ };
+
}
})();
\ No newline at end of file
diff --git a/webchat/components/chat/input/chat-input.html b/webchat/components/chat/input/chat-input.html
index e6f7877e7..45660ddb2 100644
--- a/webchat/components/chat/input/chat-input.html
+++ b/webchat/components/chat/input/chat-input.html
@@ -5,11 +5,13 @@
+ ng-keydown="$event.keyCode === 13 && chatInputCtrl.sendMessage()"
+ ng-disabled="chatInputCtrl.inputDisabled()"/>
+ action="chatInputCtrl.sendMessage"
+ disabled="chatInputCtrl.inputDisabled()">
From 6c9f81344d45793fccf483c2c64eb00f52065f73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Tue, 12 Mar 2019 20:31:06 -0300
Subject: [PATCH 19/45] Fix typo on state definition
---
webchat/components/chat/input/chat-input.component.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/webchat/components/chat/input/chat-input.component.js b/webchat/components/chat/input/chat-input.component.js
index c7e90699a..b365ac7b8 100644
--- a/webchat/components/chat/input/chat-input.component.js
+++ b/webchat/components/chat/input/chat-input.component.js
@@ -18,7 +18,7 @@
chatInputCtrl.getStateStyle = () => {
const state = chatInputCtrl.getState();
- if (_.includes(['connected', 'complete'], state)) {
+ if (_.includes(['connected', 'completed'], state)) {
return 'lawngreen';
} else if (_.includes(['failed', 'disconnected', 'closed'], state)) {
return 'red';
@@ -36,7 +36,7 @@
};
chatInputCtrl.inputDisabled = () => {
- return !_.includes(['connected', 'complete'], chatInputCtrl.state);
+ return !_.includes(['connected', 'completed'], chatInputCtrl.state);
};
}
From a2c75dada081cdf22777cf77eb67b7c48b4ccb1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Tue, 12 Mar 2019 20:31:34 -0300
Subject: [PATCH 20/45] Add structure to add video on webchat
---
webchat/home/homeController.js | 57 +++++++++++++++++++++++++---------
1 file changed, 42 insertions(+), 15 deletions(-)
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index 43a3d92d5..b4b1c08c7 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -43,13 +43,23 @@
};
homeCtrl.chatCreated = (e) => {
- UserService.getUser(e.id).then(user => {
- homeCtrl.currentChat = e.chat;
- homeCtrl.currentUser = user;
- homeCtrl.currentChat.on('ice-connection-changed', homeCtrl.stateChange);
- homeCtrl.currentChat.on('msg-list-updated', list => {
- $scope.$apply();
- });
+ homeCtrl.currentUser = homeCtrl.getUser(e.id);
+ homeCtrl.currentChat = e.chat;
+
+ const selfie = document.getElementById('video-selfie');
+ selfie.srcObject = homeCtrl.currentChat.selfStream;
+ selfie.play().then().catch(e => console.log('cant play video: ', e));
+
+ homeCtrl.currentChat.on('ice-connection-changed', homeCtrl.stateChange);
+ homeCtrl.currentChat.on('msg-list-updated', list => {
+ $scope.$apply();
+ });
+
+ homeCtrl.currentChat.on('track-received', ev => {
+ const el = document.getElementById('video-remote');
+ el.srcObject = ev.streams[0];
+ el.play().then().catch(e => console.log('cant play video: ', e));
+ $scope.$apply();
});
};
@@ -59,18 +69,35 @@
};
homeCtrl.call = (user) => {
- homeCtrl.client.requestCall(user.key);
- homeCtrl.openChat(user);
+ getMedia().then(stream => {
+ homeCtrl.client.requestCall(user.key, stream);
+ });
};
homeCtrl.promptCall = (id) => {
- UserService.getUser(id).then(user => {
- MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
- if (answer) {
- homeCtrl.client.acceptCall(id);
- }
- });
+ const user = homeCtrl.getUser(id);
+
+ MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
+ if (answer) {
+ homeCtrl.openChat(user);
+ getMedia().then(stream => {
+ homeCtrl.client.acceptCall(id, stream);
+ });
+ }
+ });
+ };
+
+ function getMedia() {
+ return new Promise((resolve, reject) => {
+ let stream;
+ navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(s => {
+ stream = s;
+ }).catch(e => console.log(e)).finally(() => resolve(stream));
});
+ }
+
+ homeCtrl.getUser = (id) => {
+ return homeCtrl.cachedUsers[id];
};
}]);
})();
From 08411a5cd52f058207b66da42bb910d422ea3592 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Wed, 13 Mar 2019 11:24:05 -0300
Subject: [PATCH 21/45] Modify toggle button to have a specific function to
call when on and off
---
.../toggle-button/toggle-button.component.js | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/webchat/components/toggle-button/toggle-button.component.js b/webchat/components/toggle-button/toggle-button.component.js
index 94c0874d4..50ce06ece 100644
--- a/webchat/components/toggle-button/toggle-button.component.js
+++ b/webchat/components/toggle-button/toggle-button.component.js
@@ -28,7 +28,8 @@
iconOff: '@',
iconColorOn: '@',
iconColorOff: '@',
- action: '<',
+ actionOn: '<',
+ actionOff: '<',
},
});
@@ -40,13 +41,14 @@
active: true,
iconColorOn: "#EEE",
iconColorOff: "#EEE",
- action: () => {}
+ actionOn: () => {},
+ actionOff: () => {},
});
};
toggleButtonCtrl.toggle = () => {
toggleButtonCtrl.active = !toggleButtonCtrl.active;
- toggleButtonCtrl.action();
+ toggleButtonCtrl.activeActionFunc();
};
Object.defineProperty(toggleButtonCtrl, 'activeIcon', {
@@ -60,6 +62,12 @@
return toggleButtonCtrl.active ? toggleButtonCtrl.iconColorOn : toggleButtonCtrl.iconColorOff;
},
});
+
+ Object.defineProperty(toggleButtonCtrl, 'activeActionFunc', {
+ get: () => {
+ return toggleButtonCtrl.active ? toggleButtonCtrl.actionOn : toggleButtonCtrl.actionOff;
+ },
+ });
}
})();
\ No newline at end of file
From 5d509d170999eadc6cc93c38049bcafc52723bcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Wed, 13 Mar 2019 11:24:51 -0300
Subject: [PATCH 22/45] Add video and audio toggle buttons on chat
---
webchat/components/chat/buttons/chat-buttons.html | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/webchat/components/chat/buttons/chat-buttons.html b/webchat/components/chat/buttons/chat-buttons.html
index 7240fd019..8a5541348 100644
--- a/webchat/components/chat/buttons/chat-buttons.html
+++ b/webchat/components/chat/buttons/chat-buttons.html
@@ -1 +1,11 @@
-
\ No newline at end of file
+
+
+
From 88e6ae0fec6a1418f179163c1f711ef0d5770a67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Wed, 13 Mar 2019 11:26:31 -0300
Subject: [PATCH 23/45] Fix navbar shown when accept call
---
webchat/home/homeController.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index b4b1c08c7..6cd162341 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -79,7 +79,7 @@
MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
if (answer) {
- homeCtrl.openChat(user);
+ homeCtrl.currentUser = user;
getMedia().then(stream => {
homeCtrl.client.acceptCall(id, stream);
});
From 382be66cacad3d0395c5c73dc088b07539b61f13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Wed, 13 Mar 2019 11:28:40 -0300
Subject: [PATCH 24/45] Add video tags on chat body
---
webchat/components/chat/body/chat-body.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/webchat/components/chat/body/chat-body.html b/webchat/components/chat/body/chat-body.html
index 48bd48557..9d87fec32 100644
--- a/webchat/components/chat/body/chat-body.html
+++ b/webchat/components/chat/body/chat-body.html
@@ -4,4 +4,8 @@
ng-repeat="message in chatBodyCtrl.messages"
message-obj="message">
-
\ No newline at end of file
+
+
+
+
+
From d5231988a77792691172d1b3a6fb2d637030c34f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Thu, 14 Mar 2019 09:10:55 -0300
Subject: [PATCH 25/45] Add videoStream passed as a binding to chat body
---
.../chat/body/chat-body.component.js | 24 +++++++++++++++++++
.../components/chat/ecis-chat.component.js | 2 ++
webchat/components/chat/ecis-chat.html | 4 +++-
webchat/home/home.html | 4 +++-
webchat/home/homeController.js | 9 ++-----
5 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/webchat/components/chat/body/chat-body.component.js b/webchat/components/chat/body/chat-body.component.js
index cc3c4f4bc..de7334f6d 100644
--- a/webchat/components/chat/body/chat-body.component.js
+++ b/webchat/components/chat/body/chat-body.component.js
@@ -7,11 +7,35 @@
controllerAs: "chatBodyCtrl",
bindings: {
messages: '<',
+ videoActive: '<',
+ selfieStream: '<',
+ remoteStream: '<',
},
});
function chatBodyController() {
const chatBodyCtrl = this;
+
+ chatBodyCtrl.$onChanges = (changesObj) => {
+ updateSelfieVideo(changesObj);
+ updateRemoteVideo(changesObj);
+ };
+
+ const updateSelfieVideo = (changesObj) => {
+ if (_.has(changesObj, 'selfieStream.currentValue')) {
+ const selfieVideo = document.getElementById('video-selfie');
+ selfieVideo.srcObject = chatBodyCtrl.selfieStream;
+ selfieVideo.play();
+ }
+ };
+
+ const updateRemoteVideo = (changesObj) => {
+ if (_.has(changesObj, 'remoteStream.currentValue')) {
+ const remoteVideo = document.getElementById('video-remote');
+ remoteVideo.srcObject = chatBodyCtrl.remoteStream;
+ remoteVideo.play();
+ }
+ };
}
})();
\ No newline at end of file
diff --git a/webchat/components/chat/ecis-chat.component.js b/webchat/components/chat/ecis-chat.component.js
index a80eb9cab..a9b7d0958 100644
--- a/webchat/components/chat/ecis-chat.component.js
+++ b/webchat/components/chat/ecis-chat.component.js
@@ -10,6 +10,8 @@
user: '<',
callFunc: '<',
state: '<',
+ selfieStream: '<',
+ remoteStream: '<',
},
});
diff --git a/webchat/components/chat/ecis-chat.html b/webchat/components/chat/ecis-chat.html
index b46d04748..1bcff6d70 100644
--- a/webchat/components/chat/ecis-chat.html
+++ b/webchat/components/chat/ecis-chat.html
@@ -6,7 +6,9 @@
+ messages="ecisChatCtrl.chat.currentMessages"
+ selfie-stream="ecisChatCtrl.selfieStream"
+ remote-stream="ecisChatCtrl.remoteStream">
+ state="homeCtrl.state"
+ selfie-stream="homeCtrl.selfieStream"
+ remote-stream="homeCtrl.remoteStream">
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index 6cd162341..f683db4e8 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -46,9 +46,7 @@
homeCtrl.currentUser = homeCtrl.getUser(e.id);
homeCtrl.currentChat = e.chat;
- const selfie = document.getElementById('video-selfie');
- selfie.srcObject = homeCtrl.currentChat.selfStream;
- selfie.play().then().catch(e => console.log('cant play video: ', e));
+ homeCtrl.selfieStream = homeCtrl.currentChat.selfStream;
homeCtrl.currentChat.on('ice-connection-changed', homeCtrl.stateChange);
homeCtrl.currentChat.on('msg-list-updated', list => {
@@ -56,10 +54,7 @@
});
homeCtrl.currentChat.on('track-received', ev => {
- const el = document.getElementById('video-remote');
- el.srcObject = ev.streams[0];
- el.play().then().catch(e => console.log('cant play video: ', e));
- $scope.$apply();
+ homeCtrl.remoteStream = ev.streams[0];
});
};
From 1a6a75882acf1985cde1d439d07cbd31cecdc056 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Thu, 14 Mar 2019 13:08:35 -0300
Subject: [PATCH 26/45] Add specific height an width to video
---
webchat/components/chat/body/chat-body.css | 28 +++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/webchat/components/chat/body/chat-body.css b/webchat/components/chat/body/chat-body.css
index 161d82c13..98db2bf5b 100644
--- a/webchat/components/chat/body/chat-body.css
+++ b/webchat/components/chat/body/chat-body.css
@@ -1,4 +1,9 @@
-.chat-body__container {
+.chat-body {
+ height: 100%;
+ width: 100%;
+}
+
+.chat-body__messages-container {
height: 100%;
max-height: 100%;
display: flex;
@@ -6,3 +11,24 @@
overflow-y: scroll;
justify-content: flex-end;
}
+
+.chat-body__video-container {
+ background-color: darkgray;
+ max-width: 100%;
+ max-height: 100%;
+ height: 100%;
+ width: 100%;
+}
+
+#video-remote {
+ height: 100%;
+ width: 100%;
+ max-width: 100%;
+ max-height: 100%;
+}
+
+#video-selfie {
+ display: none;
+ max-width: 100%;
+ max-height: 100%;
+}
\ No newline at end of file
From 21fccf4f6f2917259a097f06c52c8137656d1afe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Thu, 14 Mar 2019 13:09:12 -0300
Subject: [PATCH 27/45] Add video toggle
---
webchat/components/chat/body/chat-body.html | 4 ++--
.../chat/buttons/chat-buttons.component.js | 2 ++
webchat/components/chat/buttons/chat-buttons.html | 4 ++--
webchat/components/chat/ecis-chat.component.js | 11 +++++++++++
webchat/components/chat/ecis-chat.html | 5 ++++-
.../components/chat/header/chat-header.component.js | 3 +++
webchat/components/chat/header/chat-header.html | 5 ++++-
.../toggle-button/toggle-button.component.js | 2 +-
webchat/home/homeController.js | 12 ++++++++----
9 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/webchat/components/chat/body/chat-body.html b/webchat/components/chat/body/chat-body.html
index 9d87fec32..b1a63ce13 100644
--- a/webchat/components/chat/body/chat-body.html
+++ b/webchat/components/chat/body/chat-body.html
@@ -1,11 +1,11 @@
-
+
-
+
diff --git a/webchat/components/chat/buttons/chat-buttons.component.js b/webchat/components/chat/buttons/chat-buttons.component.js
index 08bf5d7ea..fa9bd908b 100644
--- a/webchat/components/chat/buttons/chat-buttons.component.js
+++ b/webchat/components/chat/buttons/chat-buttons.component.js
@@ -7,6 +7,8 @@
controllerAs: "chatButtonsCtrl",
bindings: {
callFunc: "<",
+ enableVideoFunc: "<",
+ disableVideoFunc: "<",
},
});
diff --git a/webchat/components/chat/buttons/chat-buttons.html b/webchat/components/chat/buttons/chat-buttons.html
index 8a5541348..4aef7cd55 100644
--- a/webchat/components/chat/buttons/chat-buttons.html
+++ b/webchat/components/chat/buttons/chat-buttons.html
@@ -1,8 +1,8 @@
+ action-on="chatButtonsCtrl.enableVideoFunc"
+ action-off="chatButtonsCtrl.disableVideoFunc">
{
ecisChatCtrl.callFunc(ecisChatCtrl.user);
};
+
+ ecisChatCtrl.disableVideo = () => {
+ ecisChatCtrl.videoActive = false;
+ console.log('disabled');
+ };
+
+ ecisChatCtrl.enableVideo = () => {
+ ecisChatCtrl.videoActive = true;
+ console.log('enabled');
+ };
+
}
})();
\ No newline at end of file
diff --git a/webchat/components/chat/ecis-chat.html b/webchat/components/chat/ecis-chat.html
index 1bcff6d70..153b534ba 100644
--- a/webchat/components/chat/ecis-chat.html
+++ b/webchat/components/chat/ecis-chat.html
@@ -2,11 +2,14 @@
diff --git a/webchat/components/chat/header/chat-header.component.js b/webchat/components/chat/header/chat-header.component.js
index f8949a503..32fe45182 100644
--- a/webchat/components/chat/header/chat-header.component.js
+++ b/webchat/components/chat/header/chat-header.component.js
@@ -9,11 +9,14 @@
user: "<",
chat: "<",
callFunc: "<",
+ enableVideoFunc: '<',
+ disableVideoFunc: '<',
},
});
function chatHeaderController() {
const chatHeaderCtrl = this;
+
}
})();
\ No newline at end of file
diff --git a/webchat/components/chat/header/chat-header.html b/webchat/components/chat/header/chat-header.html
index c357f885f..1e11de890 100644
--- a/webchat/components/chat/header/chat-header.html
+++ b/webchat/components/chat/header/chat-header.html
@@ -4,5 +4,8 @@
avatar="{{ chatHeaderCtrl.user.photo_url }}"
name="{{ chatHeaderCtrl.user.name }}"
text="{{ chatHeaderCtrl.user.email[0] }}">
-
+
diff --git a/webchat/components/toggle-button/toggle-button.component.js b/webchat/components/toggle-button/toggle-button.component.js
index 50ce06ece..ce965cdc6 100644
--- a/webchat/components/toggle-button/toggle-button.component.js
+++ b/webchat/components/toggle-button/toggle-button.component.js
@@ -47,8 +47,8 @@
};
toggleButtonCtrl.toggle = () => {
- toggleButtonCtrl.active = !toggleButtonCtrl.active;
toggleButtonCtrl.activeActionFunc();
+ toggleButtonCtrl.active = !toggleButtonCtrl.active;
};
Object.defineProperty(toggleButtonCtrl, 'activeIcon', {
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index f683db4e8..f22e830ca 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -74,14 +74,18 @@
MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
if (answer) {
- homeCtrl.currentUser = user;
- getMedia().then(stream => {
- homeCtrl.client.acceptCall(id, stream);
- });
+ homeCtrl.acceptCall(user, id);
}
});
};
+ homeCtrl.acceptCall = (user, id) => {
+ homeCtrl.currentUser = user;
+ getMedia().then(stream => {
+ homeCtrl.client.acceptCall(id, stream);
+ });
+ };
+
function getMedia() {
return new Promise((resolve, reject) => {
let stream;
From 4a41716a12e00949c51d3c45d343990fed1a1f10 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 15 Mar 2019 13:28:56 -0300
Subject: [PATCH 28/45] Apply responsability to contain remote stream to chat
---
.../components/chat/body/chat-body.component.js | 12 ++++++++----
webchat/components/chat/ecis-chat.component.js | 2 --
webchat/home/home.html | 4 ++--
webchat/home/homeController.js | 15 ++++-----------
webchat/utils/chat.js | 8 +++++++-
5 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/webchat/components/chat/body/chat-body.component.js b/webchat/components/chat/body/chat-body.component.js
index de7334f6d..fda116370 100644
--- a/webchat/components/chat/body/chat-body.component.js
+++ b/webchat/components/chat/body/chat-body.component.js
@@ -22,17 +22,21 @@
};
const updateSelfieVideo = (changesObj) => {
- if (_.has(changesObj, 'selfieStream.currentValue')) {
+ const canUpdate = _.get(changesObj, 'selfieStream.currentValue.active', false);
+
+ if (canUpdate) {
const selfieVideo = document.getElementById('video-selfie');
- selfieVideo.srcObject = chatBodyCtrl.selfieStream;
+ selfieVideo.srcObject = changesObj.selfieStream.currentValue;
selfieVideo.play();
}
};
const updateRemoteVideo = (changesObj) => {
- if (_.has(changesObj, 'remoteStream.currentValue')) {
+ const canUpdate = _.get(changesObj, 'remoteStream.currentValue.active', false);
+
+ if (canUpdate) {
const remoteVideo = document.getElementById('video-remote');
- remoteVideo.srcObject = chatBodyCtrl.remoteStream;
+ remoteVideo.srcObject = changesObj.remoteStream.currentValue;
remoteVideo.play();
}
};
diff --git a/webchat/components/chat/ecis-chat.component.js b/webchat/components/chat/ecis-chat.component.js
index 6dfcb06da..19c137352 100644
--- a/webchat/components/chat/ecis-chat.component.js
+++ b/webchat/components/chat/ecis-chat.component.js
@@ -28,12 +28,10 @@
ecisChatCtrl.disableVideo = () => {
ecisChatCtrl.videoActive = false;
- console.log('disabled');
};
ecisChatCtrl.enableVideo = () => {
ecisChatCtrl.videoActive = true;
- console.log('enabled');
};
}
diff --git a/webchat/home/home.html b/webchat/home/home.html
index b1a40114f..8a81faeb5 100644
--- a/webchat/home/home.html
+++ b/webchat/home/home.html
@@ -7,7 +7,7 @@
user="homeCtrl.currentUser"
call-func="homeCtrl.call"
state="homeCtrl.state"
- selfie-stream="homeCtrl.selfieStream"
- remote-stream="homeCtrl.remoteStream">
+ selfie-stream="homeCtrl.currentChat.selfieStream"
+ remote-stream="homeCtrl.currentChat.remoteStream">
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index f22e830ca..ff23092fe 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -37,25 +37,18 @@
homeCtrl.openChat = (user) => {
homeCtrl.currentUser = user;
- if (Utils.isMobileScreen()) {
- NavbarManagementService.toggleSidenav('left');
- }
+ homeCtrl.currentChat = homeCtrl.client.chats[user.key] || {};
+
};
homeCtrl.chatCreated = (e) => {
- homeCtrl.currentUser = homeCtrl.getUser(e.id);
- homeCtrl.currentChat = e.chat;
-
- homeCtrl.selfieStream = homeCtrl.currentChat.selfStream;
+ homeCtrl.openChat(homeCtrl.getUser(e.id));
homeCtrl.currentChat.on('ice-connection-changed', homeCtrl.stateChange);
homeCtrl.currentChat.on('msg-list-updated', list => {
$scope.$apply();
});
- homeCtrl.currentChat.on('track-received', ev => {
- homeCtrl.remoteStream = ev.streams[0];
- });
};
homeCtrl.stateChange = (state) => {
@@ -80,7 +73,7 @@
};
homeCtrl.acceptCall = (user, id) => {
- homeCtrl.currentUser = user;
+ homeCtrl.openChat(user);
getMedia().then(stream => {
homeCtrl.client.acceptCall(id, stream);
});
diff --git a/webchat/utils/chat.js b/webchat/utils/chat.js
index ffd48d558..cc210f8d0 100644
--- a/webchat/utils/chat.js
+++ b/webchat/utils/chat.js
@@ -25,10 +25,15 @@
this.rpc.onicecandidate = e => this.emit('ice-candidate-discovered', e);
this.sendChannel = this.rpc.createDataChannel('sendChannel');
this.rpc.ondatachannel = this.handleDataChannel.bind(this);
- this.rpc.ontrack = e => this.emit('track-received', e);
+ this.rpc.ontrack = this.handleTrack.bind(this);
this.rpc.oniceconnectionstatechange = this.handleIceConnectionState.bind(this);
this.rpc.onsignalingstatechange = this.handleState.bind(this);
this._currentMessages = [];
+ this._remoteStream = {};
+ }
+
+ get remoteStream() {
+ return this._remoteStream;
}
get selfStream() {
@@ -171,6 +176,7 @@
* @param {Event} e - event which contains the stream object and its tracks.
*/
handleTrack(e) {
+ this._remoteStream = e.streams[0];
this.emit('track-received', e);
}
From 6fa2345fc5c7af5c963291254e276e4ca449f74e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 15 Mar 2019 15:06:56 -0300
Subject: [PATCH 29/45] Fix dialog confirm action not triggering
---
webchat/home/homeController.js | 8 +++---
webchat/index.html | 2 +-
webchat/styles/custom/custom.css | 5 ++++
webchat/utils/confirm_dialog.css | 29 ++++++++++++++++++++++
webchat/utils/confirm_dialog.html | 15 +++++++++++
webchat/utils/messageService.js | 41 +++++++++++++++++++++++--------
6 files changed, 85 insertions(+), 15 deletions(-)
create mode 100644 webchat/utils/confirm_dialog.css
create mode 100644 webchat/utils/confirm_dialog.html
diff --git a/webchat/home/homeController.js b/webchat/home/homeController.js
index ff23092fe..7bcff2c8c 100644
--- a/webchat/home/homeController.js
+++ b/webchat/home/homeController.js
@@ -65,10 +65,10 @@
homeCtrl.promptCall = (id) => {
const user = homeCtrl.getUser(id);
- MessageService.showConfirmationDialog({}, "Ligação recebida", `Ligação de ${user.name}. Aceitar?`).then(answer => {
- if (answer) {
- homeCtrl.acceptCall(user, id);
- }
+ MessageService.showConfirmationDialog({}, {
+ title: user.name,
+ subtitle: "Está te ligando você deseja atender essa ligação?",
+ confirmAction: () => homeCtrl.acceptCall(user, id),
});
};
diff --git a/webchat/index.html b/webchat/index.html
index 4718ee2a2..26d4db6f5 100644
--- a/webchat/index.html
+++ b/webchat/index.html
@@ -66,7 +66,7 @@
-
+
diff --git a/webchat/styles/custom/custom.css b/webchat/styles/custom/custom.css
index 4da3533e0..3978a6af5 100644
--- a/webchat/styles/custom/custom.css
+++ b/webchat/styles/custom/custom.css
@@ -5,4 +5,9 @@
background-color: #EEEEEE;
border: none;
outline: none;
+}
+
+.custom-title {
+ font-size: 20px;
+ font-weight: 500;
}
\ No newline at end of file
diff --git a/webchat/utils/confirm_dialog.css b/webchat/utils/confirm_dialog.css
new file mode 100644
index 000000000..0ced2c801
--- /dev/null
+++ b/webchat/utils/confirm_dialog.css
@@ -0,0 +1,29 @@
+.dialog__container {
+ display: grid;
+ grid-template-areas:
+ "title"
+ "subtitle"
+ "buttons";
+ padding: 1.5em 2em 0;
+}
+
+.dialog__title {
+ grid-area: title;
+ margin: 0;
+}
+
+.dialog__subtitle {
+ grid-area: subtitle;
+ padding: 0.5em 0;
+}
+
+.dialog__buttons {
+ grid-area: buttons;
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+}
+
+.dialog__button {
+ margin: 1em 0;
+}
\ No newline at end of file
diff --git a/webchat/utils/confirm_dialog.html b/webchat/utils/confirm_dialog.html
new file mode 100644
index 000000000..840422bff
--- /dev/null
+++ b/webchat/utils/confirm_dialog.html
@@ -0,0 +1,15 @@
+
+
+ {{ dialogCtrl.title }}
+ {{ dialogCtrl.subtitle }}
+
+
+
+ {{ dialogCtrl.cancelText }}
+
+
+ {{ dialogCtrl.confirmText }}
+
+
+
+
diff --git a/webchat/utils/messageService.js b/webchat/utils/messageService.js
index 001b710e2..ab0df3f6a 100644
--- a/webchat/utils/messageService.js
+++ b/webchat/utils/messageService.js
@@ -28,19 +28,40 @@
return (message && msg[message.code]) || msg[message] || message;
}
- service.showConfirmationDialog = function showConfirmationDialog(event, title, textContent) {
- const confirm = $mdDialog.confirm()
- .clickOutsideToClose(true)
- .title(title)
- .textContent(textContent)
- .ariaLabel(title)
- .targetEvent(event)
- .ok('Ok')
- .cancel('Cancelar');
+ service.showConfirmationDialog = function showConfirmationDialog(event, params) {
+ const dialog = {
+ templateUrl: "app/utils/confirm_dialog.html",
+ controller: dialogController,
+ controllerAs: 'dialogCtrl',
+ parent: angular.element(document.body),
+ targetEvent: event,
+ clickOutsideToClose:true,
+ locals: params,
+ };
- return $mdDialog.show(confirm);
+ return $mdDialog.show(dialog);
};
+ function dialogController (locals) {
+ const dialogCtrl = this;
+
+ dialogCtrl.$onInit = () => {
+ _.assign(dialogCtrl, locals);
+ _.defaults(dialogCtrl, {
+ confirmAction: () => {},
+ cancelText: "Cancelar",
+ confirmText: "Confirmar",
+ });
+ };
+
+ dialogCtrl.cancelDialog = $mdDialog.cancel;
+
+ dialogCtrl.confirmDialog = () => {
+ dialogCtrl.confirmAction();
+ $mdDialog.hide();
+ };
+ }
+
service.showMessageDialog = function (event, message) {
function MessageController ($mdDialog) {
From 2a730505cb5be35b59774ff56e948ad641543ee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 15 Mar 2019 16:35:34 -0300
Subject: [PATCH 30/45] Fix message shown when there is no contacts online
---
webchat/components/contacts/list/contacts-list.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webchat/components/contacts/list/contacts-list.html b/webchat/components/contacts/list/contacts-list.html
index 5355da2eb..07426aba9 100644
--- a/webchat/components/contacts/list/contacts-list.html
+++ b/webchat/components/contacts/list/contacts-list.html
@@ -12,5 +12,5 @@
-
+
\ No newline at end of file
From 59f6457e5956aefce92952b7099bc7c748ffb5bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Esp=C3=ADndula?=
Date: Fri, 15 Mar 2019 16:46:33 -0300
Subject: [PATCH 31/45] Add is-empty-card component and message when no chat is
selected
---
webchat/home/home.html | 1 +
webchat/images/desenho-cis.png | Bin 0 -> 63421 bytes
webchat/index.html | 6 +++-
.../is-empty-card/is-empty-card.component.js | 24 ++++++++++++++++
webchat/utils/is-empty-card/is-empty-card.css | 26 ++++++++++++++++++
.../utils/is-empty-card/is-empty-card.html | 4 +++
6 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 webchat/images/desenho-cis.png
create mode 100644 webchat/utils/is-empty-card/is-empty-card.component.js
create mode 100644 webchat/utils/is-empty-card/is-empty-card.css
create mode 100644 webchat/utils/is-empty-card/is-empty-card.html
diff --git a/webchat/home/home.html b/webchat/home/home.html
index 8a81faeb5..3e4a418f3 100644
--- a/webchat/home/home.html
+++ b/webchat/home/home.html
@@ -10,4 +10,5 @@
selfie-stream="homeCtrl.currentChat.selfieStream"
remote-stream="homeCtrl.currentChat.remoteStream">
+
diff --git a/webchat/images/desenho-cis.png b/webchat/images/desenho-cis.png
new file mode 100644
index 0000000000000000000000000000000000000000..958e63f9af8086e54aa65a10f12bd9f7096ec219
GIT binary patch
literal 63421
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ðH}L1VzUQy;6)k^Pk0&egSdQi
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