From 2df5527cc0efe764eac79232a6b73390c3115d00 Mon Sep 17 00:00:00 2001 From: "R. Aidan Campbell" Date: Sat, 3 Sep 2016 12:04:46 -0400 Subject: [PATCH] major code refactorings to lower the confusing branching. This is mostly tested, but bugs may have been introduced. Progress on #32 --- static/js/index.js | 424 ++++++++++++++++++++---------------------- static/js/messages.js | 2 +- 2 files changed, 198 insertions(+), 228 deletions(-) diff --git a/static/js/index.js b/static/js/index.js index 6b4c7e0..4a59148 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -2,9 +2,7 @@ var hosts = {}; // hosts is an associative array of NvHTTP objects, keyed by se var activePolls = {}; // hosts currently being polled. An associated array of polling IDs, keyed by server UID var pairingCert; var myUniqueid; -var api; -var relaunchSourceEvent; -var unpairHostSourceEvent; +var api; // `api` should only be set if we're in a host-specific screen. on the initial screen it should always be null. // Called by the common.js module. function attachListeners() { @@ -15,16 +13,8 @@ function attachListeners() { $('#bitrateSlider').on('input', updateBitrateField); // input occurs every notch you slide $('#bitrateSlider').on('change', saveBitrate); // change occurs once the mouse lets go. $("#remoteAudioEnabledSwitch").on('click', saveRemoteAudio); - $('#hostChosen').on('click', hostChosen); $('#addHostCell').on('click', addHost); - $('#cancelAddHost').on('click', cancelAddHost); - $('#continueAddHost').on('click', continueAddHost); - $('#continueUnpairHost').on('click', unpairHost); - $('#cancelUnpairHost').on('click', cancelUnpairHost); - $('#cancelPairingDialog').on('click', pairingPopupCanceled); - $('#cancelQuitApp').on('click', cancelQuitApp); $('#backIcon').on('click', showHostsAndSettingsMode); - $('#continueQuitApp').on('click', continueQuitApp); $('#quitCurrentApp').on('click', stopGameWithConfirmation); $(window).resize(fullscreenNaclModule); chrome.app.window.current().onMaximized.addListener(fullscreenChromeWindow); @@ -89,10 +79,6 @@ function beginBackgroundPollingOfHost(host) { // The host was already online. Just start polling in the background now. activePolls[host.serverUid] = window.setInterval(function() { - if (api && activePolls[api.serverUid] != null) { - stopBackgroundPollingOfHost(api); - return; - } // every 5 seconds, poll at the address we know it was live at host.pollServer(function () { if (host.online) { @@ -115,10 +101,6 @@ function beginBackgroundPollingOfHost(host) { // Now start background polling activePolls[host.serverUid] = window.setInterval(function() { - if (api && activePolls[api.serverUid] != null) { - stopBackgroundPollingOfHost(api); - return; - } // every 5 seconds, poll at the address we know it was live at host.pollServer(function () { if (host.online) { @@ -195,16 +177,15 @@ function pairTo(nvhttpHost, onSuccess, onFailure) { return; } - var _api = nvhttpHost; - _api.pollServer(function (ret) { - if (!_api.online) { - snackbarLog('Failed to connect to ' + _api.address + '! Are you sure the host is on?'); - console.log(_api.toString()); + nvhttpHost.pollServer(function (ret) { + if (!nvhttpHost.online) { + snackbarLog('Failed to connect to ' + nvhttpHost.address + '! Are you sure the host is on?'); + console.log(nvhttpHost.toString()); onFailure(); return; } - if (_api.paired) { + if (nvhttpHost.paired) { onSuccess(); return; } @@ -213,17 +194,22 @@ function pairTo(nvhttpHost, onSuccess, onFailure) { var pairingDialog = document.querySelector('#pairingDialog'); $('#pairingDialogText').html('Please enter the number ' + randomNumber + ' on the GFE dialog on the computer. This dialog will be dismissed once complete'); pairingDialog.showModal(); - console.log('sending pairing request to ' + _api.address + ' with random number ' + randomNumber); - _api.pair(randomNumber).then(function (paired) { + $('#cancelPairingDialog').off('click'); + $('#cancelPairingDialog').on('click', function () { + pairingDialog.close(); + }); + + console.log('sending pairing request to ' + nvhttpHost.address + ' with random number ' + randomNumber); + nvhttpHost.pair(randomNumber).then(function (paired) { if (!paired) { - if (_api.currentGame != 0) { - $('#pairingDialogText').html('Error: ' + _api.address + ' is in app. Cannot pair until the app is stopped.'); + if (nvhttpHost.currentGame != 0) { + $('#pairingDialogText').html('Error: ' + nvhttpHost.address + ' is in app. Cannot pair until the app is stopped.'); } else { - $('#pairingDialogText').html('Error: failed to pair with ' + _api.address + '. failure reason unknown.'); + $('#pairingDialogText').html('Error: failed to pair with ' + nvhttpHost.address + '. failure reason unknown.'); } console.log('failed API object: '); - console.log(_api.toString()); + console.log(nvhttpHost.toString()); onFailure(); return; } @@ -232,62 +218,66 @@ function pairTo(nvhttpHost, onSuccess, onFailure) { pairingDialog.close(); onSuccess(); }, function (failedPairing) { - snackbarLog('Failed pairing to: ' + _api.address); + snackbarLog('Failed pairing to: ' + nvhttpHost.address); console.log('pairing failed, and returned ' + failedPairing); console.log('failed API object: '); - console.log(_api.toString()); + console.log(nvhttpHost.toString()); onFailure(); }); }); } -function hostChosen(sourceEvent) { +function hostChosen(host) { - // if(sourceEvent && sourceEvent.srcElement) { - // if (sourceEvent.srcElement.innerText == "") { - // console.log('user clicked image. we gotta hack to parse out the host.'); - // var serverUid = sourceEvent.currentTarget.id.substring("hostgrid-".length); - // } else { - // console.log('parsing host from grid element.'); - // var serverUid = sourceEvent.srcElement.id.substring("hostgrid-".length); - // } - // } else - if (sourceEvent && sourceEvent.target && sourceEvent.target.id ) { - console.log('hacking out the host'); - var serverUid = sourceEvent.target.id.substring("hostgrid-".length); - } else if (sourceEvent.target.parentElement && sourceEvent.target.parentElement.id) { - var serverUid = sourceEvent.target.parentElement.id.substring("hostgrid-".length); - } else { - console.log('Failed to find host! This should never happen!'); - console.log(sourceEvent); - } - - api = hosts[serverUid]; - if (!api.online) { + if (!host.online) { return; } - stopBackgroundPollingOfHost(api); - - if (!api.paired) { + stopBackgroundPollingOfHost(host); + api = host; + if (!host.paired) { // Still not paired; go to the pairing flow - pairTo(api, function(){ showApps(api); saveHosts();}, function(){}); + pairTo(host, function() { + showApps(host); + saveHosts(); + }, + function(){ + }); } else { // When we queried again, it was paired, so show apps. - showApps(api); + showApps(host); } } // the `+` was selected on the host grid. // give the user a dialog to input connection details for the PC function addHost() { - document.querySelector('#addHostDialog').showModal(); + var modal = document.querySelector('#addHostDialog'); + modal.showModal(); + + // drop the dialog if they cancel + $('#cancelAddHost').off('click'); + $('#cancelAddHost').on('click', function() { + modal.close(); + }); + + // try to pair if they continue + $('#continueAddHost').off('click'); + $('#continueAddHost').on('click', function () { + var inputHost = $('#dialogInputHost').val(); + var _nvhttpHost = new NvHTTP(inputHost, myUniqueid, inputHost); + + pairTo(_nvhttpHost, function() { + beginBackgroundPollingOfHost(_nvhttpHost); + addHostToGrid(_nvhttpHost); + saveHosts(); + }, function() { + snackbarLog('pairing to ' + inputHost + ' failed!'); + }); + modal.close(); + }); } -// user canceled the dialog for adding a new PC -function cancelAddHost() { - document.querySelector('#addHostDialog').close(); -} // host is an NvHTTP object function addHostToGrid(host, ismDNSDiscovered=false) { @@ -295,8 +285,14 @@ function addHostToGrid(host, ismDNSDiscovered=false) { var cell = $("
", {class: 'mdl-cell mdl-cell--3-col host-cell mdl-button mdl-js-button mdl-js-ripple-effect', id: 'hostgrid-' + host.serverUid, html:host.hostname }); $(cell).prepend($("", {src: "static/res/ic_desktop_windows_white_24px.svg"})); var removalButton = $("
", {class: "remove-host", id: "removeHostButton-" + host.serverUid}); - removalButton.click(confirmUnpairHost); - cell.click(hostChosen); + removalButton.off('click'); + removalButton.click(function () { + unpairClicked(host); + }); + cell.off('click'); + cell.click(function () { + hostChosen(host); + }); $(outerDiv).append(cell); if (!ismDNSDiscovered) { // we don't have the option to unpair from mDNS hosts. So don't show it to the user. @@ -306,57 +302,35 @@ function addHostToGrid(host, ismDNSDiscovered=false) { hosts[host.serverUid] = host; } -function continueAddHost() { - var inputHost = $('#dialogInputHost').val(); - var _nvhttpHost = new NvHTTP(inputHost, myUniqueid, inputHost); - - pairTo(_nvhttpHost, - function() { - beginBackgroundPollingOfHost(_nvhttpHost); - addHostToGrid(_nvhttpHost); - saveHosts(); - document.querySelector('#addHostDialog').close(); - }, - function() { - snackbarLog('pairing to ' + inputHost + ' failed!'); - }); -} - -function confirmUnpairHost(sourceEvent) { - snackbarLog('Need to parse host from event: ' + sourceEvent); +function unpairClicked(host) { var unpairHostDialog = document.querySelector('#unpairHostDialog'); document.getElementById('unpairHostDialogText').innerHTML = - ' Are you sure you want like to unpair from ' + hosts[sourceEvent.target.id.substring("removeHostButton-".length)].hostname + '?'; + ' Are you sure you want like to unpair from ' + host.hostname + '?'; unpairHostDialog.showModal(); - unpairHostSourceEvent = sourceEvent; -} -function cancelUnpairHost() { - var unpairHostDialog = document.querySelector('#unpairHostDialog'); - unpairHostDialog.close(); -} - -// locally remove the hostname/ip from the saved `hosts` array. -// note: this does not make the host forget the pairing to us. -// this means we can re-add the host, and will still be paired. -function unpairHost() { - var sourceEvent = unpairHostSourceEvent; - unpairHostSourceEvent = null; - host = hosts[sourceEvent.target.id.substring("removeHostButton-".length)]; - host.unpair().then(function (onSuccess) { - var unpairHostDialog = document.querySelector('#unpairHostDialog'); + $('#cancelUnpairHost').off('click'); + $('#cancelUnpairHost').on('click', function () { + unpairHostDialog.close(); + }); + + // locally remove the hostname/ip from the saved `hosts` array. + // note: this does not make the host forget the pairing to us. + // this means we can re-add the host, and will still be paired. + $('#continueUnpairHost').off('click'); + $('#continueUnpairHost').on('click', function () { + host.unpair().then(function (onSuccess) { + var unpairHostDialog = document.querySelector('#unpairHostDialog'); + unpairHostDialog.close(); + $('#host-container-' + host.serverUid).remove(); + snackbarLog('Successfully unpaired from host'); + delete hosts[host.serverUid]; // remove the host from the array; + saveHosts(); + }, function (onFailure) { + snackbarLog('Failed to unpair from host!'); + }); unpairHostDialog.close(); - $('#host-container-' + host.serverUid).remove(); - snackbarLog('Successfully unpaired from host'); - delete hosts[host.serverUid]; // remove the host from the array; - saveHosts(); - }, function (onFailure) { - snackbarLog('Failed to unpair from host!'); }); -} -function pairingPopupCanceled() { - document.querySelector('#pairingDialog').close(); } // puts the CSS style for current app on the app that's currently running @@ -379,9 +353,9 @@ function stylizeBoxArt(freshApi, appIdToStylize) { } // show the app list -function showApps() { - if(!api || !api.paired) { // safety checking. shouldn't happen. - console.log('Moved into showApps, but `api` did not initialize properly! Failing.'); +function showApps(host) { + if(!host || !host.paired) { // safety checking. shouldn't happen. + console.log('Moved into showApps, but `host` did not initialize properly! Failing.'); return; } $('#quitCurrentApp').show(); @@ -391,12 +365,12 @@ function showApps() { $('#naclSpinnerMessage').text('Loading apps...'); $('#naclSpinner').css('display', 'inline-block'); - api.getAppList().then(function (appList) { + host.getAppList().then(function (appList) { $('#naclSpinner').hide(); // if game grid is populated, empty it appList.forEach(function (app) { - api.getBoxArt(app.id).then(function (resolvedPromise) { + host.getBoxArt(app.id).then(function (resolvedPromise) { // put the box art into the image holder if ($('#game-' + app.id).length === 0) { // double clicking the button will cause multiple box arts to appear. @@ -404,24 +378,26 @@ function showApps() { // This isn't perfect: there's lots of RTTs before the logic prevents anything var imageBlob = new Blob([resolvedPromise], {type: "image/png"}); $("#game-grid").append($("
", {html:$("", {src: URL.createObjectURL(imageBlob), id: 'game-'+app.id, name: app.title }), class: 'box-art mdl-cell mdl-cell--3-col'}).append($("", {html: app.title, class:"game-title"}))); - $('#game-'+app.id).on('click', startGame); + $('#game-'+app.id).on('click', function () { + startGame(host, app.id); + }); // apply CSS stylization to indicate whether the app is active - stylizeBoxArt(api, app.id); + stylizeBoxArt(host, app.id); } }, function (failedPromise) { console.log('Error! Failed to retrieve box art for app ID: ' + app.id + '. Returned value was: ' + failedPromise) - console.log('failed API object: '); - console.log(api.toString()); + console.log('failed host object: '); + console.log(host.toString()); }); }); }, function (failedAppList) { $('#naclSpinner').hide(); - console.log('Failed to get applist from host: ' + api.address); - console.log('failed API object: '); - console.log(api.toString()); + console.log('Failed to get applist from host: ' + host.address); + console.log('failed host object: '); + console.log(host.toString()); }); showAppsMode(); @@ -438,9 +414,10 @@ function showHostsAndSettingsMode() { $("#main-content").removeClass("fullscreen"); $("#listener").removeClass("fullscreen"); $("body").css('backgroundColor', 'white'); - if(api && !activePolls[api.serverUid]) { + // We're no longer in a host-specific screen. Null host, and add it back to the polling list + if(api) { beginBackgroundPollingOfHost(api); - api = null; + api = null; // and null api } } @@ -454,118 +431,99 @@ function showAppsMode() { $("#main-content").removeClass("fullscreen"); $("#listener").removeClass("fullscreen"); $("body").css('backgroundColor', 'white'); - } // start the given appID. if another app is running, offer to quit it. // if the given app is already running, just resume it. -function startGame(sourceEvent) { - if(!api || !api.paired) { - console.log('attempted to start a game, but `api` did not initialize properly. Failing!'); +function startGame(host, appID) { + if(!host || !host.paired) { + console.log('attempted to start a game, but `host` did not initialize properly. Failing!'); return; } - if(sourceEvent && sourceEvent.target) { - appID = parseInt(sourceEvent.target.id.substring('game-'.length)); // parse the AppID from the ID of the grid icon. - appName = sourceEvent.target.name; - } else { - console.log('Error! failed to parse appID from grid icon! Failing...'); - snackbarLog('An error occurred while parsing the appID from the grid icon.') - return; - } - - var host = api.address; - // refresh the server info, because the user might have quit the game. - api.refreshServerInfo().then(function (ret) { - if(api.currentGame != 0 && api.currentGame != appID) { - api.getAppById(api.currentGame).then(function (currentApp) { - // This event gets saved and passed back to this callback - // after the game is quit - relaunchSourceEvent = sourceEvent; + host.refreshServerInfo().then(function (ret) { + host.getAppById(appID).then(function (appToStart) { - var quitAppDialog = document.querySelector('#quitAppDialog'); - document.getElementById('quitAppDialogText').innerHTML = - currentApp.title + ' is already running. Would you like to quit ' + - currentApp.title + '?'; - quitAppDialog.showModal(); + if(host.currentGame != 0 && host.currentGame != appID) { + host.getAppById(host.currentGame).then(function (currentApp) { + var quitAppDialog = document.querySelector('#quitAppDialog'); + document.getElementById('quitAppDialogText').innerHTML = + currentApp.title + ' is already running. Would you like to quit ' + + currentApp.title + '?'; + quitAppDialog.showModal(); + $('#cancelQuitApp').off('click'); + $('#cancelQuitApp').on('click', function () { + quitAppDialog.close(); + console.log('closing app dialog, and returning'); + }); + $('#continueQuitApp').off('click'); + $('#continueQuitApp').on('click', function () { + console.log('stopping game, and closing app dialog, and returning'); + stopGame(host, function () { + // please oh please don't infinite loop with recursion + startGame(host, appID); + }); + quitAppDialog.close(); + }); + + return; + }, function (failedCurrentApp) { + console.log('ERROR: failed to get the current running app from host!'); + console.log('Returned error was: ' + failedCurrentApp); + console.log('failed host object: '); + console.log(host.toString()); + return; + }); return; - }, function (failedCurrentApp) { - console.log('ERROR: failed to get the current running app from host!'); - console.log('Returned error was: ' + failedCurrentApp); - console.log('failed API object: '); - console.log(api.toString()); + } + + var frameRate = $("#selectFramerate").val(); + var streamWidth = $('#selectResolution option:selected').val().split(':')[0]; + var streamHeight = $('#selectResolution option:selected').val().split(':')[1]; + // we told the user it was in Mbps. We're dirty liars and use Kbps behind their back. + var bitrate = parseInt($("#bitrateSlider").val()) * 1000; + console.log('startRequest:' + host.address + ":" + streamWidth + ":" + streamHeight + ":" + frameRate + ":" + bitrate); + + var rikey = generateRemoteInputKey(); + var rikeyid = generateRemoteInputKeyId(); + + $('#loadingMessage').text('Starting ' + appToStart.title + '...'); + playGameMode(); + + if(host.currentGame == appID) { // if user wants to launch the already-running app, then we resume it. + return host.resumeApp(rikey, rikeyid).then(function (ret) { + sendMessage('startRequest', [host.address, streamWidth, streamHeight, frameRate, + bitrate.toString(), host.serverMajorVersion.toString(), rikey, rikeyid.toString()]); + }, function (failedResumeApp) { + console.log('ERROR: failed to resume the app!'); + console.log('Returned error was: ' + failedResumeApp); + return; + }); + } + + remote_audio_enabled = $("#remoteAudioEnabledSwitch").parent().hasClass('is-checked') ? 1 : 0; + + host.launchApp(appID, + streamWidth + "x" + streamHeight + "x" + frameRate, + 1, // Allow GFE to optimize game settings + rikey, rikeyid, + remote_audio_enabled, // Play audio locally too? + 0x030002 // Surround channel mask << 16 | Surround channel count + ).then(function (ret) { + sendMessage('startRequest', [host.address, streamWidth, streamHeight, frameRate, + bitrate.toString(), host.serverMajorVersion.toString(), rikey, rikeyid.toString()]); + }, function (failedLaunchApp) { + console.log('ERROR: failed to launch app with appID: ' + appID); + console.log('Returned error was: ' + failedLaunchApp); return; }); - return; - } - var frameRate = $("#selectFramerate").val(); - var streamWidth = $('#selectResolution option:selected').val().split(':')[0]; - var streamHeight = $('#selectResolution option:selected').val().split(':')[1]; - // we told the user it was in Mbps. We're dirty liars and use Kbps behind their back. - var bitrate = parseInt($("#bitrateSlider").val()) * 1000; - console.log('startRequest:' + host + ":" + streamWidth + ":" + streamHeight + ":" + frameRate + ":" + bitrate); - - var rikey = generateRemoteInputKey(); - var rikeyid = generateRemoteInputKeyId(); - - $('#loadingMessage').text('Starting ' + appName + '...'); - playGameMode(); - - if(api.currentGame == appID) { // if user wants to launch the already-running app, then we resume it. - return api.resumeApp(rikey, rikeyid).then(function (ret) { - sendMessage('startRequest', [host, streamWidth, streamHeight, frameRate, - bitrate.toString(), api.serverMajorVersion.toString(), rikey, rikeyid.toString()]); - }, function (failedResumeApp) { - console.log('ERROR: failed to resume the app!'); - console.log('Returned error was: ' + failedResumeApp); - return; - }); - } - - remote_audio_enabled = $("#remoteAudioEnabledSwitch").parent().hasClass('is-checked') ? 1 : 0; - - api.launchApp(appID, - streamWidth + "x" + streamHeight + "x" + frameRate, - 1, // Allow GFE to optimize game settings - rikey, rikeyid, - remote_audio_enabled, // Play audio locally too? - 0x030002 // Surround channel mask << 16 | Surround channel count - ).then(function (ret) { - sendMessage('startRequest', [host, streamWidth, streamHeight, frameRate, - bitrate.toString(), api.serverMajorVersion.toString(), rikey, rikeyid.toString()]); - }, function (failedLaunchApp) { - console.log('ERROR: failed to launch app with appID: ' + appID); - console.log('Returned error was: ' + failedLaunchApp); - return; }); }); } -function cancelQuitApp() { - relaunchSourceEvent = null; - document.querySelector('#quitAppDialog').close(); - console.log('closing app dialog, and returning'); -} - -function continueQuitApp(sourceEvent) { - console.log('stopping game, and closing app dialog, and returning'); - stopGame( - function() { - if (relaunchSourceEvent != null) { - // Save and null relaunchSourceEvent just in case startGame() - // wants to set it again. - var event = relaunchSourceEvent; - relaunchSourceEvent = null; - startGame(event); - } - } - ); - document.querySelector('#quitAppDialog').close(); -} - function playGameMode() { console.log("entering play game mode"); $(".mdl-layout__header").hide(); @@ -607,32 +565,44 @@ function stopGameWithConfirmation() { ' Are you sure you would like to quit ' + currentGame.title + '? Unsaved progress will be lost.'; quitAppDialog.showModal(); + $('#cancelQuitApp').off('click'); + $('#cancelQuitApp').on('click', function () { + console.log('closing app dialog, and returning'); + quitAppDialog.close(); + }); + $('#continueQuitApp').off('click'); + $('#continueQuitApp').on('click', function () { + console.log('stopping game, and closing app dialog, and returning'); + stopGame(api); + quitAppDialog.close(); + }); + }); } } -function stopGame(callbackFunction) { - if (!api.paired) { +function stopGame(host, callbackFunction) { + if (!host.paired) { return; } - api.refreshServerInfo().then(function (ret) { - api.getAppById(api.currentGame).then(function (runningApp) { + host.refreshServerInfo().then(function (ret) { + host.getAppById(host.currentGame).then(function (runningApp) { if (!runningApp) { snackbarLog('Nothing was running'); return; } var appName = runningApp.title; snackbarLog('Stopping ' + appName); - api.quitApp().then(function (ret2) { - api.refreshServerInfo().then(function (ret3) { // refresh to show no app is currently running. + host.quitApp().then(function (ret2) { + host.refreshServerInfo().then(function (ret3) { // refresh to show no app is currently running. showAppsMode(); - stylizeBoxArt(api, runningApp.id); + stylizeBoxArt(host, runningApp.id); if (typeof(callbackFunction) === "function") callbackFunction(); }, function (failedRefreshInfo2) { console.log('ERROR: failed to refresh server info!'); console.log('Returned error was: ' + failedRefreshInfo2); - console.log('failed server was: ' + api.toString()); + console.log('failed server was: ' + host.toString()); }); }, function (failedQuitApp) { console.log('ERROR: failed to quit app!'); diff --git a/static/js/messages.js b/static/js/messages.js index 8279a0d..1834c9c 100644 --- a/static/js/messages.js +++ b/static/js/messages.js @@ -29,7 +29,7 @@ function handleMessage(msg) { stylizeBoxArt(api, app.id); // and reapply stylization to indicate what's currently running }); }); - showApps(); + showApps(api); chrome.app.window.current().restore(); });