diff options
author | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 17:53:39 +0000 |
---|---|---|
committer | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-23 17:53:39 +0000 |
commit | b70a2a5e9acb924cc9ac700616f6b25852132fa5 (patch) | |
tree | 074b1a29f9624593c8f2ec3cd6782292f9f74433 /remoting | |
parent | 17c38e072b83cf5d97ba00b5df194e09a949c4c4 (diff) | |
download | chromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.zip chromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.tar.gz chromium_src-b70a2a5e9acb924cc9ac700616f6b25852132fa5.tar.bz2 |
Paired client revocation UI.
This CL implements the HTML UI for displaying and revoking pairings. It was
previously part of https://chromiumcodereview.appspot.com/18225004, but most
of that has now been landed as separate CLs.
BUG=156182
Review URL: https://chromiumcodereview.appspot.com/19824003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213161 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/plugin/host_script_object.h | 4 | ||||
-rw-r--r-- | remoting/host/remoting_me2me_host.cc | 9 | ||||
-rw-r--r-- | remoting/remoting.gyp | 1 | ||||
-rw-r--r-- | remoting/resources/remoting_strings.grd | 27 | ||||
-rw-r--r-- | remoting/webapp/all_js_load.gtestjs | 2 | ||||
-rw-r--r-- | remoting/webapp/appsv2.patch | 4 | ||||
-rw-r--r-- | remoting/webapp/client_screen.js | 20 | ||||
-rw-r--r-- | remoting/webapp/event_handlers.js | 6 | ||||
-rw-r--r-- | remoting/webapp/host_controller.js | 37 | ||||
-rw-r--r-- | remoting/webapp/host_dispatcher.js | 34 | ||||
-rw-r--r-- | remoting/webapp/main.css | 17 | ||||
-rw-r--r-- | remoting/webapp/main.html | 58 | ||||
-rw-r--r-- | remoting/webapp/paired_client_manager.js | 215 | ||||
-rw-r--r-- | remoting/webapp/remoting.js | 25 | ||||
-rw-r--r-- | remoting/webapp/ui_mode.js | 1 |
15 files changed, 431 insertions, 29 deletions
diff --git a/remoting/host/plugin/host_script_object.h b/remoting/host/plugin/host_script_object.h index 9f8a867..a3d5bcd 100644 --- a/remoting/host/plugin/host_script_object.h +++ b/remoting/host/plugin/host_script_object.h @@ -242,10 +242,6 @@ class HostNPScriptObject { // success status. void InvokeBooleanCallback(const ScopedRefNPObject& callback, bool result); - // Callback handler for DaemonController::DeletePairedClients(). - void InvokeDeletePairedClientsCallback(const ScopedRefNPObject& callback, - bool success); - // Callback handler for DaemonController::GetConfig(). void InvokeGetDaemonConfigCallback(const ScopedRefNPObject& callback, scoped_ptr<base::DictionaryValue> config); diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 28e380a7..7b7d3e3 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -481,9 +481,12 @@ void HostProcess::CreateAuthenticatorFactory() { return; } - // TODO(jamiewalch): Create a pairing registry here once all the code - // is committed. - scoped_refptr<remoting::protocol::PairingRegistry> pairing_registry = NULL; + scoped_refptr<protocol::PairingRegistry> pairing_registry = NULL; + scoped_ptr<protocol::PairingRegistry::Delegate> delegate( + CreatePairingRegistryDelegate(context_->file_task_runner())); + if (delegate) { + pairing_registry = new protocol::PairingRegistry(delegate.Pass()); + } scoped_ptr<protocol::AuthenticatorFactory> factory; diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index c649f90..fb52c13 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -2169,6 +2169,7 @@ 'webapp/host_setup_dialog.js', 'webapp/main.html', 'webapp/manifest.json', + 'webapp/paired_client_manager.js', 'webapp/remoting.js', ], }, diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd index 18c3625..3c69d18 100644 --- a/remoting/resources/remoting_strings.grd +++ b/remoting/resources/remoting_strings.grd @@ -624,6 +624,33 @@ <message name="IDR_HOST_STARTED" desc="The message reported to the EventLog by Chromoting Host every time it is started."> Host started for user: <ph name="HOST_USERNAME">%1<ex>host@email.com</ex></ph>. </message> + <message desc="Link to allow a host's paired clients to be viewed or edited." name="IDR_HOME_DAEMON_MANAGE_PAIRINGS"> + View/edit + </message> + <message desc="Link to delete a specific paired client." name="IDR_DELETE_PAIRED_CLIENT"> + Delete + </message> + <message desc="Link to delete all paired clients." name="IDR_DELETE_ALL_PAIRED_CLIENTS"> + Delete all + </message> + <message desc="Message displayed in the paired client manager dialog when all paired clients have been deleted." name="IDR_NO_PAIRED_CLIENTS"> + All paired clients have been deleted. + </message> + <message desc="Message displayed when the current computer has been paired with one or more clients, allowing them to connect without needing a PIN." name="IDR_HOME_DAEMON_PAIRED_MESSAGE"> + This computer is configured to allow one or more clients to connect without entering a PIN. + </message> + <message desc="Message displayed above the list of paired clients, explaining its purpose." name="IDR_PAIRED_CLIENTS_INTRODUCTION"> + The following clients have been paired with this computer and can connect without supplying a PIN. You can revoke this permission at any time, either individually, or for all clients. + </message> + <message desc="Table header for the dates at which clients were paired." name="IDR_PAIRED_CLIENT_DATE"> + Pairing date + </message> + <message desc="Table header for the names of paired clients." name="IDR_PAIRED_CLIENT_NAME"> + Client + </message> + <message desc="Text shown while a background operation is in progress." name="IDR_WORKING"> + Working… + </message> </messages> </release> </grit> diff --git a/remoting/webapp/all_js_load.gtestjs b/remoting/webapp/all_js_load.gtestjs index 7b29e03..61a8aa4 100644 --- a/remoting/webapp/all_js_load.gtestjs +++ b/remoting/webapp/all_js_load.gtestjs @@ -52,7 +52,6 @@ AllJsLoadTest.prototype = { 'oauth2.js', 'paired_client_manager.js', 'plugin_settings.js', - 'xhr_proxy.js', 'remoting.js', 'session_connector.js', 'server_log_entry.js', @@ -66,6 +65,7 @@ AllJsLoadTest.prototype = { 'wcs_sandbox_content.js', 'wcs_sandbox_container.js', 'xhr.js', + 'xhr_proxy.js', ], }; diff --git a/remoting/webapp/appsv2.patch b/remoting/webapp/appsv2.patch index 3305207..a214d0c 100644 --- a/remoting/webapp/appsv2.patch +++ b/remoting/webapp/appsv2.patch @@ -121,7 +121,7 @@ index f89072a..2fadd83 100644 remoting.stats = new remoting.ConnectionStats( document.getElementById('statistics')); remoting.formatIq = new remoting.FormatIq(); -@@ -156,9 +151,6 @@ remoting.initHomeScreenUi = function () { +@@ -11,9 +151,6 @@ remoting.initHomeScreenUi = function() { remoting.hostController = new remoting.HostController(); document.getElementById('share-button').disabled = !isIT2MeSupported_(); remoting.setMode(remoting.AppMode.HOME); @@ -130,7 +130,7 @@ index f89072a..2fadd83 100644 - } remoting.hostSetupDialog = new remoting.HostSetupDialog(remoting.hostController); - // Display the cached host list, then asynchronously update and re-display it. + var dialog = document.getElementById('paired-clients-list'); diff --git a/remoting/webapp/xhr_proxy.js b/remoting/webapp/xhr_proxy.js index 4c45780..653b481 100644 --- a/xhr_proxy.js diff --git a/remoting/webapp/client_screen.js b/remoting/webapp/client_screen.js index 8ecde77..3698186 100644 --- a/remoting/webapp/client_screen.js +++ b/remoting/webapp/client_screen.js @@ -357,8 +357,22 @@ remoting.onConnected = function(clientSession) { }; remoting.HostSettings.save(clientSession.hostId, pairingInfo); }; - // TODO(jamiewalch): Since we can't get a descriptive name for the local - // computer from Javascript, pass the empty string for now. - clientSession.requestPairing('', onPairingComplete); + // Use the platform name as a proxy for the local computer name. + // TODO(jamiewalch): Use a descriptive name for the local computer, for + // example, its Chrome Sync name. + var clientName = ''; + if (navigator.platform.indexOf('Mac') != -1) { + clientName = 'Mac'; + } else if (navigator.platform.indexOf('Win32') != -1) { + clientName = 'Windows'; + } else if (navigator.userAgent.match(/\bCrOS\b/)) { + clientName = 'ChromeOS'; + } else if (navigator.platform.indexOf('Linux') != -1) { + clientName = 'Linux'; + } else { + console.log('Unrecognized client platform. Using navigator.platform.'); + clientName = navigator.platform; + } + clientSession.requestPairing(clientName, onPairingComplete); } }; diff --git a/remoting/webapp/event_handlers.js b/remoting/webapp/event_handlers.js index a481e19..47e6d53 100644 --- a/remoting/webapp/event_handlers.js +++ b/remoting/webapp/event_handlers.js @@ -85,7 +85,11 @@ function onLoad() { { event: 'click', id: 'host-config-done-dismiss', fn: goHome }, { event: 'click', id: 'host-config-error-dismiss', fn: goHome }, { event: 'click', id: 'token-refresh-error-ok', fn: goHome }, - { event: 'click', id: 'token-refresh-error-sign-in', fn: doAuthRedirect } + { event: 'click', id: 'token-refresh-error-sign-in', fn: doAuthRedirect }, + { event: 'click', id: 'open-paired-client-manager-dialog', + fn: remoting.setMode.bind(null, + remoting.AppMode.HOME_MANAGE_PAIRINGS) }, + { event: 'click', id: 'close-paired-client-manager-dialog', fn: goHome } ]; for (var i = 0; i < actions.length; ++i) { diff --git a/remoting/webapp/host_controller.js b/remoting/webapp/host_controller.js index de1a0a9..f728a32 100644 --- a/remoting/webapp/host_controller.js +++ b/remoting/webapp/host_controller.js @@ -337,5 +337,42 @@ remoting.HostController.prototype.getLocalHostId = function(onDone) { }); }; +/** + * Fetch the list of paired clients for this host. + * + * @param {function(Array.<remoting.PairedClient>):void} onDone + * @param {function(remoting.Error):void} onError + * @return {void} + */ +remoting.HostController.prototype.getPairedClients = function(onDone, + onError) { + this.hostDispatcher_.getPairedClients(onDone, onError); +}; + +/** + * Delete a single paired client. + * + * @param {string} client The client id of the pairing to delete. + * @param {function():void} onDone Completion callback. + * @param {function(remoting.Error):void} onError Error callback. + * @return {void} + */ +remoting.HostController.prototype.deletePairedClient = function( + client, onDone, onError) { + this.hostDispatcher_.deletePairedClient(client, onDone, onError); +}; + +/** + * Delete all paired clients. + * + * @param {function():void} onDone Completion callback. + * @param {function(remoting.Error):void} onError Error callback. + * @return {void} + */ +remoting.HostController.prototype.clearPairedClients = function( + onDone, onError) { + this.hostDispatcher_.clearPairedClients(onDone, onError); +}; + /** @type {remoting.HostController} */ remoting.hostController = null; diff --git a/remoting/webapp/host_dispatcher.js b/remoting/webapp/host_dispatcher.js index 7b81c0e..7841eb6 100644 --- a/remoting/webapp/host_dispatcher.js +++ b/remoting/webapp/host_dispatcher.js @@ -382,23 +382,43 @@ remoting.HostDispatcher.prototype.getPairedClients = function(callback, }; /** - * @param {function(boolean):void} onDone + * The pairing API returns a boolean to indicate success or failure, but + * the JS API is defined in terms of onDone and onError callbacks. This + * function converts one to the other. + * + * @param {function():void} onDone Success callback. + * @param {function(remoting.Error):void} onError Error callback. + * @param {boolean} success True if the operation succeeded; false otherwise. + * @private + */ +remoting.HostDispatcher.runCallback_ = function(onDone, onError, success) { + if (success) { + onDone(); + } else { + onError(remoting.Error.UNEXPECTED); + } +}; + +/** + * @param {function():void} onDone * @param {function(remoting.Error):void} onError * @return {void} */ remoting.HostDispatcher.prototype.clearPairedClients = function(onDone, onError) { + var callback = + remoting.HostDispatcher.runCallback_.bind(null, onDone, onError); switch (this.state_) { case remoting.HostDispatcher.State.UNKNOWN: this.pendingRequests_.push( this.clearPairedClients.bind(this, onDone, onError)); break; case remoting.HostDispatcher.State.NATIVE_MESSAGING: - this.nativeMessagingHost_.clearPairedClients(onDone, onError); + this.nativeMessagingHost_.clearPairedClients(callback, onError); break; case remoting.HostDispatcher.State.NPAPI: try { - this.npapiHost_.clearPairedClients(onDone); + this.npapiHost_.clearPairedClients(callback); } catch (err) { onError(remoting.Error.MISSING_PLUGIN); } @@ -408,23 +428,25 @@ remoting.HostDispatcher.prototype.clearPairedClients = /** * @param {string} client - * @param {function(boolean):void} onDone + * @param {function():void} onDone * @param {function(remoting.Error):void} onError * @return {void} */ remoting.HostDispatcher.prototype.deletePairedClient = function(client, onDone, onError) { + var callback = + remoting.HostDispatcher.runCallback_.bind(null, onDone, onError); switch (this.state_) { case remoting.HostDispatcher.State.UNKNOWN: this.pendingRequests_.push( this.deletePairedClient.bind(this, client, onDone, onError)); break; case remoting.HostDispatcher.State.NATIVE_MESSAGING: - this.nativeMessagingHost_.deletePairedClient(client, onDone, onError); + this.nativeMessagingHost_.deletePairedClient(client, callback, onError); break; case remoting.HostDispatcher.State.NPAPI: try { - this.npapiHost_.deletePairedClient(client, onDone); + this.npapiHost_.deletePairedClient(client, callback); } catch (err) { onError(remoting.Error.MISSING_PLUGIN); } diff --git a/remoting/webapp/main.css b/remoting/webapp/main.css index 8faa94a..870796d 100644 --- a/remoting/webapp/main.css +++ b/remoting/webapp/main.css @@ -11,6 +11,11 @@ tfoot, thead, tr, th, td, button { margin: 0; padding: 0; border: 0; + font-weight: inherit; + font-style: inherit; + font-size: 100%; + font-family: inherit; + vertical-align: baseline; } body { @@ -297,6 +302,10 @@ section { margin-left: 5px; } +.button-row span:first-child { + width: 100%; +} + .clickable { cursor: pointer; } @@ -424,6 +433,10 @@ section { padding-__MSG_@@bidi_start_edge__: 2px; } +#paired-clients-list table { + width: 100%; +} + .message { margin-bottom: 24px; } @@ -440,6 +453,10 @@ td { vertical-align: middle; } +thead { + font-weight: bold; +} + .host-online.clickable:hover, .host-online.clickable.child-focused { background-color: #f2f2f2; diff --git a/remoting/webapp/main.html b/remoting/webapp/main.html index 4a97637..c352452 100644 --- a/remoting/webapp/main.html +++ b/remoting/webapp/main.html @@ -206,10 +206,17 @@ found in the LICENSE file. </button> </div> <!-- enabled --> <div data-daemon-state="enabled"> - <span i18n-content="HOME_DAEMON_ACTIVE_MESSAGE"></span> - <a id="change-daemon-pin" - href="#" - i18n-content="HOME_DAEMON_CHANGE_PIN_LINK"></a> + <div> + <span i18n-content="HOME_DAEMON_ACTIVE_MESSAGE"></span> + <a id="change-daemon-pin" + href="#" + i18n-content="HOME_DAEMON_CHANGE_PIN_LINK"></a> + </div> + <div id="paired-client-manager-message" hidden> + <span i18n-content="HOME_DAEMON_PAIRED_MESSAGE"></span> + <a href="#" + id="open-paired-client-manager-dialog" + i18n-content="HOME_DAEMON_MANAGE_PAIRINGS"></a> </div> </div> <!-- daemon-control --> <div id="host-list-empty" hidden> @@ -250,11 +257,11 @@ found in the LICENSE file. </div> <!-- auth-dialog --> <div class="dialog-screen" - data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed" + data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings" hidden></div> <div class="dialog-container" - data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed" + data-ui-mode="home.host home.client home.history home.confirm-host-delete home.host-setup home.token-refresh-failed home.manage-pairings" hidden> <div class="box-spacer"></div> @@ -661,6 +668,45 @@ found in the LICENSE file. </div> </div> <!-- home.confirm-host-delete --> + <div id="paired-client-manager-dialog" + class="kd-modaldialog" + data-ui-mode="home.manage-pairings" + hidden> + <p i18n-content="PAIRED_CLIENTS_INTRODUCTION" + class="message"> + </p> + <div id="paired-clients-list"> + <table> + <thead> + <tr> + <td i18n-content="PAIRED_CLIENT_DATE"></td> + <td i18n-content="PAIRED_CLIENT_NAME"></td> + </tr> + </thead> + <tbody> + </tbody> + </table> + <p id="no-paired-clients"> + <em i18n-content="NO_PAIRED_CLIENTS"></em> + </p> + </div> <!-- paired-clients-list --> + <p id="paired-client-manager-dialog-error" + class="error-state" + hidden> + </p> + <div class="button-row"> + <span id="paired-client-manager-dialog-working" + class="waiting" + i18n-content="WORKING" + hidden></span> + <button id="delete-all-paired-clients" + i18n-content="DELETE_ALL_PAIRED_CLIENTS"> + <button id="close-paired-client-manager-dialog" + i18n-content="CLOSE"> + </button> + </div> + </div> <!-- home.manage-pairings --> + <div class="box-spacer"></div> </div> <!-- dialog-container --> diff --git a/remoting/webapp/paired_client_manager.js b/remoting/webapp/paired_client_manager.js index 636716d..05d6fe9 100644 --- a/remoting/webapp/paired_client_manager.js +++ b/remoting/webapp/paired_client_manager.js @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * @fileoverview + * Dialog for showing the list of clients that are paired with this host. + */ + 'use strict'; /** @suppress {duplicate} */ @@ -24,6 +29,50 @@ remoting.PairedClient = function(pairedClient) { this.clientId = /** @type {string} */ (pairedClient['clientId']); this.clientName = /** @type {string} */ (pairedClient['clientName']); this.createdTime = /** @type {number} */ (pairedClient['createdTime']); + + /** @type {Element} */ + this.tableRow = null; + /** @type {Element} */ + this.deleteButton = null; +}; + +/** + * Create the DOM elements representing this client in the paired client + * manager dialog. + * + * @param {remoting.PairedClientManager} parent The paired client manager + * dialog containing this row. + * @param {Element} tbody The <tbody> element to which to append the row. + */ +remoting.PairedClient.prototype.createDom = function(parent, tbody) { + this.tableRow = document.createElement('tr'); + var td = document.createElement('td'); + td.innerText = new Date(this.createdTime).toLocaleDateString(); + this.tableRow.appendChild(td); + td = document.createElement('td'); + td.innerText = this.clientName; + this.tableRow.appendChild(td); + td = document.createElement('td'); + this.deleteButton = document.createElement('a'); + this.deleteButton.href = '#'; + this.deleteButton.innerText = chrome.i18n.getMessage( + /*i18n-content*/'DELETE_PAIRED_CLIENT'); + this.deleteButton.addEventListener( + 'click', + parent.deletePairedClient.bind(parent, this), + false); + td.appendChild(this.deleteButton); + this.tableRow.appendChild(td); + tbody.appendChild(this.tableRow); +}; + +/** + * Show or hide the "Delete" button for this row. + * + * @param {boolean} show True to show the button; false to hide it. + */ +remoting.PairedClient.prototype.showButton = function(show) { + this.deleteButton.hidden = !show; }; /** @@ -45,8 +94,7 @@ remoting.PairedClient.prototype.isValid = function() { */ remoting.PairedClient.convertToPairedClientArray = function(pairedClients) { if (!(pairedClients instanceof Array)) { - console.error('pairedClients is not an Array: ' + - pairedClients); + console.error('pairedClients is not an Array:', pairedClients); return null; } @@ -62,3 +110,166 @@ remoting.PairedClient.convertToPairedClientArray = function(pairedClients) { } return result; } + +/** + * @param {remoting.HostController} hostController + * @param {HTMLElement} listContainer HTML <div> to contain the list of paired + * clients. + * @param {HTMLElement} message HTML <div> containing the message notifying + * the user that clients are paired and containing the link to open the + * dialog. + * @param {HTMLElement} deleteAllButton HTML <button> inititating the "delete + * all" action. + * @param {HTMLElement} closeButton HTML <button> to close the dialog. + * @param {HTMLElement} noPairedClients HTML <div> containing a message shown + * when all clients have been deleted. + * @param {HTMLElement} workingSpinner HTML element containing a spinner + * graphic shown while a deletion is in progress. + * @param {HTMLElement} errorDiv HTML <div> containing an error message shown + * if a delete operation fails. + * @constructor + */ +remoting.PairedClientManager = function(hostController, listContainer, message, + deleteAllButton, closeButton, + noPairedClients, workingSpinner, + errorDiv) { + /** + * @private + */ + this.hostController_ = hostController; + /** + * @private + */ + this.message_ = message; + /** + * @private + */ + this.deleteAllButton_ = deleteAllButton; + /** + * @private + */ + this.closeButton_ = closeButton; + /** + * @private + */ + this.noPairedClients_ = noPairedClients; + /** + * @private + */ + this.workingSpinner_ = workingSpinner; + /** + * @private + */ + this.errorDiv_ = errorDiv; + /** + * @type {Element} + * @private + */ + this.clientRows_ = listContainer.querySelector('tbody'); + /** + * @type {Array.<remoting.PairedClient>} + */ + this.pairedClients_ = []; + + this.deleteAllButton_.addEventListener('click', + this.deleteAll_.bind(this), + false); +}; + +/** + * Populate the dialog with the list of paired clients and show or hide the + * message as appropriate. + * + * @param {*} pairedClients The list of paired clients as returned by the + * native host component. + * @return {void} Nothing. + */ +remoting.PairedClientManager.prototype.setPairedClients = + function(pairedClients) { + // Reset table. + while (this.clientRows_.lastChild) { + this.clientRows_.removeChild(this.clientRows_.lastChild); + } + + this.pairedClients_ = + remoting.PairedClient.convertToPairedClientArray(pairedClients); + for (var i = 0; i < this.pairedClients_.length; ++i) { + var client = this.pairedClients_[i]; + client.createDom(this, this.clientRows_); + } + + // Show or hide the "this computer has paired clients" message. + this.setWorking_(false) +}; + +/** + * Enter or leave "working" mode. This indicates to the user that a delete + * operation is in progress. All dialog UI is disabled until it completes. + * + * @param {boolean} working True to enter "working" mode; false to leave it. + * @private + */ +remoting.PairedClientManager.prototype.setWorking_ = function(working) { + var hasPairedClients = (this.pairedClients_.length != 0); + for (var i = 0; i < this.pairedClients_.length; ++i) { + this.pairedClients_[i].showButton(!working); + } + this.closeButton_.disabled = working; + this.workingSpinner_.hidden = !working; + this.errorDiv_.hidden = true; + this.message_.hidden = !hasPairedClients; + this.deleteAllButton_.disabled = working || !hasPairedClients; + this.noPairedClients_.hidden = hasPairedClients; +}; + +/** + * Error callback for delete operations. + * + * @param {remoting.Error} error The error message. + * @private + */ +remoting.PairedClientManager.prototype.onError_ = function(error) { + this.setWorking_(false); + l10n.localizeElementFromTag(this.errorDiv_, error); + this.errorDiv_.hidden = false; +}; + +/** + * Delete a single paired client. + * + * @param {remoting.PairedClient} client The pairing to delete. + */ +remoting.PairedClientManager.prototype.deletePairedClient = function(client) { + this.setWorking_(true); + this.hostController_.deletePairedClient(client.clientId, + this.setWorking_.bind(this, false), + this.onError_.bind(this)); + this.clientRows_.removeChild(client.tableRow); + for (var i = 0; i < this.pairedClients_.length; ++i) { + if (this.pairedClients_[i] == client) { + this.pairedClients_.splice(i, 1); + break; + } + } +}; + +/** + * Delete all paired clients. + * + * @private + */ +remoting.PairedClientManager.prototype.deleteAll_ = function() { + this.setWorking_(true); + this.hostController_.clearPairedClients( + this.setWorking_.bind(this, false), + this.onError_.bind(this)); + + while (this.clientRows_.lastChild) { + this.clientRows_.removeChild(this.clientRows_.lastChild); + } + this.pairedClients_ = []; +}; + + +/** @type {remoting.PairedClientManager} */ +remoting.pairedClientManager = null; diff --git a/remoting/webapp/remoting.js b/remoting/webapp/remoting.js index 8d97f73..50fe034 100644 --- a/remoting/webapp/remoting.js +++ b/remoting/webapp/remoting.js @@ -156,6 +156,17 @@ remoting.initHomeScreenUi = function() { } remoting.hostSetupDialog = new remoting.HostSetupDialog(remoting.hostController); + var dialog = document.getElementById('paired-clients-list'); + var message = document.getElementById('paired-client-manager-message'); + var deleteAll = document.getElementById('delete-all-paired-clients'); + var close = document.getElementById('close-paired-client-manager-dialog'); + var working = document.getElementById('paired-client-manager-dialog-working'); + var error = document.getElementById('paired-client-manager-dialog-error'); + var noPairedClients = document.getElementById('no-paired-clients'); + remoting.pairedClientManager = + new remoting.PairedClientManager(remoting.hostController, dialog, message, + deleteAll, close, noPairedClients, + working, error); // Display the cached host list, then asynchronously update and re-display it. remoting.updateLocalHostState(); remoting.hostList.refresh(remoting.updateLocalHostState); @@ -163,7 +174,7 @@ remoting.initHomeScreenUi = function() { }; /** - * Fetches local host state and updates host list accordingly. + * Fetches local host state and updates the DOM accordingly. */ remoting.updateLocalHostState = function() { /** @@ -182,7 +193,19 @@ remoting.updateLocalHostState = function() { remoting.hostList.display(); }; + /** + * @param {remoting.Error} error + */ + var onError = function(error) { + console.error('Failed to get pairing status: ' + error); + remoting.pairedClientManager.setPairedClients([]); + } + remoting.hostController.getLocalHostId(onHostId); + remoting.hostController.getPairedClients( + remoting.pairedClientManager.setPairedClients.bind( + remoting.pairedClientManager), + onError); }; /** diff --git a/remoting/webapp/ui_mode.js b/remoting/webapp/ui_mode.js index 4587d83..966207f 100644 --- a/remoting/webapp/ui_mode.js +++ b/remoting/webapp/ui_mode.js @@ -47,6 +47,7 @@ remoting.AppMode = { HOST_SETUP_PROCESSING: 'home.host-setup.processing', HOST_SETUP_DONE: 'home.host-setup.done', HOST_SETUP_ERROR: 'home.host-setup.error', + HOME_MANAGE_PAIRINGS: 'home.manage-pairings', IN_SESSION: 'in-session' }; |