Merge pull request #13961 from wayne-chen/loginOnLaunch

Pop up login dialog on launch
This commit is contained in:
Wayne Chen 2018-09-11 17:43:29 -07:00 committed by GitHub
commit cb0aa8055f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 230 additions and 93 deletions

View file

@ -0,0 +1,5 @@
<svg width="31" height="23" viewBox="0 0 31 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.59534 11.0156C6.16042 13.4128 9.65987 15.5898 13.6042 16.1774C17.686 16.7856 22.4164 15.7196 27.3057 11.0659C22.0721 6.07309 17.0642 5.14115 12.9153 5.90073C8.99427 6.61859 5.69298 8.87688 3.59534 11.0156ZM12.455 3.27591C17.7727 2.30235 23.9836 3.74895 30.1053 10.1333L31 11.0664L30.1053 11.9994C24.3636 17.9875 18.4774 19.5983 13.2276 18.8161C8.06048 18.0463 3.70384 14.9892 0.837069 11.9994L0 11.1265L0.778477 10.1986C3.05338 7.48717 7.2318 4.23217 12.455 3.27591Z" fill="#3D3D3D"/>
<ellipse cx="15.6539" cy="10.9218" rx="3.65386" ry="3.81061" fill="#3D3D3D"/>
<line x1="25" y1="2.12132" x2="7.12132" y2="20" stroke="#3D3D3D" stroke-width="3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 825 B

View file

@ -0,0 +1,4 @@
<svg width="31" height="16" viewBox="0 0 31 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.59534 8.01564C6.16042 10.4128 9.65987 12.5898 13.6042 13.1774C17.686 13.7856 22.4164 12.7196 27.3057 8.06585C22.0721 3.07309 17.0642 2.14115 12.9153 2.90073C8.99427 3.61859 5.69298 5.87688 3.59534 8.01564ZM12.455 0.275915C17.7727 -0.697651 23.9836 0.748949 30.1053 7.13329L31 8.06636L30.1053 8.99944C24.3636 14.9875 18.4774 16.5983 13.2276 15.8161C8.06048 15.0463 3.70384 11.9892 0.837069 8.99944L0 8.12646L0.778477 7.1986C3.05338 4.48717 7.2318 1.23217 12.455 0.275915Z" fill="#3D3D3D"/>
<ellipse cx="15.644" cy="7.92179" rx="3.65386" ry="3.81061" fill="#3D3D3D"/>
</svg>

After

Width:  |  Height:  |  Size: 721 B

View file

