From 45921a5c8db47ed6e5266f9903eee71acb9da7d2 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Mon, 20 Jun 2016 14:15:45 -0700
Subject: [PATCH 01/18] Restore split behavior for inputs/displays on quit

---
 plugins/openvr/src/OpenVrDisplayPlugin.cpp   | 5 ++++-
 plugins/openvr/src/OpenVrHelpers.cpp         | 8 +++++++-
 plugins/openvr/src/ViveControllerManager.cpp | 4 ++++
 3 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
index 4e84c6d0fa..a532261014 100644
--- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp
+++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp
@@ -161,7 +161,10 @@ static bool isBadPose(vr::HmdMatrix34_t* mat) {
 
 bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
     handleOpenVrEvents();
-
+    if (openVrQuitRequested()) {
+        QMetaObject::invokeMethod(qApp, "quit");
+        return false;
+    }
     double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
     double frameDuration = 1.f / displayFrequency;
     double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp
index c93a2178b5..ee61a07da6 100644
--- a/plugins/openvr/src/OpenVrHelpers.cpp
+++ b/plugins/openvr/src/OpenVrHelpers.cpp
@@ -34,6 +34,11 @@ using Lock = std::unique_lock<Mutex>;
 static int refCount { 0 };
 static Mutex mutex;
 static vr::IVRSystem* activeHmd { nullptr };
+static bool _openVrQuitRequested { false };
+
+bool openVrQuitRequested() {
+    return _openVrQuitRequested;
+}
 
 static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
 
@@ -99,6 +104,7 @@ void releaseOpenVrSystem() {
                 qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
             #endif
             vr::VR_Shutdown();
+            _openVrQuitRequested = false;
             activeHmd = nullptr;
         }
     }
