diff --git a/src/styles/components/_presentation.scss b/src/styles/components/_presentation.scss index 45a9f591..f40b2158 100644 --- a/src/styles/components/_presentation.scss +++ b/src/styles/components/_presentation.scss @@ -76,6 +76,7 @@ canvas { position: relative; display: block; + margin: 0 auto; } } @@ -86,7 +87,11 @@ text-align: center; } -.presentation .overlaybar a.btn { +.presentation .overlaybar .overlaybar-content { + max-width: 100%; +} + +.presentation .overlaybar a.overlaybar-button { position: absolute; font-size: 20px; padding: 4px 6px; @@ -100,13 +105,69 @@ .presentation .overlaybar a.btn-next { right: 0px; -} - -.presentation .overlaybar span { - line-height: 30px; + left: auto; } .pageinfo input { display: inline; width: 70px; } + +.presentation .thumbnail { + text-shadow: none; + color: #333; + margin-top: 20px; + width: 160px; + display: inline-block; + margin-left: 20px; + cursor: pointer; + position: relative; +} + +.presentation .thumbnail:first-child { + margin-left: 0; +} + +.presentation .thumbnail .caption { + padding-bottom: 0; + text-overflow: ellipsis; + overflow: hidden; +} + +.presentation .thumbnail .caption .size { + font-size: 10px; +} + +.presentation .thumbnail .active { + font-size: 10em; + color: #84b819; + opacity: 0.7; + position: absolute; + left: 0; + right: 0; + text-align: center; +} + +.presentation .thumbnail .delete { + position: absolute; + top: 1px; + right: 1px; + display: none; +} + +.presentation .thumbnail:hover .delete { + display: block; +} + +.presentation .thumbnail .filetype { + font-size: 5em; +} + +.presentations { + height: 156px; + overflow-x: auto; + overflow-y: hidden; + white-space: nowrap; + margin-right: 10px; + margin-left: -25px; +} diff --git a/static/js/directives/presentation.js b/static/js/directives/presentation.js index 2c401f54..4f5e101e 100644 --- a/static/js/directives/presentation.js +++ b/static/js/directives/presentation.js @@ -20,7 +20,7 @@ */ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, _, template) { - return ["$window", "mediaStream", "fileUpload", "fileDownload", "alertify", "translation", "randomGen", function($window, mediaStream, fileUpload, fileDownload, alertify, translation, randomGen) { + return ["$window", "mediaStream", "fileUpload", "fileDownload", "alertify", "translation", "randomGen", "fileData", function($window, mediaStream, fileUpload, fileDownload, alertify, translation, randomGen, fileData) { var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) { @@ -39,10 +39,40 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, $scope.downloading = false; $scope.downloadSize = 0; $scope.downloadProgress = 0; + $scope.sharedFilesCache = {}; + $scope.visibleSharedFiles = []; - $scope.resetProperties = function() { + var addVisibleSharedFile = function(file) { + if (file.writer) { + // only show files the user has uploaded + return; + } else if ($scope.sharedFilesCache[file.info.id]) { + // already added + return; + } + $scope.visibleSharedFiles.push({ + "id": file.info.id, + "name": file.info.name, + "size": file.info.size, + "file": file, + "sortkey": (file.info.name || "").toLowerCase() + }); + }; + + var removeVisibleSharedFile = function(fileInfo) { + var i; + for (i=0; i<$scope.visibleSharedFiles.length; i++) { + var file = $scope.visibleSharedFiles[i]; + if (file.id === fileInfo.id) { + $scope.visibleSharedFiles.splice(i, 1); + break; + } + } + }; + + $scope.resetProperties = function(keepControlsBar) { $scope.isPresenter = false; - $scope.hideControlsBar = true; + $scope.hideControlsBar = !keepControlsBar; $scope.currentFileInfo = null; $scope.currentPage = null; $scope.receivedPage = null; @@ -85,15 +115,31 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, finishDownloadPresentation(); }); - downloadScope.$on("writeComplete", function(event, url, fileInfo) { - event.stopPropagation(); - if (url.indexOf("blob:") === 0) { + $scope.openFileInfo = function(fileInfo) { + var url = fileInfo.url; + if (url && url.indexOf("blob:") === 0) { $scope.$emit("openPdf", url); } else { - fileInfo.file.file(function(fp) { - $scope.$emit("openPdf", fp); - }); + var file = fileInfo.file; + if (file.hasOwnProperty("writer")) { + $scope.$emit("openPdf", file); + } else { + file.file(function(fp) { + $scope.$emit("openPdf", fp); + }); + } } + }; + + downloadScope.$on("writeComplete", function(event, url, fileInfo) { + event.stopPropagation(); + // need to store for internal file it and received token + // to allow cleanup and prevent duplicate download + fileInfo.url = url; + $scope.sharedFilesCache[fileInfo.id] = fileInfo; + $scope.sharedFilesCache[fileInfo.info.id] = fileInfo; + addVisibleSharedFile(fileInfo); + $scope.openFileInfo(fileInfo); }); var finishDownloadPresentation = function() { @@ -107,14 +153,23 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, var downloadPresentation = function(fileInfo, from) { finishDownloadPresentation(); - var token = fileInfo.id; $scope.presentationLoaded = false; $scope.pendingPageRequest = null; + + var token = fileInfo.id; + var existing = $scope.sharedFilesCache[token]; + if (existing) { + console.log("Found existing file", existing); + $scope.openFileInfo(existing); + return; + } + downloadProgressBar.style.width = '0%'; $scope.downloadProgress = 0; $scope.downloadSize = fileInfo.size; $scope.downloading = true; downloadScope.info = fileInfo; + downloadScope.handler = mediaStream.tokens.on(token, function(event, currenttoken, to, data, type, to2, from, xfer) { //console.log("Presentation token request", currenttoken, data, type); fileDownload.handleRequest($scope, xfer, data); @@ -123,21 +178,14 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, fileDownload.startDownload(downloadScope, from, token); }; - var uploadScope = $scope.$new(); - - var finishUploadPresentation = function() { - if (uploadScope.info) { - uploadScope.$emit("cancelUpload"); - mediaStream.tokens.off(uploadScope.info.id, uploadScope.handler); - uploadScope.info = null; - uploadScope.handler = null; - } - }; - var uploadPresentation = function(fileInfo) { - finishUploadPresentation(); - var token = fileInfo.id; + if ($scope.sharedFilesCache.hasOwnProperty(token)) { + console.log("Already have an upload token for that presentation."); + return; + } + + var uploadScope = $scope.$new(); uploadScope.info = fileInfo; var session = fileUpload.startUpload(uploadScope, token); // This binds the token to transfer and ui. @@ -159,7 +207,15 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, console.log("Received presentation file request", data); $scope.$apply(function(scope) { scope.resetProperties(); - downloadPresentation(data.FileInfo, from); + if (data.FileInfo) { + downloadPresentation(data.FileInfo, from); + } else { + // close currently visible PDF + finishDownloadPresentation(); + $scope.$emit("closePdf"); + $scope.resetProperties(); + // TODO(fancycode): also cleanup downloaded file + } }); break; @@ -287,8 +343,9 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, alertify.dialog.alert(errorMessage); }); - $scope.startPresentingFile = function(file, fileInfo) { - console.log("Advertising file", file, fileInfo); + $scope.startPresentingFile = function(file) { + console.log("Advertising file", file); + var fileInfo = file.info; // TODO(fancycode): other peers should either request the file or subscribe rendered images (e.g. for mobile app), for now we send the whole file mediaStream.webrtc.callForEachCall(function(peercall) { mediaStreamSendPresentation(peercall, currentToken, { @@ -301,6 +358,8 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, $scope.currentFileInfo = fileInfo; $scope.receivedPage = null; $scope.$emit("openPdf", file); + addVisibleSharedFile(file); + $scope.sharedFilesCache[fileInfo.id] = file; }; var filesSelected = function(files) { @@ -310,15 +369,15 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, } _.each(files, function(f) { - var info = $.extend({ - id: f.id - }, f.info); - if (info.type !== "application/pdf") { - console.log("Not sharing file", f, info); + if (!f.info.hasOwnProperty("id")) { + f.info.id = f.id; + } + if (f.info.type !== "application/pdf") { + console.log("Not sharing file", f); alertify.dialog.alert(translation._("Only PDF documents can be shared at this time.")); return; } - $scope.startPresentingFile(f, info); + $scope.startPresentingFile(f); }); }; @@ -383,7 +442,6 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, currentToken = null; } $scope.$emit("closePdf"); - finishUploadPresentation(); finishDownloadPresentation(); $scope.resetProperties(); $scope.layout.presentation = false; @@ -392,6 +450,46 @@ define(['jquery', 'underscore', 'text!partials/presentation.html'], function($, mediaStream.webrtc.e.off("statechange", updater); }; + $scope.selectPresentation = function(fileInfo) { + if ($scope.currentFileInfo && fileInfo.id === $scope.currentFileInfo.id) { + // switch back to first page when clicked on current presentation + $scope.$emit("showPdfPage", 1); + return; + } + console.log("Selected", fileInfo); + $scope.startPresentingFile(fileInfo.file); + }; + + $scope.deletePresentation = function($event, fileInfo) { + $event.preventDefault(); + var token = fileInfo.id; + fileData.purgeFile(token); + delete $scope.sharedFilesCache[token]; + if (fileInfo.info) { + delete $scope.sharedFilesCache[fileInfo.info.id]; + } + removeVisibleSharedFile(fileInfo); + mediaStream.tokens.off(token); + if ($scope.currentFileInfo && fileInfo.id === $scope.currentFileInfo.id) { + mediaStream.webrtc.callForEachCall(function(peercall) { + mediaStreamSendPresentation(peercall, currentToken, { + Type: "FileInfo", + FileInfo: null + }); + }); + $scope.$emit("closePdf"); + $scope.resetProperties($scope.visibleSharedFiles.length > 0); + } + }; + + mediaStream.webrtc.e.on("done", function() { + _.each($scope.sharedFilesCache, function(file, id) { + fileData.purgeFile(id); + }); + $scope.sharedFilesCache = {}; + $scope.visibleSharedFiles = []; + }); + $(document).on("keyup", function(event) { if (!$scope.layout.presentation) { return; diff --git a/static/partials/presentation.html b/static/partials/presentation.html index 8657b5fc..061e1e61 100644 --- a/static/partials/presentation.html +++ b/static/partials/presentation.html @@ -19,10 +19,23 @@
-
- - / {{ maxPageNumber }} - -
+
+
+ +
/ {{ maxPageNumber }}
+ +
+
+
+
+ +
+
+ {{ info.name }}
+ {{ info.size|humanizeFilesize }} +
+
+
+