@ -23,6 +23,7 @@ ModalWindow {
objectName: "LoginDialog"
implicitWidth: 520
implicitHeight: 320
closeButtonVisible: true
destroyOnCloseButton: true
destroyOnHidden: true
visible: true

View file

@ -117,27 +117,27 @@ Item {
}
spacing: hifi.dimensions.contentSpacing.y / 2
TextField {
id: usernameField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Username or Email")
TextField {
id: usernameField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Username or Email")
}
TextField {
id: passwordField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Password")
echoMode: TextInput.Password
Keys.onReturnPressed: linkAccountBody.login()
TextField {
id: passwordField
anchors {
horizontalCenter: parent.horizontalCenter
}
width: 1080
placeholderText: qsTr("Password")
echoMode: TextInput.Password
Keys.onReturnPressed: linkAccountBody.login()
}
}
InfoItem {
@ -176,7 +176,7 @@ Item {
anchors {
left: parent.left
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
@ -201,7 +201,7 @@ Item {
anchors {
right: parent.right
top: form.bottom
topMargin: hifi.dimensions.contentSpacing.y / 2
topMargin: hifi.dimensions.contentSpacing.y / 2
}
spacing: hifi.dimensions.contentSpacing.x
onHeightChanged: d.resize(); onWidthChanged: d.resize();

View file

@ -15,7 +15,6 @@ import QtQuick.Controls.Styles 1.4 as OriginalStyles
import "../controls-uit"
import "../styles-uit"
Item {
id: linkAccountBody
clip: true
@ -87,6 +86,23 @@ Item {
height: 48
}
ShortcutText {
id: flavorText
anchors {
top: parent.top
left: parent.left
margins: 0
topMargin: hifi.dimensions.contentSpacing.y
}
text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!")
width: parent.width
wrapMode: Text.WordWrap
lineHeight: 1
lineHeightMode: Text.ProportionalHeight
horizontalAlignment: Text.AlignHCenter
}
ShortcutText {
id: mainTextContainer
anchors {
@ -97,7 +113,6 @@ Item {
}
visible: false
text: qsTr("Username or password incorrect.")
wrapMode: Text.WordWrap
color: hifi.colors.redAccent
@ -117,22 +132,21 @@ Item {
}
spacing: 2 * hifi.dimensions.contentSpacing.y
TextField {
id: usernameField
text: Settings.getValue("wallet/savedUsername", "");
width: parent.width
focus: true
label: "Username or Email"
placeholderText: "Username or Email"
activeFocusOnPress: true
ShortcutText {
z: 10
y: usernameField.height
anchors {
left: usernameField.left
top: usernameField.top
leftMargin: usernameField.textFieldLabel.contentWidth + 10
topMargin: -19
right: usernameField.right
top: usernameField.bottom
topMargin: 4
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Username?</a>"
@ -143,26 +157,32 @@ Item {
onLinkActivated: loginDialog.openUrl(link)
}
onFocusChanged: {
root.text = "";
}
Component.onCompleted: {
var savedUsername = Settings.getValue("wallet/savedUsername", "");
usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername;
}
}
TextField {
id: passwordField
width: parent.width
label: "Password"
echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
placeholderText: "Password"
activeFocusOnPress: true
echoMode: TextInput.Password
onHeightChanged: d.resize(); onWidthChanged: d.resize();
ShortcutText {
id: forgotPasswordShortcut
y: passwordField.height
z: 10
anchors {
left: passwordField.left
top: passwordField.top
leftMargin: passwordField.textFieldLabel.contentWidth + 10
topMargin: -19
right: passwordField.right
top: passwordField.bottom
topMargin: 4
}
text: "<a href='https://highfidelity.com/users/password/new'>Forgot Password?</a>"
@ -179,12 +199,45 @@ Item {
root.isPassword = true;
}
Keys.onReturnPressed: linkAccountBody.login()
}
Rectangle {
id: showPasswordHitbox
z: 10
x: passwordField.width - ((passwordField.height) * 31 / 23)
width: parent.width - (parent.width - (parent.height * 31/16))
height: parent.height
anchors {
right: parent.right
}
color: "transparent"
CheckBox {
id: showPassword
text: "Show password"
Image {
id: showPasswordImage
y: (passwordField.height - (passwordField.height * 16 / 23)) / 2
width: passwordField.width - (passwordField.width - (((passwordField.height) * 31/23)))
height: passwordField.height * 16 / 23
anchors {
right: parent.right
rightMargin: 3
}
source: "../../images/eyeOpen.svg"
}
MouseArea {
id: passwordFieldMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
property bool showPassword: false
onClicked: {
showPassword = !showPassword;
passwordField.echoMode = showPassword ? TextInput.Normal : TextInput.Password;
showPasswordImage.source = showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg";
showPasswordImage.height = showPassword ? passwordField.height : passwordField.height * 16 / 23;
showPasswordImage.y = showPassword ? 0 : (passwordField.height - showPasswordImage.height) / 2;
}
}
}
Keys.onReturnPressed: linkAccountBody.login()
}
InfoItem {
@ -206,6 +259,26 @@ Item {
onHeightChanged: d.resize(); onWidthChanged: d.resize();
anchors.horizontalCenter: parent.horizontalCenter
CheckBox {
id: autoLogoutCheckbox
checked: !Settings.getValue("wallet/autoLogout", true)
text: "Keep me signed in"
boxSize: 20;
labelFontSize: 15
color: hifi.colors.black
onCheckedChanged: {
Settings.setValue("wallet/autoLogout", !checked);
if (checked) {
Settings.setValue("wallet/savedUsername", Account.username);
} else {
Settings.setValue("wallet/savedUsername", "");
}
}
Component.onDestruction: {
Settings.setValue("wallet/autoLogout", !checked);
}
}
Button {
id: linkAccountButton
anchors.verticalCenter: parent.verticalCenter
@ -216,12 +289,6 @@ Item {
onClicked: linkAccountBody.login()
}
Button {
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Cancel")
onClicked: root.tryDestroy()
}
}
Row {
@ -234,7 +301,7 @@ Item {
RalewaySemiBold {
size: hifi.fontSizes.inputLabel
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Don't have an account?")
text: qsTr("New to High Fidelity?")
}
Button {
@ -287,11 +354,23 @@ Item {
bodyLoader.item.width = root.pane.width
bodyLoader.item.height = root.pane.height
}
if (Settings.getValue("loginDialogPoppedUp", false)) {
var data = {
"action": "user logged in"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
}
onHandleLoginFailed: {
console.log("Login Failed")
mainTextContainer.visible = true
toggleLoading(false)
if (Settings.getValue("loginDialogPoppedUp", false)) {
var data = {
"action": "user failed logging in"
};
UserActivityLogger.logAction("encourageLoginDialog", data);
}
}
onHandleLinkCompleted: {
console.log("Link Succeeded")

View file

@ -94,5 +94,25 @@ Frame {
color: hifi.colors.lightGray
}
}
GlyphButton {
id: closeButton
visible: window.closeButtonVisible
width: 30
y: -hifi.dimensions.modalDialogTitleHeight
anchors {
top: parent.top
right: parent.right
topMargin: 10
rightMargin: 10
}
glyph: hifi.glyphs.close
size: 23
onClicked: {
window.clickedCloseButton = true;
window.destroy();
}
}
}
}

View file

@ -19,6 +19,9 @@ ScrollingWindow {
destroyOnHidden: true
frame: ModalFrame { }
property bool closeButtonVisible: false
// only applicable for if close button is visible.
property bool clickedCloseButton: false
property int colorScheme: hifi.colorSchemes.light
property bool draggable: false

View file

@ -2299,6 +2299,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground);
AndroidHelper::instance().notifyLoadComplete();
#endif
static int CHECK_LOGIN_TIMER = 3000;
QTimer* checkLoginTimer = new QTimer(this);
checkLoginTimer->setInterval(CHECK_LOGIN_TIMER);
checkLoginTimer->setSingleShot(true);
connect(checkLoginTimer, &QTimer::timeout, this, [this]() {
auto accountManager = DependencyManager::get<AccountManager>();
auto dialogsManager = DependencyManager::get<DialogsManager>();
if (!accountManager->isLoggedIn()) {
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(true);
dialogsManager->showLoginDialog();
QJsonObject loginData = {};
loginData["action"] = "login dialog shown";
UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData);
}
});
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(false);
checkLoginTimer->start();
}
void Application::updateVerboseLogging() {
@ -2432,6 +2450,8 @@ void Application::onAboutToQuit() {
// so its persisted explicitly here
Setting::Handle<QString>{ ACTIVE_DISPLAY_PLUGIN_SETTING_NAME }.set(getActiveDisplayPlugin()->getName());
Setting::Handle<bool>{"loginDialogPoppedUp", false}.set(false);
getActiveDisplayPlugin()->deactivate();
if (_autoSwitchDisplayModeSupportedHMDPlugin
&& _autoSwitchDisplayModeSupportedHMDPlugin->isSessionActive()) {

View file

@ -331,6 +331,8 @@ signals:
void uploadRequest(QString path);
void loginDialogPoppedUp();
public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs, const glm::vec3* givenOffset = nullptr);

View file

@ -19,6 +19,7 @@
#include <plugins/PluginManager.h>
#include <plugins/SteamClientPlugin.h>
#include <ui/TabletScriptingInterface.h>
#include <UserActivityLogger.h>
#include "AccountManager.h"
#include "DependencyManager.h"
@ -37,11 +38,19 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
connect(accountManager.data(), &AccountManager::loginFailed,
this, &LoginDialog::handleLoginFailed);
#endif
}
void LoginDialog::showWithSelection()
{
LoginDialog::~LoginDialog() {
Setting::Handle<bool> loginDialogPoppedUp{ "loginDialogPoppedUp", false };
if (loginDialogPoppedUp.get()) {
QJsonObject data;
data["action"] = "user opted out";
UserActivityLogger::getInstance().logAction("encourageLoginDialog", data);
}
loginDialogPoppedUp.set(false);
}
void LoginDialog::showWithSelection() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
auto hmd = DependencyManager::get<HMDScriptingInterface>();
@ -73,9 +82,7 @@ void LoginDialog::toggleAction() {
} else {
// change the menu item to login
loginAction->setText("Login / Sign Up");
connection = connect(loginAction, &QAction::triggered, [] {
LoginDialog::showWithSelection();
});
connection = connect(loginAction, &QAction::triggered, [] { LoginDialog::showWithSelection(); });
}
}
@ -158,7 +165,6 @@ void LoginDialog::createAccountFromStream(QString username) {
QJsonDocument(payload).toJson());
});
}
}
void LoginDialog::openUrl(const QString& url) const {
@ -200,25 +206,24 @@ void LoginDialog::createFailed(QNetworkReply* reply) {
}
void LoginDialog::signup(const QString& email, const QString& username, const QString& password) {
JSONCallbackParameters callbackParams;
callbackParams.callbackReceiver = this;
callbackParams.jsonCallbackMethod = "signupCompleted";
callbackParams.errorCallbackMethod = "signupFailed";
QJsonObject payload;
QJsonObject userObject;
userObject.insert("email", email);
userObject.insert("username", username);
userObject.insert("password", password);
payload.insert("user", userObject);
static const QString API_SIGNUP_PATH = "api/v1/users";
qDebug() << "Sending a request to create an account for" << username;
auto accountManager = DependencyManager::get<AccountManager>();
accountManager->sendRequest(API_SIGNUP_PATH, AccountManagerAuth::None,
QNetworkAccessManager::PostOperation, callbackParams,
@ -240,41 +245,37 @@ QString errorStringFromAPIObject(const QJsonValue& apiObject) {
}
void LoginDialog::signupFailed(QNetworkReply* reply) {
// parse the returned JSON to see what the problem was
auto jsonResponse = QJsonDocument::fromJson(reply->readAll());
static const QString RESPONSE_DATA_KEY = "data";
auto dataJsonValue = jsonResponse.object()[RESPONSE_DATA_KEY];
if (dataJsonValue.isObject()) {
auto dataObject = dataJsonValue.toObject();
static const QString EMAIL_DATA_KEY = "email";
static const QString USERNAME_DATA_KEY = "username";
static const QString PASSWORD_DATA_KEY = "password";
QStringList errorStringList;
if (dataObject.contains(EMAIL_DATA_KEY)) {
errorStringList.append(QString("Email %1.").arg(errorStringFromAPIObject(dataObject[EMAIL_DATA_KEY])));
}
if (dataObject.contains(USERNAME_DATA_KEY)) {
errorStringList.append(QString("Username %1.").arg(errorStringFromAPIObject(dataObject[USERNAME_DATA_KEY])));
}
if (dataObject.contains(PASSWORD_DATA_KEY)) {
errorStringList.append(QString("Password %1.").arg(errorStringFromAPIObject(dataObject[PASSWORD_DATA_KEY])));
}
emit handleSignupFailed(errorStringList.join('\n'));
} else {
static const QString DEFAULT_SIGN_UP_FAILURE_MESSAGE = "There was an unknown error while creating your account. Please try again later.";
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
}
}

View file

@ -27,7 +27,10 @@ public:
LoginDialog(QQuickItem* parent = nullptr);
~LoginDialog();
static void showWithSelection();
signals:
void handleLoginCompleted();
void handleLoginFailed();
@ -62,7 +65,6 @@ protected slots:
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url) const;
};
#endif // hifi_LoginDialog_h

View file

@ -38,10 +38,10 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
if (_disabled.get()) {
return;
}
auto accountManager = DependencyManager::get<AccountManager>();
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
// Adding the action name
QHttpPart actionPart;
actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\"");
@ -53,7 +53,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
elapsedPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"elapsed_ms\"");
elapsedPart.setBody(QString::number(_timer.elapsed()).toLocal8Bit());
multipart->append(elapsedPart);
// If there are action details, add them to the multipart
if (!details.isEmpty()) {
QHttpPart detailsPart;
@ -62,13 +62,13 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact));
multipart->append(detailsPart);
}
// if no callbacks specified, call our owns
if (params.isEmpty()) {
params.callbackReceiver = this;
params.errorCallbackMethod = "requestError";
}
accountManager->sendRequest(USER_ACTIVITY_URL,
AccountManagerAuth::Optional,
QNetworkAccessManager::PostOperation,
@ -88,7 +88,7 @@ void UserActivityLogger::launch(QString applicationVersion, bool previousSession
actionDetails.insert(VERSION_KEY, applicationVersion);
actionDetails.insert(CRASH_KEY, previousSessionCrashed);
actionDetails.insert(RUNTIME_KEY, previousSessionRuntime);
logAction(ACTION_NAME, actionDetails);
}
@ -105,9 +105,9 @@ void UserActivityLogger::changedDisplayName(QString displayName) {
const QString ACTION_NAME = "changed_display_name";
QJsonObject actionDetails;
const QString DISPLAY_NAME = "display_name";
actionDetails.insert(DISPLAY_NAME, displayName);
logAction(ACTION_NAME, actionDetails);
}
@ -116,10 +116,10 @@ void UserActivityLogger::changedModel(QString typeOfModel, QString modelURL) {
QJsonObject actionDetails;
const QString TYPE_OF_MODEL = "type_of_model";
const QString MODEL_URL = "model_url";
actionDetails.insert(TYPE_OF_MODEL, typeOfModel);
actionDetails.insert(MODEL_URL, modelURL);
logAction(ACTION_NAME, actionDetails);
}
@ -127,9 +127,9 @@ void UserActivityLogger::changedDomain(QString domainURL) {
const QString ACTION_NAME = "changed_domain";
QJsonObject actionDetails;
const QString DOMAIN_URL = "domain_url";
actionDetails.insert(DOMAIN_URL, domainURL);
logAction(ACTION_NAME, actionDetails);
}
@ -151,10 +151,10 @@ void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceNam
QJsonObject actionDetails;
const QString TYPE_OF_DEVICE = "type_of_device";
const QString DEVICE_NAME = "device_name";
actionDetails.insert(TYPE_OF_DEVICE, typeOfDevice);
actionDetails.insert(DEVICE_NAME, deviceName);
logAction(ACTION_NAME, actionDetails);
}
@ -163,9 +163,9 @@ void UserActivityLogger::loadedScript(QString scriptName) {
const QString ACTION_NAME = "loaded_script";
QJsonObject actionDetails;
const QString SCRIPT_NAME = "script_name";
actionDetails.insert(SCRIPT_NAME, scriptName);
logAction(ACTION_NAME, actionDetails);
}
@ -199,10 +199,10 @@ void UserActivityLogger::wentTo(AddressManager::LookupTrigger lookupTrigger, QSt
const QString TRIGGER_TYPE_KEY = "trigger";
const QString DESTINATION_TYPE_KEY = "destination_type";
const QString DESTINATION_NAME_KEY = "detination_name";
actionDetails.insert(TRIGGER_TYPE_KEY, trigger);
actionDetails.insert(DESTINATION_TYPE_KEY, destinationType);
actionDetails.insert(DESTINATION_NAME_KEY, destinationName);
logAction(ACTION_NAME, actionDetails);
}