diff --git a/services/web/app/views/project/editor/chat.jade b/services/web/app/views/project/editor/chat.jade index 754d044392..e79bd15a5b 100644 --- a/services/web/app/views/project/editor/chat.jade +++ b/services/web/app/views/project/editor/chat.jade @@ -12,7 +12,9 @@ aside.chat( .loading(ng-show="chat.loading") i.fa.fa-fw.fa-spin.fa-refresh | Loading... - ul.list-unstyled + ul.list-unstyled( + ng-click="resetUnreadMessages()" + ) li.message( ng-repeat="message in chat.messages" ng-controller="ChatMessageController" @@ -34,6 +36,11 @@ aside.chat( p(ng-repeat="content in message.contents track by $index") {{ content }} .new-message - textarea(placeholder="Your message...", on-enter="sendMessage()", ng-model="newMessageContent") + textarea( + placeholder="Your message...", + on-enter="sendMessage()", + ng-model="newMessageContent", + ng-click="resetUnreadMessages()" + ) diff --git a/services/web/app/views/project/editor/header.jade b/services/web/app/views/project/editor/header.jade index 833ce393f5..4eef791740 100644 --- a/services/web/app/views/project/editor/header.jade +++ b/services/web/app/views/project/editor/header.jade @@ -64,4 +64,9 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading") ng-click="toggleChat()", ng-controller="ChatButtonController" ) - i.fa.fa-fw.fa-comment \ No newline at end of file + i.fa.fa-fw.fa-comment( + ng-class="{ 'bounce': unreadMessages > 0 }" + ) + span.label.label-info( + ng-show="unreadMessages > 0" + ) {{ unreadMessages }} \ No newline at end of file diff --git a/services/web/public/coffee/ide/chat/controllers/ChatButtonController.coffee b/services/web/public/coffee/ide/chat/controllers/ChatButtonController.coffee index 74e6d31ee3..f4e1e27df0 100644 --- a/services/web/public/coffee/ide/chat/controllers/ChatButtonController.coffee +++ b/services/web/public/coffee/ide/chat/controllers/ChatButtonController.coffee @@ -1,7 +1,46 @@ define [ "base" ], (App) -> - App.controller "ChatButtonController", ["$scope", ($scope) -> + App.controller "ChatButtonController", ($scope, ide) -> $scope.toggleChat = () -> $scope.ui.chatOpen = !$scope.ui.chatOpen - ] \ No newline at end of file + $scope.resetUnreadMessages() + + $scope.unreadMessages = 0 + $scope.resetUnreadMessages = () -> + $scope.unreadMessages = 0 + + $scope.$on "chat:resetUnreadMessages", (e) -> + $scope.resetUnreadMessages() + + $scope.$on "chat:newMessage", (e, message) -> + if message? + if message.user.id != ide.$scope.user.id + if !$scope.ui.chatOpen + $scope.unreadMessages += 1 + flashTitle() + + focussed = true + newMessageNotificationTimeout = null + originalTitle = null + $(window).on "focus", () -> + clearNewMessageNotification() + focussed = true + $(window).on "blur", () -> + focussed = false + + flashTitle = () -> + if !focussed and !newMessageNotificationTimeout? + originalTitle ||= window.document.title + do changeTitle = () => + if window.document.title == originalTitle + window.document.title = "New Message" + else + window.document.title = originalTitle + newMessageNotificationTimeout = setTimeout changeTitle, 800 + + clearNewMessageNotification = () -> + clearTimeout newMessageNotificationTimeout + newMessageNotificationTimeout = null + if originalTitle? + window.document.title = originalTitle diff --git a/services/web/public/coffee/ide/chat/controllers/ChatController.coffee b/services/web/public/coffee/ide/chat/controllers/ChatController.coffee index 8affd3d26a..ec06f3ca64 100644 --- a/services/web/public/coffee/ide/chat/controllers/ChatController.coffee +++ b/services/web/public/coffee/ide/chat/controllers/ChatController.coffee @@ -2,7 +2,7 @@ define [ "base" "ide/chat/services/chatMessages" ], (App) -> - App.controller "ChatController", ($scope, chatMessages, @ide, $location) -> + App.controller "ChatController", ($scope, chatMessages, ide, $location) -> $scope.chat = chatMessages.state $scope.$watch "chat.messages", (messages) -> @@ -12,6 +12,13 @@ define [ $scope.$on "layout:chat:resize", () -> $scope.$emit "updateScrollPosition" + + $scope.$watch "chat.newMessage", (message) -> + if message? + ide.$scope.$broadcast "chat:newMessage", message + + $scope.resetUnreadMessages = () -> + ide.$scope.$broadcast "chat:resetUnreadMessages" $scope.sendMessage = -> chatMessages diff --git a/services/web/public/coffee/ide/chat/services/chatMessages.coffee b/services/web/public/coffee/ide/chat/services/chatMessages.coffee index 74d074e3c2..355e0cdda2 100644 --- a/services/web/public/coffee/ide/chat/services/chatMessages.coffee +++ b/services/web/public/coffee/ide/chat/services/chatMessages.coffee @@ -12,10 +12,13 @@ define [ loading: false atEnd: false nextBeforeTimestamp: null + newMessage: null } ide.socket.on "new-chat-message", (message) => - appendMessage(message) + ide.$scope.$apply () -> + chat.state.newMessage = message + appendMessage(message) chat.loadMoreMessages = () -> return if chat.state.atEnd diff --git a/services/web/public/stylesheets/app/base.less b/services/web/public/stylesheets/app/base.less index 9203300aa4..9935c3f374 100644 --- a/services/web/public/stylesheets/app/base.less +++ b/services/web/public/stylesheets/app/base.less @@ -12,4 +12,89 @@ img { margin-top: -10px; } -} \ No newline at end of file +} + +@-webkit-keyframes bounce { + 0%, 10%, 26%, 40%, 50% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 20%, 21% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 35% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 45% { + -webkit-transform: translate3d(0,-2px,0); + transform: translate3d(0,-2px,0); + } + + 50% { + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } +} + +@keyframes bounce { + 0%, 10%, 26%, 40%, 50% { + -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); + -webkit-transform: translate3d(0,0,0); + -ms-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } + + 20%, 21% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -10px, 0); + -ms-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); + } + + 35% { + -webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); + -webkit-transform: translate3d(0, -5px, 0); + -ms-transform: translate3d(0, -5px, 0); + transform: translate3d(0, -5px, 0); + } + + 45% { + -webkit-transform: translate3d(0,-2px,0); + -ms-transform: translate3d(0,-2px,0); + transform: translate3d(0,-2px,0); + } + + 50% { + -webkit-transform: translate3d(0,0,0); + -ms-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); + } +} + +.bounce { + -webkit-animation-duration: 2s; + animation-duration: 2s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + -webkit-animation-iteration-count: infinite; + animation-iteration-count: infinite; + -webkit-animation-name: bounce; + animation-name: bounce; + -webkit-transform-origin: center bottom; + -ms-transform-origin: center bottom; + transform-origin: center bottom; +} diff --git a/services/web/public/stylesheets/app/editor/pdf.less b/services/web/public/stylesheets/app/editor/pdf.less index 04873b32ad..e88d9bfd65 100644 --- a/services/web/public/stylesheets/app/editor/pdf.less +++ b/services/web/public/stylesheets/app/editor/pdf.less @@ -75,14 +75,6 @@ .toolbar { .log-btn { - position: relative; - .label { - position: absolute; - top: 0; - right: 0; - padding: .15em .6em .2em; - font-size: 60%; - } &.active, &:active { .label { display: none; diff --git a/services/web/public/stylesheets/app/editor/toolbar.less b/services/web/public/stylesheets/app/editor/toolbar.less index 7d8c4530b5..d372eb50bf 100644 --- a/services/web/public/stylesheets/app/editor/toolbar.less +++ b/services/web/public/stylesheets/app/editor/toolbar.less @@ -2,6 +2,17 @@ height: 40px; border-bottom: 1px solid @toolbar-border-color; + a { + position: relative; + .label { + position: absolute; + top: 0; + right: 0; + padding: .15em .6em .2em; + font-size: 60%; + } + } + a:not(.btn) { display: inline-block; color: @gray-light; @@ -32,6 +43,10 @@ background-color: @link-color; .box-shadow(inset 0 3px 5px rgba(0, 0, 0, 0.225)); } + .label { + top: 4px; + right: 4px; + } } .toolbar-right {