diff --git a/html/main.html b/html/main.html index 3dccb6e3..32d57348 100644 --- a/html/main.html +++ b/html/main.html @@ -4,6 +4,9 @@ <%template "head" .%> +
+ +
diff --git a/src/styles/components/helptour.scss b/src/styles/components/helptour.scss new file mode 100644 index 00000000..9e4669eb --- /dev/null +++ b/src/styles/components/helptour.scss @@ -0,0 +1,150 @@ +/*! + * Spreed WebRTC. + * Copyright (C) 2013-2014 struktur AG + * + * This file is part of Spreed WebRTC. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +$darken-tour-opacity: .4; + +#helptour { +} + +.helptour { + .tourControl { + top: initial; + bottom: 10px; + left: 10px; + max-width: none; + } + .in { + display: block; + } + .popover { + opacity: 0.95; + } + .roomPane { + top: 97px; + left: 10px; + .arrow { + left: 10%; + } + } + .buddyListPane { + left: initial; + right: 280px; + top: 70px; + .arrow { + top: 20%; + } + } + .chatPane { + left: initial; + right: 540px; + top: 70px; + .arrow { + top: 20%; + } + } + .settingsPane { + left: initial; + right: 540px; + top: 200px; + .arrow { + top: 20%; + } + } + .mediaAccessPane { + top: 10px; + left: initial; + right: 10px; + } + .welcomePane { + position: relative; + margin: 0 auto; + top: 150px; + .btn { + margin-top: 10px; + } + } +} + +.arrow+.secondArrow { + border-width: 11px; + &:after { + border-width: 10px; + content: ""; + } +} +.arrow+.secondArrow, +.arrow+.secondArrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: rgba(0, 0, 0, 0); + border-style: solid; +} +.secondArrow.right { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); + &:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #FFF; + } +} +.helptourShowRoomPane { + #bar, + #buddylist { + opacity: $darken-tour-opacity; + } + .overlaybar { + background: $componentfg2; + } +} +.helptourShowSettingsPane { + #roombar { + opacity: $darken-tour-opacity; + } +} +.helptourShowChatPane { + #roombar, + #buddylist { + opacity: $darken-tour-opacity; + } +} +.helptourShowBuddyListPane { + #bar, + #roombar { + opacity: $darken-tour-opacity; + } +} +.helptourShowMediaAccessPane, +.helptourShowWelcomePane { + #bar, + #roombar, + #buddylist { + opacity: $darken-tour-opacity; + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss index a260a449..6c8a0979 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -52,5 +52,6 @@ @import "components/contactsmanager"; @import "components/presentation"; @import "components/youtubevideo"; +@import "components/helptour"; @import "shame"; diff --git a/static/js/directives/directives.js b/static/js/directives/directives.js index fea45f1b..12988ac4 100644 --- a/static/js/directives/directives.js +++ b/static/js/directives/directives.js @@ -43,7 +43,8 @@ define([ 'directives/odfcanvas', 'directives/presentation', 'directives/youtubevideo', - 'directives/bfi'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPictureCapture, buddyPictureUpload, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, odfcanvas, presentation, youtubevideo, bfi) { + 'directives/bfi', + 'directives/helptour'], function(_, onEnter, onEscape, statusMessage, buddyList, buddyPictureCapture, buddyPictureUpload, settings, chat, audioVideo, usability, audioLevel, fileInfo, screenshare, roomBar, socialShare, page, contactRequest, defaultDialog, pdfcanvas, odfcanvas, presentation, youtubevideo, bfi, helptour) { var directives = { onEnter: onEnter, @@ -68,7 +69,8 @@ define([ odfcanvas: odfcanvas, presentation: presentation, youtubevideo: youtubevideo, - bfi: bfi + bfi: bfi, + helptour: helptour }; var initialize = function(angModule) { diff --git a/static/js/directives/helptour.js b/static/js/directives/helptour.js new file mode 100644 index 00000000..d1a2095b --- /dev/null +++ b/static/js/directives/helptour.js @@ -0,0 +1,288 @@ +/* + * Spreed WebRTC. + * Copyright (C) 2013-2014 struktur AG + * + * This file is part of Spreed WebRTC. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +define(['jquery', 'angular', 'text!partials/helpoverlay.html', 'text!partials/helptour.html'], function($, angular, template, templatehelptour) { + + //helptour + return [function() { + + var controller = ['$scope', '$timeout', '$modal', '$rootScope', 'mediaStream', function($scope, $timeout, $modal, $rootScope, mediaStream) { + var isToggled = false; + var displayTime = 12000; + var shown = localStorage.getItem('mediastream-helptour'); + var timeoutAutoStepsIn = []; + var timeoutAutoStepsOut = []; + var menus = {}; + var backupLayout = null; + var tourLayout = { + buddylist: true, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: false + }; + var tourLayoutShowRoomPane = { + buddylist: true, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: true, + screenshare: false, + settings: false + }; + var tourLayoutShowBuddyListPane = { + buddylist: true, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: false + }; + var tourLayoutShowChatPane = { + buddylist: true, + buddylistAutoHide: false, + chat: true, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: false + }; + var tourLayoutShowSettingsPane = { + buddylist: false, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: true + }; + var tourLayoutShowMediaAccessPane = { + buddylist: true, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: false + }; + var tourLayoutShowWelcomePane = { + buddylist: true, + buddylistAutoHide: false, + chat: false, + chatMaximized: false, + main: null, + presentation: false, + roombar: false, + screenshare: false, + settings: false + }; + menus.triggerMediaAccess = function() { + if (isToggled) { + mediaStream.webrtc.testMediaAccess(function() {}); + } else { + mediaStream.webrtc.stop(); + } + }; + menus.initRoomLayout = function() { + $scope.layout = angular.extend($scope.layout, tourLayout); + }; + menus.toggleRoom = function() { + $scope.layout = angular.extend($scope.layout, tourLayoutShowRoomPane); + }; + menus.toggleChat = function() { + $scope.layout = angular.extend($scope.layout, tourLayoutShowChatPane); + }; + menus.toggleSettings = function() { + $scope.layout = angular.extend($scope.layout, tourLayoutShowSettingsPane); + }; + menus.toggleCSS = function(css) { + $('body').toggleClass(css); + }; + var toggleTargetMenu = function() { + var menu = $($scope.steps[$scope.currentIndex]).data('menu'); + var css = $($scope.steps[$scope.currentIndex]).data('css'); + if (menu) { + isToggled = !isToggled; + if(!isToggled) { + menus.initRoomLayout(); + } else { + menus[menu](); + } + } + if (css) { + menus.toggleCSS(css); + } + }; + var outStep = function() { + toggleTargetMenu(); + $($scope.steps[$scope.currentIndex]).removeClass('in'); + }; + var inStep = function(i) { + $scope.currentIndex = i; + toggleTargetMenu(); + $($scope.steps[$scope.currentIndex]).addClass('in'); + }; + var autoStep = function(i, elem, startIndex) { + var inTime = startIndex ? displayTime * (i - startIndex) : displayTime * i; + var outTime = startIndex ? displayTime * ((i + 1) - startIndex) : displayTime * (i + 1); + timeoutAutoStepsIn[i] = $timeout(function() { + inStep(i); + }, inTime, true); + timeoutAutoStepsOut[i] = $timeout(function() { + if ($scope.steps.length === i + 1) { + $scope.togglePause(); + } else { + outStep(); + } + }, outTime, true); + }; + var autoTour = function(startIndex) { + if (!angular.isNumber(startIndex)) { + startIndex = 0; + // start again from the beginning and ensure window is hidden + } else if ($scope.currentIndex > $scope.steps.length - 2) { + outStep(); + startIndex = 0; + } else { + outStep(); + } + $scope.steps.each(function(i, x) { + if (i >= startIndex) { + autoStep(i, x, startIndex); + } + }); + }; + var manualTour = function() { + timeoutAutoStepsIn.forEach(function(promise) { + $timeout.cancel(promise); + }); + timeoutAutoStepsOut.forEach(function(promise) { + $timeout.cancel(promise); + }); + }; + var initTour = function() { + backupLayout = angular.extend({}, $scope.layout); + menus.initRoomLayout(); + autoTour(); + }; + var reset = function() { + outStep(); + $scope.tourPaused = false; + $scope.currentIndex = null; + $scope.layout = angular.extend($scope.layout, backupLayout); + }; + var introTour = function() { + var controller = ['$scope', '$modalInstance', function(scope, $modalInstance) { + scope.goTour = function() { + $modalInstance.dismiss(); + initTour(); + }; + }]; + $modal.open({ + template: templatehelptour, + controller: controller, + size: 'sm' + }); + }; + $scope.tourPaused = false; + $scope.currentIndex = null; + $scope.stepBeginning = function() { + if (!$scope.tourPaused) { + $scope.togglePause(); + } + outStep(); + inStep(0); + }; + $scope.stepBackward = function() { + if (!$scope.tourPaused) { + $scope.togglePause(); + } + outStep(); + inStep($scope.currentIndex - 1); + }; + $scope.stepForward = function() { + if (!$scope.tourPaused) { + $scope.togglePause(); + } + outStep(); + inStep($scope.currentIndex + 1); + }; + $scope.stepEnd = function() { + if (!$scope.tourPaused) { + $scope.togglePause(); + } + outStep(); + inStep($scope.steps.length - 1); + }; + $scope.togglePause = function() { + $scope.tourPaused = !$scope.tourPaused; + if ($scope.tourPaused) { + manualTour(); + } else { + autoTour($scope.currentIndex + 1); + } + }; + $scope.exitTour = function() { + if (!$scope.tourPaused) { + manualTour(); + } + reset(); + }; + $scope.$on('showHelpTour', function() { + introTour(); + }); + $rootScope.$on("room", function(event, room) { + if (!shown) { + introTour(); + localStorage.setItem('mediastream-helptour', 1); + } + }); + }]; + + var link = function($scope, $elem, $attrs) { + $scope.steps = $elem.find('.tourSteps .popover'); + }; + + return { + restrict: 'E', + scope: true, + replace: true, + template: template, + controller: controller, + link: link + }; + }]; +}); diff --git a/static/js/directives/settings.js b/static/js/directives/settings.js index b8dd5173..30cd16e0 100644 --- a/static/js/directives/settings.js +++ b/static/js/directives/settings.js @@ -22,7 +22,7 @@ define(['jquery', 'underscore', 'text!partials/settings.html'], function($, _, t return ["$compile", "mediaStream", function($compile, mediaStream) { - var controller = ['$scope', 'desktopNotify', 'mediaSources', 'safeApply', 'availableLanguages', 'translation', 'localStorage', 'userSettingsData', function($scope, desktopNotify, mediaSources, safeApply, availableLanguages, translation, localStorage, userSettingsData) { + var controller = ['$scope', 'desktopNotify', 'mediaSources', 'safeApply', 'availableLanguages', 'translation', 'localStorage', 'userSettingsData', '$rootScope', function($scope, desktopNotify, mediaSources, safeApply, availableLanguages, translation, localStorage, userSettingsData, $rootScope) { $scope.layout.settings = false; $scope.showAdvancedSettings = true; @@ -51,6 +51,10 @@ define(['jquery', 'underscore', 'text!partials/settings.html'], function($, _, t } }); + $scope.takeTour = function() { + $rootScope.$broadcast('showHelpTour'); + }; + $scope.saveSettings = function() { var form = $scope.settingsform; if (form.$valid && form.$dirty) { diff --git a/static/partials/helpoverlay.html b/static/partials/helpoverlay.html new file mode 100644 index 00000000..436ec2a7 --- /dev/null +++ b/static/partials/helpoverlay.html @@ -0,0 +1,58 @@ +
+
+
+
+

{{_('A room')}}

+
{{_('Place to meet people(buddy). Change rooms here and easily send friends a room link with the social media links provided. Not all room names are available and certain characters are automatically removed.')}}
+
+
+
+

{{_('Buddy List')}}

+
{{_('List of all buddys in the same room and saved contacts.')}}
+
+
+
+

{{_('A buddy')}}

+
{{_('An individual in the same room. If someone is in the same room, a buddy card will be shown. Simply hover over the card to callout chat/call actions. If a star appears next to a buddy comment, the buddy is logged in. When the star is filled the buddy is a contact. Click the star to add a buddy as a contact or remove a contact.')}}
+
+
+
+

{{_('Chat sessions')}}

+
{{_('Group and individual chat sessions. Chat with everyone in a room or chat individually. In group chats you are able to share files and your current location. In individual chats you are also able to start a video call.')}}
+
+
+
+

{{_('Settings')}}

+
{{_('Choose a name, upload a picture, signin, and customize you experience.')}}
+
+
+
+
+

{{_('Media permission')}}

+
{{_('Everytime a call is made or recieved a permission window from the browser will show up like this. To make a call you must accept, otherwise the browser cannot use your wecam and video to make a call. This is a security and privacy precaution.')}}
+
+
+

{{_('Welcome')}}

+
+ {{_('Welcome to spreed-webrtc! Start calling and chatting now.')}} + + +
+
+
+
+

{{_('Tour Control')}} / {{_('Window %d of %d', currentIndex + 1, steps.length)}}

+
+
+ + +
+
+ + + + +
+
+
+
diff --git a/static/partials/helptour.html b/static/partials/helptour.html new file mode 100644 index 00000000..38adf7da --- /dev/null +++ b/static/partials/helptour.html @@ -0,0 +1,15 @@ +
+ + + +
diff --git a/static/partials/settings.html b/static/partials/settings.html index ba1a3493..b673294e 100644 --- a/static/partials/settings.html +++ b/static/partials/settings.html @@ -270,6 +270,15 @@ {{_('Show advanced settings')}}{{_('Hide advanced settings')}} + + {{_('Help')}} +
+ +
+ +
+
+