@@ -257,8 +263,8 @@ void handleOpenVrEvents() {
     while (activeHmd->PollNextEvent(&event, sizeof(event))) {
         switch (event.eventType) {
             case vr::VREvent_Quit:
+                _openVrQuitRequested = true;
                 activeHmd->AcknowledgeQuit_Exiting();
-                QMetaObject::invokeMethod(qApp, "quit");
                 break;
 
             case vr::VREvent_KeyboardDone: 
diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp
index 85feebda11..930b3dd450 100644
--- a/plugins/openvr/src/ViveControllerManager.cpp
+++ b/plugins/openvr/src/ViveControllerManager.cpp
@@ -214,6 +214,10 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
 void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
     auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
     handleOpenVrEvents();
+    if (openVrQuitRequested()) {
+        deactivate();
+        return;
+    }
 
     // because update mutates the internal state we need to lock
     userInputMapper->withLock([&, this]() {

From cea0e182c0b24928aae74352355fe552167dcf4c Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 14 Jul 2016 11:02:32 -0700
Subject: [PATCH 02/18] Interface login through steam

---
 interface/resources/images/steam-sign-in.png  | Bin 0 -> 5861 bytes
 interface/resources/qml/LoginDialog.qml       |  50 +++--
 interface/src/Application.cpp                 |   7 +-
 interface/src/main.cpp                        |   9 +-
 interface/src/ui/LoginDialog.cpp              |  15 ++
 interface/src/ui/LoginDialog.h                |   1 +
 libraries/networking/src/AccountManager.cpp   |  23 +++
 libraries/networking/src/AccountManager.h     |   1 +
 .../src/steamworks-wrapper/SteamClient.cpp    | 171 ++++++++++++++++++
 .../src/steamworks-wrapper/SteamClient.h      |  15 +-
 10 files changed, 275 insertions(+), 17 deletions(-)
 create mode 100644 interface/resources/images/steam-sign-in.png

diff --git a/interface/resources/images/steam-sign-in.png b/interface/resources/images/steam-sign-in.png
new file mode 100644
index 0000000000000000000000000000000000000000..148e6ab280472d5b4b3f069a2c267790e19d13c2
GIT binary patch
literal 5861
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

literal 0
HcmV?d00001

diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index f75e83e36e..eccea00143 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -203,24 +203,48 @@ ScrollingWindow {
                 }
             }
 
-            Rectangle {
+            Row {
                 width: loginDialog.inputWidth
                 height: loginDialog.inputHeight
-                radius: height / 2
-                color: "#353535"
 
-                TextInput {
-                    anchors.fill: parent
-                    text: "Login"
-                    color: "white"
-                    horizontalAlignment: Text.AlignHCenter
+                Rectangle {
+                    width: loginDialog.inputWidth / 3
+                    height: loginDialog.inputHeight
+                    radius: height / 2
+                    color: "#353535"
+
+                    TextInput {
+                        anchors.fill: parent
+                        text: "Login"
+                        color: "white"
+                        horizontalAlignment: Text.AlignHCenter
+                    }
+
+                    MouseArea {
+                        anchors.fill: parent
+                        cursorShape: Qt.PointingHandCursor
+                        onClicked: {
+                            loginDialog.login(username.text, password.text)
+                        }
+                    }
                 }
 
-                MouseArea {
-                    anchors.fill: parent
-                    cursorShape: Qt.PointingHandCursor
-                    onClicked: {
-                        loginDialog.login(username.text, password.text)
+                Image {
+                    source: "../images/steam-sign-in.png"
+                    width: loginDialog.inputWidth / 3
+                    height: loginDialog.inputHeight
+                    anchors {
+                        verticalCenter: parent.verticalCenter
+                        right: parent.right
+                        leftMargin: loginDialog.inputHeight / 4
+                    }
+
+                    MouseArea {
+                        anchors.fill: parent
+                        cursorShape: Qt.PointingHandCursor
+                        onClicked: {
+                            loginDialog.loginThroughSteam()
+                        }
                     }
                 }
             }
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 7695bd1b99..1729a773c6 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -86,15 +86,16 @@
 #include <PhysicsHelpers.h>
 #include <plugins/PluginManager.h>
 #include <plugins/CodecPlugin.h>
+#include <RecordingScriptingInterface.h>
 #include <RenderableWebEntityItem.h>
 #include <RenderShadowTask.h>
 #include <RenderDeferredTask.h>
 #include <ResourceCache.h>
 #include <SceneScriptingInterface.h>
-#include <RecordingScriptingInterface.h>
+#include <ScriptEngines.h>
 #include <ScriptCache.h>
 #include <SoundCache.h>
-#include <ScriptEngines.h>
+#include <steamworks-wrapper/SteamClient.h>
 #include <Tooltip.h>
 #include <udt/PacketHeaders.h>
 #include <UserActivityLogger.h>
@@ -2910,6 +2911,8 @@ void Application::idle(float nsecsElapsed) {
 
     PROFILE_RANGE(__FUNCTION__);
 
+    SteamClient::runCallbacks();
+
     float secondsSinceLastUpdate = nsecsElapsed / NSECS_PER_MSEC / MSECS_PER_SECOND;
 
     // If the offscreen Ui has something active that is NOT the root, then assume it has keyboard focus.
diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 8fc0384aee..527b7f2331 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -8,6 +8,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
+#include <thread>
+
 #include <QCommandLineParser>
 #include <QDebug>
 #include <QDir>
@@ -20,12 +22,13 @@
 #include <gl/OpenGLVersionChecker.h>
 #include <SharedUtil.h>
 
+#include <steamworks-wrapper/SteamClient.h>
+
 #include "AddressManager.h"
 #include "Application.h"
 #include "InterfaceLogging.h"
 #include "UserActivityLogger.h"
 #include "MainWindow.h"
-#include <thread>
 
 #ifdef HAS_BUGSPLAT
 #include <BuildInfo.h>
@@ -137,6 +140,8 @@ int main(int argc, const char* argv[]) {
     // or in the main window ctor, before GL startup.
     Application::initPlugins(arguments);
 
+    SteamClient::init();
+
     int exitCode;
     {
         QSettings::setDefaultFormat(QSettings::IniFormat);
@@ -202,6 +207,8 @@ int main(int argc, const char* argv[]) {
 
     Application::shutdownPlugins();
 
+    SteamClient::shutdown();
+
     qCDebug(interfaceapp, "Normal exit.");
 #if !defined(DEBUG) && !defined(Q_OS_LINUX)
     // HACK: exit immediately (don't handle shutdown callbacks) for Release build
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index 80d52b7a07..8240340381 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -14,6 +14,7 @@
 #include <QDesktopServices>
 
 #include <NetworkingConstants.h>
+#include <steamworks-wrapper/SteamClient.h>
 
 #include "AccountManager.h"
 #include "DependencyManager.h"
@@ -82,6 +83,20 @@ void LoginDialog::login(const QString& username, const QString& password) {
     DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
 }
 
+void LoginDialog::loginThroughSteam() {
+    qDebug() << "Attempting to login through Steam";
+    setStatusText("Logging in...");
+
+    SteamClient::requestTicket([this](Ticket ticket) {
+        if (ticket.isNull()) {
+            setStatusText("Steam client not logged into an account");
+            return;
+        }
+
+        DependencyManager::get<AccountManager>()->requestAccessTokenWithSteam(ticket);
+    });
+}
+
 void LoginDialog::openUrl(const QString& url) {
     qDebug() << url;
     QDesktopServices::openUrl(url);
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index 25ecf45898..0dd4b5e96f 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -41,6 +41,7 @@ protected:
     void handleLoginFailed();
 
     Q_INVOKABLE void login(const QString& username, const QString& password);
+    Q_INVOKABLE void loginThroughSteam();
     Q_INVOKABLE void openUrl(const QString& url);
 private:
     QString _statusText;
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index c4bfae7cac..8c0fa5ed92 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -505,6 +505,29 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas
     connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
 }
 
+void AccountManager::requestAccessTokenWithSteam(QByteArray authSessionTicket) {
+    QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
+
+    QNetworkRequest request;
+    request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter());
+
+    QUrl grantURL = _authURL;
+    grantURL.setPath("/oauth/token");
+
+    const QString ACCOUNT_MANAGER_REQUESTED_SCOPE = "owner";
+
+    QByteArray postData;
+    postData.append("grant_type=password&");
+    postData.append("ticket=" + QUrl::toPercentEncoding(authSessionTicket) + "&");
+    postData.append("scope=" + ACCOUNT_MANAGER_REQUESTED_SCOPE);
+
+    request.setUrl(grantURL);
+    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+
+    QNetworkReply* requestReply = networkAccessManager.post(request, postData);
+    connect(requestReply, &QNetworkReply::finished, this, &AccountManager::requestAccessTokenFinished);
+    connect(requestReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestAccessTokenError(QNetworkReply::NetworkError)));
+}
 
 void AccountManager::requestAccessTokenFinished() {
     QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index 846cdb6220..eb4d224501 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -96,6 +96,7 @@ public:
 
 public slots:
     void requestAccessToken(const QString& login, const QString& password);
+    void requestAccessTokenWithSteam(QByteArray authSessionTicket);
 
     void requestAccessTokenFinished();
     void requestProfileFinished();
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index 0f06e03672..1dbfc0ce00 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -11,9 +11,180 @@
 
 #include "SteamClient.h"
 
+#include <atomic>
+#include <memory>
+
+#include <QtCore/QDebug>
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Woverloaded-virtual"
+#endif
+
 #include <steam/steam_api.h>
 
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+
+static const Ticket INVALID_TICKET = Ticket();
+
+class SteamTicketRequests {
+public:
+    SteamTicketRequests();
+    ~SteamTicketRequests();
+
+    HAuthTicket startRequest(TicketRequestCallback callback);
+    void stopRequest(HAuthTicket authTicket);
+
+    STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse,
+                   GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse);
+
+private:
+    void stopAll();
+
+    struct PendingTicket {
+        HAuthTicket authTicket;
+        Ticket ticket;
+        TicketRequestCallback callback;
+    };
+
+    std::vector<PendingTicket> _pendingTickets;
+};
+
+SteamTicketRequests::SteamTicketRequests() :
+    _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse)
+{
+}
+
+SteamTicketRequests::~SteamTicketRequests() {
+    stopAll();
+}
+
+HAuthTicket SteamTicketRequests::startRequest(TicketRequestCallback callback) {
+    static const uint32 MAX_TICKET_SIZE { 1024 };
+    uint32 ticketSize { 0 };
+    char ticket[MAX_TICKET_SIZE];
+
+    auto authTicket = SteamUser()->GetAuthSessionTicket(ticket, MAX_TICKET_SIZE, &ticketSize);
+    qDebug() << "Got Steam auth session ticket:" << authTicket;
+
+    if (authTicket == k_HAuthTicketInvalid) {
+        qWarning() << "Auth session ticket is invalid.";
+        callback(INVALID_TICKET);
+    } else {
+        PendingTicket pendingTicket{ authTicket, QByteArray(ticket, ticketSize).toHex(), callback };
+        _pendingTickets.push_back(pendingTicket);
+    }
+
+    return authTicket;
+}
+
+void SteamTicketRequests::stopRequest(HAuthTicket authTicket) {
+    auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) {
+        return pendingTicket.authTicket == authTicket;
+    });
+
+    if (it != _pendingTickets.end()) {
+        SteamUser()->CancelAuthTicket(it->authTicket);
+        it->callback(INVALID_TICKET);
+        _pendingTickets.erase(it);
+    }
+}
+
+void SteamTicketRequests::stopAll() {
+    auto steamUser = SteamUser();
+    if (steamUser) {
+        for (const auto& pendingTicket : _pendingTickets) {
+            steamUser->CancelAuthTicket(pendingTicket.authTicket);
+            pendingTicket.callback(INVALID_TICKET);
+        }
+    }
+    _pendingTickets.clear();
+}
+
+void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketResponse_t* pCallback) {
+    auto authTicket = pCallback->m_hAuthTicket;
+
+    auto it = std::find_if(_pendingTickets.begin(), _pendingTickets.end(), [&authTicket](const PendingTicket& pendingTicket) {
+        return pendingTicket.authTicket == authTicket;
+    });
+
+
+    if (it != _pendingTickets.end()) {
+
+        if (pCallback->m_eResult == k_EResultOK) {
+            qDebug() << "Got steam callback, auth session ticket is valid. Send it." << authTicket;
+            it->callback(it->ticket);
+        } else {
+            qWarning() << "Steam auth session ticket callback encountered an error:" << pCallback->m_eResult;
+            it->callback(INVALID_TICKET);
+        }
+
+        _pendingTickets.erase(it);
+    } else {
+        qWarning() << "Could not find steam auth session ticket in list of pending tickets:" << authTicket;
+    }
+}
 
 
 
+static std::atomic_bool initialized { false };
+static std::unique_ptr<SteamTicketRequests> steamTicketRequests;
+
+bool SteamClient::init() {
+    if (!initialized) {
+        initialized = SteamAPI_Init();
+    }
+
+    if (!steamTicketRequests && initialized) {
+        steamTicketRequests.reset(new SteamTicketRequests());
+    }
+
+    return initialized;
+}
+
+void SteamClient::shutdown() {
+    if (initialized) {
+        SteamAPI_Shutdown();
+    }
+
+    if (steamTicketRequests) {
+        steamTicketRequests.reset();
+    }
+}
+
+void SteamClient::runCallbacks() {
+    if (!initialized) {
+        init();
+    }
+
+    if (!initialized) {
+        qDebug() << "Steam not initialized";
+        return;
+    }
+
+    auto steamPipe = SteamAPI_GetHSteamPipe();
+    if (!steamPipe) {
+        qDebug() << "Could not get SteamPipe";
+        return;
+    }
+
+    Steam_RunCallbacks(steamPipe, false);
+}
+
+void SteamClient::requestTicket(TicketRequestCallback callback) {
+    if (!initialized) {
+        init();
+    }
+
+    if (!initialized) {
+        qDebug() << "Steam not initialized";
+        return;
+    }
+
+    steamTicketRequests->startRequest(callback);
+}
+
 
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
index 369641b0c7..ac5c648ead 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
@@ -13,8 +13,21 @@
 #ifndef hifi_SteamClient_h
 #define hifi_SteamClient_h
 
-class SteamClient {
+#include <functional>
 
+#include <QtCore/QByteArray>
+
+using Ticket = QByteArray;
+using TicketRequestCallback = std::function<void(Ticket)>;
+
+class SteamClient {
+public:
+    static bool init();
+    static void shutdown();
+
+    static void runCallbacks();
+
+    static void requestTicket(TicketRequestCallback callback);
 
 };
 

From 205df0cf5170a1ab663dd7ca94f7c0571f917f1d Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Wed, 20 Jul 2016 10:23:10 -0700
Subject: [PATCH 03/18] login UI first draft

---
 interface/resources/qml/LoginDialog.qml       | 355 ++++--------------
 .../qml/LoginDialog/CompleteProfileBody.qml   |  93 +++++
 .../qml/LoginDialog/EmailSentBody.qml         |  83 ++++
 .../qml/LoginDialog/LinkAccountBody.qml       | 165 ++++++++
 .../qml/LoginDialog/RecoverPasswordBody.qml   | 106 ++++++
 .../resources/qml/LoginDialog/SignInBody.qml  | 110 ++++++
 .../qml/LoginDialog/UsernameCollisionBody.qml | 162 ++++++++
 .../resources/qml/LoginDialog/WelcomeBody.qml |  83 ++++
 interface/resources/qml/LoginDialogSave.qml   | 197 ++++++++++
 .../qml/controls-uit/HorizontalRule.qml       |  20 +
 .../qml/controls-uit/HorizontalSpacer.qml     |  21 ++
 .../qml/controls-uit/VerticalSpacer.qml       |   3 +
 .../resources/qml/dialogs/MessageDialog.qml   |   6 +-
 .../resources/qml/styles-uit/ButtonLabel.qml  |  18 +
 .../resources/qml/styles-uit/IconButton.qml   |  20 +
 .../resources/qml/styles-uit/InputLabel.qml   |  18 +
 .../resources/qml/styles-uit/ListItem.qml     |  18 +
 interface/resources/qml/styles-uit/Logs.qml   |  18 +
 .../resources/qml/styles-uit/MenuItem.qml     |  19 +
 .../resources/qml/styles-uit/OverlayTitle.qml |  18 +
 .../resources/qml/styles-uit/SectionName.qml  |  19 +
 .../resources/qml/styles-uit/ShortcutText.qml |  18 +
 .../resources/qml/styles-uit/TabName.qml      |  19 +
 .../qml/styles-uit/TextFieldInput.qml         |  18 +
 interface/src/Application.cpp                 |   9 +-
 interface/src/ui/LoginDialog.cpp              |  33 +-
 interface/src/ui/LoginDialog.h                |  17 +-
 .../networking/src/NetworkingConstants.h      |   2 +-
 28 files changed, 1321 insertions(+), 347 deletions(-)
 create mode 100644 interface/resources/qml/LoginDialog/CompleteProfileBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/EmailSentBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/LinkAccountBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/SignInBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
 create mode 100644 interface/resources/qml/LoginDialog/WelcomeBody.qml
 create mode 100644 interface/resources/qml/LoginDialogSave.qml
 create mode 100644 interface/resources/qml/controls-uit/HorizontalRule.qml
 create mode 100644 interface/resources/qml/controls-uit/HorizontalSpacer.qml
 create mode 100644 interface/resources/qml/styles-uit/ButtonLabel.qml
 create mode 100644 interface/resources/qml/styles-uit/IconButton.qml
 create mode 100644 interface/resources/qml/styles-uit/InputLabel.qml
 create mode 100644 interface/resources/qml/styles-uit/ListItem.qml
 create mode 100644 interface/resources/qml/styles-uit/Logs.qml
 create mode 100644 interface/resources/qml/styles-uit/MenuItem.qml
 create mode 100644 interface/resources/qml/styles-uit/OverlayTitle.qml
 create mode 100644 interface/resources/qml/styles-uit/SectionName.qml
 create mode 100644 interface/resources/qml/styles-uit/ShortcutText.qml
 create mode 100644 interface/resources/qml/styles-uit/TabName.qml
 create mode 100644 interface/resources/qml/styles-uit/TextFieldInput.qml

diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index eccea00143..3e8747c076 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -10,320 +10,91 @@
 
 import Hifi 1.0
 import QtQuick 2.4
-import "controls"
-import "styles"
+
+import "controls-uit"
+import "styles-uit"
 import "windows"
 
-ScrollingWindow {
+import "LoginDialog"
+
+ModalWindow {
     id: root
     HifiConstants { id: hifi }
     objectName: "LoginDialog"
-    height: loginDialog.implicitHeight
-    width: loginDialog.implicitWidth
-    // FIXME make movable
-    anchors.centerIn: parent
-    destroyOnHidden: false
-    hideBackground: true
-    shown: false
+    implicitWidth: 520
+    implicitHeight: 320
+    destroyOnCloseButton: true
+    destroyOnHidden: true
+    visible: true
+
+    property string iconText: ""
+    property int iconSize: 50
+
+    property string title: ""
+    property int titleWidth: 0
+
+    Component {
+        id: signInBody
+        SignInBody {}
+    }
+    Component {
+        id: welcomeBody
+        WelcomeBody {}
+    }
 
     LoginDialog {
         id: loginDialog
-        implicitWidth: backgroundRectangle.width
-        implicitHeight: backgroundRectangle.height
-        readonly property int inputWidth: 500
-        readonly property int inputHeight: 60
-        readonly property int borderWidth: 30
-        readonly property int closeMargin: 16
-        readonly property real tan30: 0.577  // tan(30°)
-        readonly property int inputSpacing: 16
 
-        Rectangle {
-            id: backgroundRectangle
-            width: loginDialog.inputWidth + loginDialog.borderWidth * 2
-            height: loginDialog.inputHeight * 6 + loginDialog.closeMargin * 2
-            radius: loginDialog.closeMargin * 2
-            color: "#2c86b1"
-            opacity: 0.85
+        Loader {
+            id: bodyLoader
+            anchors.fill: parent
+            sourceComponent: signInBody
         }
 
-        Column {
-            id: mainContent
-            width: loginDialog.inputWidth
-            spacing: loginDialog.inputSpacing
-            anchors {
-                horizontalCenter: parent.horizontalCenter
-                verticalCenter: parent.verticalCenter
+        Connections {
+            target: loginDialog
+            onHandleLoginCompleted: {
+                console.log("Login Succeeded")
+                bodyLoader.sourceComponent = welcomeBody
+                bodyLoader.active = true
             }
-
-            Item {
-                // Offset content down a little
-                width: loginDialog.inputWidth
-                height: loginDialog.closeMargin
+            onHandleLoginFailed: {
+                console.log("Login Failed")
             }
-
-            Rectangle {
-                width: loginDialog.inputWidth
-                height: loginDialog.inputHeight
-                radius: height / 2
-                color: "#ebebeb"
-
-                Image {
-                    source: "../images/login-username.svg"
-                    width: loginDialog.inputHeight * 0.65
-                    height: width
-                    sourceSize: Qt.size(width, height);
-                    anchors {
-                        verticalCenter: parent.verticalCenter
-                        left: parent.left
-                        leftMargin: loginDialog.inputHeight / 4
-                    }
-                }
-
-                TextInput {
-                    id: username
-                    anchors {
-                        fill: parent
-                        leftMargin: loginDialog.inputHeight
-                        rightMargin: loginDialog.inputHeight / 2
-                    }
-
-                    helperText: "username or email"
-                    color: hifi.colors.text
-
-                    KeyNavigation.tab: password
-                    KeyNavigation.backtab: password
-                }
-            }
-
-            Rectangle {
-                width: loginDialog.inputWidth
-                height: loginDialog.inputHeight
-                radius: height / 2
-                color: "#ebebeb"
-
-                Image {
-                    source: "../images/login-password.svg"
-                    width: loginDialog.inputHeight * 0.65
-                    height: width
-                    sourceSize: Qt.size(width, height);
-                    anchors {
-                        verticalCenter: parent.verticalCenter
-                        left: parent.left
-                        leftMargin: loginDialog.inputHeight / 4
-                    }
-                }
-
-                TextInput {
-                    id: password
-                    anchors {
-                        fill: parent
-                        leftMargin: loginDialog.inputHeight
-                        rightMargin: loginDialog.inputHeight / 2
-                    }
-
-                    helperText: "password"
-                    echoMode: TextInput.Password
-                    color: hifi.colors.text
-
-                    KeyNavigation.tab: username
-                    KeyNavigation.backtab: username
-                    onFocusChanged: {
-                        if (password.focus) {
-                            password.selectAll()
-                        }
-                    }
-                }
-            }
-
-            Item {
-                width: loginDialog.inputWidth
-                height: loginDialog.inputHeight / 2
-
-                Text {
-                    id: messageText
-
-                    visible: loginDialog.statusText != "" && loginDialog.statusText != "Logging in..."
-
-                    width: loginDialog.inputWidth
-                    height: loginDialog.inputHeight / 2
-                    horizontalAlignment: Text.AlignHCenter
-                    verticalAlignment: Text.AlignVCenter
-
-                    text: loginDialog.statusText
-                    color: "white"
-                }
-
-                Row {
-                    id: messageSpinner
-
-                    visible: loginDialog.statusText == "Logging in..."
-                    onVisibleChanged: visible ? messageSpinnerAnimation.restart() : messageSpinnerAnimation.stop()
-
-                    spacing: 24
-                    anchors {
-                        verticalCenter: parent.verticalCenter
-                        horizontalCenter: parent.horizontalCenter
-                    }
-
-                    Rectangle {
-                        id: spinner1
-                        width: 10
-                        height: 10
-                        color: "#ebebeb"
-                        opacity: 0.05
-                    }
-
-                    Rectangle {
-                        id: spinner2
-                        width: 10
-                        height: 10
-                        color: "#ebebeb"
-                        opacity: 0.05
-                    }
-
-                    Rectangle {
-                        id: spinner3
-                        width: 10
-                        height: 10
-                        color: "#ebebeb"
-                        opacity: 0.05
-                    }
-
-                    SequentialAnimation {
-                        id: messageSpinnerAnimation
-                        running: messageSpinner.visible
-                        loops: Animation.Infinite
-                        NumberAnimation { target: spinner1; property: "opacity"; to: 1.0; duration: 1000 }
-                        NumberAnimation { target: spinner2; property: "opacity"; to: 1.0; duration: 1000 }
-                        NumberAnimation { target: spinner3; property: "opacity"; to: 1.0; duration: 1000 }
-                        NumberAnimation { target: spinner1; property: "opacity"; to: 0.05; duration: 0 }
-                        NumberAnimation { target: spinner2; property: "opacity"; to: 0.05; duration: 0 }
-                        NumberAnimation { target: spinner3; property: "opacity"; to: 0.05; duration: 0 }
-                    }
-                }
-            }
-
-            Row {
-                width: loginDialog.inputWidth
-                height: loginDialog.inputHeight
-
-                Rectangle {
-                    width: loginDialog.inputWidth / 3
-                    height: loginDialog.inputHeight
-                    radius: height / 2
-                    color: "#353535"
-
-                    TextInput {
-                        anchors.fill: parent
-                        text: "Login"
-                        color: "white"
-                        horizontalAlignment: Text.AlignHCenter
-                    }
-
-                    MouseArea {
-                        anchors.fill: parent
-                        cursorShape: Qt.PointingHandCursor
-                        onClicked: {
-                            loginDialog.login(username.text, password.text)
-                        }
-                    }
-                }
-
-                Image {
-                    source: "../images/steam-sign-in.png"
-                    width: loginDialog.inputWidth / 3
-                    height: loginDialog.inputHeight
-                    anchors {
-                        verticalCenter: parent.verticalCenter
-                        right: parent.right
-                        leftMargin: loginDialog.inputHeight / 4
-                    }
-
-                    MouseArea {
-                        anchors.fill: parent
-                        cursorShape: Qt.PointingHandCursor
-                        onClicked: {
-                            loginDialog.loginThroughSteam()
-                        }
-                    }
-                }
-            }
-
-            Item {
-                anchors { left: parent.left; right: parent.right; }
-                height: loginDialog.inputHeight
-
-                Image {
-                    id: hifiIcon
-                    source: "../images/hifi-logo-blackish.svg"
-                    width: loginDialog.inputHeight
-                    height: width
-                    sourceSize: Qt.size(width, height);
-                    anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter }
-                }
-
-                Text {
-                    anchors { verticalCenter: parent.verticalCenter; right: hifiIcon.left; margins: loginDialog.inputSpacing }
-                    text: "Password?"
-                    scale: 0.8
-                    font.underline: true
-                    color: "#e0e0e0"
-                    MouseArea {
-                        anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
-                        cursorShape: Qt.PointingHandCursor
-                        onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
-                    }
-                }
-
-                Text {
-                    anchors { verticalCenter: parent.verticalCenter; left: hifiIcon.right; margins: loginDialog.inputSpacing }
-                    text: "Register"
-                    scale: 0.8
-                    font.underline: true
-                    color: "#e0e0e0"
-
-                    MouseArea {
-                        anchors { fill: parent; margins: -loginDialog.inputSpacing / 2 }
-                        cursorShape: Qt.PointingHandCursor
-                        onClicked: loginDialog.openUrl(loginDialog.rootUrl + "/signup")
-                    }
-                }
-
-            }
-        }
-    }
-
-    onShownChanged: {
-        if (!shown) {
-            username.text = ""
-            password.text = ""
-            loginDialog.statusText = ""
-        } else {
-            username.forceActiveFocus()
         }
     }
 
     Keys.onPressed: {
-        switch (event.key) {
+        if (!visible) {
+            return
+        }
+
+        if (event.modifiers === Qt.ControlModifier)
+            switch (event.key) {
+            case Qt.Key_A:
+                event.accepted = true
+                detailedText.selectAll()
+                break
+            case Qt.Key_C:
+                event.accepted = true
+                detailedText.copy()
+                break
+            case Qt.Key_Period:
+                if (Qt.platform.os === "osx") {
+                    event.accepted = true
+                    content.reject()
+                }
+                break
+        } else switch (event.key) {
             case Qt.Key_Escape:
             case Qt.Key_Back:
-                root.shown = false;
-                event.accepted = true;
-                break;
+                event.accepted = true
+                destroy()
+                break
 
             case Qt.Key_Enter:
             case Qt.Key_Return:
-                if (username.activeFocus) {
-                    event.accepted = true
-                    password.forceActiveFocus()
-                } else if (password.activeFocus) {
-                    event.accepted = true
-                    if (username.text == "") {
-                        username.forceActiveFocus()
-                    } else {
-                        loginDialog.login(username.text, password.text)
-                    }
-                }
+                event.accepted = true
                 break
         }
     }
diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
new file mode 100644
index 0000000000..b6ef012e2a
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
@@ -0,0 +1,93 @@
+//
+//  CompleteProfileBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: completeProfileBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth)
+            var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: 3 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+            width: 200
+
+            text: qsTr("Create your profile")
+            color: hifi.buttons.blue
+        }
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Cancel")
+
+
+            onClicked: root.destroy()
+        }
+    }
+
+    ShortcutText {
+        id: additionalTextContainer
+        anchors {
+            top: buttons.bottom
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: "Already have a High Fidelity profile? Link to an existing profile here."
+
+        font.underline: true
+        wrapMode: Text.WordWrap
+        color: hifi.colors.blueAccent
+        lineHeight: 2
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+    Component.onCompleted: {
+        root.title = qsTr("Complete Your Profile")
+        root.iconText = "<"
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml
new file mode 100644
index 0000000000..eede996412
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/EmailSentBody.qml
@@ -0,0 +1,83 @@
+//
+//  EmailSentBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: emailSentBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    property string email: "clement@highfidelity.com"
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+            var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: qsTr("An email with instructions on reseting your password was sent to <br/><b>") + email + "</b>"
+        wrapMode: Text.WordWrap
+        color: hifi.colors.baseGrayHighlight
+        lineHeight: 2
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: mainTextContainer.bottom
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Close");
+
+            onClicked: root.destroy()
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = qsTr("Email Sent")
+        root.iconText = ""
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
new file mode 100644
index 0000000000..b7ff756fa3
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
@@ -0,0 +1,165 @@
+//
+//  LinkAccountBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: linkAccountBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    property bool existingEmail: true
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0)
+            var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) +
+                               4 * hifi.dimensions.contentSpacing.y + form.height +
+                               4 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+        visible: existingEmail
+
+        text: qsTr("Your Steam account's email matches an existing High Fidelity Profile")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.redAccent
+        lineHeight: 2
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+
+    Column {
+        id: form
+        anchors {
+            top: mainTextContainer.bottom
+            left: parent.left
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: 2 * hifi.dimensions.contentSpacing.y
+
+        Row {
+            spacing: hifi.dimensions.contentSpacing.x
+
+            TextField {
+                id: usernameField
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+                width: 350
+
+                label: "User Name or Email"
+            }
+
+            ShortcutText {
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+
+                text: "Need help?"
+
+                color: hifi.colors.blueAccent
+                font.underline: true
+
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+        Row {
+            spacing: hifi.dimensions.contentSpacing.x
+
+            TextField {
+                id: passwordField
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+                width: 350
+
+                label: "Password"
+                echoMode: TextInput.Password
+            }
+
+            ShortcutText {
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+
+                text: "Need help?"
+
+                color: hifi.colors.blueAccent
+                font.underline: true
+
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: form.bottom
+            right: parent.right
+            margins: 0
+            topMargin: 3 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+            width: 200
+
+            text: qsTr("Link Account")
+            color: hifi.buttons.blue
+        }
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Cancel")
+
+            onClicked: root.destroy()
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = qsTr("Sign Into High Fidelity")
+        root.iconText = "<"
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
new file mode 100644
index 0000000000..74dcb18054
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
@@ -0,0 +1,106 @@
+//
+//  RecoverPasswordBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: recoverPasswordBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+            var targetHeight = mainTextContainer.height +
+                               3 * hifi.dimensions.contentSpacing.y + emailField.height +
+                               4 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            left: parent.left
+            right: parent.right
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: qsTr("In order to help you reset your password, we will send an<br/>email with instructions to your email address.")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.baseGrayHighlight
+        lineHeight: 1
+        horizontalAlignment: Text.AlignHLeft
+    }
+
+
+    TextField {
+        id: emailField
+        anchors {
+            top: mainTextContainer.bottom
+            left: parent.left
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+
+        width: 350
+
+        label: "Email address"
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: emailField.bottom
+            right: parent.right
+            margins: 0
+            topMargin: 3 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+            width: 200
+
+            text: qsTr("Send recovery email")
+            color: hifi.buttons.blue
+        }
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Back")
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = qsTr("Recover Password")
+        root.iconText = "<"
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml
new file mode 100644
index 0000000000..d3f6926bd2
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/SignInBody.qml
@@ -0,0 +1,110 @@
+//
+//  SignInBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: signInBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    property bool required: false
+
+    function login() {
+        console.log("Trying to log in")
+        loginDialog.loginThroughSteam()
+    }
+
+    function cancel() {
+        root.destroy()
+    }
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+            var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: required ? qsTr("This domain's owner requires that you sign in:")
+                       : qsTr("Sign in to access your user account:")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.baseGrayHighlight
+        lineHeight: 2
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: mainTextContainer.bottom
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            width: undefined // invalidate so that the image's size sets the width
+            height: undefined // invalidate so that the image's size sets the height
+            focus: true
+
+            style: OriginalStyles.ButtonStyle {
+                background: Image {
+                    id: buttonImage
+                    source: "../../images/steam-sign-in.png"
+                }
+            }
+            onClicked: signInBody.login()
+        }
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Cancel");
+
+            onClicked: signInBody.cancel()
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = required ? qsTr("Sign In Required")
+                              : qsTr("Sign In")
+        root.iconText = ""
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
new file mode 100644
index 0000000000..ea5a9bb614
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
@@ -0,0 +1,162 @@
+//
+//  UsernameCollisionBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+import QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4 as OriginalStyles
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: usernameCollisionBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0)
+            var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) +
+                               4 * hifi.dimensions.contentSpacing.y + form.height +
+                               4 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: qsTr("Choose your High Fidelity user name:")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.baseGrayHighlight
+        lineHeight: 1
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+
+    Column {
+        id: form
+        anchors {
+            top: mainTextContainer.bottom
+            left: parent.left
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: 2 * hifi.dimensions.contentSpacing.y
+
+        Row {
+            spacing: hifi.dimensions.contentSpacing.x
+
+            TextField {
+                id: usernameField
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+                width: 350
+
+                label: "User Name or Email"
+            }
+
+            ShortcutText {
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+
+                text: "Need help?"
+
+                color: hifi.colors.blueAccent
+                font.underline: true
+
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+        Row {
+            spacing: hifi.dimensions.contentSpacing.x
+
+            TextField {
+                id: passwordField
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+                width: 350
+
+                label: "Password"
+                echoMode: TextInput.Password
+            }
+
+            ShortcutText {
+                anchors {
+                    verticalCenter: parent.verticalCenter
+                }
+
+                text: "Need help?"
+
+                color: hifi.colors.blueAccent
+                font.underline: true
+
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: form.bottom
+            right: parent.right
+            margins: 0
+            topMargin: 3 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+            width: 200
+
+            text: qsTr("Create your profile")
+            color: hifi.buttons.blue
+        }
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Cancel")
+
+            onClicked: root.destroy()
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = qsTr("Complete Your Profile")
+        root.iconText = "<"
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml
new file mode 100644
index 0000000000..8b771eac1f
--- /dev/null
+++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml
@@ -0,0 +1,83 @@
+//
+//  WelcomeBody.qml
+//
+//  Created by Clement on 7/18/16
+//  Copyright 2015 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.4
+
+import "../controls-uit"
+import "../styles-uit"
+
+Item {
+    id: welcomeBody
+    clip: true
+    width: pane.width
+    height: pane.height
+
+    property bool welcomeBack: true
+
+    QtObject {
+        id: d
+        readonly property int minWidth: 480
+        readonly property int maxWidth: 1280
+        readonly property int minHeight: 120
+        readonly property int maxHeight: 720
+
+        function resize() {
+            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
+            var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
+
+            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
+            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
+        }
+    }
+
+    MenuItem {
+        id: mainTextContainer
+        anchors {
+            top: parent.top
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: hifi.dimensions.contentSpacing.y
+        }
+
+        text: qsTr("You are now signed into High Fidelity")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.baseGrayHighlight
+        lineHeight: 2
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+    Row {
+        id: buttons
+        anchors {
+            top: mainTextContainer.bottom
+            horizontalCenter: parent.horizontalCenter
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        spacing: hifi.dimensions.contentSpacing.x
+        onHeightChanged: d.resize(); onWidthChanged: d.resize();
+
+        Button {
+            anchors.verticalCenter: parent.verticalCenter
+
+            text: qsTr("Close");
+
+            onClicked: root.destroy()
+        }
+    }
+
+    Component.onCompleted: {
+        root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.getUsername() + qsTr("</b>!")
+        root.iconText = ""
+        d.resize();
+    }
+}
diff --git a/interface/resources/qml/LoginDialogSave.qml b/interface/resources/qml/LoginDialogSave.qml
new file mode 100644
index 0000000000..46246fc1a5
--- /dev/null
+++ b/interface/resources/qml/LoginDialogSave.qml
@@ -0,0 +1,197 @@
+Window {
+    id: root
+    HifiConstants { id: hifi }
+
+    width: 550
+    height: 200
+
+    anchors.centerIn: parent
+    resizable: true
+
+    property bool required: false
+
+    Component {
+        id: welcomeBody
+
+        Column {
+            anchors.centerIn: parent
+
+            OverlayTitle {
+                anchors.horizontalCenter: parent.horizontalCenter
+
+                text: "Welcomeback Atlante45!"
+                color: hifi.colors.baseGrayHighlight
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+
+            VerticalSpacer {}
+
+            HorizontalRule {}
+
+            MenuItem {
+                id: details
+                anchors.horizontalCenter: parent.horizontalCenter
+
+                text: "You are now signed into High Fidelity"
+                color: hifi.colors.baseGrayHighlight
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+
+            VerticalSpacer {}
+
+            Button {
+                anchors.horizontalCenter: parent.horizontalCenter
+
+                text: "Close"
+            }
+        }
+    }
+
+    Component {
+        id: signInBody
+
+        Column {
+          anchors.centerIn: parent
+
+          OverlayTitle {
+            anchors.horizontalCenter: parent.horizontalCenter
+
+            text: required ? "Sign In Required" : "Sign In"
+            color: hifi.colors.baseGrayHighlight
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+          }
+
+          VerticalSpacer {}
+
+          HorizontalRule {}
+
+          MenuItem {
+            id: details
+            anchors.horizontalCenter: parent.horizontalCenter
+
+            text: required ? "This domain's owner requires that you sign in:"
+                           : "Sign in to access your user account:"
+            color: hifi.colors.baseGrayHighlight
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+          }
+
+          VerticalSpacer {}
+
+          Row {
+            anchors.horizontalCenter: parent.horizontalCenter
+
+            Button {
+              anchors.verticalCenter: parent.verticalCenter
+              width: undefined // invalidate so that the image's size sets the width
+              height: undefined // invalidate so that the image's size sets the height
+
+              style: Original.ButtonStyle {
+                background: Image {
+                    id: buttonImage
+                    source: "../images/steam-sign-in.png"
+                }
+              }
+
+              onClicked: body.sourceComponent = completeProfileBody
+            }
+
+            HorizontalSpacer {}
+
+            Button {
+              anchors.verticalCenter: parent.verticalCenter
+
+              text: "Cancel"
+
+              onClicked: required = !required
+            }
+          }
+        }
+    }
+
+    Component {
+        id: completeProfileBody
+
+        Column {
+            anchors.centerIn: parent
+
+            Row {
+                anchors.horizontalCenter: parent.horizontalCenter
+
+                HiFiGlyphs {
+                    anchors.verticalCenter: parent.verticalCenter
+
+                    text: hifi.glyphs.avatar
+                }
+
+                OverlayTitle {
+                    anchors.verticalCenter: parent.verticalCenter
+
+                    text: "Complete Your Profile"
+                    color: hifi.colors.baseGrayHighlight
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignHCenter
+                }
+            }
+
+            VerticalSpacer {}
+
+            HorizontalRule {}
+
+            VerticalSpacer {}
+
+            Row {
+                anchors.horizontalCenter: parent.horizontalCenter
+
+                Button {
+                  anchors.verticalCenter: parent.verticalCenter
+
+                  width: 200
+
+                  text: "Create your profile"
+                  color: hifi.buttons.blue
+
+                  onClicked: body.sourceComponent = welcomeBody
+                }
+
+                HorizontalSpacer {}
+
+                Button {
+                  anchors.verticalCenter: parent.verticalCenter
+
+                  text: "Cancel"
+
+                  onClicked: body.sourceComponent = signInBody
+
+                }
+            }
+
+            VerticalSpacer {}
+
+            ShortcutText {
+                text: "Already have a High Fidelity profile? Link to an existing profile here."
+
+                color: hifi.colors.blueAccent
+                font.underline: true
+
+                verticalAlignment: Text.AlignVCenter
+                horizontalAlignment: Text.AlignHCenter
+            }
+        }
+    }
+
+    Rectangle {
+      anchors.fill: root
+      color: hifi.colors.faintGray
+      radius: hifi.dimensions.borderRadius
+
+      Loader {
+        id: body
+        anchors.centerIn: parent
+        sourceComponent: signInBody
+      }
+    }
+}
diff --git a/interface/resources/qml/controls-uit/HorizontalRule.qml b/interface/resources/qml/controls-uit/HorizontalRule.qml
new file mode 100644
index 0000000000..425500f1d4
--- /dev/null
+++ b/interface/resources/qml/controls-uit/HorizontalRule.qml
@@ -0,0 +1,20 @@
+//
+//  HorizontalRule.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+
+Rectangle {
+  anchors.left: parent.left
+  anchors.right: parent.right
+  height: 1
+  color: hifi.colors.lightGray
+}
diff --git a/interface/resources/qml/controls-uit/HorizontalSpacer.qml b/interface/resources/qml/controls-uit/HorizontalSpacer.qml
new file mode 100644
index 0000000000..545154ab44
--- /dev/null
+++ b/interface/resources/qml/controls-uit/HorizontalSpacer.qml
@@ -0,0 +1,21 @@
+//
+//  HorizontalSpacer.qml
+//
+//  Created by Clement on 7/18/16
+//  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 {
+    id: root
+    property alias size: root.width
+
+    width: hifi.dimensions.controlInterlineHeight
+    height: 1  // Must be non-zero
+}
diff --git a/interface/resources/qml/controls-uit/VerticalSpacer.qml b/interface/resources/qml/controls-uit/VerticalSpacer.qml
index 6fc49605c0..2df65f1002 100644
--- a/interface/resources/qml/controls-uit/VerticalSpacer.qml
+++ b/interface/resources/qml/controls-uit/VerticalSpacer.qml
@@ -13,6 +13,9 @@ import QtQuick 2.5
 import "../styles-uit"
 
 Item {
+    id: root
+    property alias size: root.height
+
     width: 1  // Must be non-zero
     height: hifi.dimensions.controlInterlineHeight
 }
diff --git a/interface/resources/qml/dialogs/MessageDialog.qml b/interface/resources/qml/dialogs/MessageDialog.qml
index d390ea08bf..40c5a01e15 100644
--- a/interface/resources/qml/dialogs/MessageDialog.qml
+++ b/interface/resources/qml/dialogs/MessageDialog.qml
@@ -21,8 +21,6 @@ import "messageDialog"
 ModalWindow {
     id: root
     HifiConstants { id: hifi }
-    implicitWidth: 640
-    implicitHeight: 320
     destroyOnCloseButton: true
     destroyOnHidden: true
     visible: true
@@ -70,7 +68,7 @@ ModalWindow {
         QtObject {
             id: d
             readonly property int minWidth: 480
-            readonly property int maxWdith: 1280
+            readonly property int maxWidth: 1280
             readonly property int minHeight: 120
             readonly property int maxHeight: 720
 
@@ -80,7 +78,7 @@ ModalWindow {
                         + (informativeTextContainer.text != "" ? informativeTextContainer.contentHeight + 3 * hifi.dimensions.contentSpacing.y : 0)
                         + buttons.height
                         + (content.state === "expanded" ? details.implicitHeight + hifi.dimensions.contentSpacing.y : 0)
-                root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWdith) ? d.maxWidth : targetWidth)
+                root.width = (targetWidth < d.minWidth) ? d.minWidth : ((targetWidth > d.maxWidth) ? d.maxWidth : targetWidth)
                 root.height = (targetHeight < d.minHeight) ? d.minHeight: ((targetHeight > d.maxHeight) ? d.maxHeight : targetHeight)
             }
         }
diff --git a/interface/resources/qml/styles-uit/ButtonLabel.qml b/interface/resources/qml/styles-uit/ButtonLabel.qml
new file mode 100644
index 0000000000..aade5fb439
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ButtonLabel.qml
@@ -0,0 +1,18 @@
+//
+//  ButtonLabel.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayBold {
+    font.pixelSize: hifi.fontSizes.buttonLabel
+}
diff --git a/interface/resources/qml/styles-uit/IconButton.qml b/interface/resources/qml/styles-uit/IconButton.qml
new file mode 100644
index 0000000000..84c1ef14c1
--- /dev/null
+++ b/interface/resources/qml/styles-uit/IconButton.qml
@@ -0,0 +1,20 @@
+//
+//  IconButton.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+    font.pixelSize: hifi.fontSizes.iconButton
+    font.capitalization: Font.AllUppercase
+    font.letterSpacing: 1.5
+}
diff --git a/interface/resources/qml/styles-uit/InputLabel.qml b/interface/resources/qml/styles-uit/InputLabel.qml
new file mode 100644
index 0000000000..59657a554d
--- /dev/null
+++ b/interface/resources/qml/styles-uit/InputLabel.qml
@@ -0,0 +1,18 @@
+//
+//  InputLabel.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewaySemiBold {
+    font.pixelSize: hifi.fontSizes.inputLabel
+}
diff --git a/interface/resources/qml/styles-uit/ListItem.qml b/interface/resources/qml/styles-uit/ListItem.qml
new file mode 100644
index 0000000000..f707686edc
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ListItem.qml
@@ -0,0 +1,18 @@
+//
+//  ListItem.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+    font.pixelSize: hifi.fontSizes.listItem
+}
diff --git a/interface/resources/qml/styles-uit/Logs.qml b/interface/resources/qml/styles-uit/Logs.qml
new file mode 100644
index 0000000000..577fe2f8d8
--- /dev/null
+++ b/interface/resources/qml/styles-uit/Logs.qml
@@ -0,0 +1,18 @@
+//
+//  Logs.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+AnonymousProRegular {
+    font.pixelSize: hifi.fontSizes.logs
+}
diff --git a/interface/resources/qml/styles-uit/MenuItem.qml b/interface/resources/qml/styles-uit/MenuItem.qml
new file mode 100644
index 0000000000..4431c357bf
--- /dev/null
+++ b/interface/resources/qml/styles-uit/MenuItem.qml
@@ -0,0 +1,19 @@
+//
+//  MenuItem.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewaySemiBold {
+    lineHeight: 2
+    font.pixelSize: hifi.fontSizes.menuItem
+}
diff --git a/interface/resources/qml/styles-uit/OverlayTitle.qml b/interface/resources/qml/styles-uit/OverlayTitle.qml
new file mode 100644
index 0000000000..e23b9eca14
--- /dev/null
+++ b/interface/resources/qml/styles-uit/OverlayTitle.qml
@@ -0,0 +1,18 @@
+//
+//  OverlayTitle.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+    font.pixelSize: hifi.fontSizes.overlayTitle
+}
diff --git a/interface/resources/qml/styles-uit/SectionName.qml b/interface/resources/qml/styles-uit/SectionName.qml
new file mode 100644
index 0000000000..5438fec7bc
--- /dev/null
+++ b/interface/resources/qml/styles-uit/SectionName.qml
@@ -0,0 +1,19 @@
+//
+//  SectionName.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+    font.pixelSize: hifi.fontSizes.sectionName
+    font.capitalization: Font.AllUppercase
+}
diff --git a/interface/resources/qml/styles-uit/ShortcutText.qml b/interface/resources/qml/styles-uit/ShortcutText.qml
new file mode 100644
index 0000000000..a3ab351870
--- /dev/null
+++ b/interface/resources/qml/styles-uit/ShortcutText.qml
@@ -0,0 +1,18 @@
+//
+//  ShortcutText.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayLight {
+    font.pixelSize: hifi.fontSizes.shortcutText
+}
diff --git a/interface/resources/qml/styles-uit/TabName.qml b/interface/resources/qml/styles-uit/TabName.qml
new file mode 100644
index 0000000000..eb4e790e7e
--- /dev/null
+++ b/interface/resources/qml/styles-uit/TabName.qml
@@ -0,0 +1,19 @@
+//
+//  TabName.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+RalewayRegular {
+    font.pixelSize: hifi.fontSizes.tabName
+    font.capitalization: Font.AllUppercase
+}
diff --git a/interface/resources/qml/styles-uit/TextFieldInput.qml b/interface/resources/qml/styles-uit/TextFieldInput.qml
new file mode 100644
index 0000000000..010b4d03ad
--- /dev/null
+++ b/interface/resources/qml/styles-uit/TextFieldInput.qml
@@ -0,0 +1,18 @@
+//
+//  TextFieldInput.qml
+//
+//  Created by Clement on 7/18/16
+//  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 QtQuick.Controls 1.4
+import QtQuick.Controls.Styles 1.4
+import "."
+
+FiraSansSemiBold {
+    font.pixelSize: hifi.fontSizes.textFieldInput
+}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 1729a773c6..58284682f1 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2242,11 +2242,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
                 break;
 
             case Qt::Key_X:
-                if (isShifted && isMeta) {
+                if (isMeta) {
                     auto offscreenUi = DependencyManager::get<OffscreenUi>();
-                    offscreenUi->togglePinned();
-                    //offscreenUi->getRootContext()->engine()->clearComponentCache();
-                    //OffscreenUi::information("Debugging", "Component cache cleared");
+//                    offscreenUi->togglePinned();
+                    offscreenUi->getRootContext()->engine()->clearComponentCache();
+                    qDebug() << "Component cache cleared";
+//                    OffscreenUi::information("Debugging", "Component cache cleared");
                     // placeholder for dialogs being converted to QML.
                 }
                 break;
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index 8240340381..15a277f394 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -22,9 +22,7 @@
 
 HIFI_QML_DEF(LoginDialog)
 
-LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent),
-    _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString())
-{
+LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
     auto accountManager = DependencyManager::get<AccountManager>();
     connect(accountManager.data(), &AccountManager::loginComplete,
         this, &LoginDialog::handleLoginCompleted);
@@ -54,42 +52,16 @@ void LoginDialog::toggleAction() {
     }
 }
 
-void LoginDialog::handleLoginCompleted(const QUrl&) {
-    hide();
-}
-
-void LoginDialog::handleLoginFailed() {
-    setStatusText("Invalid username or password");
-}
-
-void LoginDialog::setStatusText(const QString& statusText) {
-    if (statusText != _statusText) {
-        _statusText = statusText;
-        emit statusTextChanged();
-    }
-}
-
-QString LoginDialog::statusText() const {
-    return _statusText;
-}
-
-QString LoginDialog::rootUrl() const {
-    return _rootUrl;
-}
-
 void LoginDialog::login(const QString& username, const QString& password) {
     qDebug() << "Attempting to login " << username;
-    setStatusText("Logging in...");
     DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
 }
 
 void LoginDialog::loginThroughSteam() {
     qDebug() << "Attempting to login through Steam";
-    setStatusText("Logging in...");
-
     SteamClient::requestTicket([this](Ticket ticket) {
         if (ticket.isNull()) {
-            setStatusText("Steam client not logged into an account");
+            emit handleLoginFailed();
             return;
         }
 
@@ -98,6 +70,5 @@ void LoginDialog::loginThroughSteam() {
 }
 
 void LoginDialog::openUrl(const QString& url) {
-    qDebug() << url;
     QDesktopServices::openUrl(url);
 }
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index 0dd4b5e96f..dcd0e04894 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -20,32 +20,19 @@ class LoginDialog : public OffscreenQmlDialog {
     Q_OBJECT
     HIFI_QML_DECL
 
-    Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
-    Q_PROPERTY(QString rootUrl READ rootUrl)
-
 public:
     static void toggleAction();
 
     LoginDialog(QQuickItem* parent = nullptr);
 
-    void setStatusText(const QString& statusText);
-    QString statusText() const;
-
-    QString rootUrl() const;
-
 signals:
-    void statusTextChanged();
-
-protected:
-    void handleLoginCompleted(const QUrl& authURL);
+    void handleLoginCompleted();
     void handleLoginFailed();
 
+protected:
     Q_INVOKABLE void login(const QString& username, const QString& password);
     Q_INVOKABLE void loginThroughSteam();
     Q_INVOKABLE void openUrl(const QString& url);
-private:
-    QString _statusText;
-    const QString _rootUrl;
 };
 
 #endif // hifi_LoginDialog_h
diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h
index a512ae8887..d1e0a46c71 100644
--- a/libraries/networking/src/NetworkingConstants.h
+++ b/libraries/networking/src/NetworkingConstants.h
@@ -15,7 +15,7 @@
 #include <QtCore/QUrl>
 
 namespace NetworkingConstants {
-    const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com");
+    const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000");
 }
 
 #endif // hifi_NetworkingConstants_h

From f0ff9752480485c9ddb5f8bae1afbb67550fe47c Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 22 Jul 2016 19:48:52 -0700
Subject: [PATCH 04/18] UI wiring

---
 interface/resources/qml/LoginDialog.qml       |  23 +--
 .../qml/LoginDialog/CompleteProfileBody.qml   |  37 +++++
 .../qml/LoginDialog/EmailSentBody.qml         |   6 +-
 .../qml/LoginDialog/LinkAccountBody.qml       |  81 +++++++++-
 .../qml/LoginDialog/RecoverPasswordBody.qml   |  38 ++++-
 .../resources/qml/LoginDialog/SignInBody.qml  |  18 +++
 .../qml/LoginDialog/UsernameCollisionBody.qml | 149 ++++++++----------
 .../resources/qml/LoginDialog/WelcomeBody.qml |  17 +-
 .../scripting/AccountScriptingInterface.cpp   |   3 +
 .../src/scripting/AccountScriptingInterface.h |   5 +
 interface/src/ui/LoginDialog.cpp              |  92 +++++++++++
 interface/src/ui/LoginDialog.h                |  24 ++-
 libraries/networking/src/AccountManager.cpp   |   1 +
 .../src/steamworks-wrapper/SteamClient.cpp    |  23 ++-
 .../src/steamworks-wrapper/SteamClient.h      |   2 +
 15 files changed, 394 insertions(+), 125 deletions(-)

diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml
index 3e8747c076..1f84024e15 100644
--- a/interface/resources/qml/LoginDialog.qml
+++ b/interface/resources/qml/LoginDialog.qml
@@ -33,34 +33,13 @@ ModalWindow {
     property string title: ""
     property int titleWidth: 0
 
-    Component {
-        id: signInBody
-        SignInBody {}
-    }
-    Component {
-        id: welcomeBody
-        WelcomeBody {}
-    }
-
     LoginDialog {
         id: loginDialog
 
         Loader {
             id: bodyLoader
             anchors.fill: parent
-            sourceComponent: signInBody
-        }
-
-        Connections {
-            target: loginDialog
-            onHandleLoginCompleted: {
-                console.log("Login Succeeded")
-                bodyLoader.sourceComponent = welcomeBody
-                bodyLoader.active = true
-            }
-            onHandleLoginFailed: {
-                console.log("Login Failed")
-            }
+            source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml"
         }
     }
 
diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
index b6ef012e2a..12d2cee73e 100644
--- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
+++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
@@ -54,6 +54,8 @@ Item {
 
             text: qsTr("Create your profile")
             color: hifi.buttons.blue
+
+            onClicked: loginDialog.createAccountFromStream()
         }
 
         Button {
@@ -83,6 +85,15 @@ Item {
         lineHeight: 2
         lineHeightMode: Text.ProportionalHeight
         horizontalAlignment: Text.AlignHCenter
+
+        MouseArea {
+            anchors.fill: parent
+            onClicked: {
+                bodyLoader.source = "LinkAccountBody.qml"
+                bodyLoader.item.width = root.pane.width
+                bodyLoader.item.height = root.pane.height
+            }
+        }
     }
 
     Component.onCompleted: {
@@ -90,4 +101,30 @@ Item {
         root.iconText = "<"
         d.resize();
     }
+
+    Connections {
+        target: loginDialog
+        onHandleCreateCompleted: {
+            console.log("Create Succeeded")
+
+            loginDialog.loginThroughSteam()
+        }
+        onHandleCreateFailed: {
+            console.log("Create Failed: " + error)
+
+            bodyLoader.source = "UsernameCollisionBody.qml"
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+        onHandleLoginCompleted: {
+            console.log("Login Succeeded")
+
+            bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+        onHandleLoginFailed: {
+            console.log("Login Failed")
+        }
+    }
 }
diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml
index eede996412..489385864b 100644
--- a/interface/resources/qml/LoginDialog/EmailSentBody.qml
+++ b/interface/resources/qml/LoginDialog/EmailSentBody.qml
@@ -17,10 +17,10 @@ import "../styles-uit"
 Item {
     id: emailSentBody
     clip: true
-    width: pane.width
-    height: pane.height
+    width: root.pane.width
+    height: root.pane.height
 
-    property string email: "clement@highfidelity.com"
+    property string email: ""
 
     QtObject {
         id: d
diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
index b7ff756fa3..da5f0f1a42 100644
--- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml
+++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
@@ -19,10 +19,14 @@ import "../styles-uit"
 Item {
     id: linkAccountBody
     clip: true
-    width: pane.width
-    height: pane.height
+    width: root.pane.width
+    height: root.pane.height
 
-    property bool existingEmail: true
+    property bool existingEmail: false
+
+    function login() {
+        loginDialog.login(usernameField.text, passwordField.text)
+    }
 
     QtObject {
         id: d
@@ -64,7 +68,7 @@ Item {
     Column {
         id: form
         anchors {
-            top: mainTextContainer.bottom
+            top: mainTextContainer.visible ? mainTextContainer.bottom : parent.top
             left: parent.left
             margins: 0
             topMargin: 2 * hifi.dimensions.contentSpacing.y
@@ -96,6 +100,15 @@ Item {
 
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
+
+                MouseArea {
+                    anchors.fill: parent
+                    onClicked: {
+                        bodyLoader.source = "RecoverPasswordBody.qml"
+                        bodyLoader.item.width = root.pane.width
+                        bodyLoader.item.height = root.pane.height
+                    }
+                }
             }
         }
         Row {
@@ -124,6 +137,15 @@ Item {
 
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
+
+                MouseArea {
+                    anchors.fill: parent
+                    onClicked: {
+                        bodyLoader.source = "RecoverPasswordBody.qml"
+                        bodyLoader.item.width = root.pane.width
+                        bodyLoader.item.height = root.pane.height
+                    }
+                }
             }
         }
 
@@ -141,11 +163,14 @@ Item {
         onHeightChanged: d.resize(); onWidthChanged: d.resize();
 
         Button {
+            id: linkAccountButton
             anchors.verticalCenter: parent.verticalCenter
             width: 200
 
-            text: qsTr("Link Account")
+            text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Login")
             color: hifi.buttons.blue
+
+            onClicked: linkAccountBody.login()
         }
 
         Button {
@@ -161,5 +186,51 @@ Item {
         root.title = qsTr("Sign Into High Fidelity")
         root.iconText = "<"
         d.resize();
+
+        usernameField.forceActiveFocus()
+    }
+
+    Connections {
+        target: loginDialog
+        onHandleLoginCompleted: {
+            console.log("Login Succeeded, linking steam account")
+
+            if (loginDialog.isSteamRunning()) {
+                loginDialog.linkSteam()
+            } else {
+                bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+                bodyLoader.item.width = root.pane.width
+                bodyLoader.item.height = root.pane.height
+            }
+        }
+        onHandleLoginFailed: {
+            console.log("Login Failed")
+
+        }
+        onHandleLinkCompleted: {
+            console.log("Link Succeeded")
+
+            bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+        onHandleLinkFailed: {
+            console.log("Link Failed")
+
+        }
+    }
+
+    Keys.onPressed: {
+        if (!visible) {
+            return
+        }
+
+        switch (event.key) {
+            case Qt.Key_Enter:
+            case Qt.Key_Return:
+                event.accepted = true
+                linkAccountBody.login()
+                break
+        }
     }
 }
diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
index 74dcb18054..3c6e101e2a 100644
--- a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
+++ b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
@@ -19,8 +19,16 @@ import "../styles-uit"
 Item {
     id: recoverPasswordBody
     clip: true
-    width: pane.width
-    height: pane.height
+    width: root.pane.width
+    height: root.pane.height
+
+    function send() {
+        loginDialog.sendRecoveryEmail(emailField.text)
+
+        bodyLoader.setSource("EmailSentBody.qml", { "email": emailField.text })
+        bodyLoader.item.width = root.pane.width
+        bodyLoader.item.height = root.pane.height
+    }
 
     QtObject {
         id: d
@@ -70,6 +78,10 @@ Item {
         width: 350
 
         label: "Email address"
+
+        Component.onCompleted: {
+            emailField.forceActiveFocus()
+        }
     }
 
     Row {
@@ -89,12 +101,20 @@ Item {
 
             text: qsTr("Send recovery email")
             color: hifi.buttons.blue
+
+            onClicked: recoverPasswordBody.send()
         }
 
         Button {
             anchors.verticalCenter: parent.verticalCenter
 
             text: qsTr("Back")
+
+            onClicked: {
+                bodyLoader.source = "LinkAccountBody.qml"
+                bodyLoader.item.width = root.pane.width
+                bodyLoader.item.height = root.pane.height
+            }
         }
     }
 
@@ -103,4 +123,18 @@ Item {
         root.iconText = "<"
         d.resize();
     }
+
+    Keys.onPressed: {
+        if (!visible) {
+            return
+        }
+
+        switch (event.key) {
+            case Qt.Key_Enter:
+            case Qt.Key_Return:
+                event.accepted = true
+                recoverPasswordBody.send()
+                break
+        }
+    }
 }
diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml
index d3f6926bd2..2da0ea856d 100644
--- a/interface/resources/qml/LoginDialog/SignInBody.qml
+++ b/interface/resources/qml/LoginDialog/SignInBody.qml
@@ -107,4 +107,22 @@ Item {
         root.iconText = ""
         d.resize();
     }
+
+    Connections {
+        target: loginDialog
+        onHandleLoginCompleted: {
+            console.log("Login Succeeded")
+
+            bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true })
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+        onHandleLoginFailed: {
+            console.log("Login Failed")
+
+            bodyLoader.source = "CompleteProfileBody.qml"
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+    }
 }
diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
index ea5a9bb614..f0663631a8 100644
--- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
+++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
@@ -19,8 +19,8 @@ import "../styles-uit"
 Item {
     id: usernameCollisionBody
     clip: true
-    width: pane.width
-    height: pane.height
+    width: root.pane.width
+    height: root.pane.height
 
     QtObject {
         id: d
@@ -30,26 +30,59 @@ Item {
         readonly property int maxHeight: 720
 
         function resize() {
-            var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0)
-            var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) +
-                               4 * hifi.dimensions.contentSpacing.y + form.height +
-                               4 * hifi.dimensions.contentSpacing.y + buttons.height
+            var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth,
+                                                            termsContainer.contentWidth))
+            var targetHeight =  mainTextContainer.height +
+                                2 * hifi.dimensions.contentSpacing.y + textField.height +
+                                5 * hifi.dimensions.contentSpacing.y + termsContainer.height +
+                                1 * hifi.dimensions.contentSpacing.y + buttons.height
 
             root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
             root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
         }
     }
 
-    MenuItem {
+    ShortcutText {
         id: mainTextContainer
         anchors {
             top: parent.top
-            horizontalCenter: parent.horizontalCenter
+            left: parent.left
             margins: 0
             topMargin: hifi.dimensions.contentSpacing.y
         }
 
-        text: qsTr("Choose your High Fidelity user name:")
+        text: qsTr("Your Steam username is not available.")
+        wrapMode: Text.WordWrap
+        color: hifi.colors.redAccent
+        lineHeight: 1
+        lineHeightMode: Text.ProportionalHeight
+        horizontalAlignment: Text.AlignHCenter
+    }
+
+
+    TextField {
+        id: textField
+        anchors {
+            top: mainTextContainer.bottom
+            left: parent.left
+            margins: 0
+            topMargin: 2 * hifi.dimensions.contentSpacing.y
+        }
+        width: 250
+
+        placeholderText: "Choose your own"
+    }
+
+    MenuItem {
+        id: termsContainer
+        anchors {
+            top: textField.bottom
+            left: parent.left
+            margins: 0
+            topMargin: 3 * hifi.dimensions.contentSpacing.y
+        }
+
+        text: qsTr("By creating this user profile, you agree to <a href=\"https://highfidelity.com/terms\">High Fidelity's Terms of Service</a>")
         wrapMode: Text.WordWrap
         color: hifi.colors.baseGrayHighlight
         lineHeight: 1
@@ -57,82 +90,13 @@ Item {
         horizontalAlignment: Text.AlignHCenter
     }
 
-
-    Column {
-        id: form
-        anchors {
-            top: mainTextContainer.bottom
-            left: parent.left
-            margins: 0
-            topMargin: 2 * hifi.dimensions.contentSpacing.y
-        }
-        spacing: 2 * hifi.dimensions.contentSpacing.y
-
-        Row {
-            spacing: hifi.dimensions.contentSpacing.x
-
-            TextField {
-                id: usernameField
-                anchors {
-                    verticalCenter: parent.verticalCenter
-                }
-                width: 350
-
-                label: "User Name or Email"
-            }
-
-            ShortcutText {
-                anchors {
-                    verticalCenter: parent.verticalCenter
-                }
-
-                text: "Need help?"
-
-                color: hifi.colors.blueAccent
-                font.underline: true
-
-                verticalAlignment: Text.AlignVCenter
-                horizontalAlignment: Text.AlignHCenter
-            }
-        }
-        Row {
-            spacing: hifi.dimensions.contentSpacing.x
-
-            TextField {
-                id: passwordField
-                anchors {
-                    verticalCenter: parent.verticalCenter
-                }
-                width: 350
-
-                label: "Password"
-                echoMode: TextInput.Password
-            }
-
-            ShortcutText {
-                anchors {
-                    verticalCenter: parent.verticalCenter
-                }
-
-                text: "Need help?"
-
-                color: hifi.colors.blueAccent
-                font.underline: true
-
-                verticalAlignment: Text.AlignVCenter
-                horizontalAlignment: Text.AlignHCenter
-            }
-        }
-
-    }
-
     Row {
         id: buttons
         anchors {
-            top: form.bottom
+            top: termsContainer.bottom
             right: parent.right
             margins: 0
-            topMargin: 3 * hifi.dimensions.contentSpacing.y
+            topMargin: 1 * hifi.dimensions.contentSpacing.y
         }
         spacing: hifi.dimensions.contentSpacing.x
         onHeightChanged: d.resize(); onWidthChanged: d.resize();
@@ -143,6 +107,10 @@ Item {
 
             text: qsTr("Create your profile")
             color: hifi.buttons.blue
+
+            onClicked: {
+                loginDialog.createAccountFromStream(textField.text)
+            }
         }
 
         Button {
@@ -159,4 +127,25 @@ Item {
         root.iconText = "<"
         d.resize();
     }
+    Connections {
+        target: loginDialog
+        onHandleCreateCompleted: {
+            console.log("Create Succeeded")
+
+            loginDialog.loginThroughSteam()
+        }
+        onHandleCreateFailed: {
+            console.log("Create Failed: " + error)
+        }
+        onHandleLoginCompleted: {
+            console.log("Login Succeeded")
+
+            bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : false })
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
+        }
+        onHandleLoginFailed: {
+            console.log("Login Failed")
+        }
+    }
 }
diff --git a/interface/resources/qml/LoginDialog/WelcomeBody.qml b/interface/resources/qml/LoginDialog/WelcomeBody.qml
index 8b771eac1f..ecc848cdc0 100644
--- a/interface/resources/qml/LoginDialog/WelcomeBody.qml
+++ b/interface/resources/qml/LoginDialog/WelcomeBody.qml
@@ -20,7 +20,13 @@ Item {
     width: pane.width
     height: pane.height
 
-    property bool welcomeBack: true
+    property bool welcomeBack: false
+
+    function setTitle() {
+        root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.username + qsTr("</b>!")
+        root.iconText = ""
+        d.resize();
+    }
 
     QtObject {
         id: d
@@ -75,9 +81,10 @@ Item {
         }
     }
 
-    Component.onCompleted: {
-        root.title = (welcomeBack ? qsTr("Welcome back <b>") : qsTr("Welcome <b>")) + Account.getUsername() + qsTr("</b>!")
-        root.iconText = ""
-        d.resize();
+    Component.onCompleted: welcomeBody.setTitle()
+
+    Connections {
+        target: Account
+        onUsernameChanged: welcomeBody.setTitle()
     }
 }
diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp
index 1328197195..4090c99ac8 100644
--- a/interface/src/scripting/AccountScriptingInterface.cpp
+++ b/interface/src/scripting/AccountScriptingInterface.cpp
@@ -15,6 +15,9 @@
 
 AccountScriptingInterface* AccountScriptingInterface::getInstance() {
     static AccountScriptingInterface sharedInstance;
+    auto accountManager = DependencyManager::get<AccountManager>();
+    QObject::connect(accountManager.data(), &AccountManager::profileChanged,
+                     &sharedInstance, &AccountScriptingInterface::usernameChanged);
     return &sharedInstance;
 }
 
diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h
index 888149b836..49648781ce 100644
--- a/interface/src/scripting/AccountScriptingInterface.h
+++ b/interface/src/scripting/AccountScriptingInterface.h
@@ -17,6 +17,11 @@
 class AccountScriptingInterface : public QObject {
     Q_OBJECT
 
+    Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
+
+signals:
+    void usernameChanged();
+
 public slots:
     static AccountScriptingInterface* getInstance();
     QString getUsername();
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index 15a277f394..b65e111b16 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -12,6 +12,8 @@
 #include "LoginDialog.h"
 
 #include <QDesktopServices>
+#include <QJsonDocument>
+#include <QNetworkReply>
 
 #include <NetworkingConstants.h>
 #include <steamworks-wrapper/SteamClient.h>
@@ -52,6 +54,10 @@ void LoginDialog::toggleAction() {
     }
 }
 
+bool LoginDialog::isSteamRunning() {
+    return SteamClient::isRunning();
+}
+
 void LoginDialog::login(const QString& username, const QString& password) {
     qDebug() << "Attempting to login " << username;
     DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
@@ -69,6 +75,92 @@ void LoginDialog::loginThroughSteam() {
     });
 }
 
+void LoginDialog::linkSteam() {
+    qDebug() << "Attempting to link Steam account";
+    SteamClient::requestTicket([this](Ticket ticket) {
+        if (ticket.isNull()) {
+            emit handleLoginFailed();
+            return;
+        }
+
+        JSONCallbackParameters callbackParams;
+        callbackParams.jsonCallbackReceiver = this;
+        callbackParams.jsonCallbackMethod = "linkCompleted";
+        callbackParams.errorCallbackReceiver = this;
+        callbackParams.errorCallbackMethod = "linkFailed";
+
+        const QString LINK_STEAM_PATH = "api/v1/user/link_steam";
+
+        QJsonObject payload;
+        payload.insert("ticket", QJsonValue::fromVariant(QVariant(ticket)));
+
+        auto accountManager = DependencyManager::get<AccountManager>();
+        accountManager->sendRequest(LINK_STEAM_PATH, AccountManagerAuth::Required,
+                                    QNetworkAccessManager::PostOperation, callbackParams,
+                                    QJsonDocument(payload).toJson());
+    });
+}
+
+void LoginDialog::createAccountFromStream(QString username) {
+    qDebug() << "Attempting to create account from Steam info";
+    SteamClient::requestTicket([this, username](Ticket ticket) {
+        if (ticket.isNull()) {
+            emit handleLoginFailed();
+            return;
+        }
+
+        JSONCallbackParameters callbackParams;
+        callbackParams.jsonCallbackReceiver = this;
+        callbackParams.jsonCallbackMethod = "createCompleted";
+        callbackParams.errorCallbackReceiver = this;
+        callbackParams.errorCallbackMethod = "createFailed";
+
+        const QString CREATE_ACCOUNT_FROM_STEAM_PATH = "api/v1/user/create_from_steam";
+
+        QJsonObject payload;
+        payload.insert("ticket", QJsonValue::fromVariant(QVariant(ticket)));
+        if (!username.isEmpty()) {
+            payload.insert("username", QJsonValue::fromVariant(QVariant(username)));
+        }
+
+        auto accountManager = DependencyManager::get<AccountManager>();
+        accountManager->sendRequest(CREATE_ACCOUNT_FROM_STEAM_PATH, AccountManagerAuth::None,
+                                    QNetworkAccessManager::PostOperation, callbackParams,
+                                    QJsonDocument(payload).toJson());
+    });
+
+}
+
 void LoginDialog::openUrl(const QString& url) {
     QDesktopServices::openUrl(url);
 }
+
+void LoginDialog::sendRecoveryEmail(const QString& email) {
+    const QString PASSWORD_RESET_PATH = "/users/password";
+
+    QJsonObject payload;
+    payload.insert("user_email", QJsonValue::fromVariant(QVariant(email)));
+
+
+    auto accountManager = DependencyManager::get<AccountManager>();
+    accountManager->sendRequest(PASSWORD_RESET_PATH, AccountManagerAuth::None,
+                                QNetworkAccessManager::PostOperation, JSONCallbackParameters(),
+                                QJsonDocument(payload).toJson());
+}
+
+void LoginDialog::linkCompleted(QNetworkReply& reply) {
+    emit handleLinkCompleted();
+}
+
+void LoginDialog::linkFailed(QNetworkReply& reply) {
+    emit handleLinkFailed(reply.errorString());
+}
+
+void LoginDialog::createCompleted(QNetworkReply& reply) {
+    emit handleCreateCompleted();
+}
+
+void LoginDialog::createFailed(QNetworkReply& reply) {
+    emit handleCreateFailed(reply.errorString());
+}
+
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index dcd0e04894..ef2c937201 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -16,6 +16,8 @@
 
 #include <OffscreenQmlDialog.h>
 
+class QNetworkReply;
+
 class LoginDialog : public OffscreenQmlDialog {
     Q_OBJECT
     HIFI_QML_DECL
@@ -29,10 +31,30 @@ signals:
     void handleLoginCompleted();
     void handleLoginFailed();
 
-protected:
+    void handleLinkCompleted();
+    void handleLinkFailed(QString error);
+
+    void handleCreateCompleted();
+    void handleCreateFailed(QString error);
+
+public slots:
+    void linkCompleted(QNetworkReply& reply);
+    void linkFailed(QNetworkReply& reply);
+
+    void createCompleted(QNetworkReply& reply);
+    void createFailed(QNetworkReply& reply);
+
+protected slots:
+    Q_INVOKABLE bool isSteamRunning();
+
     Q_INVOKABLE void login(const QString& username, const QString& password);
     Q_INVOKABLE void loginThroughSteam();
+    Q_INVOKABLE void linkSteam();
+    Q_INVOKABLE void createAccountFromStream(QString username = QString());
+
     Q_INVOKABLE void openUrl(const QString& url);
+    Q_INVOKABLE void sendRecoveryEmail(const QString& email);
+
 };
 
 #endif // hifi_LoginDialog_h
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 8c0fa5ed92..52d9e87636 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -568,6 +568,7 @@ void AccountManager::requestAccessTokenFinished() {
 void AccountManager::requestAccessTokenError(QNetworkReply::NetworkError error) {
     // TODO: error handling
     qCDebug(networking) << "AccountManager requestError - " << error;
+    emit loginFailed();
 }
 
 void AccountManager::requestProfile() {
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index 1dbfc0ce00..a8a57064de 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -133,8 +133,16 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes
 static std::atomic_bool initialized { false };
 static std::unique_ptr<SteamTicketRequests> steamTicketRequests;
 
-bool SteamClient::init() {
+
+bool SteamClient::isRunning() {
     if (!initialized) {
+        init();
+    }
+    return initialized;
+}
+
+bool SteamClient::init() {
+    if (SteamAPI_IsSteamRunning() && !initialized) {
         initialized = SteamAPI_Init();
     }
 
@@ -157,11 +165,6 @@ void SteamClient::shutdown() {
 
 void SteamClient::runCallbacks() {
     if (!initialized) {
-        init();
-    }
-
-    if (!initialized) {
-        qDebug() << "Steam not initialized";
         return;
     }
 
@@ -176,7 +179,13 @@ void SteamClient::runCallbacks() {
 
 void SteamClient::requestTicket(TicketRequestCallback callback) {
     if (!initialized) {
-        init();
+        if (SteamAPI_IsSteamRunning()) {
+            init();
+        } else {
+            qWarning() << "Steam is not running";
+            callback(INVALID_TICKET);
+            return;
+        }
     }
 
     if (!initialized) {
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
index ac5c648ead..f7ca775829 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
@@ -22,6 +22,8 @@ using TicketRequestCallback = std::function<void(Ticket)>;
 
 class SteamClient {
 public:
+    static bool isRunning();
+
     static bool init();
     static void shutdown();
 

From 0663766074da67ea4377002d9fdaeca2edca655d Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Mon, 25 Jul 2016 14:52:02 -0700
Subject: [PATCH 05/18] Replace metaverse server with testing URL

---
 libraries/networking/src/NetworkingConstants.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h
index d1e0a46c71..154470201f 100644
--- a/libraries/networking/src/NetworkingConstants.h
+++ b/libraries/networking/src/NetworkingConstants.h
@@ -15,7 +15,7 @@
 #include <QtCore/QUrl>
 
 namespace NetworkingConstants {
-    const QUrl METAVERSE_SERVER_URL = QUrl("http://localhost:3000");
+    const QUrl METAVERSE_SERVER_URL = QUrl("https://hifi.ngrok.io");
 }
 
 #endif // hifi_NetworkingConstants_h

From e5290076be650d63e1a97af0fefe4556c6ab2386 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Tue, 26 Jul 2016 17:25:52 -0700
Subject: [PATCH 06/18] First draft for Steam friends

---
 .../src/steamworks-wrapper/SteamClient.cpp    | 51 ++++++++++++++-----
 1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index a8a57064de..de95269d1a 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -12,7 +12,6 @@
 #include "SteamClient.h"
 
 #include <atomic>
-#include <memory>
 
 #include <QtCore/QDebug>
 
@@ -37,13 +36,15 @@ public:
 
     HAuthTicket startRequest(TicketRequestCallback callback);
     void stopRequest(HAuthTicket authTicket);
+    void stopAll();
 
     STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse,
                    GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse);
 
-private:
-    void stopAll();
+    STEAM_CALLBACK(SteamTicketRequests, onGameRichPresenceJoinRequested,
+                   GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse);
 
+private:
     struct PendingTicket {
         HAuthTicket authTicket;
         Ticket ticket;
@@ -54,7 +55,8 @@ private:
 };
 
 SteamTicketRequests::SteamTicketRequests() :
-    _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse)
+    _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse),
+    _gameRichPresenceJoinRequestedResponse(this, &SteamTicketRequests::onGameRichPresenceJoinRequested)
 {
 }
 
@@ -128,10 +130,35 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes
     }
 }
 
+#include <QString>
+#include <QCoreApplication>
+#include <QtGui/QEvent.h>
+#include <QMimeData>
+#include <QUrl>
+const QString PREFIX = "--url \"";
+const QString SUFFIX = "\"";
+
+
+void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
+    auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect);
+
+    if (url.startsWith(PREFIX) && url.endsWith(SUFFIX)) {
+        url.remove(0, PREFIX.size());
+        url.remove(-SUFFIX.size(), SUFFIX.size());
+    }
+
+    qDebug() << "Joining:" << url;
+    auto mimeData = new QMimeData();
+    mimeData->setUrls(QList<QUrl>() << QUrl(url));
+    auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
+
+    QCoreApplication::postEvent(qApp, event);
+}
+
 
 
 static std::atomic_bool initialized { false };
-static std::unique_ptr<SteamTicketRequests> steamTicketRequests;
+static SteamTicketRequests steamTicketRequests;
 
 
 bool SteamClient::isRunning() {
@@ -144,12 +171,12 @@ bool SteamClient::isRunning() {
 bool SteamClient::init() {
     if (SteamAPI_IsSteamRunning() && !initialized) {
         initialized = SteamAPI_Init();
-    }
 
-    if (!steamTicketRequests && initialized) {
-        steamTicketRequests.reset(new SteamTicketRequests());
+        if (initialized) {
+            SteamFriends()->SetRichPresence("status", "Localhost");
+            SteamFriends()->SetRichPresence("connect", "--url \"hifi://10.0.0.185:40117/10,10,10\"");
+        }
     }
-
     return initialized;
 }
 
@@ -158,9 +185,7 @@ void SteamClient::shutdown() {
         SteamAPI_Shutdown();
     }
 
-    if (steamTicketRequests) {
-        steamTicketRequests.reset();
-    }
+    steamTicketRequests.stopAll();
 }
 
 void SteamClient::runCallbacks() {
@@ -193,7 +218,7 @@ void SteamClient::requestTicket(TicketRequestCallback callback) {
         return;
     }
 
-    steamTicketRequests->startRequest(callback);
+    steamTicketRequests.startRequest(callback);
 }
 
 

From a13950752bd12e01e7780186ad266b9ac0bd8f95 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Wed, 27 Jul 2016 13:32:31 -0700
Subject: [PATCH 07/18] Update steam location with discoverability

---
 interface/src/DiscoverabilityManager.cpp      | 15 ++--
 .../src/steamworks-wrapper/SteamClient.cpp    | 69 ++++++++++++-------
 .../src/steamworks-wrapper/SteamClient.h      |  3 +
 3 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index 4051bd8a1e..3b7d544394 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -15,6 +15,7 @@
 #include <AddressManager.h>
 #include <DomainHandler.h>
 #include <NodeList.h>
+#include <steamworks-wrapper/SteamClient.h>
 #include <UserActivityLogger.h>
 #include <UUID.h>
 
@@ -36,11 +37,11 @@ const QString SESSION_ID_KEY = "session_id";
 
 void DiscoverabilityManager::updateLocation() {
     auto accountManager = DependencyManager::get<AccountManager>();
-    
-    if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
-        auto addressManager = DependencyManager::get<AddressManager>();
-        DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
+    auto addressManager = DependencyManager::get<AddressManager>();
+    auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
 
+
+    if (_mode.get() != Discoverability::None && accountManager->isLoggedIn()) {
         // construct a QJsonObject given the user's current address information
         QJsonObject rootObject;
 
@@ -48,8 +49,6 @@ void DiscoverabilityManager::updateLocation() {
 
         QString pathString = addressManager->currentPath();
 
-        const QString LOCATION_KEY_IN_ROOT = "location";
-
         const QString PATH_KEY_IN_LOCATION = "path";
         locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
 
@@ -90,6 +89,7 @@ void DiscoverabilityManager::updateLocation() {
             // we have a changed location, send it now
             _lastLocationObject = locationObject;
 
+            const QString LOCATION_KEY_IN_ROOT = "location";
             rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
 
             apiPath = API_USER_LOCATION_PATH;
@@ -109,6 +109,9 @@ void DiscoverabilityManager::updateLocation() {
         accountManager->sendRequest(API_USER_HEARTBEAT_PATH, AccountManagerAuth::Optional,
                                    QNetworkAccessManager::PutOperation, callbackParameters);
     }
+
+    // Update Steam
+    SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentAddress());
 }
 
 void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index de95269d1a..f775efe2f6 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -13,7 +13,12 @@
 
 #include <atomic>
 
+#include <QtCore/QCoreApplication>
 #include <QtCore/QDebug>
+#include <QtCore/QMimeData>
+#include <QtCore/QString>
+#include <QtCore/QUrl>
+#include <QtGui/QEvent.h>
 
 #if defined(__GNUC__) && !defined(__clang__)
 #pragma GCC diagnostic push
@@ -41,9 +46,6 @@ public:
     STEAM_CALLBACK(SteamTicketRequests, onGetAuthSessionTicketResponse,
                    GetAuthSessionTicketResponse_t, _getAuthSessionTicketResponse);
 
-    STEAM_CALLBACK(SteamTicketRequests, onGameRichPresenceJoinRequested,
-                   GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse);
-
 private:
     struct PendingTicket {
         HAuthTicket authTicket;
@@ -55,8 +57,7 @@ private:
 };
 
 SteamTicketRequests::SteamTicketRequests() :
-    _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse),
-    _gameRichPresenceJoinRequestedResponse(this, &SteamTicketRequests::onGameRichPresenceJoinRequested)
+    _getAuthSessionTicketResponse(this, &SteamTicketRequests::onGetAuthSessionTicketResponse)
 {
 }
 
@@ -130,24 +131,37 @@ void SteamTicketRequests::onGetAuthSessionTicketResponse(GetAuthSessionTicketRes
     }
 }
 
-#include <QString>
-#include <QCoreApplication>
-#include <QtGui/QEvent.h>
-#include <QMimeData>
-#include <QUrl>
-const QString PREFIX = "--url \"";
-const QString SUFFIX = "\"";
 
+const QString CONNECT_PREFIX = "--url \"";
+const QString CONNECT_SUFFIX = "\"";
 
-void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
+class SteamCallbackManager {
+public:
+    SteamCallbackManager();
+
+    STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested,
+                   GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse);
+
+    SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; }
+
+private:
+    SteamTicketRequests _steamTicketRequests;
+};
+
+SteamCallbackManager::SteamCallbackManager() :
+_gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested)
+{
+}
+
+void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
     auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect);
 
-    if (url.startsWith(PREFIX) && url.endsWith(SUFFIX)) {
-        url.remove(0, PREFIX.size());
-        url.remove(-SUFFIX.size(), SUFFIX.size());
+    if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) {
+        url.remove(0, CONNECT_PREFIX.size());
+        url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size());
     }
 
-    qDebug() << "Joining:" << url;
+    qDebug() << "Joining Steam Friends at:" << url;
     auto mimeData = new QMimeData();
     mimeData->setUrls(QList<QUrl>() << QUrl(url));
     auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
@@ -156,9 +170,8 @@ void SteamTicketRequests::onGameRichPresenceJoinRequested(GameRichPresenceJoinRe
 }
 
 
-
 static std::atomic_bool initialized { false };
-static SteamTicketRequests steamTicketRequests;
+static SteamCallbackManager steamCallbackManager;
 
 
 bool SteamClient::isRunning() {
@@ -171,11 +184,6 @@ bool SteamClient::isRunning() {
 bool SteamClient::init() {
     if (SteamAPI_IsSteamRunning() && !initialized) {
         initialized = SteamAPI_Init();
-
-        if (initialized) {
-            SteamFriends()->SetRichPresence("status", "Localhost");
-            SteamFriends()->SetRichPresence("connect", "--url \"hifi://10.0.0.185:40117/10,10,10\"");
-        }
     }
     return initialized;
 }
@@ -185,7 +193,7 @@ void SteamClient::shutdown() {
         SteamAPI_Shutdown();
     }
 
-    steamTicketRequests.stopAll();
+    steamCallbackManager.getTicketRequests().stopAll();
 }
 
 void SteamClient::runCallbacks() {
@@ -218,7 +226,16 @@ void SteamClient::requestTicket(TicketRequestCallback callback) {
         return;
     }
 
-    steamTicketRequests.startRequest(callback);
+    steamCallbackManager.getTicketRequests().startRequest(callback);
 }
 
+void SteamClient::updateLocation(QString status, QUrl locationUrl) {
+    if (!initialized) {
+        return;
+    }
 
+    auto connectStr = locationUrl.isEmpty() ? "" : CONNECT_PREFIX + locationUrl.toString() + CONNECT_SUFFIX;
+
+    SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data());
+    SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data());
+}
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
index f7ca775829..9ce127f3cb 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
@@ -20,6 +20,8 @@
 using Ticket = QByteArray;
 using TicketRequestCallback = std::function<void(Ticket)>;
 
+class QUrl;
+
 class SteamClient {
 public:
     static bool isRunning();
@@ -30,6 +32,7 @@ public:
     static void runCallbacks();
 
     static void requestTicket(TicketRequestCallback callback);
+    static void updateLocation(QString status, QUrl locationUrl);
 
 };
 

From 42c9a695ff5edd327329fa096879abb17765119e Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Wed, 27 Jul 2016 17:45:45 -0700
Subject: [PATCH 08/18] Invite friends in HMD

---
 interface/src/Application.cpp                 |   4 +++
 .../src/steamworks-wrapper/SteamClient.cpp    |  14 +++++++--
 .../src/steamworks-wrapper/SteamClient.h      |  16 +++++++++++
 scripts/system/assets/images/tools/steam.jpeg | Bin 0 -> 979 bytes
 scripts/system/steam.js                       |  27 ++++++++++++++++++
 5 files changed, 58 insertions(+), 3 deletions(-)
 create mode 100644 scripts/system/assets/images/tools/steam.jpeg
 create mode 100644 scripts/system/steam.js

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 58284682f1..a1016d90fb 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1599,6 +1599,8 @@ void Application::initializeUi() {
     rootContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
 
     rootContext->setContextProperty("ApplicationCompositor", &getApplicationCompositor());
+
+    rootContext->setContextProperty("Steam", new SteamScriptingInterface(engine));
     
 
     _glWidget->installEventFilter(offscreenUi.data());
@@ -4799,6 +4801,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
 
     scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());
     scriptEngine->registerGlobalObject("Users", DependencyManager::get<UsersScriptingInterface>().data());
+
+    scriptEngine->registerGlobalObject("Steam", new SteamScriptingInterface(scriptEngine));
 }
 
 bool Application::canAcceptURL(const QString& urlString) const {
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index f775efe2f6..ee6711e6f6 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -149,7 +149,7 @@ private:
 };
 
 SteamCallbackManager::SteamCallbackManager() :
-_gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested)
+    _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested)
 {
 }
 
@@ -161,7 +161,7 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR
         url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size());
     }
 
-    qDebug() << "Joining Steam Friends at:" << url;
+    qDebug() << "Joining Steam Friend at:" << url;
     auto mimeData = new QMimeData();
     mimeData->setUrls(QList<QUrl>() << QUrl(url));
     auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
@@ -169,7 +169,6 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR
     QCoreApplication::postEvent(qApp, event);
 }
 
-
 static std::atomic_bool initialized { false };
 static SteamCallbackManager steamCallbackManager;
 
@@ -239,3 +238,12 @@ void SteamClient::updateLocation(QString status, QUrl locationUrl) {
     SteamFriends()->SetRichPresence("status", status.toLocal8Bit().data());
     SteamFriends()->SetRichPresence("connect", connectStr.toLocal8Bit().data());
 }
+
+void SteamClient::openInviteOverlay() {
+    if (!initialized) {
+        return;
+    }
+
+    qDebug() << "Inviting steam friends";
+    SteamFriends()->ActivateGameOverlayInviteDialog(CSteamID());
+}
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
index 9ce127f3cb..7c958c4b39 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
@@ -15,6 +15,7 @@
 
 #include <functional>
 
+#include <QtCore/QObject>
 #include <QtCore/QByteArray>
 
 using Ticket = QByteArray;
@@ -33,6 +34,21 @@ public:
 
     static void requestTicket(TicketRequestCallback callback);
     static void updateLocation(QString status, QUrl locationUrl);
+    static void openInviteOverlay();
+
+};
+
+class SteamScriptingInterface : public QObject {
+    Q_OBJECT
+
+    Q_PROPERTY(bool isRunning READ isRunning)
+
+public:
+    SteamScriptingInterface(QObject* parent) : QObject(parent) {}
+
+public slots:
+    bool isRunning() const { return SteamClient::isRunning(); }
+    void openInviteOverlay() const { SteamClient::openInviteOverlay(); }
 
 };
 
diff --git a/scripts/system/assets/images/tools/steam.jpeg b/scripts/system/assets/images/tools/steam.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..a39fdf9bc62c72467460026d639f13fc6910831c
GIT binary patch
literal 979
zcmex=<NpH&0WUXCHwH#V1_nk3Mh1rew;5U(IN8`a*x5Ka*g3ekIJqUo_$9=7c-2Kj
z1;iB8HPqBq<+W9GO|%Tu)YTO<)XmKd4NR?E^p#DGEo}`fG)zs+7`eE(czJpGczH#&
zB}FB*u>o^zf-vy^0D~Y0gAs!fGoum%lOQ9rAmjfd4AKk?Ow5cRr@{dn3oAP_Bh&vQ
z45k7MjLgi8EF3JXtPp8NCT11}RzV>)MZ-XLVG$+A#KK0S;7K1uohDv<sA3$n@ZkR=
z45Fy!fdI%V5kX{Iz~KKa1|DWc1|~sfK?Zw<nOme?wVrF{X<l7A=X($zgQ&;q=AHju
z{af`=wR#oPwBo?9{T<ey!@mapXx?vH9~$!7<YmHU$M)Q`rxmF|v6CAvdrDrNC%wm0
z`un@1tQ<Q)N^AfAXV|$x@6zHu(<3H460_8reOJIlb@#n9Te*x|R4kgNvIrjzbmOYc
z^v-y5>}biWfTeoY%~70>``pjeUEy0>($=!2;Y-a92A1ZY`BK`}T9%8N`dqFn?})C|
zI`GWEwaDj~jO}#>yXw`>llf#%N#~yJYj>N#sLWC&XR@^DhSPGVwVMt&S|5H~<vvlS
z-b=Z4o%D9gwXXdiEh>JfR7`#um9y16f5)x(ibvX>(Na?nZq-m{`SqZ`D(I$!z#IMj
z@>{-czVmqHrsf6peHA?)mH!1>f0lT2u591>6$h_g`(yF>+?!A_YwyCYt24jFMv1*y
zGHtelWpH=EfdgMHm&#3i{;e!m(3JJ0Q;4S>^Dg6ej(@dgylp#sORliY+bUGOf}6KR
z<(KSpxkFP^@2bYW<DP9=H8)=Opz;BJMaMMRKuIl2yOMB`c`w#yX&=k~&K<n}^6s?@
zf6CmO|M=G2Z`m1%_Y(FiKIvFy#`INNQQ^n)>Fc?i_THY?YUSd|p;<XqM8I9EHD|ij
zMFX$xd!mXP?@m~FqIpNxiYxk$wBJQXG`-IE4PLueHmIp9fWOjRe!8Fg-n84X^^;3>
z>^l@I+q!h6hL@cvLxD);<lv{du@`Uj9q*On5%_Rvt8<TZbn}^iSGL~l*Wx%Z=gN!p
zJ=!yRiVrP2ny}=_g$UKyi>@ZFFMj=1GxlF(C-BPYm>(a<SrOh?Q};w2mkHCN`By8f
yP{~rL{J5s)qAUB^YF814<$OKL8`ZNUT&6`=GS|s?1f1Vn?aKDsRV1MP|4jfR!)yNl

literal 0
HcmV?d00001

diff --git a/scripts/system/steam.js b/scripts/system/steam.js
new file mode 100644
index 0000000000..ee0111bd14
--- /dev/null
+++ b/scripts/system/steam.js
@@ -0,0 +1,27 @@
+//
+//  steam.js
+//  scripts/system/
+//
+//  Created by Clement on 7/28/16
+//  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
+//
+
+var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
+
+
+var steamInviteButton = toolBar.addButton({
+    objectName: "steamInvite",
+    imageURL: Script.resolvePath("assets/images/tools/steam.jpeg"),
+    visible: Steam.isRunning,
+});
+
+steamInviteButton.clicked.connect(function(){
+    Steam.openInviteOverlay();
+});
+
+Script.scriptEnding.connect(function () {
+    toolBar.removeButton("steamInvite");
+});
\ No newline at end of file

From 3d6a6b9d6bbea153179ad50ef4c52a623261f2db Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 28 Jul 2016 16:54:17 -0700
Subject: [PATCH 09/18] Add steam.js to default scripts

---
 scripts/defaultScripts.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js
index 0efcd0c140..46439541f1 100644
--- a/scripts/defaultScripts.js
+++ b/scripts/defaultScripts.js
@@ -9,6 +9,7 @@
 //
 
 
+Script.load("system/steam.js");
 Script.load("system/progress.js");
 Script.load("system/away.js");
 Script.load("system/users.js");

From af76e47629751638fe9abfece4c1a1889c11cf02 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Thu, 28 Jul 2016 16:59:25 -0700
Subject: [PATCH 10/18] Fix linux build

---
 .../steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index ee6711e6f6..544868b2f7 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -18,7 +18,7 @@
 #include <QtCore/QMimeData>
 #include <QtCore/QString>
 #include <QtCore/QUrl>
-#include <QtGui/QEvent.h>
+#include <QtGui/qevent.h>
 
 #if defined(__GNUC__) && !defined(__clang__)
 #pragma GCC diagnostic push

From 6b861e680f46f5bddae958b67b55045f1360660c Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 29 Jul 2016 11:46:21 -0700
Subject: [PATCH 11/18] More work on lobby creation/invites

---
 .../src/steamworks-wrapper/SteamClient.cpp    | 51 +++++++++++++++++--
 1 file changed, 48 insertions(+), 3 deletions(-)

diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index 544868b2f7..c28be7f344 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -142,6 +142,15 @@ public:
     STEAM_CALLBACK(SteamCallbackManager, onGameRichPresenceJoinRequested,
                    GameRichPresenceJoinRequested_t, _gameRichPresenceJoinRequestedResponse);
 
+    STEAM_CALLBACK(SteamCallbackManager, onLobbyCreated,
+                   LobbyCreated_t, _lobbyCreatedResponse);
+
+    STEAM_CALLBACK(SteamCallbackManager, onGameLobbyJoinRequested,
+                   GameLobbyJoinRequested_t, _gameLobbyJoinRequestedResponse);
+
+    STEAM_CALLBACK(SteamCallbackManager, onLobbyEnter,
+                   LobbyEnter_t, _lobbyEnterResponse);
+
     SteamTicketRequests& getTicketRequests() { return _steamTicketRequests; }
 
 private:
@@ -149,7 +158,10 @@ private:
 };
 
 SteamCallbackManager::SteamCallbackManager() :
-    _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested)
+    _gameRichPresenceJoinRequestedResponse(this, &SteamCallbackManager::onGameRichPresenceJoinRequested),
+    _lobbyCreatedResponse(this, &SteamCallbackManager::onLobbyCreated),
+    _gameLobbyJoinRequestedResponse(this, &SteamCallbackManager::onGameLobbyJoinRequested),
+    _lobbyEnterResponse(this, &SteamCallbackManager::onLobbyEnter)
 {
 }
 
@@ -169,6 +181,38 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR
     QCoreApplication::postEvent(qApp, event);
 }
 
+void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) {
+    qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby;
+    if (pCallback->m_eResult == k_EResultOK) {
+        qDebug() << "Inviting steam friends";
+
+        SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect",
+                                         SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect"));
+        SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby,
+                                               "Creator", "true");
+        SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby);
+    }
+}
+
+void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) {
+    qDebug() << "onGameLobbyJoinRequested";
+    SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby);
+}
+
+void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) {
+    qDebug() << "onLobbyEnter";
+    auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, SteamUser()->GetSteamID(), "creator");
+    if (strcmp(creator, "true") == 0) {
+        qDebug() << "Created lobby";
+        SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
+    } else if (pCallback->m_EChatRoomEnterResponse == k_EChatRoomEnterResponseSuccess) {
+        qDebug() << "Success";
+        auto connectValue = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
+        qDebug() << connectValue;
+    }
+}
+
+
 static std::atomic_bool initialized { false };
 static SteamCallbackManager steamCallbackManager;
 
@@ -244,6 +288,7 @@ void SteamClient::openInviteOverlay() {
         return;
     }
 
-    qDebug() << "Inviting steam friends";
-    SteamFriends()->ActivateGameOverlayInviteDialog(CSteamID());
+    qDebug() << "Creating steam lobby";
+    static const int MAX_LOBBY_SIZE = 20;
+    SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE);
 }

From 7ec2f98cf2a081e8073ca5d682c34d82d90e3997 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 29 Jul 2016 13:34:10 -0700
Subject: [PATCH 12/18] Join lobby on startup

---
 interface/src/Application.cpp                 |  8 +++
 .../src/steamworks-wrapper/SteamClient.cpp    | 54 +++++++++++--------
 .../src/steamworks-wrapper/SteamClient.h      |  1 +
 3 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a1016d90fb..00a092f8c7 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3224,6 +3224,14 @@ void Application::init() {
         addressLookupString = arguments().value(urlIndex + 1);
     }
 
+    // when +connect_lobby in command line, join steam lobby
+    const QString STEAM_LOBBY_COMMAND_LINE_KEY = "+connect_lobby";
+    int lobbyIndex = arguments().indexOf(STEAM_LOBBY_COMMAND_LINE_KEY);
+    if (lobbyIndex != -1) {
+        QString lobbyId = arguments().value(lobbyIndex + 1);
+        SteamClient::joinLobby(lobbyId);
+    }
+
     Setting::Handle<bool> firstRun { Settings::firstRun, true };
     if (addressLookupString.isEmpty() && firstRun.get()) {
         qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index c28be7f344..3efeba956a 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -165,9 +165,7 @@ SteamCallbackManager::SteamCallbackManager() :
 {
 }
 
-void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
-    auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect);
-
+void parseUrlAndGo(QString url) {
     if (url.startsWith(CONNECT_PREFIX) && url.endsWith(CONNECT_SUFFIX)) {
         url.remove(0, CONNECT_PREFIX.size());
         url.remove(-CONNECT_SUFFIX.size(), CONNECT_SUFFIX.size());
@@ -176,40 +174,50 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR
     qDebug() << "Joining Steam Friend at:" << url;
     auto mimeData = new QMimeData();
     mimeData->setUrls(QList<QUrl>() << QUrl(url));
-    auto event = new QDropEvent(QPointF(0,0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
+    auto event = new QDropEvent(QPointF(0, 0), Qt::MoveAction, mimeData, Qt::LeftButton, Qt::NoModifier);
 
     QCoreApplication::postEvent(qApp, event);
 }
 
+void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinRequested_t* pCallback) {
+    auto url = QString::fromLocal8Bit(pCallback->m_rgchConnect);
+
+    parseUrlAndGo(url);
+}
+
+
+
 void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) {
     qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby;
     if (pCallback->m_eResult == k_EResultOK) {
         qDebug() << "Inviting steam friends";
 
-        SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect",
-                                         SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect"));
-        SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby,
-                                               "Creator", "true");
+        auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect");
+        SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url);
+        SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, "creator", "true");
         SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby);
     }
 }
 
 void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) {
-    qDebug() << "onGameLobbyJoinRequested";
+    qDebug() << "Joining Steam lobby";
     SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby);
 }
 
 void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) {
-    qDebug() << "onLobbyEnter";
-    auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby, SteamUser()->GetSteamID(), "creator");
-    if (strcmp(creator, "true") == 0) {
-        qDebug() << "Created lobby";
-        SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
-    } else if (pCallback->m_EChatRoomEnterResponse == k_EChatRoomEnterResponseSuccess) {
-        qDebug() << "Success";
-        auto connectValue = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
-        qDebug() << connectValue;
+    if (pCallback->m_EChatRoomEnterResponse != k_EChatRoomEnterResponseSuccess) {
+        qWarning() << "An error occured while joing the Steam lobby:" << pCallback->m_EChatRoomEnterResponse;
+        return;
     }
+
+    auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby,
+                                                          SteamUser()->GetSteamID(), "creator");
+    if (strcmp(creator, "true") != 0) {
+        auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
+        parseUrlAndGo(url);
+    }
+
+    SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
 }
 
 
@@ -218,9 +226,6 @@ static SteamCallbackManager steamCallbackManager;
 
 
 bool SteamClient::isRunning() {
-    if (!initialized) {
-        init();
-    }
     return initialized;
 }
 
@@ -292,3 +297,10 @@ void SteamClient::openInviteOverlay() {
     static const int MAX_LOBBY_SIZE = 20;
     SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE);
 }
+
+
+void SteamClient::joinLobby(QString lobbyIdStr) {
+    qDebug() << "Trying to join Steam lobby:" << lobbyIdStr;
+    CSteamID lobbyId(lobbyIdStr.toULongLong());
+    SteamMatchmaking()->JoinLobby(lobbyId);
+}
\ No newline at end of file
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
index 7c958c4b39..5bf0d4db56 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.h
@@ -35,6 +35,7 @@ public:
     static void requestTicket(TicketRequestCallback callback);
     static void updateLocation(QString status, QUrl locationUrl);
     static void openInviteOverlay();
+    static void joinLobby(QString lobbyId);
 
 };
 

From 2af32ca60ed54c78df0771aeeb15d9f7c6cdb077 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 29 Jul 2016 13:55:39 -0700
Subject: [PATCH 13/18] Only leave lobby if you were invited

---
 .../src/steamworks-wrapper/SteamClient.cpp    | 32 ++++++++++++-------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index 3efeba956a..f6cf13effd 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -188,19 +188,17 @@ void SteamCallbackManager::onGameRichPresenceJoinRequested(GameRichPresenceJoinR
 
 
 void SteamCallbackManager::onLobbyCreated(LobbyCreated_t* pCallback) {
-    qDebug() << pCallback->m_eResult << pCallback->m_ulSteamIDLobby;
     if (pCallback->m_eResult == k_EResultOK) {
-        qDebug() << "Inviting steam friends";
+        qDebug() << "Inviting steam friends" << pCallback->m_ulSteamIDLobby;
 
         auto url = SteamFriends()->GetFriendRichPresence(SteamUser()->GetSteamID(), "connect");
         SteamMatchmaking()->SetLobbyData(pCallback->m_ulSteamIDLobby, "connect", url);
-        SteamMatchmaking()->SetLobbyMemberData(pCallback->m_ulSteamIDLobby, "creator", "true");
         SteamFriends()->ActivateGameOverlayInviteDialog(pCallback->m_ulSteamIDLobby);
     }
 }
 
 void SteamCallbackManager::onGameLobbyJoinRequested(GameLobbyJoinRequested_t* pCallback) {
-    qDebug() << "Joining Steam lobby";
+    qDebug() << "Joining Steam lobby" << pCallback->m_steamIDLobby.ConvertToUint64();
     SteamMatchmaking()->JoinLobby(pCallback->m_steamIDLobby);
 }
 
@@ -210,14 +208,14 @@ void SteamCallbackManager::onLobbyEnter(LobbyEnter_t* pCallback) {
         return;
     }
 
-    auto creator = SteamMatchmaking()->GetLobbyMemberData(pCallback->m_ulSteamIDLobby,
-                                                          SteamUser()->GetSteamID(), "creator");
-    if (strcmp(creator, "true") != 0) {
-        auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
-        parseUrlAndGo(url);
-    }
+    qDebug() << "Entered Steam lobby" << pCallback->m_ulSteamIDLobby;
 
-    SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
+    if (SteamMatchmaking()->GetLobbyOwner(pCallback->m_ulSteamIDLobby) != SteamUser()->GetSteamID()) {
+        auto url = SteamMatchmaking()->GetLobbyData(pCallback->m_ulSteamIDLobby, "connect");
+        qDebug() << "Jumping to" << url;
+        parseUrlAndGo(url);
+        SteamMatchmaking()->LeaveLobby(pCallback->m_ulSteamIDLobby);
+    }
 }
 
 
@@ -293,13 +291,23 @@ void SteamClient::openInviteOverlay() {
         return;
     }
 
-    qDebug() << "Creating steam lobby";
+    qDebug() << "Creating Steam lobby";
     static const int MAX_LOBBY_SIZE = 20;
     SteamMatchmaking()->CreateLobby(k_ELobbyTypePrivate, MAX_LOBBY_SIZE);
 }
 
 
 void SteamClient::joinLobby(QString lobbyIdStr) {
+    if (!initialized) {
+        if (SteamAPI_IsSteamRunning()) {
+            init();
+        }
+        else {
+            qWarning() << "Steam is not running";
+            return;
+        }
+    }
+
     qDebug() << "Trying to join Steam lobby:" << lobbyIdStr;
     CSteamID lobbyId(lobbyIdStr.toULongLong());
     SteamMatchmaking()->JoinLobby(lobbyId);

From aa2ae31aab3b5b9b6fe4b9359f40ee5e3efa9600 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 29 Jul 2016 19:30:20 -0700
Subject: [PATCH 14/18] CR UI fixes

---
 .../qml/LoginDialog/CompleteProfileBody.qml   |  15 +-
 .../qml/LoginDialog/EmailSentBody.qml         |  83 -----------
 .../qml/LoginDialog/LinkAccountBody.qml       |  51 ++-----
 .../qml/LoginDialog/RecoverPasswordBody.qml   | 140 ------------------
 .../qml/LoginDialog/UsernameCollisionBody.qml |  30 +++-
 interface/src/ui/LoginDialog.cpp              |   4 +-
 .../networking/src/NetworkingConstants.h      |   2 +-
 7 files changed, 50 insertions(+), 275 deletions(-)
 delete mode 100644 interface/resources/qml/LoginDialog/EmailSentBody.qml
 delete mode 100644 interface/resources/qml/LoginDialog/RecoverPasswordBody.qml

diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
index 12d2cee73e..fc7eac3d00 100644
--- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
+++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml
@@ -77,22 +77,17 @@ Item {
             topMargin: hifi.dimensions.contentSpacing.y
         }
 
-        text: "Already have a High Fidelity profile? Link to an existing profile here."
+        text: "<a href='https://fake.link'>Already have a High Fidelity profile? Link to an existing profile here.</a>"
 
-        font.underline: true
         wrapMode: Text.WordWrap
-        color: hifi.colors.blueAccent
         lineHeight: 2
         lineHeightMode: Text.ProportionalHeight
         horizontalAlignment: Text.AlignHCenter
 
-        MouseArea {
-            anchors.fill: parent
-            onClicked: {
-                bodyLoader.source = "LinkAccountBody.qml"
-                bodyLoader.item.width = root.pane.width
-                bodyLoader.item.height = root.pane.height
-            }
+        onLinkActivated: {
+            bodyLoader.source = "LinkAccountBody.qml"
+            bodyLoader.item.width = root.pane.width
+            bodyLoader.item.height = root.pane.height
         }
     }
 
diff --git a/interface/resources/qml/LoginDialog/EmailSentBody.qml b/interface/resources/qml/LoginDialog/EmailSentBody.qml
deleted file mode 100644
index 489385864b..0000000000
--- a/interface/resources/qml/LoginDialog/EmailSentBody.qml
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-//  EmailSentBody.qml
-//
-//  Created by Clement on 7/18/16
-//  Copyright 2015 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.4
-
-import "../controls-uit"
-import "../styles-uit"
-
-Item {
-    id: emailSentBody
-    clip: true
-    width: root.pane.width
-    height: root.pane.height
-
-    property string email: ""
-
-    QtObject {
-        id: d
-        readonly property int minWidth: 480
-        readonly property int maxWidth: 1280
-        readonly property int minHeight: 120
-        readonly property int maxHeight: 720
-
-        function resize() {
-            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
-            var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height
-
-            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
-            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
-        }
-    }
-
-    MenuItem {
-        id: mainTextContainer
-        anchors {
-            top: parent.top
-            horizontalCenter: parent.horizontalCenter
-            margins: 0
-            topMargin: hifi.dimensions.contentSpacing.y
-        }
-
-        text: qsTr("An email with instructions on reseting your password was sent to <br/><b>") + email + "</b>"
-        wrapMode: Text.WordWrap
-        color: hifi.colors.baseGrayHighlight
-        lineHeight: 2
-        lineHeightMode: Text.ProportionalHeight
-        horizontalAlignment: Text.AlignHCenter
-    }
-
-    Row {
-        id: buttons
-        anchors {
-            top: mainTextContainer.bottom
-            horizontalCenter: parent.horizontalCenter
-            margins: 0
-            topMargin: 2 * hifi.dimensions.contentSpacing.y
-        }
-        spacing: hifi.dimensions.contentSpacing.x
-        onHeightChanged: d.resize(); onWidthChanged: d.resize();
-
-        Button {
-            anchors.verticalCenter: parent.verticalCenter
-
-            text: qsTr("Close");
-
-            onClicked: root.destroy()
-        }
-    }
-
-    Component.onCompleted: {
-        root.title = qsTr("Email Sent")
-        root.iconText = ""
-        d.resize();
-    }
-}
diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
index da5f0f1a42..137556964f 100644
--- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml
+++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml
@@ -22,9 +22,8 @@ Item {
     width: root.pane.width
     height: root.pane.height
 
-    property bool existingEmail: false
-
     function login() {
+        mainTextContainer.visible = false
         loginDialog.login(usernameField.text, passwordField.text)
     }
 
@@ -36,8 +35,8 @@ Item {
         readonly property int maxHeight: 720
 
         function resize() {
-            var targetWidth = Math.max(titleWidth, mainTextContainer.visible ? mainTextContainer.contentWidth : 0)
-            var targetHeight = (mainTextContainer.visible ? mainTextContainer.height : 0) +
+            var targetWidth = Math.max(titleWidth, form.contentWidth)
+            var targetHeight =  hifi.dimensions.contentSpacing.y + mainTextContainer.height +
                                4 * hifi.dimensions.contentSpacing.y + form.height +
                                4 * hifi.dimensions.contentSpacing.y + buttons.height
 
@@ -46,29 +45,29 @@ Item {
         }
     }
 
-    MenuItem {
+    ShortcutText {
         id: mainTextContainer
         anchors {
             top: parent.top
-            horizontalCenter: parent.horizontalCenter
+            left: parent.left
             margins: 0
             topMargin: hifi.dimensions.contentSpacing.y
         }
-        visible: existingEmail
 
-        text: qsTr("Your Steam account's email matches an existing High Fidelity Profile")
+        visible: false
+
+        text: qsTr("Username or password incorrect.")
         wrapMode: Text.WordWrap
         color: hifi.colors.redAccent
-        lineHeight: 2
+        lineHeight: 1
         lineHeightMode: Text.ProportionalHeight
         horizontalAlignment: Text.AlignHCenter
     }
 
-
     Column {
         id: form
         anchors {
-            top: mainTextContainer.visible ? mainTextContainer.bottom : parent.top
+            top: mainTextContainer.bottom
             left: parent.left
             margins: 0
             topMargin: 2 * hifi.dimensions.contentSpacing.y
@@ -93,22 +92,12 @@ Item {
                     verticalCenter: parent.verticalCenter
                 }
 
-                text: "Need help?"
-
-                color: hifi.colors.blueAccent
-                font.underline: true
+                text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
 
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
 
-                MouseArea {
-                    anchors.fill: parent
-                    onClicked: {
-                        bodyLoader.source = "RecoverPasswordBody.qml"
-                        bodyLoader.item.width = root.pane.width
-                        bodyLoader.item.height = root.pane.height
-                    }
-                }
+                onLinkActivated: loginDialog.openUrl(link)
             }
         }
         Row {
@@ -130,22 +119,12 @@ Item {
                     verticalCenter: parent.verticalCenter
                 }
 
-                text: "Need help?"
-
-                color: hifi.colors.blueAccent
-                font.underline: true
+                text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
 
                 verticalAlignment: Text.AlignVCenter
                 horizontalAlignment: Text.AlignHCenter
 
-                MouseArea {
-                    anchors.fill: parent
-                    onClicked: {
-                        bodyLoader.source = "RecoverPasswordBody.qml"
-                        bodyLoader.item.width = root.pane.width
-                        bodyLoader.item.height = root.pane.height
-                    }
-                }
+                onLinkActivated: loginDialog.openUrl(link)
             }
         }
 
@@ -205,7 +184,7 @@ Item {
         }
         onHandleLoginFailed: {
             console.log("Login Failed")
-
+            mainTextContainer.visible = true
         }
         onHandleLinkCompleted: {
             console.log("Link Succeeded")
diff --git a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml b/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
deleted file mode 100644
index 3c6e101e2a..0000000000
--- a/interface/resources/qml/LoginDialog/RecoverPasswordBody.qml
+++ /dev/null
@@ -1,140 +0,0 @@
-//
-//  RecoverPasswordBody.qml
-//
-//  Created by Clement on 7/18/16
-//  Copyright 2015 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.4
-import QtQuick.Controls 1.4
-import QtQuick.Controls.Styles 1.4 as OriginalStyles
-
-import "../controls-uit"
-import "../styles-uit"
-
-Item {
-    id: recoverPasswordBody
-    clip: true
-    width: root.pane.width
-    height: root.pane.height
-
-    function send() {
-        loginDialog.sendRecoveryEmail(emailField.text)
-
-        bodyLoader.setSource("EmailSentBody.qml", { "email": emailField.text })
-        bodyLoader.item.width = root.pane.width
-        bodyLoader.item.height = root.pane.height
-    }
-
-    QtObject {
-        id: d
-        readonly property int minWidth: 480
-        readonly property int maxWidth: 1280
-        readonly property int minHeight: 120
-        readonly property int maxHeight: 720
-
-        function resize() {
-            var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
-            var targetHeight = mainTextContainer.height +
-                               3 * hifi.dimensions.contentSpacing.y + emailField.height +
-                               4 * hifi.dimensions.contentSpacing.y + buttons.height
-
-            root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
-            root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
-        }
-    }
-
-    MenuItem {
-        id: mainTextContainer
-        anchors {
-            top: parent.top
-            left: parent.left
-            right: parent.right
-            margins: 0
-            topMargin: hifi.dimensions.contentSpacing.y
-        }
-
-        text: qsTr("In order to help you reset your password, we will send an<br/>email with instructions to your email address.")
-        wrapMode: Text.WordWrap
-        color: hifi.colors.baseGrayHighlight
-        lineHeight: 1
-        horizontalAlignment: Text.AlignHLeft
-    }
-
-
-    TextField {
-        id: emailField
-        anchors {
-            top: mainTextContainer.bottom
-            left: parent.left
-            margins: 0
-            topMargin: 2 * hifi.dimensions.contentSpacing.y
-        }
-
-        width: 350
-
-        label: "Email address"
-
-        Component.onCompleted: {
-            emailField.forceActiveFocus()
-        }
-    }
-
-    Row {
-        id: buttons
-        anchors {
-            top: emailField.bottom
-            right: parent.right
-            margins: 0
-            topMargin: 3 * hifi.dimensions.contentSpacing.y
-        }
-        spacing: hifi.dimensions.contentSpacing.x
-        onHeightChanged: d.resize(); onWidthChanged: d.resize();
-
-        Button {
-            anchors.verticalCenter: parent.verticalCenter
-            width: 200
-
-            text: qsTr("Send recovery email")
-            color: hifi.buttons.blue
-
-            onClicked: recoverPasswordBody.send()
-        }
-
-        Button {
-            anchors.verticalCenter: parent.verticalCenter
-
-            text: qsTr("Back")
-
-            onClicked: {
-                bodyLoader.source = "LinkAccountBody.qml"
-                bodyLoader.item.width = root.pane.width
-                bodyLoader.item.height = root.pane.height
-            }
-        }
-    }
-
-    Component.onCompleted: {
-        root.title = qsTr("Recover Password")
-        root.iconText = "<"
-        d.resize();
-    }
-
-    Keys.onPressed: {
-        if (!visible) {
-            return
-        }
-
-        switch (event.key) {
-            case Qt.Key_Enter:
-            case Qt.Key_Return:
-                event.accepted = true
-                recoverPasswordBody.send()
-                break
-        }
-    }
-}
diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
index f0663631a8..7e22b11f8b 100644
--- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
+++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml
@@ -22,6 +22,11 @@ Item {
     width: root.pane.width
     height: root.pane.height
 
+    function create() {
+        mainTextContainer.visible = false
+        loginDialog.createAccountFromStream(textField.text)
+    }
+
     QtObject {
         id: d
         readonly property int minWidth: 480
@@ -82,12 +87,14 @@ Item {
             topMargin: 3 * hifi.dimensions.contentSpacing.y
         }
 
-        text: qsTr("By creating this user profile, you agree to <a href=\"https://highfidelity.com/terms\">High Fidelity's Terms of Service</a>")
+        text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
         wrapMode: Text.WordWrap
         color: hifi.colors.baseGrayHighlight
         lineHeight: 1
         lineHeightMode: Text.ProportionalHeight
         horizontalAlignment: Text.AlignHCenter
+
+        onLinkActivated: loginDialog.openUrl(link)
     }
 
     Row {
@@ -108,9 +115,7 @@ Item {
             text: qsTr("Create your profile")
             color: hifi.buttons.blue
 
-            onClicked: {
-                loginDialog.createAccountFromStream(textField.text)
-            }
+            onClicked: usernameCollisionBody.create()
         }
 
         Button {
@@ -136,6 +141,9 @@ Item {
         }
         onHandleCreateFailed: {
             console.log("Create Failed: " + error)
+
+            mainTextContainer.visible = true
+            mainTextContainer.text = "\"" + textField.text + qsTr("\" is invalid or already taken.")
         }
         onHandleLoginCompleted: {
             console.log("Login Succeeded")
@@ -148,4 +156,18 @@ Item {
             console.log("Login Failed")
         }
     }
+
+    Keys.onPressed: {
+        if (!visible) {
+            return
+        }
+
+        switch (event.key) {
+            case Qt.Key_Enter:
+            case Qt.Key_Return:
+                event.accepted = true
+                usernameCollisionBody.create()
+                break
+        }
+    }
 }
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index b65e111b16..78aacb1216 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -132,7 +132,9 @@ void LoginDialog::createAccountFromStream(QString username) {
 }
 
 void LoginDialog::openUrl(const QString& url) {
-    QDesktopServices::openUrl(url);
+    auto offscreenUi = DependencyManager::get<OffscreenUi>();
+    auto browser = offscreenUi->load("Browser.qml");
+    browser->setProperty("url", url);
 }
 
 void LoginDialog::sendRecoveryEmail(const QString& email) {
diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h
index 154470201f..bbbaa5ebbe 100644
--- a/libraries/networking/src/NetworkingConstants.h
+++ b/libraries/networking/src/NetworkingConstants.h
@@ -15,7 +15,7 @@
 #include <QtCore/QUrl>
 
 namespace NetworkingConstants {
-    const QUrl METAVERSE_SERVER_URL = QUrl("https://hifi.ngrok.io");
+    const QUrl METAVERSE_SERVER_URL = QUrl("http://10.0.0.146:8080");
 }
 
 #endif // hifi_NetworkingConstants_h

From 51b45f8f73e58408b78d72ed3cea03016d3bfe6b Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Wed, 3 Aug 2016 16:04:48 -0700
Subject: [PATCH 15/18] Steam invite are facing the current position

---
 interface/src/DiscoverabilityManager.cpp    |  2 +-
 interface/src/avatar/MyAvatar.cpp           | 12 ++++---
 libraries/networking/src/AddressManager.cpp | 39 +++++++++++++++++++--
 libraries/networking/src/AddressManager.h   |  6 ++--
 4 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/interface/src/DiscoverabilityManager.cpp b/interface/src/DiscoverabilityManager.cpp
index 3b7d544394..dd80dadca7 100644
--- a/interface/src/DiscoverabilityManager.cpp
+++ b/interface/src/DiscoverabilityManager.cpp
@@ -111,7 +111,7 @@ void DiscoverabilityManager::updateLocation() {
     }
 
     // Update Steam
-    SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentAddress());
+    SteamClient::updateLocation(domainHandler.getHostname(), addressManager->currentFacingAddress());
 }
 
 void DiscoverabilityManager::handleHeartbeatResponse(QNetworkReply& requestReply) {
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index de5455fc14..57e379a9ac 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -122,11 +122,13 @@ MyAvatar::MyAvatar(RigPointer rig) :
         _driveKeys[i] = 0.0f;
     }
 
+
+    // Necessary to select the correct slot
+    using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
+
     // connect to AddressManager signal for location jumps
-    connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired, 
-        [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){
-        goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation);
-    });
+    connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
+            this, static_cast<SlotType>(&MyAvatar::goToLocation));
 
     _characterController.setEnabled(true);
 
@@ -1859,7 +1861,7 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition,
         glm::quat quatOrientation = cancelOutRollAndPitch(newOrientation);
 
         if (shouldFaceLocation) {
-            quatOrientation = newOrientation * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
+            quatOrientation = newOrientation * glm::angleAxis(PI, Vectors::UP);
 
             // move the user a couple units away
             const float DISTANCE_TO_USER = 2.0f;
diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp
index ae6aad3c4f..6760d44244 100644
--- a/libraries/networking/src/AddressManager.cpp
+++ b/libraries/networking/src/AddressManager.cpp
@@ -17,6 +17,7 @@
 #include <QStringList>
 
 #include <GLMHelpers.h>
+#include <NumericalConstants.h>
 #include <SettingHandle.h>
 #include <UUID.h>
 
@@ -46,7 +47,7 @@ bool AddressManager::isConnected() {
     return DependencyManager::get<NodeList>()->getDomainHandler().isConnected();
 }
 
-const QUrl AddressManager::currentAddress() const {
+QUrl AddressManager::currentAddress() const {
     QUrl hifiURL;
 
     hifiURL.setScheme(HIFI_URL_SCHEME);
@@ -61,6 +62,21 @@ const QUrl AddressManager::currentAddress() const {
     return hifiURL;
 }
 
+QUrl AddressManager::currentFacingAddress() const {
+    QUrl hifiURL;
+
+    hifiURL.setScheme(HIFI_URL_SCHEME);
+    hifiURL.setHost(_host);
+
+    if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) {
+        hifiURL.setPort(_port);
+    }
+
+    hifiURL.setPath(currentFacingPath());
+
+    return hifiURL;
+}
+
 void AddressManager::loadSettings(const QString& lookupString) {
     if (lookupString.isEmpty()) {
         handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings);
@@ -97,7 +113,7 @@ void AddressManager::storeCurrentAddress() {
     currentAddressHandle.set(currentAddress());
 }
 
-const QString AddressManager::currentPath(bool withOrientation) const {
+QString AddressManager::currentPath(bool withOrientation) const {
 
     if (_positionGetter) {
         QString pathString = "/" + createByteArray(_positionGetter());
@@ -121,6 +137,25 @@ const QString AddressManager::currentPath(bool withOrientation) const {
     }
 }
 
+QString AddressManager::currentFacingPath() const {
+    if (_positionGetter && _orientationGetter) {
+        auto position = _positionGetter();
+        auto orientation = _orientationGetter();
+
+        // move the user a couple units away
+        const float DISTANCE_TO_USER = 2.0f;
+        position += orientation * Vectors::FRONT * DISTANCE_TO_USER;
+
+        // rotate the user by 180 degrees
+        orientation = orientation * glm::angleAxis(PI, Vectors::UP);
+
+        return "/" + createByteArray(position) + "/" + createByteArray(orientation);
+    } else {
+        qCDebug(networking) << "Cannot create address path without a getter for position/orientation.";
+        return QString();
+    }
+}
+
 const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
     static bool hasSetupParameters = false;
     static JSONCallbackParameters callbackParams;
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 2e9f177137..8ccddc5975 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -57,8 +57,10 @@ public:
     bool isConnected();
     const QString& getProtocol() { return HIFI_URL_SCHEME; };
 
-    const QUrl currentAddress() const;
-    const QString currentPath(bool withOrientation = true) const;
+    QUrl currentAddress() const;
+    QUrl currentFacingAddress() const;
+    QString currentPath(bool withOrientation = true) const;
+    QString currentFacingPath() const;
 
     const QUuid& getRootPlaceID() const { return _rootPlaceID; }
     const QString& getPlaceName() const { return _placeName; }

From 61d07cf952c1ab2224d68c390825156e0ae1a362 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Fri, 5 Aug 2016 13:08:18 -0700
Subject: [PATCH 16/18] Restore mateverse URL

---
 libraries/networking/src/NetworkingConstants.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h
index bbbaa5ebbe..a512ae8887 100644
--- a/libraries/networking/src/NetworkingConstants.h
+++ b/libraries/networking/src/NetworkingConstants.h
@@ -15,7 +15,7 @@
 #include <QtCore/QUrl>
 
 namespace NetworkingConstants {
-    const QUrl METAVERSE_SERVER_URL = QUrl("http://10.0.0.146:8080");
+    const QUrl METAVERSE_SERVER_URL = QUrl("https://metaverse.highfidelity.com");
 }
 
 #endif // hifi_NetworkingConstants_h

From f08b2f9257f834380ca73c9fcbeacf5882b2cb36 Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Mon, 8 Aug 2016 15:39:44 -0700
Subject: [PATCH 17/18] Remove temporary override to reload QML

---
 interface/src/Application.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 00a092f8c7..ac024f89c3 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -2244,12 +2244,11 @@ void Application::keyPressEvent(QKeyEvent* event) {
                 break;
 
             case Qt::Key_X:
-                if (isMeta) {
+                if (isShifted && isMeta) {
                     auto offscreenUi = DependencyManager::get<OffscreenUi>();
-//                    offscreenUi->togglePinned();
-                    offscreenUi->getRootContext()->engine()->clearComponentCache();
-                    qDebug() << "Component cache cleared";
-//                    OffscreenUi::information("Debugging", "Component cache cleared");
+                    offscreenUi->togglePinned();
+                    //offscreenUi->getRootContext()->engine()->clearComponentCache();
+                    //OffscreenUi::information("Debugging", "Component cache cleared");
                     // placeholder for dialogs being converted to QML.
                 }
                 break;

From 25b60d0c62026d5fcf8f97f33f76ca40aaa7302a Mon Sep 17 00:00:00 2001
From: Atlante45 <clement.brisset@gmail.com>
Date: Tue, 9 Aug 2016 09:46:58 -0700
Subject: [PATCH 18/18] CR feedback

---
 interface/resources/qml/LoginDialogSave.qml   | 197 ------------------
 interface/src/ui/LoginDialog.cpp              |  19 +-
 interface/src/ui/LoginDialog.h                |   7 +-
 .../src/steamworks-wrapper/SteamClient.cpp    |   3 +-
 4 files changed, 7 insertions(+), 219 deletions(-)
 delete mode 100644 interface/resources/qml/LoginDialogSave.qml

diff --git a/interface/resources/qml/LoginDialogSave.qml b/interface/resources/qml/LoginDialogSave.qml
deleted file mode 100644
index 46246fc1a5..0000000000
--- a/interface/resources/qml/LoginDialogSave.qml
+++ /dev/null
@@ -1,197 +0,0 @@
-Window {
-    id: root
-    HifiConstants { id: hifi }
-
-    width: 550
-    height: 200
-
-    anchors.centerIn: parent
-    resizable: true
-
-    property bool required: false
-
-    Component {
-        id: welcomeBody
-
-        Column {
-            anchors.centerIn: parent
-
-            OverlayTitle {
-                anchors.horizontalCenter: parent.horizontalCenter
-
-                text: "Welcomeback Atlante45!"
-                color: hifi.colors.baseGrayHighlight
-                verticalAlignment: Text.AlignVCenter
-                horizontalAlignment: Text.AlignHCenter
-            }
-
-            VerticalSpacer {}
-
-            HorizontalRule {}
-
-            MenuItem {
-                id: details
-                anchors.horizontalCenter: parent.horizontalCenter
-
-                text: "You are now signed into High Fidelity"
-                color: hifi.colors.baseGrayHighlight
-                verticalAlignment: Text.AlignVCenter
-                horizontalAlignment: Text.AlignHCenter
-            }
-
-            VerticalSpacer {}
-
-            Button {
-                anchors.horizontalCenter: parent.horizontalCenter
-
-                text: "Close"
-            }
-        }
-    }
-
-    Component {
-        id: signInBody
-
-        Column {
-          anchors.centerIn: parent
-
-          OverlayTitle {
-            anchors.horizontalCenter: parent.horizontalCenter
-
-            text: required ? "Sign In Required" : "Sign In"
-            color: hifi.colors.baseGrayHighlight
-            verticalAlignment: Text.AlignVCenter
-            horizontalAlignment: Text.AlignHCenter
-          }
-
-          VerticalSpacer {}
-
-          HorizontalRule {}
-
-          MenuItem {
-            id: details
-            anchors.horizontalCenter: parent.horizontalCenter
-
-            text: required ? "This domain's owner requires that you sign in:"
-                           : "Sign in to access your user account:"
-            color: hifi.colors.baseGrayHighlight
-            verticalAlignment: Text.AlignVCenter
-            horizontalAlignment: Text.AlignHCenter
-          }
-
-          VerticalSpacer {}
-
-          Row {
-            anchors.horizontalCenter: parent.horizontalCenter
-
-            Button {
-              anchors.verticalCenter: parent.verticalCenter
-              width: undefined // invalidate so that the image's size sets the width
-              height: undefined // invalidate so that the image's size sets the height
-
-              style: Original.ButtonStyle {
-                background: Image {
-                    id: buttonImage
-                    source: "../images/steam-sign-in.png"
-                }
-              }
-
-              onClicked: body.sourceComponent = completeProfileBody
-            }
-
-            HorizontalSpacer {}
-
-            Button {
-              anchors.verticalCenter: parent.verticalCenter
-
-              text: "Cancel"
-
-              onClicked: required = !required
-            }
-          }
-        }
-    }
-
-    Component {
-        id: completeProfileBody
-
-        Column {
-            anchors.centerIn: parent
-
-            Row {
-                anchors.horizontalCenter: parent.horizontalCenter
-
-                HiFiGlyphs {
-                    anchors.verticalCenter: parent.verticalCenter
-
-                    text: hifi.glyphs.avatar
-                }
-
-                OverlayTitle {
-                    anchors.verticalCenter: parent.verticalCenter
-
-                    text: "Complete Your Profile"
-                    color: hifi.colors.baseGrayHighlight
-                    verticalAlignment: Text.AlignVCenter
-                    horizontalAlignment: Text.AlignHCenter
-                }
-            }
-
-            VerticalSpacer {}
-
-            HorizontalRule {}
-
-            VerticalSpacer {}
-
-            Row {
-                anchors.horizontalCenter: parent.horizontalCenter
-
-                Button {
-                  anchors.verticalCenter: parent.verticalCenter
-
-                  width: 200
-
-                  text: "Create your profile"
-                  color: hifi.buttons.blue
-
-                  onClicked: body.sourceComponent = welcomeBody
-                }
-
-                HorizontalSpacer {}
-
-                Button {
-                  anchors.verticalCenter: parent.verticalCenter
-
-                  text: "Cancel"
-
-                  onClicked: body.sourceComponent = signInBody
-
-                }
-            }
-
-            VerticalSpacer {}
-
-            ShortcutText {
-                text: "Already have a High Fidelity profile? Link to an existing profile here."
-
-                color: hifi.colors.blueAccent
-                font.underline: true
-
-                verticalAlignment: Text.AlignVCenter
-                horizontalAlignment: Text.AlignHCenter
-            }
-        }
-    }
-
-    Rectangle {
-      anchors.fill: root
-      color: hifi.colors.faintGray
-      radius: hifi.dimensions.borderRadius
-
-      Loader {
-        id: body
-        anchors.centerIn: parent
-        sourceComponent: signInBody
-      }
-    }
-}
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
index 78aacb1216..08e2b6479d 100644
--- a/interface/src/ui/LoginDialog.cpp
+++ b/interface/src/ui/LoginDialog.cpp
@@ -54,11 +54,11 @@ void LoginDialog::toggleAction() {
     }
 }
 
-bool LoginDialog::isSteamRunning() {
+bool LoginDialog::isSteamRunning() const {
     return SteamClient::isRunning();
 }
 
-void LoginDialog::login(const QString& username, const QString& password) {
+void LoginDialog::login(const QString& username, const QString& password) const {
     qDebug() << "Attempting to login " << username;
     DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
 }
@@ -131,25 +131,12 @@ void LoginDialog::createAccountFromStream(QString username) {
 
 }
 
-void LoginDialog::openUrl(const QString& url) {
+void LoginDialog::openUrl(const QString& url) const {
     auto offscreenUi = DependencyManager::get<OffscreenUi>();
     auto browser = offscreenUi->load("Browser.qml");
     browser->setProperty("url", url);
 }
 
-void LoginDialog::sendRecoveryEmail(const QString& email) {
-    const QString PASSWORD_RESET_PATH = "/users/password";
-
-    QJsonObject payload;
-    payload.insert("user_email", QJsonValue::fromVariant(QVariant(email)));
-
-
-    auto accountManager = DependencyManager::get<AccountManager>();
-    accountManager->sendRequest(PASSWORD_RESET_PATH, AccountManagerAuth::None,
-                                QNetworkAccessManager::PostOperation, JSONCallbackParameters(),
-                                QJsonDocument(payload).toJson());
-}
-
 void LoginDialog::linkCompleted(QNetworkReply& reply) {
     emit handleLinkCompleted();
 }
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
index ef2c937201..8b6dc40302 100644
--- a/interface/src/ui/LoginDialog.h
+++ b/interface/src/ui/LoginDialog.h
@@ -45,15 +45,14 @@ public slots:
     void createFailed(QNetworkReply& reply);
 
 protected slots:
-    Q_INVOKABLE bool isSteamRunning();
+    Q_INVOKABLE bool isSteamRunning() const;
 
-    Q_INVOKABLE void login(const QString& username, const QString& password);
+    Q_INVOKABLE void login(const QString& username, const QString& password) const;
     Q_INVOKABLE void loginThroughSteam();
     Q_INVOKABLE void linkSteam();
     Q_INVOKABLE void createAccountFromStream(QString username = QString());
 
-    Q_INVOKABLE void openUrl(const QString& url);
-    Q_INVOKABLE void sendRecoveryEmail(const QString& email);
+    Q_INVOKABLE void openUrl(const QString& url) const;
 
 };
 
diff --git a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
index f6cf13effd..2e8f6bd7b3 100644
--- a/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
+++ b/libraries/steamworks-wrapper/src/steamworks-wrapper/SteamClient.cpp
@@ -301,8 +301,7 @@ void SteamClient::joinLobby(QString lobbyIdStr) {
     if (!initialized) {
         if (SteamAPI_IsSteamRunning()) {
             init();
-        }
-        else {
+        } else {
             qWarning() << "Steam is not running";
             return;
         }