From 3132cca482cb13c6f7d56b83fb88723779367e1b Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 14 Jul 2016 14:49:03 -0700 Subject: [PATCH 01/20] prototype --- interface/resources/qml/AddressBarDialog.qml | 162 +++++++++++++++++- .../qml/controls-uit/HorizontalSpacer.qml | 19 ++ interface/resources/qml/hifi/Card.qml | 64 +++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml create mode 100644 interface/resources/qml/hifi/Card.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index dc060b70e1..8ac199b326 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -11,8 +11,13 @@ import Hifi 1.0 import QtQuick 2.4 import "controls" +import "controls-uit" as HifiControls import "styles" import "windows" +import "hifi" + +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 Window { id: root @@ -43,12 +48,52 @@ Window { // Explicitly center in order to avoid warnings at shutdown anchors.centerIn = parent; } - + + function goCard(card) { + addressLine.text = card.userStory.name; + toggleOrGo(); + } + AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height + Column { + width: backgroundImage.width; + anchors { + left: backgroundImage.left; + leftMargin: backgroundImage.height + 2 * hifi.layout.spacing + bottom: backgroundImage.top; + bottomMargin: 2 * hifi.layout.spacing; + } + Text { + text: "Suggestions" + } + Row { + Card { + id: s0; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s1; + width: 200; + height: 200; + goFunction: goCard + } + HifiControls.HorizontalSpacer { } + Card { + id: s2; + width: 200; + height: 200; + goFunction: goCard + } + } + } + Image { id: backgroundImage source: "../images/address-bar.svg" @@ -135,9 +180,124 @@ Window { } } + function getRequest(url, cb) { // cb(error, responseOfCorrectContentType) of url. General for 'get' text/html/json, but without redirects. + // TODO: make available to other .qml. + var request = new XMLHttpRequest(); + // QT bug: apparently doesn't handle onload. Workaround using readyState. + request.onreadystatechange = function () { + var READY_STATE_DONE = 4; + var HTTP_OK = 200; + if (request.readyState >= READY_STATE_DONE) { + var error = (request.status !== HTTP_OK) && request.status.toString() + ':' + request.statusText, + response = !error && request.responseText, + contentType = !error && request.getResponseHeader('content-type'); + if (!error && contentType.indexOf('application/json') === 0) { + try { + response = JSON.parse(response); + } catch (e) { + error = e; + } + } + cb(error, response); + } + }; + request.open("GET", url, true); + request.send(); + } + // call iterator(element, icb) once for each element of array, and then cb(error) when icb(error) has been called by each iterator. + // short-circuits if error. Note that iterator MUST be an asynchronous function. (Use setTimeout if necessary.) + function asyncEach(array, iterator, cb) { + var count = array.length; + function icb(error) { + if (!--count || error) { + count = -1; // don't cb multiple times (e.g., if error) + cb(error); + } + } + if (!count) { + return cb(); + } + array.forEach(function (element) { + iterator(element, icb); + }); + } + + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) + asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { + var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; + getRequest(url, function (error, json) { + var previews = !error && json.data.place.previews; + if (previews) { + if (!domainInfo.thumbnail) { // just grab tghe first one + domainInfo.thumbnail = previews.thumbnail; + } + if (!domainInfo.lobby) { + domainInfo.lobby = previews.lobby; + } + } + icb(error); + }); + }, cb); + } + + function getDomains(options, cb) { // cb(error, arrayOfData) + if (!options.page) { + options.page = 1; + } + // FIXME: really want places I'm allowed in, not just open ones + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + getRequest(url, function (error, json) { + if (!error && (json.status !== 'success')) { + error = new Error("Bad response: " + JSON.stringify(json)); + } + if (error) { + error.message += ' for ' + url; + return cb(error); + } + var domains = json.data.domains; + asyncEach(domains, addPictureToDomain, function (error) { + if (json.current_page < json.total_pages) { + options.page++; + return getDomains(options, function (error, others) { + cb(error, domains.concat(others)); + }); + } + cb(null, domains); + }); + }); + } + + function fillDestinations () { + function fill1(target, data) { + if (!data) { + return; + } + console.log('suggestion:', JSON.stringify(data)); + target.userStory = data; + if (data.lobby) { + console.log('target', target); + console.log('target.image', target.image); + console.log('url', data.lobby); + target.image.source = data.lobby; + } + target.placeText = data.name; + target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); + } + getDomains({minUsers: 0, maxUsers: 20}, function (e, d) { + var withLobby = !e && d.filter(function (d1) { return d1.lobby; }); + console.log(e, d.length, withLobby.length); + withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); + console.log(withLobby.map(function (d) { return d.online_users; })); + fill1(s0, withLobby[0]); + fill1(s1, withLobby[1]); + fill1(s2, withLobby[2]); + }); + } + onVisibleChanged: { if (visible) { addressLine.forceActiveFocus() + fillDestinations(); } else { addressLine.text = "" } diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml new file mode 100644 index 0000000000..a9fac2bcf7 --- /dev/null +++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml @@ -0,0 +1,19 @@ +// +// HorizontalSpacer.qml +// +// Created by Howard Stearns on 12 July 2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 + +import "../styles-uit" + +Item { + HifiConstants { id: hifi } + height: 1 // Must be non-zero + width: hifi.dimensions.controlInterlineHeight +} diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml new file mode 100644 index 0000000000..342c469303 --- /dev/null +++ b/interface/resources/qml/hifi/Card.qml @@ -0,0 +1,64 @@ +// +// Card.qml +// qml/hifi +// +// Displays a clickable card representing a user story or destination. +// +// Created by Howard Stearns on 7/13/2016 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 +import QtQuick 2.5 +import "../styles-uit" + +Rectangle { + property var goFunction: null; + property var userStory: null; + property alias image: lobby; + property alias placeText: place.text; + property alias usersText: users.text; + HifiConstants { id: hifi } + Image { + id: lobby; + width: parent.width; + height: parent.height; + fillMode: Image.PreserveAspectCrop; + // source gets filled in later + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + onStatusChanged: { + if (status == Image.Error) { + console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); + // FIXME: let's get our own + source = "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif" + } + } + } + RalewaySemiBold { + id: place; + color: hifi.colors.lightGrayText; + anchors { + leftMargin: 2 * hifi.layout.spacing; + topMargin: 2 * hifi.layout.spacing; + } + } + RalewayRegular { + id: users; + color: hifi.colors.lightGrayText; + anchors { + bottom: parent.bottom; + right: parent.right; + bottomMargin: 2 * hifi.layout.spacing; + rightMargin: 2 * hifi.layout.spacing; + } + } + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.LeftButton; + onClicked: goFunction(parent); + } +} From 1c2d13d1eea38b5d88630decb110250fc8c79b37 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 14 Jul 2016 15:38:22 -0700 Subject: [PATCH 02/20] Don't show where you are now. --- interface/resources/qml/AddressBarDialog.qml | 8 ++++---- interface/src/ui/AddressBarDialog.cpp | 4 ++++ interface/src/ui/AddressBarDialog.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8ac199b326..fe5c1d8030 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -283,11 +283,11 @@ Window { target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); } - getDomains({minUsers: 0, maxUsers: 20}, function (e, d) { - var withLobby = !e && d.filter(function (d1) { return d1.lobby; }); - console.log(e, d.length, withLobby.length); + getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { + var here = addressBarDialog.getHost(); // don't show where we are now. + var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); + console.log(error, domains.length, withLobby.length); withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - console.log(withLobby.map(function (d) { return d.online_users; })); fill1(s0, withLobby[0]); fill1(s1, withLobby[1]); fill1(s2, withLobby[2]); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 6fb437e312..7a32381db3 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,6 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } +QString AddressBarDialog::getHost() const { + return DependencyManager::get()->getHost(); +} + void AddressBarDialog::loadAddress(const QString& address) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index bbce52c67c..b5a21dd47a 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,6 +34,7 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); + Q_INVOKABLE QString getHost() const; Q_INVOKABLE void loadAddress(const QString& address); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); From ce99146733deb50a425aa200ba213e056112a4b8 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 10:27:52 -0700 Subject: [PATCH 03/20] filter by typing --- interface/resources/qml/AddressBarDialog.qml | 53 +++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index fe5c1d8030..bcb55ba568 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -16,9 +16,6 @@ import "styles" import "windows" import "hifi" -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - Window { id: root HifiConstants { id: hifi } @@ -48,12 +45,13 @@ Window { // Explicitly center in order to avoid warnings at shutdown anchors.centerIn = parent; } - + function goCard(card) { addressLine.text = card.userStory.name; toggleOrGo(); } - + property var suggestionChoices: null; + AddressBarDialog { id: addressBarDialog implicitWidth: backgroundImage.width @@ -63,8 +61,8 @@ Window { width: backgroundImage.width; anchors { left: backgroundImage.left; - leftMargin: backgroundImage.height + 2 * hifi.layout.spacing bottom: backgroundImage.top; + leftMargin: parent.height + 2 * hifi.layout.spacing; bottomMargin: 2 * hifi.layout.spacing; } Text { @@ -175,6 +173,7 @@ Window { } font.pixelSize: hifi.fonts.pixelSize * root.scale * 0.75 helperText: "Go to: place, @user, /path, network address" + onTextChanged: filterChoicesByText() } } @@ -184,7 +183,7 @@ Window { // TODO: make available to other .qml. var request = new XMLHttpRequest(); // QT bug: apparently doesn't handle onload. Workaround using readyState. - request.onreadystatechange = function () { + request.onreadystatechange = function () { var READY_STATE_DONE = 4; var HTTP_OK = 200; if (request.readyState >= READY_STATE_DONE) { @@ -221,7 +220,7 @@ Window { iterator(element, icb); }); } - + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; @@ -266,34 +265,52 @@ Window { }); }); } - - function fillDestinations () { + + function filterChoicesByText() { function fill1(target, data) { if (!data) { + target.visible = false; return; } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; if (data.lobby) { - console.log('target', target); - console.log('target.image', target.image); - console.log('url', data.lobby); target.image.source = data.lobby; } target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); + target.visible = true; } + if (!suggestionChoices) { + return; + } + var words = addressLine.text.toUpperCase().split(/\s+/); + var filtered = !words.length ? suggestionChoices : suggestionChoices.filter(function (domain) { + var text = domain.names.concat(domain.tags).join(' '); + if (domain.description) { + text += domain.description; + } + text = text.toUpperCase(); + return words.every(function (word) { + return text.indexOf(word) >= 0; + }); + }); + fill1(s0, filtered[0]); + fill1(s1, filtered[1]); + fill1(s2, filtered[2]); + } + + function fillDestinations() { + suggestionChoices = null; getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { var here = addressBarDialog.getHost(); // don't show where we are now. var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); - console.log(error, domains.length, withLobby.length); withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - fill1(s0, withLobby[0]); - fill1(s1, withLobby[1]); - fill1(s2, withLobby[2]); + suggestionChoices = withLobby; + filterChoicesByText(); }); } - + onVisibleChanged: { if (visible) { addressLine.forceActiveFocus() From 8faa961c8dfdb3f8586c73545708250bd6a4bca2 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 11:41:00 -0700 Subject: [PATCH 04/20] all domains, not just the 'active' ones, which turns out to be defined as having at least one person in it. --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index bcb55ba568..b9b6e58913 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -244,7 +244,7 @@ Window { options.page = 1; } // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); From 99e229467bdaec842c1aa58cb0b8adac97f72c3d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 11:49:21 -0700 Subject: [PATCH 05/20] Don't show the word "suggesions" when there aren't any. --- interface/resources/qml/AddressBarDialog.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b9b6e58913..220aa794b9 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -66,6 +66,7 @@ Window { bottomMargin: 2 * hifi.layout.spacing; } Text { + id: suggestionsLabel; text: "Suggestions" } Row { @@ -298,6 +299,7 @@ Window { fill1(s0, filtered[0]); fill1(s1, filtered[1]); fill1(s2, filtered[2]); + suggestionsLabel.visible = filtered.length; } function fillDestinations() { From 6fb2cedfe04b2248406718adaa4fe856b21542df Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 13:42:31 -0700 Subject: [PATCH 06/20] card default image: settable, use to start, and reset when needed --- interface/resources/qml/AddressBarDialog.qml | 4 +--- interface/resources/qml/hifi/Card.qml | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 220aa794b9..4cb550cd53 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -275,9 +275,7 @@ Window { } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; - if (data.lobby) { - target.image.source = data.lobby; - } + target.image.source = data.lobby || ''; // should fail to load and thus use default target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 342c469303..8b2d1133da 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -21,11 +21,14 @@ Rectangle { property alias image: lobby; property alias placeText: place.text; property alias usersText: users.text; + // FIXME: let's get our own + property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; HifiConstants { id: hifi } Image { id: lobby; width: parent.width; height: parent.height; + source: defaultPicture; fillMode: Image.PreserveAspectCrop; // source gets filled in later anchors.verticalCenter: parent.verticalCenter; @@ -33,8 +36,7 @@ Rectangle { onStatusChanged: { if (status == Image.Error) { console.log("source: " + source + ": failed to load " + JSON.stringify(userStory)); - // FIXME: let's get our own - source = "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif" + source = defaultPicture; } } } From 87e54482dda406b1853ffb826dd3b9115b7d5d2e Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:30:28 -0700 Subject: [PATCH 07/20] check pictures only for the domains that have someone in them, but use all domains when filtering (occupied or no, picture or no). Also, make darn sure we use default picture if no data. --- interface/resources/qml/AddressBarDialog.qml | 52 ++++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 4cb550cd53..d87cfe1f48 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -50,7 +50,8 @@ Window { addressLine.text = card.userStory.name; toggleOrGo(); } - property var suggestionChoices: null; + property var allDomains: []; + property var suggestionChoices: []; AddressBarDialog { id: addressBarDialog @@ -223,12 +224,14 @@ Window { } function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) + // This requests data for all the names at once, and just uses the first one to come back. + // We might change this to check one at a time, which would be less requests and more latency. asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; getRequest(url, function (error, json) { var previews = !error && json.data.place.previews; if (previews) { - if (!domainInfo.thumbnail) { // just grab tghe first one + if (!domainInfo.thumbnail) { // just grab the first one domainInfo.thumbnail = previews.thumbnail; } if (!domainInfo.lobby) { @@ -255,15 +258,13 @@ Window { return cb(error); } var domains = json.data.domains; - asyncEach(domains, addPictureToDomain, function (error) { - if (json.current_page < json.total_pages) { - options.page++; - return getDomains(options, function (error, others) { - cb(error, domains.concat(others)); - }); - } - cb(null, domains); - }); + if (json.current_page < json.total_pages) { + options.page++; + return getDomains(options, function (error, others) { + cb(error, domains.concat(others)); + }); + } + cb(null, domains); }); } @@ -275,16 +276,13 @@ Window { } console.log('suggestion:', JSON.stringify(data)); target.userStory = data; - target.image.source = data.lobby || ''; // should fail to load and thus use default + target.image.source = data.lobby || target.defaultPicture; target.placeText = data.name; target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; } - if (!suggestionChoices) { - return; - } var words = addressLine.text.toUpperCase().split(/\s+/); - var filtered = !words.length ? suggestionChoices : suggestionChoices.filter(function (domain) { + var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { var text = domain.names.concat(domain.tags).join(' '); if (domain.description) { text += domain.description; @@ -301,13 +299,25 @@ Window { } function fillDestinations() { - suggestionChoices = null; + allDomains = suggestionChoices = []; getDomains({minUsers: 0, maxUsers: 20}, function (error, domains) { + if (error) { + console.log('domain query failed:', error); + return filterChoicesByText(); + } var here = addressBarDialog.getHost(); // don't show where we are now. - var withLobby = !error && domains.filter(function (domain) { return domain.lobby && (domain.name !== here); }); - withLobby.sort(function (a, b) { return b.online_users - a.online_users; }); - suggestionChoices = withLobby; - filterChoicesByText(); + allDomains = domains.filter(function (domain) { return domain.name !== here; }); + allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); + // Whittle down suggestions to those that have at least one user, and try to get pictures. + suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; }); + asyncEach(domains, addPictureToDomain, function (error) { + if (error) { + console.log('place picture query failed:', error); + } + // Whittle down more by requiring a picture. + suggestionChoices = suggestionChoices.filter(function (domain) { return domain.lobby; }); + filterChoicesByText(); + }); }); } From 562446e7b86659712823df026512f27973f5d89a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:44:27 -0700 Subject: [PATCH 08/20] use AddressManager, rather than adding a trampoline to hostname in AddressBarDialog. --- interface/resources/qml/AddressBarDialog.qml | 2 +- interface/src/ui/AddressBarDialog.cpp | 4 ---- interface/src/ui/AddressBarDialog.h | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index d87cfe1f48..43a1c10c0f 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -305,7 +305,7 @@ Window { console.log('domain query failed:', error); return filterChoicesByText(); } - var here = addressBarDialog.getHost(); // don't show where we are now. + var here = AddressManager.hostname; // don't show where we are now. allDomains = domains.filter(function (domain) { return domain.name !== here; }); allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); // Whittle down suggestions to those that have at least one user, and try to get pictures. diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 7a32381db3..6fb437e312 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,10 +40,6 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } -QString AddressBarDialog::getHost() const { - return DependencyManager::get()->getHost(); -} - void AddressBarDialog::loadAddress(const QString& address) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index b5a21dd47a..bbce52c67c 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,7 +34,6 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); - Q_INVOKABLE QString getHost() const; Q_INVOKABLE void loadAddress(const QString& address); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); From cc367f7b49b635a10cc3a3cb7c6587364a52846f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 14:56:31 -0700 Subject: [PATCH 09/20] We don't need HorizontalSpacer.qml. --- interface/resources/qml/AddressBarDialog.qml | 4 +--- .../qml/controls-uit/HorizontalSpacer.qml | 19 ------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 43a1c10c0f..8764639972 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -11,7 +11,6 @@ import Hifi 1.0 import QtQuick 2.4 import "controls" -import "controls-uit" as HifiControls import "styles" import "windows" import "hifi" @@ -71,20 +70,19 @@ Window { text: "Suggestions" } Row { + spacing: hifi.layout.spacing; Card { id: s0; width: 200; height: 200; goFunction: goCard } - HifiControls.HorizontalSpacer { } Card { id: s1; width: 200; height: 200; goFunction: goCard } - HifiControls.HorizontalSpacer { } Card { id: s2; width: 200; diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml deleted file mode 100644 index a9fac2bcf7..0000000000 --- a/interface/resources/qml/controls-uit/HorizontalSpacer.qml +++ /dev/null @@ -1,19 +0,0 @@ -// -// HorizontalSpacer.qml -// -// Created by Howard Stearns on 12 July 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 - -import "../styles-uit" - -Item { - HifiConstants { id: hifi } - height: 1 // Must be non-zero - width: hifi.dimensions.controlInterlineHeight -} From 289617d4ce5a4add7e9e3921dc49a55247d84c9a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:14:29 -0700 Subject: [PATCH 10/20] styling --- interface/resources/qml/AddressBarDialog.qml | 31 ++++++++-------- interface/resources/qml/hifi/Card.qml | 37 ++++++++++++++++---- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8764639972..b739842d1c 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -51,6 +51,8 @@ Window { } property var allDomains: []; property var suggestionChoices: []; + property int cardWidth: 200; + property int cardHeight: 152; AddressBarDialog { id: addressBarDialog @@ -60,33 +62,29 @@ Window { Column { width: backgroundImage.width; anchors { - left: backgroundImage.left; bottom: backgroundImage.top; - leftMargin: parent.height + 2 * hifi.layout.spacing; bottomMargin: 2 * hifi.layout.spacing; - } - Text { - id: suggestionsLabel; - text: "Suggestions" + right: backgroundImage.right; + rightMargin: -104; // FIXME } Row { spacing: hifi.layout.spacing; Card { id: s0; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } Card { id: s1; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } Card { id: s2; - width: 200; - height: 200; + width: cardWidth; + height: cardHeight; goFunction: goCard } } @@ -221,10 +219,14 @@ Window { }); } + function identity(x) { + return x; + } + function addPictureToDomain(domainInfo, cb) { // asynchronously add thumbnail and lobby to domainInfo, if available, and cb(error) // This requests data for all the names at once, and just uses the first one to come back. // We might change this to check one at a time, which would be less requests and more latency. - asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(function (x) { return x; }), function (name, icb) { + asyncEach([domainInfo.name].concat(domainInfo.names || null).filter(identity), function (name, icb) { var url = "https://metaverse.highfidelity.com/api/v1/places/" + name; getRequest(url, function (error, json) { var previews = !error && json.data.place.previews; @@ -279,7 +281,7 @@ Window { target.usersText = data.online_users + ((data.online_users === 1) ? ' user' : ' users'); target.visible = true; } - var words = addressLine.text.toUpperCase().split(/\s+/); + var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity); var filtered = !words.length ? suggestionChoices : allDomains.filter(function (domain) { var text = domain.names.concat(domain.tags).join(' '); if (domain.description) { @@ -293,7 +295,6 @@ Window { fill1(s0, filtered[0]); fill1(s1, filtered[1]); fill1(s2, filtered[2]); - suggestionsLabel.visible = filtered.length; } function fillDestinations() { diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 8b2d1133da..3a2a52e3c0 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -13,6 +13,7 @@ import Hifi 1.0 import QtQuick 2.5 +import QtGraphicalEffects 1.0 import "../styles-uit" Rectangle { @@ -21,6 +22,8 @@ Rectangle { property alias image: lobby; property alias placeText: place.text; property alias usersText: users.text; + property int textPadding: 20; + property int textSize: 24; // FIXME: let's get our own property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; HifiConstants { id: hifi } @@ -40,22 +43,44 @@ Rectangle { } } } + DropShadow { + source: place; + anchors.fill: place; + horizontalOffset: 0; + radius: 2; + samples: 9; + color: hifi.colors.black; + verticalOffset: 1; + spread: 0; + } + DropShadow { + source: users; + anchors.fill: users; + horizontalOffset: 0; + radius: 2; + samples: 9; + color: hifi.colors.black; + verticalOffset: 1; + spread: 0; + } RalewaySemiBold { id: place; - color: hifi.colors.lightGrayText; + color: hifi.colors.white; + size: textSize; anchors { - leftMargin: 2 * hifi.layout.spacing; - topMargin: 2 * hifi.layout.spacing; + top: parent.top; + left: parent.left; + margins: textPadding; } } RalewayRegular { id: users; - color: hifi.colors.lightGrayText; + size: textSize; + color: hifi.colors.white; anchors { bottom: parent.bottom; right: parent.right; - bottomMargin: 2 * hifi.layout.spacing; - rightMargin: 2 * hifi.layout.spacing; + margins: textPadding; } } MouseArea { From f00114e53e3e0e8f93459670e950f6dac28b4ddb Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:46:20 -0700 Subject: [PATCH 11/20] Make it work with laser pointers (selection at least), and fix magic numbers. --- interface/resources/qml/hifi/Card.qml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 3a2a52e3c0..b4a709215f 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -43,25 +43,30 @@ Rectangle { } } } + property int dropHorizontalOffset: 0; + property int dropVerticalOffset: 1; + property int dropRadius: 2; + property int dropSamples: 9; + property int dropSpread: 0; DropShadow { source: place; anchors.fill: place; - horizontalOffset: 0; - radius: 2; - samples: 9; + horizontalOffset: dropHorizontalOffset; + verticalOffset: dropVerticalOffset; + radius: dropRadius; + samples: dropSamples; color: hifi.colors.black; - verticalOffset: 1; - spread: 0; + spread: dropSpread; } DropShadow { source: users; anchors.fill: users; - horizontalOffset: 0; - radius: 2; - samples: 9; + horizontalOffset: dropHorizontalOffset; + verticalOffset: dropVerticalOffset; + radius: dropRadius; + samples: dropSamples; color: hifi.colors.black; - verticalOffset: 1; - spread: 0; + spread: dropSpread; } RalewaySemiBold { id: place; @@ -87,5 +92,6 @@ Rectangle { anchors.fill: parent; acceptedButtons: Qt.LeftButton; onClicked: goFunction(parent); + hoverEnabled: true; } } From b106dee3e9cb876cbead876246f79fcbb825d947 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 16:52:13 -0700 Subject: [PATCH 12/20] Provide our own default image. --- interface/resources/images/default-domain.gif | Bin 0 -> 4714 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 interface/resources/images/default-domain.gif diff --git a/interface/resources/images/default-domain.gif b/interface/resources/images/default-domain.gif new file mode 100644 index 0000000000000000000000000000000000000000..1418a54cddf29bcbf4e3e32ec8b903a7d1ac88a4 GIT binary patch literal 4714 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 literal 0 HcmV?d00001 From f73c2b884dfa3ea4e3eb657d56ed19983f79bef0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Fri, 15 Jul 2016 17:01:37 -0700 Subject: [PATCH 13/20] Remove now-superflous Column. --- interface/resources/qml/AddressBarDialog.qml | 40 ++++++++++---------- interface/resources/qml/hifi/Card.qml | 3 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b739842d1c..edc69d7c7d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -59,7 +59,7 @@ Window { implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height - Column { + Row { width: backgroundImage.width; anchors { bottom: backgroundImage.top; @@ -67,26 +67,24 @@ Window { right: backgroundImage.right; rightMargin: -104; // FIXME } - Row { - spacing: hifi.layout.spacing; - Card { - id: s0; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } - Card { - id: s1; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } - Card { - id: s2; - width: cardWidth; - height: cardHeight; - goFunction: goCard - } + spacing: hifi.layout.spacing; + Card { + id: s0; + width: cardWidth; + height: cardHeight; + goFunction: goCard + } + Card { + id: s1; + width: cardWidth; + height: cardHeight; + goFunction: goCard + } + Card { + id: s2; + width: cardWidth; + height: cardHeight; + goFunction: goCard } } diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index b4a709215f..7758c5800a 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -24,8 +24,7 @@ Rectangle { property alias usersText: users.text; property int textPadding: 20; property int textSize: 24; - // FIXME: let's get our own - property string defaultPicture: "http://www.davidluke.com/wp-content/themes/david-luke/media/ims/placeholder720.gif"; + property string defaultPicture: "../../images/default-domain.gif"; HifiConstants { id: hifi } Image { id: lobby; From d446e1bcf54eee13320b071910e689b5be910993 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 15 Jul 2016 19:15:58 -0700 Subject: [PATCH 14/20] Phone home with suggestions activity usage. --- interface/resources/qml/AddressBarDialog.qml | 6 +++--- interface/src/ui/AddressBarDialog.cpp | 4 ++-- interface/src/ui/AddressBarDialog.h | 2 +- libraries/networking/src/AddressManager.cpp | 4 ++-- libraries/networking/src/AddressManager.h | 5 +++-- libraries/networking/src/UserActivityLogger.cpp | 3 +++ 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index edc69d7c7d..e8318ef1b8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -47,7 +47,7 @@ Window { function goCard(card) { addressLine.text = card.userStory.name; - toggleOrGo(); + toggleOrGo(true); } property var allDomains: []; property var suggestionChoices: []; @@ -327,9 +327,9 @@ Window { } } - function toggleOrGo() { + function toggleOrGo(fromSuggestions) { if (addressLine.text !== "") { - addressBarDialog.loadAddress(addressLine.text) + addressBarDialog.loadAddress(addressLine.text, fromSuggestions) } root.shown = false; } diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 6fb437e312..a4ef8a913f 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -40,10 +40,10 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare _forwardEnabled = !(DependencyManager::get()->getForwardStack().isEmpty()); } -void AddressBarDialog::loadAddress(const QString& address) { +void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) { qDebug() << "Called LoadAddress with address " << address; if (!address.isEmpty()) { - DependencyManager::get()->handleLookupString(address); + DependencyManager::get()->handleLookupString(address, fromSuggestions); } } diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index bbce52c67c..6c7620164b 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -34,7 +34,7 @@ protected: void displayAddressOfflineMessage(); void displayAddressNotFoundMessage(); - Q_INVOKABLE void loadAddress(const QString& address); + Q_INVOKABLE void loadAddress(const QString& address, bool fromSuggestions = false); Q_INVOKABLE void loadHome(); Q_INVOKABLE void loadBack(); Q_INVOKABLE void loadForward(); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 7ed3888be0..b3a022cc3a 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -221,7 +221,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { return false; } -void AddressManager::handleLookupString(const QString& lookupString) { +void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) { if (!lookupString.isEmpty()) { // make this a valid hifi URL and handle it off to handleUrl QString sanitizedString = lookupString.trimmed(); @@ -236,7 +236,7 @@ void AddressManager::handleLookupString(const QString& lookupString) { lookupURL = QUrl(lookupString); } - handleUrl(lookupURL); + handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput); } } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index a3aaee3ba2..c013da3a72 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -49,7 +49,8 @@ public: StartupFromSettings, DomainPathResponse, Internal, - AttemptedRefresh + AttemptedRefresh, + Suggestions }; bool isConnected(); @@ -77,7 +78,7 @@ public: std::function localSandboxNotRunningDoThat); public slots: - void handleLookupString(const QString& lookupString); + void handleLookupString(const QString& lookupString, bool fromSuggestions = false); // we currently expect this to be called from NodeList once handleLookupString has been called with a path bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index eba4d31167..75e15db2a4 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -178,6 +178,9 @@ void UserActivityLogger::wentTo(AddressManager::LookupTrigger lookupTrigger, QSt case AddressManager::StartupFromSettings: trigger = "StartupFromSettings"; break; + case AddressManager::Suggestions: + trigger = "Suggesions"; + break; default: return; } From cf025072e16f2fee538af06b7b10b6715ac463a8 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 16 Jul 2016 09:50:56 -0700 Subject: [PATCH 15/20] Only consider "active" domains for all purposes (which on the back end means at least one person connected). --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index e8318ef1b8..f08ef5c608 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -246,7 +246,7 @@ Window { options.page = 1; } // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); From 1a66574adb44542157af46d04167645ef6533bb0 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 14:29:49 -0700 Subject: [PATCH 16/20] add protocol version signature to metaverse heartbeat --- domain-server/src/DomainServer.cpp | 4 +++- libraries/networking/src/udt/PacketHeaders.cpp | 14 +++++++++++--- libraries/networking/src/udt/PacketHeaders.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8b3f09d1f7..bcfdd42c34 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1083,9 +1083,11 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { // Setup the domain object to send to the data server QJsonObject domainObject; - // add the version + // add the versions static const QString VERSION_KEY = "version"; domainObject[VERSION_KEY] = BuildInfo::VERSION; + static const QString PROTOCOL_KEY = "protocol"; + domainObject[PROTOCOL_KEY] = protocolVersionsSignatureBase64(); // add networking if (!networkAddress.isEmpty()) { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index fca006ae87..8542e9f60e 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -99,8 +99,9 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion) { } #endif -QByteArray protocolVersionsSignature() { - static QByteArray protocolVersionSignature; +static QByteArray protocolVersionSignature; +static QString protocolVersionSignatureBase64; +static void ensureProtocolVersionsSignature() { static std::once_flag once; std::call_once(once, [&] { QByteArray buffer; @@ -114,8 +115,11 @@ QByteArray protocolVersionsSignature() { QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(buffer); protocolVersionSignature = hash.result(); + protocolVersionSignatureBase64 = protocolVersionSignature.toBase64(); }); - +} +QByteArray protocolVersionsSignature() { + ensureProtocolVersionsSignature(); #if (PR_BUILD || DEV_BUILD) if (sendWrongProtocolVersion) { return QByteArray("INCORRECTVERSION"); // only for debugging version checking @@ -124,3 +128,7 @@ QByteArray protocolVersionsSignature() { return protocolVersionSignature; } +QString protocolVersionsSignatureBase64() { + ensureProtocolVersionsSignature(); + return protocolVersionSignatureBase64; +} diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 85030135a1..0e10a8fd76 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -113,6 +113,7 @@ extern const QSet NON_SOURCED_PACKETS; PacketVersion versionForPacketType(PacketType packetType); QByteArray protocolVersionsSignature(); /// returns a unqiue signature for all the current protocols +QString protocolVersionsSignatureBase64(); #if (PR_BUILD || DEV_BUILD) void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debugging version negotiation From 98f76924c5867307e14ac0329ef626a7542c56b2 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 14:49:56 -0700 Subject: [PATCH 17/20] log hearbeat like we do updates --- domain-server/src/DomainServer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bcfdd42c34..88f4b94883 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1121,7 +1121,12 @@ void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { QString domainUpdateJSON = QString("{\"domain\":%1}").arg(QString(QJsonDocument(domainObject).toJson(QJsonDocument::Compact))); static const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; - DependencyManager::get()->sendRequest(DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())), + QString path = DOMAIN_UPDATE.arg(uuidStringWithoutCurlyBraces(getID())); +#if DEV_BUILD || PR_BUILD + qDebug() << "Domain metadata sent to" << path; + qDebug() << "Domain metadata update:" << domainUpdateJSON; +#endif + DependencyManager::get()->sendRequest(path, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation, JSONCallbackParameters(nullptr, QString(), this, "handleMetaverseHeartbeatError"), From 17e64cf5e6e4cc46660590f2e50d5e09273fcf20 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 16:58:29 -0700 Subject: [PATCH 18/20] suggest only unrestricted domains, and also use server to sort --- interface/resources/qml/AddressBarDialog.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index f08ef5c608..b806bbbca2 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -245,8 +245,10 @@ Window { if (!options.page) { options.page = 1; } - // FIXME: really want places I'm allowed in, not just open ones - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + // FIXME: really want places I'm allowed in, not just open ones. + // FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME: add maturity + var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&restriction=open&sort_by=users&sort_order=desc&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); @@ -304,7 +306,6 @@ Window { } var here = AddressManager.hostname; // don't show where we are now. allDomains = domains.filter(function (domain) { return domain.name !== here; }); - allDomains.sort(function (a, b) { return b.online_users - a.online_users; }); // Whittle down suggestions to those that have at least one user, and try to get pictures. suggestionChoices = allDomains.filter(function (domain) { return domain.online_users; }); asyncEach(domains, addPictureToDomain, function (error) { From 57955a2b5612d287ad33dac1b96cf33fd1b33fec Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 18 Jul 2016 18:12:27 -0700 Subject: [PATCH 19/20] Expose protocol version to qml, and use it in query. --- interface/resources/qml/AddressBarDialog.qml | 19 +++++++++++++++---- libraries/networking/src/AddressManager.cpp | 5 +++++ libraries/networking/src/AddressManager.h | 1 + 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index b806bbbca2..0c9e5c9dbc 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -51,6 +51,7 @@ Window { } property var allDomains: []; property var suggestionChoices: []; + property var domainsBaseUrl: null; property int cardWidth: 200; property int cardHeight: 152; @@ -245,10 +246,20 @@ Window { if (!options.page) { options.page = 1; } - // FIXME: really want places I'm allowed in, not just open ones. - // FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. - // FIXME: add maturity - var url = "https://metaverse.highfidelity.com/api/v1/domains/all?open&active&restriction=open&sort_by=users&sort_order=desc&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; + if (!domainsBaseUrl) { + var domainsOptions = [ + 'open', // published hours handle now + 'active', // has at least one person connected. FIXME: really want any place that is verified accessible. + // FIXME: really want places I'm allowed in, not just open ones. + 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. + // FIXME add maturity + 'protocol=' + AddressManager.protocolVersion(), + 'sort_by=users', + 'sort_order=desc', + ]; + domainsBaseUrl = "https://metaverse.highfidelity.com/api/v1/domains/all?" + domainsOptions.join('&'); + } + var url = domainsBaseUrl + "&page=" + options.page + "&users=" + options.minUsers + "-" + options.maxUsers; getRequest(url, function (error, json) { if (!error && (json.status !== 'success')) { error = new Error("Bad response: " + JSON.stringify(json)); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index b3a022cc3a..ae6aad3c4f 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -24,6 +24,7 @@ #include "NodeList.h" #include "NetworkLogging.h" #include "UserActivityLogger.h" +#include "udt/PacketHeaders.h" const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; @@ -37,6 +38,10 @@ AddressManager::AddressManager() : } +QString AddressManager::protocolVersion() { + return protocolVersionsSignatureBase64(); +} + bool AddressManager::isConnected() { return DependencyManager::get()->getDomainHandler().isConnected(); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index c013da3a72..2e9f177137 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -39,6 +39,7 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString hostname READ getHost) Q_PROPERTY(QString pathname READ currentPath) public: + Q_INVOKABLE QString protocolVersion(); using PositionGetter = std::function; using OrientationGetter = std::function; From 4a6132874f57b8ff463ee9117cd48ad9a6a15904 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 19 Jul 2016 13:43:28 -0700 Subject: [PATCH 20/20] encodeURIComponent of the protocol signature --- interface/resources/qml/AddressBarDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 0c9e5c9dbc..792410c59d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -253,7 +253,7 @@ Window { // FIXME: really want places I'm allowed in, not just open ones. 'restriction=open', // Not by whitelist, etc. FIXME: If logged in, add hifi to the restriction options, in order to include places that require login. // FIXME add maturity - 'protocol=' + AddressManager.protocolVersion(), + 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), 'sort_by=users', 'sort_order=desc', ];