Commerce: Tons of Interface changes (#11463)

* canRez(Tmp)Certified()

* CertifiedItem beginnings

* Skeleton of verifyOwnerChallenge()

* Controlled failure; updateLocation() skeletion

* Controlled failure on checkout page with ctrl+f

* Skeleton Purchases first-use tutorial

* Initial progress on new setup

* Security pic tip

* Skeleton Certificate page

* Updates to Certificate

* General progress; setup is nearly complete

* Better buttons; last step almost done

* Initial progress on wallet home

* Completed recent transactions

* Security page

* Scrollbar

* Fix auth error text

* PassphraseSelection

* Change security pic

* Minor layout changes; beginnings of emulated header

* Various layout changes; wallet nav bar

* Help screen

* Quick onaccepted change

* First pass at new purchases

* Small style updates

* Some error progress

* Lightbox in purchases

* Collapse other help answers when clicking on another

* REZZED notif

* Commerce Lightbox

* Lots of new interactions in Purchases

* Hook up 'view certificate'

* Fix errors, fix close button on cert

* Purchases timer; much faster filter

* Add debugCheckout

* Purchase updates

* GlyphButton; separator; Checkout Success; Ledger fix; debug modes

* Lock glyph below security pic should be white

* Various fixes, round 1

* Circular mask

* Passphrase change button fix; TextField error edge highlighting

* Recent Activity fixes

* Various changes

* Standard Security Pic location

* Color changes

* Filter bar changes

* Styling for multiple owned items

* Minor language change

* Header dropdown (harder than expected)

* Small fixes

* View backup instructions

* marketplaces.js onCommerceScreen

* Beginnign of new injection

* Marketplace injection changes

* Purchase button style changes

* More button styling

* MY PURCHASES button

* marketplace onUsernameChanged

* New help QA

* Help text changes etc

* Downscale security image, reducing filesize

* Lots of bugfixes

* Cleanup before PR

* Only open cert during inspection if commerce switch is on

* Help text changes

* Purchase status incl. change to confirmed; Help text; Open Explorer to hifikey

* Quick glyph change

* New 'wallet not set up' flow for when entering Purchases or Checkout without set-up wallet
This commit is contained in:
Zach Fox 2017-09-27 14:43:51 -07:00 committed by GitHub
parent 9e1a181cbc
commit 7ad3a5a1e3
63 changed files with 4767 additions and 2435 deletions

View file

@ -102,7 +102,7 @@ static const QString ENTITY_SCRIPT_SERVER_LOGGING_NAME = "entity-script-server";
void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
// about each other.
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));
if (_entityViewer.getTree() && !_shuttingDown) {
@ -116,7 +116,7 @@ void EntityScriptServer::handleReloadEntityServerScriptPacket(QSharedPointer<Rec
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
// These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them
// about each other.
if (senderNode->getCanRez() || senderNode->getCanRezTmp()) {
if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) {
MessageID messageID;
message->readPrimitive(&messageID);
auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID));

View file

@ -269,6 +269,8 @@ void DomainGatekeeper::updateNodePermissions() {
userPerms.permissions |= NodePermissions::Permission::canAdjustLocks;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
} else {
@ -358,6 +360,8 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
userPerms.permissions |= NodePermissions::Permission::canAdjustLocks;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryEntities;
userPerms.permissions |= NodePermissions::Permission::canRezPermanentCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canRezTemporaryCertifiedEntities;
userPerms.permissions |= NodePermissions::Permission::canWriteToAssetServer;
userPerms.permissions |= NodePermissions::Permission::canReplaceDomainContent;
newNode->setPermissions(userPerms);

View file

@ -959,7 +959,8 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
bool isAgentWithoutRights = nodeA->getType() == NodeType::Agent
&& nodeB->getType() == NodeType::EntityScriptServer
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp();
&& !nodeA->getCanRez() && !nodeA->getCanRezTmp()
&& !nodeA->getCanRezCertified() && !nodeA->getCanRezTmpCertified();
if (isAgentWithoutRights) {
return false;
@ -968,7 +969,7 @@ bool DomainServer::isInInterestSet(const SharedNodePointer& nodeA, const SharedN
bool isScriptServerForIneffectiveAgent =
(nodeA->getType() == NodeType::EntityScriptServer && nodeB->getType() == NodeType::Agent)
&& ((nodeBData && !nodeBData->getNodeInterestSet().contains(NodeType::EntityScriptServer))
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp()));
|| (!nodeB->getCanRez() && !nodeB->getCanRezTmp() && !nodeB->getCanRezCertified() && !nodeB->getCanRezTmpCertified()));
return !isScriptServerForIneffectiveAgent;
} else {

View file

@ -302,6 +302,14 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canReplaceDomainContent);
packPermissions();
}
if (oldVersion < 1.9) {
unpackPermissions();
// This was prior to addition of canRez(Tmp)Certified; add those to localhost permissions by default
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canRezPermanentCertifiedEntities);
_standardAgentPermissions[NodePermissions::standardNameLocalhost]->set(NodePermissions::Permission::canRezTemporaryCertifiedEntities);
packPermissions();
}
}
unpackPermissions();

View file

@ -15,8 +15,11 @@ import QtQuick.Controls.Styles 1.4
import "../styles-uit"
Original.Button {
id: root;
property int color: 0
property int colorScheme: hifi.colorSchemes.light
property string buttonGlyph: "";
width: 120
height: hifi.dimensions.controlLineHeight
@ -28,6 +31,13 @@ Original.Button {
background: Rectangle {
radius: hifi.buttons.radius
border.width: (control.color === hifi.buttons.none ||
(control.color === hifi.buttons.noneBorderless && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessWhite && control.hovered) ||
(control.color === hifi.buttons.noneBorderlessGray && control.hovered)) ? 1 : 0;
border.color: control.color === hifi.buttons.noneBorderless ? hifi.colors.blueHighlight :
(control.color === hifi.buttons.noneBorderlessGray ? hifi.colors.baseGray : hifi.colors.white);
gradient: Gradient {
GradientStop {
position: 0.2
@ -60,14 +70,35 @@ Original.Button {
}
}
label: RalewayBold {
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
label: Item {
HiFiGlyphs {
id: buttonGlyph;
visible: root.buttonGlyph !== "";
text: root.buttonGlyph === "" ? hifi.glyphs.question : root.buttonGlyph;
// Size
size: 34;
// Anchors
anchors.right: buttonText.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme];
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayBold {
id: buttonText;
anchors.centerIn: parent;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: hifi.fontSizes.buttonLabel
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
}
}
}
}

View file

@ -17,6 +17,19 @@ RalewaySemiBold {
property int colorScheme: hifi.colorSchemes.light
size: hifi.fontSizes.inputLabel
color: enabled ? (colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGray : hifi.colors.lightGrayText)
: (colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
color: {
if (colorScheme === hifi.colorSchemes.dark) {
if (enabled) {
hifi.colors.lightGrayText
} else {
hifi.colors.baseGrayHighlight
}
} else {
if (enabled) {
hifi.colors.lightGray
} else {
hifi.colors.lightGrayText
}
}
}
}

View file

@ -12,8 +12,13 @@ import QtQuick 2.5
import "../styles-uit"
Item {
property int colorScheme: 0;
readonly property var topColor: [ hifi.colors.baseGrayShadow, hifi.colors.faintGray ];
readonly property var bottomColor: [ hifi.colors.baseGrayHighlight, hifi.colors.faintGray ];
// Size
height: 2;
height: colorScheme === 0 ? 2 : 1;
Rectangle {
// Size
width: parent.width;
@ -21,18 +26,19 @@ Item {
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: height;
// Style
color: hifi.colors.baseGrayShadow;
color: topColor[colorScheme];
}
Rectangle {
visible: colorScheme === 0;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: -height;
// Style
color: hifi.colors.baseGrayHighlight;
color: bottomColor[colorScheme];
}
}

View file

@ -20,9 +20,13 @@ TextField {
property int colorScheme: hifi.colorSchemes.light
readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light
readonly property bool isFaintGrayColorScheme: colorScheme == hifi.colorSchemes.faintGray
property bool isSearchField: false
property string label: ""
property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0)
property bool hasRoundedBorder: false
property bool error: false;
property bool hasClearButton: false;
placeholderText: textField.placeholderText
@ -35,16 +39,53 @@ TextField {
y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0
style: TextFieldStyle {
textColor: isLightColorScheme
? (textField.activeFocus ? hifi.colors.black : hifi.colors.lightGray)
: (textField.activeFocus ? hifi.colors.white : hifi.colors.lightGrayText)
textColor: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.lightGray
}
} else {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.lightGrayText
}
}
}
background: Rectangle {
color: isLightColorScheme
? (textField.activeFocus ? hifi.colors.white : hifi.colors.textFieldLightBackground)
: (textField.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow)
border.color: hifi.colors.primaryHighlight
border.width: textField.activeFocus ? 1 : 0
radius: isSearchField ? textField.height / 2 : 0
color: {
if (isLightColorScheme) {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.textFieldLightBackground
}
} else if (isFaintGrayColorScheme) {
if (textField.activeFocus) {
hifi.colors.white
} else {
hifi.colors.faintGray50
}
} else {
if (textField.activeFocus) {
hifi.colors.black
} else {
hifi.colors.baseGrayShadow
}
}
}
border.color: textField.error ? hifi.colors.redHighlight :
(textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray))
border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0
radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0)
HiFiGlyphs {
text: hifi.glyphs.search
@ -55,12 +96,29 @@ TextField {
anchors.leftMargin: hifi.dimensions.textPadding - 2
visible: isSearchField
}
HiFiGlyphs {
text: hifi.glyphs.error
color: textColor
size: 40
anchors.right: parent.right
anchors.rightMargin: hifi.dimensions.textPadding - 2
anchors.verticalCenter: parent.verticalCenter
visible: hasClearButton && textField.text !== "";
MouseArea {
anchors.fill: parent;
onClicked: {
textField.text = "";
}
}
}
}
placeholderTextColor: hifi.colors.lightGray
placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray
selectedTextColor: hifi.colors.black
selectionColor: hifi.colors.primaryHighlight
padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding
padding.right: hifi.dimensions.textPadding
padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding
}
HifiControls.Label {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,153 @@
//
// CommerceLightbox.qml
// qml/hifi/commerce/common
//
// CommerceLightbox
//
// Created by Zach Fox on 2017-09-19
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
property string titleText;
property string bodyImageSource;
property string bodyText;
property string button1text;
property string button1method;
property string button2text;
property string button2method;
readonly property string securityPicBodyText: "When you see your Security Pic, your actions and data are securely making use of your " +
"Wallet's private keys.<br><br>You can change your Security Pic in your Wallet.";
id: root;
visible: false;
anchors.fill: parent;
color: Qt.rgba(0, 0, 0, 0.5);
z: 999;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Rectangle {
anchors.centerIn: parent;
width: parent.width - 100;
height: childrenRect.height + 30;
color: "white";
RalewaySemiBold {
id: titleText;
text: root.titleText;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.baseGray;
size: 24;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
Image {
id: bodyImage;
visible: root.bodyImageSource;
source: root.bodyImageSource ? root.bodyImageSource : "";
anchors.top: root.titleText ? titleText.bottom : parent.top;
anchors.topMargin: root.titleText ? 20 : 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 140;
fillMode: Image.PreserveAspectFit;
mipmap: true;
}
RalewayRegular {
id: bodyText;
text: root.bodyText;
anchors.top: root.bodyImageSource ? bodyImage.bottom : (root.titleText ? titleText.bottom : parent.top);
anchors.topMargin: root.bodyImageSource ? 20 : (root.titleText ? 20 : 30);
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.baseGray;
size: 20;
verticalAlignment: Text.AlignTop;
wrapMode: Text.WordWrap;
}
Item {
id: buttons;
anchors.top: bodyText.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
height: 70;
// Button 1
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessGray;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 10;
width: root.button2text ? parent.width/2 - anchors.leftMargin*2 : parent.width - anchors.leftMargin * 2;
text: root.button1text;
onClicked: {
eval(button1method);
}
}
// Button 2
HifiControlsUit.Button {
visible: root.button2text;
color: hifi.buttons.noneBorderless;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 10;
width: parent.width/2 - anchors.rightMargin*2;
text: root.button2text;
onClicked: {
eval(button2method);
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToParent(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -0,0 +1,333 @@
//
// EmulatedMarketplaceHeader.qml
// qml/hifi/commerce/common
//
// EmulatedMarketplaceHeader
//
// Created by Zach Fox on 2017-09-18
// Copyright 2017 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 as Hifi
import QtQuick 2.7
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
property string referrerURL: "https://metaverse.highfidelity.com/marketplace?";
readonly property int additionalDropdownHeight: usernameDropdown.height - myUsernameButton.anchors.bottomMargin;
height: mainContainer.height + additionalDropdownHeight;
Hifi.QmlCommerce {
id: commerce;
onLoginStatusResult: {
if (!isLoggedIn) {
sendToParent({method: "needsLogIn"});
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onSecurityImageResult: {
if (exists) {
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
}
Component.onCompleted: {
commerce.getLoginStatus();
commerce.getSecurityImage();
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
commerce.getLoginStatus();
}
}
Rectangle {
id: mainContainer;
color: hifi.colors.white;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
height: 70;
Image {
id: marketplaceHeaderImage;
source: "images/marketplaceHeaderImage.png";
anchors.top: parent.top;
anchors.topMargin: 2;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 8;
width: 140;
fillMode: Image.PreserveAspectFit;
MouseArea {
anchors.fill: parent;
onClicked: {
sendToParent({method: "header_marketplaceImageClicked", referrerURL: root.referrerURL});
}
}
}
Item {
id: buttonAndUsernameContainer;
anchors.left: marketplaceHeaderImage.right;
anchors.leftMargin: 8;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.right: securityImage.left;
anchors.rightMargin: 6;
Rectangle {
id: myPurchasesLink;
anchors.right: myUsernameButton.left;
anchors.rightMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: myPurchasesText.paintedWidth + 10;
RalewaySemiBold {
id: myPurchasesText;
text: "My Purchases";
// Text size
size: 18;
// Style
color: hifi.colors.blueAccent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
// Anchors
anchors.centerIn: parent;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToParent({method: 'header_goToPurchases'});
}
onEntered: myPurchasesText.color = hifi.colors.blueHighlight;
onExited: myPurchasesText.color = hifi.colors.blueAccent;
}
}
FontLoader { id: ralewayRegular; source: "../../../../fonts/Raleway-Regular.ttf"; }
TextMetrics {
id: textMetrics;
font.family: ralewayRegular.name
text: usernameText.text;
}
Rectangle {
id: myUsernameButton;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: 40;
width: usernameText.width + 25;
color: "white";
radius: 4;
border.width: 1;
border.color: hifi.colors.lightGray;
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 18;
// Style
color: hifi.colors.baseGray;
elide: Text.ElideRight;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
width: Math.min(textMetrics.width + 25, 110);
// Anchors
anchors.centerIn: parent;
rightPadding: 10;
}
HiFiGlyphs {
id: dropdownIcon;
text: hifi.glyphs.caratDn;
// Size
size: 50;
// Anchors
anchors.right: parent.right;
anchors.rightMargin: -14;
anchors.verticalCenter: parent.verticalCenter;
horizontalAlignment: Text.AlignHCenter;
// Style
color: hifi.colors.baseGray;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
usernameDropdown.visible = !usernameDropdown.visible;
}
onEntered: usernameText.color = hifi.colors.baseGrayShadow;
onExited: usernameText.color = hifi.colors.baseGray;
}
}
}
Image {
id: securityImage;
source: "";
visible: securityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 16;
width: height;
mipmap: true;
MouseArea {
enabled: securityImage.visible;
anchors.fill: parent;
onClicked: {
sendToParent({method: "showSecurityPicLightbox", securityImageSource: securityImage.source});
}
}
}
LinearGradient {
z: 996;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 10;
start: Qt.point(0, 0);
end: Qt.point(0, height);
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.lightGrayText }
GradientStop { position: 1.0; color: hifi.colors.white }
}
}
Item {
id: usernameDropdown;
z: 998;
visible: false;
anchors.top: buttonAndUsernameContainer.bottom;
anchors.topMargin: -buttonAndUsernameContainer.anchors.bottomMargin;
anchors.right: buttonAndUsernameContainer.right;
height: childrenRect.height;
width: 100;
Rectangle {
id: myItemsButton;
color: hifi.colors.white;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
RalewaySemiBold {
anchors.fill: parent;
text: "My Items"
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
size: 18;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
myItemsButton.color = hifi.colors.blueHighlight;
}
onExited: {
myItemsButton.color = hifi.colors.white;
}
onClicked: {
sendToParent({method: "header_myItemsClicked"});
}
}
}
Rectangle {
id: logOutButton;
color: hifi.colors.white;
anchors.top: myItemsButton.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
RalewaySemiBold {
anchors.fill: parent;
text: "Log Out"
color: hifi.colors.baseGray;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
size: 18;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
onEntered: {
logOutButton.color = hifi.colors.blueHighlight;
}
onExited: {
logOutButton.color = hifi.colors.white;
}
onClicked: {
Account.logOut();
}
}
}
}
DropShadow {
z: 997;
visible: usernameDropdown.visible;
anchors.fill: usernameDropdown;
horizontalOffset: 3;
verticalOffset: 3;
radius: 8.0;
samples: 17;
color: "#80000000";
source: usernameDropdown;
}
}
//
// FUNCTION DEFINITIONS START
//
signal sendToParent(var msg);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,321 @@
//
// InspectionCertificate.qml
// qml/hifi/commerce/inspectionCertificate
//
// InspectionCertificate
//
// Created by Zach Fox on 2017-09-14
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string marketplaceId: "";
property string itemName: "--";
property string itemOwner: "--";
property string itemEdition: "--";
property string dateOfPurchase: "";
property bool closeGoesToPurchases: false;
// Style
color: hifi.colors.faintGray;
Hifi.QmlCommerce {
id: commerce;
}
Image {
anchors.fill: parent;
source: "images/cert-bg.jpg";
}
// Title text
RalewayLight {
id: titleBarText;
text: "Certificate";
// Text size
size: 40;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
// Title text
RalewayRegular {
id: popText;
text: "PROOF OF PURCHASE";
// Text size
size: 16;
// Anchors
anchors.top: titleBarText.bottom;
anchors.topMargin: 4;
anchors.left: titleBarText.left;
anchors.right: titleBarText.right;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
//
// "CERTIFICATE" START
//
Item {
id: certificateContainer;
anchors.top: popText.bottom;
anchors.topMargin: 30;
anchors.bottom: buttonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
RalewayRegular {
id: itemNameHeader;
text: "ITEM NAME";
// Text size
size: 16;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
RalewaySemiBold {
id: itemName;
text: root.itemName;
// Text size
size: 28;
// Anchors
anchors.top: itemNameHeader.bottom;
anchors.topMargin: 4;
anchors.left: itemNameHeader.left;
anchors.right: itemNameHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.blueAccent;
elide: Text.ElideRight;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
}
onEntered: itemName.color = hifi.colors.blueHighlight;
onExited: itemName.color = hifi.colors.blueAccent;
}
}
RalewayRegular {
id: ownedByHeader;
text: "OWNER";
// Text size
size: 16;
// Anchors
anchors.top: itemName.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
RalewayRegular {
id: ownedBy;
text: root.itemOwner;
// Text size
size: 22;
// Anchors
anchors.top: ownedByHeader.bottom;
anchors.topMargin: 4;
anchors.left: ownedByHeader.left;
anchors.right: ownedByHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
elide: Text.ElideRight;
}
RalewayRegular {
id: editionHeader;
text: "EDITION";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
AnonymousProRegular {
id: edition;
text: root.itemEdition;
// Text size
size: 22;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: dateOfPurchaseHeader;
text: "DATE OF PURCHASE";
visible: root.dateOfPurchase !== "";
// Text size
size: 16;
// Anchors
anchors.top: ownedBy.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.leftMargin: 45;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.baseGray;
}
AnonymousProRegular {
id: dateOfPurchase;
text: root.dateOfPurchase;
visible: root.dateOfPurchase !== "";
// Text size
size: 22;
// Anchors
anchors.top: editionHeader.bottom;
anchors.topMargin: 4;
anchors.left: editionHeader.left;
anchors.right: editionHeader.right;
height: paintedHeight;
// Style
color: hifi.colors.darkGray;
}
RalewayRegular {
id: errorText;
text: "Here we will display some text if there's an <b>error</b> with the certificate " +
"(DMCA takedown, invalid cert, location of item updated)";
// Text size
size: 20;
// Anchors
anchors.top: root.dateOfPurchase !== "" ? dateOfPurchase.bottom : edition.bottom;
anchors.topMargin: 40;
anchors.left: root.dateOfPurchase !== "" ? dateOfPurchase.left : edition.left;
anchors.right: root.dateOfPurchase !== "" ? dateOfPurchase.right : edition.right;
anchors.bottom: parent.bottom;
// Style
wrapMode: Text.WordWrap;
color: hifi.colors.redHighlight;
verticalAlignment: Text.AlignTop;
}
}
//
// "CERTIFICATE" END
//
Item {
id: buttonsContainer;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderless;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "close";
onClicked: {
sendToScript({method: 'inspectionCertificate_closeClicked', closeGoesToPurchases: root.closeGoesToPurchases});
}
}
// "Show In Marketplace" button
HifiControlsUit.Button {
id: showInMarketplaceButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.right: parent.right;
anchors.rightMargin: 30;
width: parent.width/2 - 50;
height: 50;
text: "View In Market"
onClicked: {
sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', itemId: root.marketplaceId});
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'inspectionCertificate_setMarketplaceId':
root.marketplaceId = message.marketplaceId;
root.closeGoesToPurchases = message.closeGoesToPurchases;
break;
case 'inspectionCertificate_setItemInfo':
root.itemName = message.itemName;
root.itemOwner = message.itemOwner;
root.itemEdition = message.itemEdition;
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendToScript(var message);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View file

@ -0,0 +1,196 @@
//
// FirstUseTutorial.qml
// qml/hifi/commerce/purchases
//
// FirstUseTutorial
//
// Created by Zach Fox on 2017-09-13
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string activeView: "step_1";
// Style
color: hifi.colors.baseGray;
//
// "STEP 1" START
//
Item {
id: step_1;
visible: root.activeView === "step_1";
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
RalewayRegular {
id: step1text;
text: "<b>This is the first-time Purchases tutorial.</b><br><br>Here is some <b>bold text</b> " +
"inside Step 1.";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
//
// "STEP 1" END
//
//
// "STEP 2" START
//
Item {
id: step_2;
visible: root.activeView === "step_2";
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: tutorialActionButtonsContainer.top;
RalewayRegular {
id: step2text;
text: "<b>STEP TWOOO!!!</b>";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
//
// "STEP 2" END
//
Item {
id: tutorialActionButtonsContainer;
// Size
width: root.width;
height: 70;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
// "Skip" or "Back" button
HifiControlsUit.Button {
id: skipOrBackButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: root.activeView === "step_1" ? "Skip" : "Back";
onClicked: {
if (root.activeView === "step_1") {
sendSignalToParent({method: 'tutorial_skipClicked'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) - 1);
}
}
}
// "Next" or "Finish" button
HifiControlsUit.Button {
id: nextButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: root.activeView === "step_2" ? "Finish" : "Next";
onClicked: {
// If this is the final step...
if (root.activeView === "step_2") {
sendSignalToParent({method: 'tutorial_finished'});
} else {
root.activeView = "step_" + (parseInt(root.activeView.split("_")[1]) + 1);
}
}
}
}
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript, in this case the Marketplaces JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
case 'updatePurchases':
referrerURL = message.referrerURL;
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));
}
}
signal sendSignalToParent(var message);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -13,7 +13,9 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -21,116 +23,395 @@ import "../wallet" as HifiWallet
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
property string itemName: "";
property string itemId: "";
property string itemPreviewImageUrl: "";
property string itemHref: "";
// Style
color: hifi.colors.white;
// Size
width: parent.width;
height: 120;
property string purchaseStatus;
property bool purchaseStatusChanged;
property bool canRezCertifiedItems: false;
property string itemName;
property string itemId;
property string itemPreviewImageUrl;
property string itemHref;
property int ownedItemCount;
property int itemEdition;
Image {
id: itemPreviewImage;
source: root.itemPreviewImageUrl;
height: 110;
width: parent.width;
onPurchaseStatusChangedChanged: {
if (root.purchaseStatusChanged === true && root.purchaseStatus === "confirmed") {
statusText.text = "CONFIRMED!";
statusText.color = hifi.colors.blueAccent;
confirmedTimer.start();
root.purchaseStatusChanged = false;
}
}
Timer {
id: confirmedTimer;
interval: 3000;
onTriggered: {
root.purchaseStatus = "";
}
}
Rectangle {
id: mainContainer;
// Style
color: hifi.colors.white;
// Size
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.top: parent.top;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
width: 180;
fillMode: Image.PreserveAspectFit;
height: root.height - 10;
MouseArea {
anchors.fill: parent;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
}
}
RalewayRegular {
id: itemName;
anchors.top: itemPreviewImage.top;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 30;
// Text size
size: 20;
// Style
color: hifi.colors.blueAccent;
text: root.itemName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
onEntered: {
itemName.color = hifi.colors.blueHighlight;
}
onExited: {
itemName.color = hifi.colors.blueAccent;
}
}
}
Item {
id: buttonContainer;
anchors.top: itemName.bottom;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 8;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
// "Rez" button
HifiControlsUit.Button {
id: rezButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
Image {
id: itemPreviewImage;
source: root.itemPreviewImageUrl;
anchors.left: parent.left;
anchors.right: parent.right;
height: parent.height/2 - 4;
text: "Rez Item"
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
anchors.top: parent.top;
anchors.bottom: parent.bottom;
width: height;
fillMode: Image.PreserveAspectCrop;
MouseArea {
anchors.fill: parent;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
}
}
// "More Info" button
HifiControlsUit.Button {
id: moreInfoButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: parent.height/2 - 4;
text: "More Info"
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
RalewaySemiBold {
id: itemName;
anchors.top: itemPreviewImage.top;
anchors.topMargin: 4;
anchors.left: itemPreviewImage.right;
anchors.leftMargin: 8;
anchors.right: buttonContainer.left;
anchors.rightMargin: 8;
height: paintedHeight;
// Text size
size: 24;
// Style
color: hifi.colors.blueAccent;
text: root.itemName;
elide: Text.ElideRight;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemInfoClicked', itemId: root.itemId});
}
onEntered: {
itemName.color = hifi.colors.blueHighlight;
}
onExited: {
itemName.color = hifi.colors.blueAccent;
}
}
}
Item {
id: certificateContainer;
anchors.top: itemName.bottom;
anchors.topMargin: 4;
anchors.left: itemName.left;
anchors.right: buttonContainer.left;
anchors.rightMargin: 2;
height: 24;
HiFiGlyphs {
id: certificateIcon;
text: hifi.glyphs.scriptNew;
// Size
size: 30;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: 32;
// Style
color: hifi.colors.lightGray;
}
RalewayRegular {
id: viewCertificateText;
text: "VIEW CERTIFICATE";
size: 14;
anchors.left: certificateIcon.right;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
color: hifi.colors.lightGray;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
sendToPurchases({method: 'purchases_itemCertificateClicked', itemMarketplaceId: root.itemId});
}
onEntered: {
certificateIcon.color = hifi.colors.black;
viewCertificateText.color = hifi.colors.black;
}
onExited: {
certificateIcon.color = hifi.colors.lightGray;
viewCertificateText.color = hifi.colors.lightGray;
}
}
}
Item {
id: statusContainer;
visible: root.purchaseStatus || root.ownedItemCount > 1;
anchors.left: itemName.left;
anchors.top: certificateContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: parent.bottom;
anchors.right: buttonContainer.left;
anchors.rightMargin: 2;
RalewaySemiBold {
id: statusText;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
width: paintedWidth;
text: {
if (root.purchaseStatus === "pending") {
"PENDING..."
} else if (root.purchaseStatus === "invalidated") {
"INVALIDATED"
} else if (root.ownedItemCount > 1) {
"<font color='#6a6a6a'>(#" + root.itemEdition + ")</font> <u>You own " + root.ownedItemCount + " others</u>"
} else {
""
}
}
size: 18;
color: {
if (root.purchaseStatus === "pending") {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
}
verticalAlignment: Text.AlignTop;
}
HiFiGlyphs {
id: statusIcon;
text: {
if (root.purchaseStatus === "pending") {
hifi.glyphs.question
} else if (root.purchaseStatus === "invalidated") {
hifi.glyphs.question
} else {
""
}
}
// Size
size: 36;
// Anchors
anchors.top: parent.top;
anchors.topMargin: -8;
anchors.left: statusText.right;
anchors.bottom: parent.bottom;
// Style
color: {
if (root.purchaseStatus === "pending") {
hifi.colors.blueAccent
} else if (root.purchaseStatus === "invalidated") {
hifi.colors.redAccent
} else if (root.ownedItemCount > 1) {
hifi.colors.blueAccent
} else {
hifi.colors.baseGray
}
}
verticalAlignment: Text.AlignTop;
}
MouseArea {
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
if (root.purchaseStatus === "pending") {
sendToPurchases({method: 'showPendingLightbox'});
} else if (root.purchaseStatus === "invalidated") {
sendToPurchases({method: 'showInvalidatedLightbox'});
} else if (root.ownedItemCount > 1) {
sendToPurchases({method: 'setFilterText', filterText: root.itemName});
}
}
onEntered: {
if (root.purchaseStatus === "pending") {
statusText.color = hifi.colors.blueHighlight;
statusIcon.color = hifi.colors.blueHighlight;
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redAccent;
statusIcon.color = hifi.colors.redAccent;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueHighlight;
statusIcon.color = hifi.colors.blueHighlight;
}
}
onExited: {
if (root.purchaseStatus === "pending") {
statusText.color = hifi.colors.blueAccent;
statusIcon.color = hifi.colors.blueAccent;
} else if (root.purchaseStatus === "invalidated") {
statusText.color = hifi.colors.redHighlight;
statusIcon.color = hifi.colors.redHighlight;
} else if (root.ownedItemCount > 1) {
statusText.color = hifi.colors.blueAccent;
statusIcon.color = hifi.colors.blueAccent;
}
}
}
}
Rectangle {
id: rezzedNotifContainer;
z: 998;
visible: false;
color: hifi.colors.blueHighlight;
anchors.fill: buttonContainer;
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
RalewayBold {
anchors.fill: parent;
text: "REZZED";
size: 18;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignHCenter;
}
Timer {
id: rezzedNotifContainerTimer;
interval: 2000;
onTriggered: rezzedNotifContainer.visible = false
}
}
Button {
id: buttonContainer;
property int color: hifi.buttons.red;
property int colorScheme: hifi.colorSchemes.light;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
width: height;
enabled: root.canRezCertifiedItems && root.purchaseStatus !== "invalidated";
onClicked: {
if (urlHandler.canHandleUrl(root.itemHref)) {
urlHandler.handleUrl(root.itemHref);
}
rezzedNotifContainer.visible = true;
rezzedNotifContainerTimer.start();
}
style: ButtonStyle {
background: Rectangle {
gradient: Gradient {
GradientStop {
position: 0.2
color: {
if (!control.enabled) {
hifi.buttons.disabledColorStart[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else {
hifi.buttons.colorStart[control.color]
}
}
}
GradientStop {
position: 1.0
color: {
if (!control.enabled) {
hifi.buttons.disabledColorFinish[control.colorScheme]
} else if (control.pressed) {
hifi.buttons.pressedColor[control.color]
} else if (control.hovered) {
hifi.buttons.hoveredColor[control.color]
} else {
hifi.buttons.colorFinish[control.color]
}
}
}
}
}
label: Item {
HiFiGlyphs {
id: lightningIcon;
text: hifi.glyphs.lightning;
// Size
size: 32;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.right: parent.right;
horizontalAlignment: Text.AlignHCenter;
// Style
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
}
RalewayBold {
anchors.top: lightningIcon.bottom;
anchors.topMargin: -20;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
font.capitalization: Font.AllUppercase
color: enabled ? hifi.buttons.textColor[control.color]
: hifi.buttons.disabledTextColor[control.colorScheme]
size: 16;
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: "Rez It"
}
}
}
}
}
DropShadow {
anchors.fill: mainContainer;
horizontalOffset: 3;
verticalOffset: 3;
radius: 8.0;
samples: 17;
color: "#80000000";
source: mainContainer;
}
//

View file

@ -18,6 +18,7 @@ import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../wallet" as HifiWallet
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -30,19 +31,13 @@ Rectangle {
property bool securityImageResultReceived: false;
property bool purchasesReceived: false;
property bool punctuationMode: false;
property bool canRezCertifiedItems: false;
property bool pendingInventoryReply: true;
// Style
color: hifi.colors.baseGray;
color: hifi.colors.white;
Hifi.QmlCommerce {
id: commerce;
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
@ -52,9 +47,18 @@ Rectangle {
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
@ -64,103 +68,101 @@ Rectangle {
securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
notSetUpTimer.start();
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
} else if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && !passphraseModal.visible) {
passphraseModal.visible = true;
if (!isAuthenticated && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
root.activeView = "purchasesMain";
commerce.inventory();
sendToScript({method: 'purchases_getIsFirstUse'});
}
}
onInventoryResult: {
purchasesReceived = true;
if (result.status !== 'success') {
console.log("Failed to get purchases", result.message);
} else {
purchasesModel.clear();
purchasesModel.append(result.data.assets);
filteredPurchasesModel.clear();
filteredPurchasesModel.append(result.data.assets);
if (previousPurchasesModel.count !== 0) {
checkIfAnyItemStatusChanged();
} else {
// Fill statusChanged default value
// Not doing this results in the default being true...
for (var i = 0; i < purchasesModel.count; i++) {
purchasesModel.setProperty(i, "statusChanged", false);
}
}
previousPurchasesModel.append(result.data.assets);
buildFilteredPurchasesModel();
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
}
root.pendingInventoryReply = false;
}
}
HifiWallet.SecurityImageModel {
id: securityImageModel;
Timer {
id: notSetUpTimer;
interval: 200;
onTriggered: {
sendToScript({method: 'checkout_walletNotSetUp'});
}
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
Connections {
onSendToParent: {
sendToScript(msg);
}
}
}
//
// TITLE BAR START
//
Item {
HifiCommerceCommon.EmulatedMarketplaceHeader {
id: titleBarContainer;
z: 998;
visible: !needsLogIn.visible;
// Size
height: 50;
width: parent.width;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
id: titleBarText;
text: "PURCHASES";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Security Image (TEMPORARY!)
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
height: parent.height - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
Image {
id: securityImageOverlay;
source: "../wallet/images/lockOverlay.png";
width: securityImage.width * 0.45;
height: securityImage.height * 0.45;
anchors.bottom: securityImage.bottom;
anchors.right: securityImage.right;
mipmap: true;
opacity: 0.9;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendToParent: {
if (msg.method === 'needsLogIn' && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
} else if (msg.method === 'showSecurityPicLightbox') {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = msg.securityImageSource;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.button2text = "GO TO WALLET";
lightboxPopup.button2method = "sendToParent({method: 'purchases_openWallet'});";
lightboxPopup.visible = true;
} else {
sendToScript(msg);
}
}
}
}
//
@ -171,10 +173,11 @@ Rectangle {
id: initialize;
visible: root.activeView === "initialize";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
color: hifi.colors.baseGray;
color: hifi.colors.white;
Component.onCompleted: {
securityImageResultReceived = false;
@ -206,101 +209,45 @@ Rectangle {
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
visible: root.activeView === "passphraseModal";
anchors.fill: parent;
titleBarText: "Purchases";
titleBarIcon: hifi.glyphs.wallet;
Connections {
onSendSignalToParent: {
if (msg.method === "authSuccess") {
root.activeView = "initialize";
sendToScript({method: 'purchases_getIsFirstUse'});
} else {
sendToScript(msg);
}
}
}
}
FirstUseTutorial {
id: firstUseTutorial;
visible: root.activeView === "firstUseTutorial";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: -titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
//
// "WALLET NOT SET UP" START
//
Item {
id: notSetUp;
visible: root.activeView === "notSetUp";
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
RalewayRegular {
id: notSetUpText;
text: "<b>Your wallet isn't set up.</b><br><br>Set up your Wallet (no credit card necessary) to claim your <b>free HFC</b> " +
"and get items from the Marketplace.";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.bottom: notSetUpActionButtonsContainer.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
Item {
id: notSetUpActionButtonsContainer;
// Size
width: root.width;
height: 70;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 24;
// "Cancel" button
HifiControlsUit.Button {
id: cancelButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL});
}
}
// "Set Up" button
HifiControlsUit.Button {
id: setUpButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: "Set Up Wallet"
onClicked: {
sendToScript({method: 'checkout_setUpClicked'});
switch (message.method) {
case 'tutorial_skipClicked':
case 'tutorial_finished':
sendToScript({method: 'purchases_setIsFirstUse'});
root.activeView = "purchasesMain";
commerce.inventory();
break;
}
}
}
}
//
// "WALLET NOT SET UP" END
//
//
// PURCHASES CONTENTS START
@ -310,13 +257,10 @@ Rectangle {
visible: root.activeView === "purchasesMain";
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 8;
anchors.bottom: actionButtonsContainer.top;
anchors.bottomMargin: 8;
anchors.topMargin: 8 - titleBarContainer.additionalDropdownHeight;
anchors.bottom: parent.bottom;
//
// FILTER BAR START
@ -329,32 +273,38 @@ Rectangle {
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.rightMargin: 12;
anchors.top: parent.top;
anchors.topMargin: 4;
RalewayRegular {
id: myPurchasesText;
anchors.top: parent.top;
anchors.topMargin: 10;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 4;
width: paintedWidth;
text: "My Purchases";
color: hifi.colors.baseGray;
size: 28;
}
HifiControlsUit.TextField {
id: filterBar;
property int previousLength: 0;
anchors.fill: parent;
placeholderText: "Filter";
colorScheme: hifi.colorSchemes.faintGray;
hasClearButton: true;
hasRoundedBorder: true;
anchors.left: myPurchasesText.right;
anchors.leftMargin: 16;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
placeholderText: "filter items";
onTextChanged: {
if (filterBar.text.length < previousLength) {
filteredPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
for (var i = 0; i < filteredPurchasesModel.count; i++) {
if (filteredPurchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) === -1) {
filteredPurchasesModel.remove(i);
i--;
}
}
previousLength = filterBar.text.length;
buildFilteredPurchasesModel();
}
onAccepted: {
@ -366,29 +316,107 @@ Rectangle {
// FILTER BAR END
//
HifiControlsUit.Separator {
id: separator;
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: filterBarContainer.bottom;
anchors.topMargin: 16;
}
ListModel {
id: purchasesModel;
}
ListModel {
id: previousPurchasesModel;
}
ListModel {
id: filteredPurchasesModel;
}
Rectangle {
id: cantRezCertified;
visible: !root.canRezCertifiedItems;
color: "#FFC3CD";
radius: 4;
border.color: hifi.colors.redAccent;
border.width: 1;
anchors.top: separator.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 80;
HiFiGlyphs {
id: lightningIcon;
text: hifi.glyphs.lightning;
// Size
size: 36;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 18;
anchors.left: parent.left;
anchors.leftMargin: 12;
horizontalAlignment: Text.AlignHCenter;
// Style
color: hifi.colors.lightGray;
}
RalewayRegular {
text: "You don't have permission to rez certified items in this domain. " +
'<b><font color="' + hifi.colors.blueAccent + '"><a href="#">Learn More</a></font></b>';
// Text size
size: 18;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 4;
anchors.left: lightningIcon.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 4;
// Style
color: hifi.colors.baseGray;
wrapMode: Text.WordWrap;
// Alignment
verticalAlignment: Text.AlignVCenter;
onLinkActivated: {
lightboxPopup.titleText = "Rez Permission Required";
lightboxPopup.bodyText = "You don't have permission to rez certified items in this domain.<br><br>" +
"Use the <b>GOTO app</b> to visit another domain or <b>go to your own sandbox.</b>";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.button2text = "OPEN GOTO";
lightboxPopup.button2method = "sendToParent({method: 'purchases_openGoTo'});";
lightboxPopup.visible = true;
}
}
}
ListView {
id: purchasesContentsList;
visible: purchasesModel.count !== 0;
clip: true;
model: filteredPurchasesModel;
// Anchors
anchors.top: filterBarContainer.bottom;
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
anchors.topMargin: 12;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width;
delegate: PurchasedItem {
canRezCertifiedItems: root.canRezCertifiedItems;
itemName: title;
itemId: id;
itemPreviewImageUrl: preview;
itemHref: root_file_url;
purchaseStatus: status;
purchaseStatusChanged: statusChanged;
anchors.topMargin: 12;
anchors.bottomMargin: 12;
@ -396,6 +424,24 @@ Rectangle {
onSendToPurchases: {
if (msg.method === 'purchases_itemInfoClicked') {
sendToScript({method: 'purchases_itemInfoClicked', itemId: itemId});
} else if (msg.method === 'purchases_itemCertificateClicked') {
sendToScript(msg);
} else if (msg.method === "showInvalidatedLightbox") {
lightboxPopup.titleText = "Item Invalidated";
lightboxPopup.bodyText = 'Your item is marked "invalidated" because this item has been suspended ' +
"from the Marketplace due to a claim against its author.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
} else if (msg.method === "showPendingLightbox") {
lightboxPopup.titleText = "Item Pending";
lightboxPopup.bodyText = 'Your item is marked "pending" while your purchase is being confirmed. ' +
"Usually, purchases take about 90 seconds to confirm.";
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
} else if (msg.method === "setFilterText") {
filterBar.text = msg.filterText;
}
}
}
@ -414,7 +460,7 @@ Rectangle {
// Explanitory text
RalewayRegular {
id: haventPurchasedYet;
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to your <b>Purchases</b>.";
text: "<b>You haven't purchased anything yet!</b><br><br>Get an item from <b>Marketplace</b> to add it to My Purchases.";
// Text size
size: 22;
// Anchors
@ -426,7 +472,7 @@ Rectangle {
anchors.rightMargin: 24;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.baseGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
@ -452,42 +498,6 @@ Rectangle {
// PURCHASES CONTENTS END
//
//
// ACTION BUTTONS START
//
Item {
id: actionButtonsContainer;
visible: purchasesContentsContainer.visible;
// Size
width: parent.width;
height: 40;
// Anchors
anchors.left: parent.left;
anchors.bottom: keyboard.top;
anchors.bottomMargin: 8;
// "Back" button
HifiControlsUit.Button {
id: backButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Back"
onClicked: {
sendToScript({method: 'purchases_backClicked', referrerURL: referrerURL});
}
}
}
//
// ACTION BUTTONS END
//
HifiControlsUit.Keyboard {
id: keyboard;
raised: HMD.mounted && filterBar.focus;
@ -499,9 +509,60 @@ Rectangle {
}
}
onVisibleChanged: {
if (!visible) {
inventoryTimer.stop();
}
}
Timer {
id: inventoryTimer;
interval: 90000;
onTriggered: {
if (root.activeView === "purchasesMain" && !root.pendingInventoryReply) {
root.pendingInventoryReply = true;
commerce.inventory();
}
}
}
//
// FUNCTION DEFINITIONS START
//
function buildFilteredPurchasesModel() {
filteredPurchasesModel.clear();
for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).title.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) {
if (purchasesModel.get(i).status !== "confirmed") {
filteredPurchasesModel.insert(0, purchasesModel.get(i));
} else {
filteredPurchasesModel.append(purchasesModel.get(i));
}
}
}
}
function checkIfAnyItemStatusChanged() {
var currentPurchasesModelId, currentPurchasesModelEdition, currentPurchasesModelStatus;
var previousPurchasesModelStatus;
for (var i = 0; i < purchasesModel.count; i++) {
currentPurchasesModelId = purchasesModel.get(i).id;
currentPurchasesModelEdition = purchasesModel.get(i).edition_number;
currentPurchasesModelStatus = purchasesModel.get(i).status;
for (var j = 0; j < previousPurchasesModel.count; j++) {
previousPurchasesModelStatus = previousPurchasesModel.get(j).status;
if (currentPurchasesModelId === previousPurchasesModel.get(j).id &&
currentPurchasesModelEdition === previousPurchasesModel.get(j).edition_number &&
currentPurchasesModelStatus !== previousPurchasesModelStatus) {
purchasesModel.setProperty(i, "statusChanged", true);
}
}
}
}
//
// Function Name: fromScript()
//
@ -519,6 +580,17 @@ Rectangle {
switch (message.method) {
case 'updatePurchases':
referrerURL = message.referrerURL;
titleBarContainer.referrerURL = message.referrerURL;
root.canRezCertifiedItems = message.canRezCertifiedItems;
filterBar.text = message.filterText ? message.filterText : "";
break;
case 'purchases_getIsFirstUseResult':
if (message.isFirstUseOfPurchases && root.activeView !== "firstUseTutorial") {
root.activeView = "firstUseTutorial";
} else if (!message.isFirstUseOfPurchases && root.activeView === "initialize") {
root.activeView = "purchasesMain";
commerce.inventory();
}
break;
default:
console.log('Unrecognized message from marketplaces.js:', JSON.stringify(message));

View file

@ -1,8 +1,8 @@
//
// SendMoney.qml
// Help.qml
// qml/hifi/commerce/wallet
//
// SendMoney
// Help
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -12,8 +12,8 @@
//
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick 2.7
import QtQuick.Controls 2.2
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -24,35 +24,44 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath;
Hifi.QmlCommerce {
id: commerce;
onKeyFilePathIfExistsResult: {
keyFilePath = path;
}
}
// "Unavailable"
RalewayRegular {
id: helpText;
text: "Help me!";
Component.onCompleted: {
commerce.getKeyFilePathIfExists();
}
RalewaySemiBold {
id: helpTitleText;
text: "Help Topics";
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: paintedWidth;
height: 30;
// Text size
size: 24;
size: 18;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
HifiControlsUit.Button {
id: clearCachedPassphraseButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: resetButton.top;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
text: "DEBUG: Clear Cached Passphrase";
anchors.top: parent.top;
anchors.left: helpTitleText.right;
anchors.leftMargin: 20;
height: 40;
width: 150;
text: "DBG: Clear Pass";
onClicked: {
commerce.setPassphrase("");
sendSignalToWallet({method: 'passphraseReset'});
@ -62,18 +71,184 @@ Item {
id: resetButton;
color: hifi.buttons.red;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: helpText.bottom;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter;
height: 50;
width: 250;
text: "DEBUG: Reset Wallet!";
anchors.top: clearCachedPassphraseButton.top;
anchors.left: clearCachedPassphraseButton.right;
height: 40;
width: 150;
text: "DBG: RST Wallet";
onClicked: {
commerce.reset();
sendSignalToWallet({method: 'walletReset'});
}
}
ListModel {
id: helpModel;
ListElement {
isExpanded: false;
question: "What are private keys?"
answer: qsTr("A private key is a secret piece of text that is used to decrypt code.<br><br>In High Fidelity, <b>your private keys are used to decrypt the contents of your Wallet and Purchases.</b>");
}
ListElement {
isExpanded: false;
question: "Where are my private keys stored?"
answer: qsTr('Your private keys are <b>only stored on your hard drive</b> in High Fidelity Interface\'s AppData directory.<br><br><b><font color="#0093C5"><a href="#privateKeyPath">Tap here to open the file path of your hifikey in your file explorer.</a></font></b><br><br> You may backup this file by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. Restore your backup by replacing the file in Interface\'s AppData directory with your backed-up copy.');
}
ListElement {
isExpanded: false;
question: "What happens if I lose my passphrase?"
answer: qsTr("If you lose your passphrase, you will no longer have access to the contents of your Wallet or My Purchases.<br><br><b>Nobody can help you recover your passphrase, including High Fidelity.</b> Please write it down and store it securely.");
}
ListElement {
isExpanded: false;
question: "What is a 'Security Pic'?"
answer: qsTr("Your Security Pic is an encrypted image that you selected during Wallet Setup. <b>It acts as an extra layer of Wallet security.</b><br><br>When you see your Security Pic, you know that your actions and data are securely making use of your private keys.<br><br><b>If you don't see your Security Pic on a page that is asking you for your Wallet passphrase, someone untrustworthy may be trying to gain access to your Wallet.</b><br><br>The Pic is stored on your hard drive inside the same file as your private keys.");
}
ListElement {
isExpanded: false;
question: "My HFC balance isn't what I expect it to be. Why?"
answer: qsTr('High Fidelity Coin (HFC) transactions are backed by a <b>blockchain</b>, which takes time to update. The status of a transaction usually updates within 90 seconds.<br><br><b><font color="#0093C5"><a href="#blockchain">Tap here to learn more about the blockchain.</a></font></b>');
}
ListElement {
isExpanded: false;
question: "My friend purchased my item from the Marketplace, but I still haven't received the money from the sale. Why not?"
answer: qsTr('High Fidelity Coin (HFC) transactions are backed by a <b>blockchain</b>, which takes time to update. The status of a transaction usually updates within 90 seconds, at which point you will receive your money.<br><br><b><font color="#0093C5"><a href="#blockchain">Tap here to learn more about the blockchain.</a></font></b>');
}
ListElement {
isExpanded: false;
question: "Do I get charged money if a transaction fails?"
answer: qsTr("<b>No.</b> Your HFC balance only changes after a transaction is confirmed.");
}
ListElement {
isExpanded: false;
question: "How do I convert HFC to other currencies?"
answer: qsTr("We are still building the tools needed to support a vibrant economy in High Fidelity. <b>There is currently no way to convert HFC to other currencies.</b>");
}
}
ListView {
id: helpListView;
ScrollBar.vertical: ScrollBar {
policy: helpListView.contentHeight > helpListView.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
parent: helpListView.parent;
anchors.top: helpListView.top;
anchors.right: helpListView.right;
anchors.bottom: helpListView.bottom;
width: 20;
}
anchors.top: helpTitleText.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right
clip: true;
model: helpModel;
delegate: Item {
width: parent.width;
height: model.isExpanded ? questionContainer.height + answerContainer.height : questionContainer.height;
HifiControlsUit.Separator {
colorScheme: 1;
visible: index === 0;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
Item {
id: questionContainer;
anchors.top: parent.top;
anchors.left: parent.left;
width: parent.width;
height: questionText.paintedHeight + 50;
RalewaySemiBold {
id: plusMinusButton;
text: model.isExpanded ? "-" : "+";
// Anchors
anchors.top: parent.top;
anchors.topMargin: model.isExpanded ? -9 : 0;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
width: 60;
// Text size
size: 60;
// Style
color: hifi.colors.white;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewaySemiBold {
id: questionText;
text: model.question;
size: 18;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: plusMinusButton.right;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 10;
wrapMode: Text.WordWrap;
height: paintedHeight;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
}
MouseArea {
id: securityTabMouseArea;
anchors.fill: parent;
onClicked: {
model.isExpanded = !model.isExpanded;
if (model.isExpanded) {
collapseAllOtherHelpItems(index);
}
}
}
}
Rectangle {
id: answerContainer;
visible: model.isExpanded;
color: Qt.rgba(0, 0, 0, 0.5);
anchors.top: questionContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: answerText.paintedHeight + 50;
RalewayRegular {
id: answerText;
text: model.answer;
size: 18;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: parent.left;
anchors.leftMargin: 32;
anchors.right: parent.right;
anchors.rightMargin: 32;
wrapMode: Text.WordWrap;
height: paintedHeight;
color: hifi.colors.white;
onLinkActivated: {
if (link === "#privateKeyPath") {
Qt.openUrlExternally("file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')));
} else if (link === "#blockchain") {
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
}
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
}
}
}
//
// FUNCTION DEFINITIONS START
//
@ -97,6 +272,14 @@ Item {
}
}
signal sendSignalToWallet(var msg);
function collapseAllOtherHelpItems(thisIndex) {
for (var i = 0; i < helpModel.count; i++) {
if (i !== thisIndex) {
helpModel.setProperty(i, "isExpanded", false);
}
}
}
//
// FUNCTION DEFINITIONS END
//

View file

@ -24,6 +24,12 @@ Item {
HifiConstants { id: hifi; }
id: root;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
}

View file

@ -1,127 +0,0 @@
//
// NotSetUp.qml
// qml/hifi/commerce/wallet
//
// NotSetUp
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
Hifi.QmlCommerce {
id: commerce;
}
//
// TAB CONTENTS START
//
// Text below title bar
RalewaySemiBold {
id: notSetUpText;
text: "Your Wallet Account Has Not Been Set Up";
// Text size
size: 22;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 100;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// Explanitory text
RalewayRegular {
text: "To buy and sell items in High Fidelity Coin (HFC), you first need " +
"to set up your wallet.<br><b>You do not need to submit a credit card or personal information to set up your wallet.</b>";
// Text size
size: 18;
// Anchors
anchors.top: notSetUpText.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 100;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "Set Up" button
HifiControlsUit.Button {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 150;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Set Up My Wallet";
onClicked: {
sendSignalToWallet({method: 'setUpClicked'});
}
}
//
// TAB CONTENTS END
//
//
// FUNCTION DEFINITIONS START
//
//
// Function Name: fromScript()
//
// Relevant Variables:
// None
//
// Arguments:
// message: The message sent from the JavaScript.
// Messages are in format "{method, params}", like json-rpc.
//
// Description:
// Called when a message is received from a script.
//
function fromScript(message) {
switch (message.method) {
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}
}
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -1,8 +1,8 @@
//
// PassphraseSelectionLightbox.qml
// PassphraseChange.qml
// qml/hifi/commerce/wallet
//
// PassphraseSelectionLightbox
// PassphraseChange
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -20,12 +20,31 @@ import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
// Style
color: hifi.colors.baseGray;
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;
text: Account.username;
// Text size
size: 24;
// Style
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
//
// SECURE PASSPHRASE SELECTION START
@ -33,60 +52,31 @@ Rectangle {
Item {
id: choosePassphraseContainer;
// Anchors
anchors.fill: parent;
anchors.top: usernameText.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE PASSPHRASE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
// "Change Passphrase" text
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
id: passphraseTitle;
text: "Change Passphrase:";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
size: 18;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.leftMargin: 20;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
isChangingPassphrase: true;
anchors.top: passphraseTitle.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
@ -96,11 +86,10 @@ Rectangle {
if (msg.method === 'statusResult') {
if (msg.status) {
// Success submitting new passphrase
root.resetSubmitButton();
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changePassphraseSuccess"});
} else {
// Error submitting new passphrase
root.resetSubmitButton();
resetSubmitButton();
passphraseSelection.setErrorText("Backend error");
}
} else {
@ -115,40 +104,37 @@ Rectangle {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
height: 40;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 30;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
width: 150;
text: "Cancel"
onClicked: {
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changePassphraseCancelled"});
}
}
// "Submit" button
HifiControlsUit.Button {
id: passphraseSubmitButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
width: 150;
text: "Submit";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {

View file

@ -17,6 +17,7 @@ import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -26,11 +27,20 @@ Item {
id: root;
z: 998;
property bool keyboardRaised: false;
property string titleBarIcon: "";
property string titleBarText: "";
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
passphraseModalSecurityImage.source = "";
passphraseModalSecurityImage.source = "image://security/securityImage";
}
@ -40,7 +50,7 @@ Item {
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
} else {
root.visible = false;
sendSignalToParent({method: 'authSuccess'});;
}
}
}
@ -66,24 +76,88 @@ Item {
}
}
// Background rectangle
Rectangle {
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
color: "black";
opacity: 0.9;
}
Rectangle {
Item {
id: titleBar;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 50;
// Wallet icon
HiFiGlyphs {
id: titleBarIcon;
text: root.titleBarIcon;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
RalewaySemiBold {
id: titleBarText;
text: root.titleBarText;
anchors.top: parent.top;
anchors.left: titleBarIcon.right;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
size: 20;
color: hifi.colors.white;
verticalAlignment: Text.AlignVCenter;
}
Image {
id: titleBarSecurityImage;
source: "";
visible: titleBarSecurityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
Item {
id: passphraseContainer;
anchors.top: titleBar.bottom;
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 250;
color: hifi.colors.baseGray;
RalewaySemiBold {
id: instructionsText;
text: "Enter Wallet Passphrase";
size: 16;
text: "Please Enter Your Passphrase";
size: 24;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
@ -91,17 +165,34 @@ Item {
width: passphraseField.width;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignLeft;
}
// Error text above buttons
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 15;
// Anchors
anchors.bottom: passphraseField.top;
anchors.bottomMargin: 4;
anchors.left: passphraseField.left;
anchors.right: parent.right;
height: 20;
// Style
color: hifi.colors.redHighlight;
}
HifiControlsUit.TextField {
id: passphraseField;
colorScheme: hifi.colorSchemes.dark;
anchors.top: instructionsText.bottom;
anchors.topMargin: 4;
anchors.topMargin: 40;
anchors.left: instructionsText.left;
width: 280;
width: 260;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
@ -133,7 +224,7 @@ Item {
anchors.top: passphraseField.bottom;
anchors.topMargin: 8;
height: 30;
text: "Show passphrase as plain text";
text: "Show passphrase";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
@ -144,16 +235,18 @@ Item {
Item {
id: securityImageContainer;
// Anchors
anchors.top: instructionsText.top;
anchors.top: passphraseField.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 8;
height: 145;
Image {
id: passphraseModalSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
@ -162,54 +255,43 @@ Item {
commerce.getSecurityImage();
}
}
Image {
id: passphraseModalSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphraseModalSecurityImage.width * 0.45;
height: passphraseModalSecurityImage.height * 0.45;
anchors.bottom: passphraseModalSecurityImage.bottom;
Item {
id: iconAndTextContainer;
anchors.left: passphraseModalSecurityImage.left;
anchors.right: passphraseModalSecurityImage.right;
mipmap: true;
opacity: 0.9;
anchors.bottom: parent.bottom;
height: 24;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 30;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: passphraseModalSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// Error text above buttons
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
// Anchors
anchors.bottom: passphrasePopupActionButtonsContainer.top;
anchors.bottomMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
//
@ -217,29 +299,10 @@ Item {
//
Item {
id: passphrasePopupActionButtonsContainer;
// Size
width: root.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 10;
// "Cancel" button
HifiControlsUit.Button {
id: cancelPassphraseInputButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2 - anchors.leftMargin*2;
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
}
}
// "Submit" button
HifiControlsUit.Button {
@ -247,16 +310,38 @@ Item {
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 20;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
anchors.rightMargin: 16;
width: parent.width/2 -4;
text: "Submit"
onClicked: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
// "Cancel" button
HifiControlsUit.Button {
id: cancelPassphraseInputButton;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: submitPassphraseInputButton.bottom;
anchors.topMargin: 20;
height: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
width: parent.width/2 - 4;
text: "Cancel"
onClicked: {
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
}
}
}
}

View file

@ -24,6 +24,8 @@ Item {
HifiConstants { id: hifi; }
id: root;
property bool isChangingPassphrase: false;
property bool isShowingTip: false;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
@ -51,23 +53,62 @@ Item {
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.focus = true;
if (root.isChangingPassphrase) {
currentPassphraseField.focus = true;
} else {
passphraseField.focus = true;
}
sendMessageToLightbox({method: 'disableHmdPreview'});
} else {
sendMessageToLightbox({method: 'maybeEnableHmdPreview'});
}
}
HifiControlsUit.TextField {
id: currentPassphraseField;
colorScheme: hifi.colorSchemes.dark;
visible: root.isChangingPassphrase;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: passphraseField.right;
height: 50;
echoMode: TextInput.Password;
placeholderText: "enter current passphrase";
onFocusChanged: {
if (focus) {
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
} else if (!passphraseFieldAgain.focus) {
sendSignalToWallet({method: 'walletSetup_lowerKeyboard'});
}
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
sendSignalToWallet({method: 'walletSetup_raiseKeyboard'});
}
}
onAccepted: {
passphraseField.focus = true;
}
}
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: parent.top;
anchors.topMargin: 30;
colorScheme: hifi.colorSchemes.dark;
anchors.top: root.isChangingPassphrase ? currentPassphraseField.bottom : parent.top;
anchors.topMargin: root.isChangingPassphrase ? 40 : 0;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: 280;
anchors.leftMargin: 20;
width: 285;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
placeholderText: root.isShowingTip ? "" : "enter new passphrase";
onFocusChanged: {
if (focus) {
@ -91,13 +132,14 @@ Item {
}
HifiControlsUit.TextField {
id: passphraseFieldAgain;
colorScheme: hifi.colorSchemes.dark;
anchors.top: passphraseField.bottom;
anchors.topMargin: 10;
anchors.topMargin: root.isChangingPassphrase ? 20 : 40;
anchors.left: passphraseField.left;
anchors.right: passphraseField.right;
height: 50;
echoMode: TextInput.Password;
placeholderText: "re-enter passphrase";
placeholderText: root.isShowingTip ? "" : "re-enter new passphrase";
onFocusChanged: {
if (focus) {
@ -124,16 +166,16 @@ Item {
Item {
id: securityImageContainer;
// Anchors
anchors.top: passphraseField.top;
anchors.top: root.isChangingPassphrase ? currentPassphraseField.top : passphraseField.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.right: parent.right;
anchors.bottom: root.isChangingPassphrase ? passphraseField.bottom : passphraseFieldAgain.bottom;
Image {
id: passphrasePageSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
@ -142,123 +184,125 @@ Item {
commerce.getSecurityImage();
}
}
Image {
id: topSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphrasePageSecurityImage.width * 0.45;
height: passphrasePageSecurityImage.height * 0.45;
anchors.bottom: passphrasePageSecurityImage.bottom;
Item {
id: iconAndTextContainer;
anchors.left: passphrasePageSecurityImage.left;
anchors.right: passphrasePageSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: passphrasePageSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
anchors.bottom: parent.bottom;
height: 22;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 35;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
}
// Error text below TextFields
// Error text above TextFields
RalewaySemiBold {
id: errorText;
text: "";
// Text size
size: 16;
size: 15;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 0;
anchors.bottom: passphraseField.top;
anchors.bottomMargin: 4;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.leftMargin: 20;
anchors.right: securityImageContainer.left;
anchors.rightMargin: 4;
height: 30;
// Style
color: hifi.colors.redHighlight;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below TextFields
RalewaySemiBold {
id: passwordReqs;
text: "Passphrase must be at least 3 characters";
// Text size
size: 16;
// Anchors
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Show passphrase text
HifiControlsUit.CheckBox {
id: showPassphrase;
visible: !root.isShowingTip;
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.top: passwordReqs.bottom;
anchors.leftMargin: 20;
anchors.top: passphraseFieldAgain.bottom;
anchors.topMargin: 16;
height: 30;
text: "Show passphrase as plain text";
text: "Show passphrase";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
passphraseFieldAgain.echoMode = checked ? TextInput.Normal : TextInput.Password;
if (root.isChangingPassphrase) {
currentPassphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
}
}
}
// Text below checkbox
RalewayRegular {
text: "Your passphrase is used to encrypt your private keys. <b>Please write it down.</b> If it is lost, you will not be able to recover it.";
visible: !root.isShowingTip;
text: "Your passphrase is used to encrypt your private keys. Only you have it.<br><br>Please write it down.<br><br><b>If it is lost, you will not be able to recover it.</b>";
// Text size
size: 16;
size: 18;
// Anchors
anchors.top: showPassphrase.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.leftMargin: 24;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.rightMargin: 24;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
function validateAndSubmitPassphrase() {
if (passphraseField.text.length < 3) {
setErrorText("Passphrase too short.");
setErrorText("Passphrase must be at least 3 characters.");
passphraseField.error = true;
passphraseFieldAgain.error = true;
currentPassphraseField.error = true;
return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) {
setErrorText("Passphrases don't match.");
passphraseField.error = true;
passphraseFieldAgain.error = true;
currentPassphraseField.error = true;
return false;
} else {
passphraseField.error = false;
passphraseFieldAgain.error = false;
currentPassphraseField.error = false;
setErrorText("");
commerce.setPassphrase(passphraseField.text);
return true;
@ -270,6 +314,7 @@ Item {
}
function clearPassphraseFields() {
currentPassphraseField.text = "";
passphraseField.text = "";
passphraseFieldAgain.text = "";
setErrorText("");

View file

@ -25,29 +25,16 @@ Item {
HifiConstants { id: hifi; }
id: root;
property string keyFilePath: "";
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) { // "If security image is set up"
var path = "image://security/securityImage";
topSecurityImage.source = "";
topSecurityImage.source = path;
changeSecurityImageImage.source = "";
changeSecurityImageImage.source = path;
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
keyFilePath = path;
}
}
SecurityImageModel {
id: securityImageModel;
}
// Username Text
RalewayRegular {
id: usernameText;
@ -55,253 +42,258 @@ Item {
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.left: parent.left;
anchors.right: securityImageContainer.left;
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
}
}
Image {
id: topSecurityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
}
Image {
id: topSecurityImageMask;
source: "images/lockOverlay.png";
width: topSecurityImage.width * 0.45;
height: topSecurityImage.height * 0.45;
anchors.bottom: topSecurityImage.bottom;
anchors.right: topSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
RalewayRegular {
text: "security image";
// Text size
size: 12;
// Anchors
anchors.top: topSecurityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
anchors.left: parent.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
Item {
id: securityContainer;
anchors.top: securityImageContainer.bottom;
anchors.top: usernameText.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
anchors.bottom: parent.bottom;
RalewayRegular {
RalewaySemiBold {
id: securityText;
text: "Security";
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
size: 18;
// Style
color: hifi.colors.blueHighlight;
}
Rectangle {
id: securityTextSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: securityText.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changePassphraseContainer;
anchors.top: securityText.bottom;
anchors.topMargin: 16;
anchors.top: securityTextSeparator.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
Image {
HiFiGlyphs {
id: changePassphraseImage;
text: hifi.glyphs.passphrase;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
height: parent.height;
width: height;
source: "images/lockOverlay.png";
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
visible: false;
// Style
color: hifi.colors.white;
}
ColorOverlay {
anchors.fill: changePassphraseImage;
source: changePassphraseImage;
color: "white"
RalewaySemiBold {
text: "Passphrase";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: changePassphraseImage.right;
anchors.leftMargin: 30;
width: 50;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Passphrase" button
HifiControlsUit.Button {
id: changePassphraseButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changePassphraseImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Passphrase";
width: 140;
height: 40;
text: "Change";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changePassphrase'});
}
}
}
Item {
id: changeSecurityImageContainer;
anchors.top: changePassphraseContainer.bottom;
anchors.topMargin: 8;
Rectangle {
id: changePassphraseSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: changePassphraseContainer.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
Item {
id: changeSecurityImageContainer;
anchors.top: changePassphraseSeparator.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
anchors.rightMargin: 55;
height: 75;
Image {
HiFiGlyphs {
id: changeSecurityImageImage;
text: hifi.glyphs.securityImage;
// Size
size: 80;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
height: parent.height;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
// Style
color: hifi.colors.white;
}
// "Change Security Image" button
RalewaySemiBold {
text: "Security Pic";
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 30;
width: 50;
// Text size
size: 18;
// Style
color: hifi.colors.white;
}
// "Change Passphrase" button
HifiControlsUit.Button {
id: changeSecurityImageButton;
color: hifi.buttons.black;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.verticalCenter: parent.verticalCenter;
anchors.left: changeSecurityImageImage.right;
anchors.leftMargin: 16;
width: 250;
height: 50;
text: "Change My Security Image";
width: 140;
height: 40;
text: "Change";
onClicked: {
sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'});
}
}
}
}
Item {
id: yourPrivateKeysContainer;
anchors.top: securityContainer.bottom;
anchors.topMargin: 20;
anchors.left: parent.left;
anchors.right: parent.right;
height: childrenRect.height;
RalewaySemiBold {
id: yourPrivateKeysText;
text: "Your Private Keys";
Rectangle {
id: privateKeysSeparator;
// Size
width: parent.width;
height: 1;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 30;
// Text size
size: 22;
anchors.top: changeSecurityImageContainer.bottom;
anchors.topMargin: 8;
// Style
color: hifi.colors.faintGray;
}
// Text below "your private keys"
RalewayRegular {
id: explanitoryText;
text: "Your money and purchases are secured with private keys that only you " +
"have access to. <b>If they are lost, you will not be able to access your money or purchases. " +
"To safeguard your private keys, back up this file regularly:</b>";
// Text size
size: 18;
// Anchors
anchors.top: yourPrivateKeysText.bottom;
anchors.topMargin: 10;
Item {
id: yourPrivateKeysContainer;
anchors.top: privateKeysSeparator.bottom;
anchors.left: parent.left;
anchors.leftMargin: 40;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanitoryText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
anchors.rightMargin: 55;
anchors.bottom: parent.bottom;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
id: yourPrivateKeysImage;
text: hifi.glyphs.walletKey;
// Size
size: parent.height*1.3;
size: 80;
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.topMargin: 20;
anchors.left: parent.left;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
color: hifi.colors.white;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
RalewaySemiBold {
id: yourPrivateKeysText;
text: "Private Keys";
size: 18;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 32;
anchors.left: yourPrivateKeysImage.right;
anchors.leftMargin: 30;
anchors.right: parent.right;
height: 30;
// Style
color: hifi.colors.white;
}
// Text below "private keys"
RalewayRegular {
id: explanitoryText;
text: "Your money and purchases are secured with private keys that only you have access to.";
// Text size
size: 18;
// Anchors
anchors.top: yourPrivateKeysText.bottom;
anchors.topMargin: 10;
anchors.left: yourPrivateKeysText.left;
anchors.right: yourPrivateKeysText.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.Button {
id: backupInstructionsButton;
text: "View Backup Instructions";
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.left: explanitoryText.left;
anchors.right: explanitoryText.right;
anchors.top: explanitoryText.bottom;
anchors.topMargin: 16;
height: 40;
onClicked: {
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
}

View file

@ -1,8 +1,8 @@
//
// SecurityImageSelectionLightbox.qml
// SecurityImageChange.qml
// qml/hifi/commerce/wallet
//
// SecurityImageSelectionLightbox
// SecurityImageChange
//
// Created by Zach Fox on 2017-08-18
// Copyright 2017 High Fidelity, Inc.
@ -20,22 +20,26 @@ import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
Item {
HifiConstants { id: hifi; }
id: root;
property bool justSubmitted: false;
// Style
color: hifi.colors.baseGray;
SecurityImageModel {
id: securityImageModel;
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
securityImageChangePageSecurityImage.source = "";
securityImageChangePageSecurityImage.source = "image://security/securityImage";
if (exists) { // Success submitting new security image
if (root.justSubmitted) {
root.resetSubmitButton();
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changeSecurityImageSuccess"});
root.justSubmitted = false;
}
} else if (root.justSubmitted) {
@ -45,71 +49,104 @@ Rectangle {
}
}
}
// Security Image
Item {
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
anchors.rightMargin: 25;
width: 130;
height: 150;
Image {
id: securityImageChangePageSecurityImage;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: iconAndTextContainer.top;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
}
}
Item {
id: iconAndTextContainer;
anchors.left: securityImageChangePageSecurityImage.left;
anchors.right: securityImageChangePageSecurityImage.right;
anchors.bottom: parent.bottom;
height: 22;
// Lock icon
HiFiGlyphs {
id: lockIcon;
text: hifi.glyphs.lock;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 10;
size: 20;
width: height;
verticalAlignment: Text.AlignBottom;
color: hifi.colors.white;
}
// "Security image" text below pic
RalewayRegular {
id: securityImageText;
text: "SECURITY PIC";
// Text size
size: 12;
// Anchors
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: lockIcon.anchors.leftMargin;
width: paintedWidth;
height: 22;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignBottom;
}
}
}
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
// Anchors
anchors.fill: parent;
anchors.top: parent.top;
anchors.topMargin: 135;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "CHANGE SECURITY IMAGE";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
// "Change Security Image" text
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
id: securityImageTitle;
text: "Change Security Pic:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
size: 18;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: 30;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.blueHighlight;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
height: 300;
Connections {
onSendSignalToWallet: {
@ -118,66 +155,43 @@ Rectangle {
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
id: securityImageNavBar;
// Size
width: parent.width;
height: 100;
height: 40;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 30;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
width: 150;
text: "Cancel"
onClicked: {
root.visible = false;
sendSignalToWallet({method: "walletSecurity_changeSecurityImageCancelled"});
}
}
// "Submit" button
HifiControlsUit.Button {
id: securityImageSubmitButton;
color: hifi.buttons.black;
enabled: securityImageSelection.currentIndex !== -1;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
width: 150;
text: "Submit";
onClicked: {
root.justSubmitted = true;

View file

@ -24,6 +24,7 @@ Item {
HifiConstants { id: hifi; }
id: root;
property int currentIndex: securityImageGrid.currentIndex;
// This will cause a bug -- if you bring up security image selection in HUD mode while
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
@ -43,6 +44,7 @@ Item {
GridView {
id: securityImageGrid;
interactive: false;
clip: true;
// Anchors
anchors.fill: parent;
@ -56,8 +58,8 @@ Item {
Item {
anchors.fill: parent;
Image {
width: parent.width - 8;
height: parent.height - 8;
width: parent.width - 12;
height: parent.height - 12;
source: sourcePath;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.verticalCenter: parent.verticalCenter;

View file

@ -13,10 +13,12 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
@ -28,18 +30,14 @@ Rectangle {
property string activeView: "initialize";
property bool keyboardRaised: false;
// Style
color: hifi.colors.baseGray;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onLoginStatusResult: {
if (!isLoggedIn && root.activeView !== "needsLogIn") {
root.activeView = "needsLogIn";
@ -49,25 +47,35 @@ Rectangle {
}
}
onAccountResult: {
if (result.status === "success") {
commerce.getKeyFilePathIfExists();
} else {
// unsure how to handle a failure here. We definitely cannot proceed.
}
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
if (path === "" && root.activeView !== "walletSetup") {
root.activeView = "walletSetup";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
}
}
onSecurityImageResult: {
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp";
if (!exists && root.activeView !== "walletSetup") { // "If security image is not set up"
root.activeView = "walletSetup";
} else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && passphraseModal && !passphraseModal.visible) {
passphraseModal.visible = true;
if (!isAuthenticated && passphraseModal && root.activeView !== "passphraseModal") {
root.activeView = "passphraseModal";
} else if (isAuthenticated) {
root.activeView = "walletHome";
}
@ -78,74 +86,11 @@ Rectangle {
id: securityImageModel;
}
Rectangle {
id: walletSetupLightboxContainer;
visible: walletSetupLightbox.visible || passphraseSelectionLightbox.visible || securityImageSelectionLightbox.visible;
z: 998;
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
color: "black";
opacity: 0.5;
}
WalletSetupLightbox {
id: walletSetupLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') {
root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
PassphraseSelectionLightbox {
id: passphraseSelectionLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
SecurityImageSelectionLightbox {
id: securityImageSelectionLightbox;
visible: false;
z: 998;
anchors.centerIn: walletSetupLightboxContainer;
width: walletSetupLightboxContainer.width - 50;
height: walletSetupLightboxContainer.height - 50;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
//
// TITLE BAR START
@ -160,6 +105,20 @@ Rectangle {
anchors.left: parent.left;
anchors.top: parent.top;
// Wallet icon
HiFiGlyphs {
id: walletIcon;
text: hifi.glyphs.wallet;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
// Title Bar text
RalewaySemiBold {
id: titleBarText;
@ -168,28 +127,119 @@ Rectangle {
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.left: walletIcon.right;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Separator
HifiControlsUit.Separator {
anchors.left: parent.left;
Image {
id: titleBarSecurityImage;
source: "";
visible: titleBarSecurityImage.source !== "" && !securityImageChange.visible;
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
//
// TITLE BAR END
//
WalletSetup {
id: walletSetup;
visible: root.activeView === "walletSetup";
z: 998;
anchors.fill: parent;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_finished') {
if (msg.referrer === '') {
root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.referrer === 'purchases') {
sendToScript({method: 'goToPurchases'});
}
} else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else {
sendToScript(msg);
}
}
}
}
PassphraseChange {
id: passphraseChange;
visible: root.activeView === "passphraseChange";
z: 998;
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') {
root.keyboardRaised = false;
} else if (msg.method === 'walletSecurity_changePassphraseCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changePassphraseSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
SecurityImageChange {
id: securityImageChange;
visible: root.activeView === "securityImageChange";
z: 998;
anchors.top: titleBarContainer.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changeSecurityImageCancelled') {
root.activeView = "security";
} else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') {
root.activeView = "security";
} else {
sendToScript(msg);
}
}
}
}
//
// TAB CONTENTS START
//
@ -231,31 +281,17 @@ Rectangle {
PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
visible: root.activeView === "passphraseModal";
anchors.fill: parent;
titleBarText: "Wallet";
titleBarIcon: hifi.glyphs.wallet;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
NotSetUp {
id: notSetUp;
visible: root.activeView === "notSetUp";
anchors.top: titleBarContainer.bottom;
anchors.bottom: tabButtonsContainer.top;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToWallet: {
if (msg.method === 'setUpClicked') {
walletSetupLightbox.visible = true;
if (msg.method === "authSuccess") {
root.activeView = "walletHome";
} else {
sendToScript(msg);
}
}
}
@ -265,47 +301,42 @@ Rectangle {
id: walletHome;
visible: root.activeView === "walletHome";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
sendToScript(msg);
}
}
}
SendMoney {
id: sendMoney;
visible: root.activeView === "sendMoney";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
}
Security {
id: security;
visible: root.activeView === "security";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
if (msg.method === 'walletSecurity_changePassphrase') {
passphraseSelectionLightbox.visible = true;
passphraseSelectionLightbox.clearPassphraseFields();
root.activeView = "passphraseChange";
passphraseChange.clearPassphraseFields();
passphraseChange.resetSubmitButton();
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
securityImageSelectionLightbox.visible = true;
root.activeView = "securityImageChange";
}
}
}
@ -315,13 +346,9 @@ Rectangle {
id: help;
visible: root.activeView === "help";
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 16;
anchors.bottom: tabButtonsContainer.top;
anchors.bottomMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
Connections {
onSendSignalToWallet: {
@ -342,11 +369,11 @@ Rectangle {
//
Item {
id: tabButtonsContainer;
visible: !needsLogIn.visible;
visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange";
property int numTabs: 5;
// Size
width: root.width;
height: 80;
height: 90;
// Anchors
anchors.left: parent.left;
anchors.bottom: parent.bottom;
@ -361,30 +388,46 @@ Rectangle {
// "WALLET HOME" tab button
Rectangle {
id: walletHomeButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "walletHome" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: homeTabIcon;
text: hifi.glyphs.home2;
// Size
size: 50;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "WALLET HOME";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "walletHome" || walletHomeTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: walletHomeTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -396,87 +439,136 @@ Rectangle {
}
}
// "SEND MONEY" tab button
// "EXCHANGE MONEY" tab button
Rectangle {
id: sendMoneyButtonContainer;
visible: !notSetUp.visible;
id: exchangeMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: walletHomeButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "SEND MONEY";
// Text size
size: 14;
HiFiGlyphs {
id: exchangeMoneyTabIcon;
text: hifi.glyphs.leftRightArrows;
// Size
size: 50;
// Anchors
anchors.fill: parent;
anchors.leftMargin: 4;
anchors.rightMargin: 4;
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
// "EXCHANGE MONEY" tab button
Rectangle {
id: exchangeMoneyButtonContainer;
visible: !notSetUp.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
RalewaySemiBold {
text: "EXCHANGE MONEY";
// Text size
size: 14;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
}
// "SEND MONEY" tab button
Rectangle {
id: sendMoneyButtonContainer;
visible: !walletSetup.visible;
color: hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: sendMoneyTabIcon;
text: hifi.glyphs.paperPlane;
// Size
size: 46;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -2;
// Style
color: hifi.colors.lightGray50;
}
RalewaySemiBold {
text: "SEND MONEY";
// Text size
size: 16;
// Anchors
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGray50;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignTop;
}
}
// "SECURITY" tab button
Rectangle {
id: securityButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: exchangeMoneyButtonContainer.right;
anchors.left: sendMoneyButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: securityTabIcon;
text: hifi.glyphs.lock;
// Size
size: 38;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: 2;
// Style
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "SECURITY";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "security" || securityTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: securityTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -487,34 +579,50 @@ Rectangle {
onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black;
}
}
// "HELP" tab button
Rectangle {
id: helpButtonContainer;
visible: !notSetUp.visible;
visible: !walletSetup.visible;
color: root.activeView === "help" ? hifi.colors.blueAccent : hifi.colors.black;
anchors.top: parent.top;
anchors.left: securityButtonContainer.right;
anchors.bottom: parent.bottom;
width: parent.width / tabButtonsContainer.numTabs;
HiFiGlyphs {
id: helpTabIcon;
text: hifi.glyphs.question;
// Size
size: 55;
// Anchors
anchors.horizontalCenter: parent.horizontalCenter;
anchors.top: parent.top;
anchors.topMargin: -6;
// Style
color: root.activeView === "help" || helpTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
}
RalewaySemiBold {
text: "HELP";
// Text size
size: hifi.fontSizes.overlayTitle;
size: 16;
// Anchors
anchors.fill: parent;
anchors.bottom: parent.bottom;
height: parent.height/2;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.faintGray;
color: root.activeView === "help" || helpTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
verticalAlignment: Text.AlignTop;
}
MouseArea {
enabled: !walletSetupLightboxContainer.visible;
id: helpTabMouseArea;
anchors.fill: parent;
hoverEnabled: enabled;
onClicked: {
@ -526,6 +634,7 @@ Rectangle {
}
}
function resetTabButtonColors() {
walletHomeButtonContainer.color = hifi.colors.black;
sendMoneyButtonContainer.color = hifi.colors.black;
@ -604,6 +713,9 @@ Rectangle {
//
function fromScript(message) {
switch (message.method) {
case 'updateWalletReferrer':
walletSetup.referrer = message.referrer;
break;
default:
console.log('Unrecognized message from wallet.js:', JSON.stringify(message));
}

View file

@ -13,7 +13,8 @@
import Hifi 1.0 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import QtQuick.Controls 2.2
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
@ -29,14 +30,6 @@ Item {
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (exists) {
// just set the source again (to be sure the change was noticed)
securityImage.source = "";
securityImage.source = "image://security/securityImage";
}
}
onBalanceResult : {
balanceText.text = result.data.balance;
}
@ -50,10 +43,6 @@ Item {
}
}
SecurityImageModel {
id: securityImageModel;
}
Connections {
target: GlobalServices
onMyUsernameChanged: {
@ -68,213 +57,195 @@ Item {
// Text size
size: 24;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.white;
elide: Text.ElideRight;
// Anchors
anchors.top: securityImageContainer.top;
anchors.bottom: securityImageContainer.bottom;
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: hfcBalanceContainer.left;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
}
// HFC Balance Container
Item {
id: hfcBalanceContainer;
// Anchors
anchors.top: securityImageContainer.top;
anchors.right: securityImageContainer.left;
anchors.rightMargin: 16;
width: 175;
height: 60;
Rectangle {
id: hfcBalanceField;
color: hifi.colors.darkGray;
anchors.right: parent.right;
anchors.left: parent.left;
anchors.bottom: parent.bottom;
height: parent.height - 15;
// "HFC" balance label
FiraSansRegular {
id: balanceLabel;
text: "HFC";
// Text size
size: 20;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: hfcBalanceField.right;
anchors.rightMargin: 4;
width: paintedWidth;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
historyReceived = false;
commerce.balance();
commerce.history();
}
}
}
// Balance Text
FiraSansSemiBold {
id: balanceText;
text: "--";
// Text size
size: 28;
// Anchors
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: balanceLabel.left;
anchors.rightMargin: 4;
// Style
color: hifi.colors.lightGrayText;
// Alignment
horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter;
}
}
// "balance" text above field
RalewayRegular {
text: "balance";
// Text size
size: 12;
// Anchors
anchors.top: parent.top;
anchors.bottom: hfcBalanceField.top;
anchors.bottomMargin: 4;
anchors.left: hfcBalanceField.left;
anchors.right: hfcBalanceField.right;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: parent.top;
anchors.right: parent.right;
width: 75;
height: childrenRect.height;
anchors.leftMargin: 20;
width: parent.width/2;
height: 80;
// "HFC" balance label
HiFiGlyphs {
id: balanceLabel;
text: hifi.glyphs.hfc;
// Size
size: 40;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
// Style
color: hifi.colors.white;
}
onVisibleChanged: {
if (visible) {
commerce.getSecurityImage();
// Balance Text
FiraSansRegular {
id: balanceText;
text: "--";
// Text size
size: 28;
// Anchors
anchors.top: balanceLabel.top;
anchors.bottom: balanceLabel.bottom;
anchors.left: balanceLabel.right;
anchors.leftMargin: 10;
anchors.right: parent.right;
anchors.rightMargin: 4;
// Style
color: hifi.colors.white;
// Alignment
verticalAlignment: Text.AlignVCenter;
onVisibleChanged: {
if (visible) {
historyReceived = false;
commerce.balance();
commerce.history();
}
}
}
Image {
id: securityImage;
// Anchors
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: parent.width - 10;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
cache: false;
source: "image://security/securityImage";
}
Image {
id: securityImageOverlay;
source: "images/lockOverlay.png";
width: securityImage.width * 0.45;
height: securityImage.height * 0.45;
anchors.bottom: securityImage.bottom;
anchors.right: securityImage.right;
mipmap: true;
opacity: 0.9;
}
// "Security image" text below pic
// "balance" text below field
RalewayRegular {
text: "security image";
text: "BALANCE (HFC)";
// Text size
size: 12;
size: 14;
// Anchors
anchors.top: securityImage.bottom;
anchors.topMargin: 4;
anchors.left: securityImageContainer.left;
anchors.right: securityImageContainer.right;
anchors.top: balanceLabel.top;
anchors.topMargin: balanceText.paintedHeight + 20;
anchors.bottom: balanceLabel.bottom;
anchors.left: balanceText.left;
anchors.right: balanceText.right;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
color: hifi.colors.white;
}
}
// Recent Activity
Item {
Rectangle {
id: recentActivityContainer;
anchors.top: securityImageContainer.bottom;
anchors.topMargin: 8;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
height: 440;
LinearGradient {
anchors.fill: parent;
start: Qt.point(0, 0);
end: Qt.point(0, height);
gradient: Gradient {
GradientStop { position: 0.0; color: hifi.colors.white }
GradientStop { position: 1.0; color: hifi.colors.faintGray }
}
}
RalewayRegular {
RalewaySemiBold {
id: recentActivityText;
text: "Recent Activity";
// Anchors
anchors.top: parent.top;
anchors.topMargin: 26;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 30;
// Text size
size: 22;
// Style
color: hifi.colors.faintGray;
color: hifi.colors.baseGrayHighlight;
}
ListModel {
id: transactionHistoryModel;
}
Rectangle {
Item {
anchors.top: recentActivityText.bottom;
anchors.topMargin: 4;
anchors.topMargin: 26;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
color: "white";
anchors.rightMargin: 24;
ListView {
id: transactionHistory;
ScrollBar.vertical: ScrollBar {
policy: transactionHistory.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded;
parent: transactionHistory.parent;
anchors.top: transactionHistory.top;
anchors.left: transactionHistory.right;
anchors.leftMargin: 4;
anchors.bottom: transactionHistory.bottom;
width: 20;
}
anchors.centerIn: parent;
width: parent.width - 12;
height: parent.height - 12;
height: parent.height;
visible: transactionHistoryModel.count !== 0;
clip: true;
model: transactionHistoryModel;
delegate: Item {
width: parent.width;
height: transactionText.height + 30;
FiraSansRegular {
id: transactionText;
text: model.text;
HifiControlsUit.Separator {
visible: index === 0;
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.top: parent.top;
}
AnonymousProRegular {
id: dateText;
text: getFormattedDate(model.created_at * 1000);
// Style
size: 18;
width: parent.width;
anchors.left: parent.left;
anchors.top: parent.top;
anchors.topMargin: 15;
width: 118;
height: paintedHeight;
anchors.verticalCenter: parent.verticalCenter;
color: "black";
color: hifi.colors.blueAccent;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter;
horizontalAlignment: Text.AlignRight;
}
AnonymousProRegular {
id: transactionText;
text: model.text;
size: 18;
anchors.top: parent.top;
anchors.topMargin: 15;
anchors.left: dateText.right;
anchors.leftMargin: 20;
anchors.right: parent.right;
height: paintedHeight;
color: hifi.colors.baseGrayHighlight;
wrapMode: Text.WordWrap;
onLinkActivated: {
sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link});
}
}
HifiControlsUit.Separator {
colorScheme: 1;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: parent.bottom;
@ -304,6 +275,30 @@ Item {
//
// FUNCTION DEFINITIONS START
//
function getFormattedDate(timestamp) {
var a = new Date(timestamp);
var year = a.getFullYear();
var month = a.getMonth();
var day = a.getDate();
var hour = a.getHours();
var drawnHour = hour;
if (hour === 0) {
drawnHour = 12;
} else if (hour > 12) {
drawnHour -= 12;
}
var amOrPm = "AM";
if (hour >= 12) {
amOrPm = "PM";
}
var min = a.getMinutes();
var sec = a.getSeconds();
return year + '-' + month + '-' + day + '<br>' + drawnHour + ':' + min + amOrPm;
}
//
// Function Name: fromScript()
//

View file

@ -0,0 +1,746 @@
//
// modalContainer.qml
// qml/hifi/commerce/wallet
//
// modalContainer
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
import "../common" as HifiCommerceCommon
// references XXX from root context
Item {
HifiConstants { id: hifi; }
id: root;
property string activeView: "step_1";
property string lastPage;
property bool hasShownSecurityImageTip: false;
property string referrer;
Image {
anchors.fill: parent;
source: "images/wallet-bg.jpg";
}
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!exists && root.lastPage === "step_2") {
// ERROR! Invalid security image.
root.activeView = "step_2";
} else {
titleBarSecurityImage.source = "";
titleBarSecurityImage.source = "image://security/securityImage";
}
}
onWalletAuthenticatedStatusResult: {
if (isAuthenticated) {
root.activeView = "step_4";
} else {
root.activeView = "step_3";
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
}
}
HifiCommerceCommon.CommerceLightbox {
id: lightboxPopup;
visible: false;
anchors.fill: parent;
}
//
// TITLE BAR START
//
Item {
id: titleBarContainer;
// Size
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
anchors.right: parent.right;
// Wallet icon
HiFiGlyphs {
id: walletIcon;
text: hifi.glyphs.wallet;
// Size
size: parent.height * 0.8;
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 8;
anchors.verticalCenter: parent.verticalCenter;
// Style
color: hifi.colors.blueHighlight;
}
// Title Bar text
RalewayRegular {
id: titleBarText;
text: "Wallet Setup" + (securityImageTip.visible ? "" : " - Step " + root.activeView.split("_")[1] + " of 4");
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: walletIcon.right;
anchors.leftMargin: 8;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
Image {
id: titleBarSecurityImage;
source: "";
visible: !securityImageTip.visible && titleBarSecurityImage.source !== "";
anchors.right: parent.right;
anchors.rightMargin: 6;
anchors.top: parent.top;
anchors.topMargin: 6;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 6;
width: height;
mipmap: true;
MouseArea {
enabled: titleBarSecurityImage.visible;
anchors.fill: parent;
onClicked: {
lightboxPopup.titleText = "Your Security Pic";
lightboxPopup.bodyImageSource = titleBarSecurityImage.source;
lightboxPopup.bodyText = lightboxPopup.securityPicBodyText;
lightboxPopup.button1text = "CLOSE";
lightboxPopup.button1method = "root.visible = false;"
lightboxPopup.visible = true;
}
}
}
}
//
// TITLE BAR END
//
//
// FIRST PAGE START
//
Item {
id: firstPageContainer;
visible: root.activeView === "step_1";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
HiFiGlyphs {
id: bigWalletIcon;
text: hifi.glyphs.wallet;
// Size
size: 180;
// Anchors
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.horizontalCenter: parent.horizontalCenter;
// Style
color: hifi.colors.white;
}
RalewayRegular {
id: firstPage_text01;
text: "Let's set up your wallet!";
// Text size
size: 26;
// Anchors
anchors.top: bigWalletIcon.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
width: paintedWidth;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: firstPage_text02;
text: "Set up your wallet to claim your <b>free High Fidelity Coin (HFC)</b> and get items from the Marketplace.<br><br>" +
"<b>No credit card is required.</b>";
// Text size
size: 18;
// Anchors
anchors.top: firstPage_text01.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 65;
anchors.right: parent.right;
anchors.rightMargin: 65;
height: paintedHeight;
width: paintedWidth;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// "Set Up" button
HifiControlsUit.Button {
id: firstPage_setUpButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: firstPage_text02.bottom;
anchors.topMargin: 40;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Set Up Wallet";
onClicked: {
root.activeView = "step_2";
}
}
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.none;
colorScheme: hifi.colorSchemes.dark;
anchors.top: firstPage_setUpButton.bottom;
anchors.topMargin: 20;
anchors.horizontalCenter: parent.horizontalCenter;
width: parent.width/2;
height: 50;
text: "Cancel";
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
}
//
// FIRST PAGE END
//
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
visible: root.activeView === "step_2";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
// Text below title bar
RalewayRegular {
id: securityImageTitleHelper;
text: "Choose a Security Pic:";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
height: 300;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.right: parent.right;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 200;
text: "Back"
onClicked: {
root.activeView = "step_1";
}
}
// "Next" button
HifiControlsUit.Button {
enabled: securityImageSelection.currentIndex !== -1;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Next";
onClicked: {
root.lastPage = "step_2";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
root.activeView = "step_3";
passphraseSelection.clearPassphraseFields();
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: securityImageTip;
visible: false;
z: 999;
anchors.fill: root;
// This object is always used in a popup.
// This MouseArea is used to prevent a user from being
// able to click on a button/mouseArea underneath the popup.
MouseArea {
anchors.fill: parent;
propagateComposedEvents: false;
}
Image {
source: "images/wallet-tip-bg.png";
anchors.fill: parent;
}
RalewayRegular {
id: tipText;
text: '<font size="5">Tip:</font><br><br>When you see your security picture like this, you know ' +
"the page asking for your passphrase is legitimate.";
// Text size
size: 18;
// Anchors
anchors.bottom: parent.bottom;
anchors.bottomMargin: 230;
anchors.left: parent.left;
anchors.leftMargin: 60;
height: paintedHeight;
width: 210;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
}
// "Got It" button
HifiControlsUit.Button {
id: tipGotItButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: tipText.bottom;
anchors.topMargin: 20;
anchors.horizontalCenter: tipText.horizontalCenter;
height: 50;
width: 150;
text: "Got It";
onClicked: {
root.hasShownSecurityImageTip = true;
securityImageTip.visible = false;
}
}
}
Item {
id: choosePassphraseContainer;
visible: root.activeView === "step_3";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
onVisibleChanged: {
if (visible) {
commerce.getWalletAuthenticatedStatus();
if (!root.hasShownSecurityImageTip) {
securityImageTip.visible = true;
}
}
}
// Text below title bar
RalewayRegular {
id: passphraseTitleHelper;
text: "Set Your Passphrase:";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
isShowingTip: securityImageTip.visible;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
Connections {
onSendMessageToLightbox: {
if (msg.method === 'statusResult') {
} else {
sendSignalToWallet(msg);
}
}
}
}
// Navigation Bar
Item {
id: passphraseNavBar;
visible: !securityImageTip.visible;
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.noneBorderlessWhite;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 200;
text: "Back"
onClicked: {
root.lastPage = "step_3";
root.activeView = "step_2";
}
}
// "Next" button
HifiControlsUit.Button {
id: passphrasePageNextButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Next";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "step_3";
commerce.generateKeyPair();
root.activeView = "step_4";
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
//
// PRIVATE KEYS READY START
//
Item {
id: privateKeysReadyContainer;
visible: root.activeView === "step_4";
// Anchors
anchors.top: titleBarContainer.bottom;
anchors.topMargin: 30;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
// Text below title bar
RalewayRegular {
id: keysReadyTitleHelper;
text: "Back Up Your Private Keys";
// Text size
size: 24;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.white;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
RalewayRegular {
id: explanationText;
text: "To protect your privacy, you control your private keys. High Fidelity has no access to your private keys and cannot " +
"recover them for you.<br><br><b>If they are lost, you will not be able to access your money or purchases.</b>";
// Text size
size: 20;
// Anchors
anchors.top: keysReadyTitleHelper.bottom;
anchors.topMargin: 24;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.white;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
Rectangle {
id: pathAndInstructionsContainer;
anchors.top: explanationText.bottom;
anchors.topMargin: 24;
anchors.left: parent.left;
anchors.right: parent.right;
height: 240;
color: Qt.rgba(0, 0, 0, 0.5);
Item {
id: instructions01Container;
anchors.fill: parent;
RalewaySemiBold {
id: keyFilePathText;
text: "Private Key File Location:";
size: 18;
anchors.top: parent.top;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
color: hifi.colors.white;
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.top: keyFilePathText.bottom;
anchors.topMargin: 8;
height: 24;
width: height;
HiFiGlyphs {
text: hifi.glyphs.folderLg;
// Size
size: parent.height;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.blueHighlight : hifi.colors.faintGray;
}
onClicked: {
Qt.openUrlExternally("file:///" + keyFilePath.text.substring(0, keyFilePath.text.lastIndexOf('/')));
}
}
RalewayRegular {
id: keyFilePath;
size: 18;
anchors.top: clipboardButton.top;
anchors.left: clipboardButton.right;
anchors.leftMargin: 8;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: paintedHeight;
wrapMode: Text.WordWrap;
color: hifi.colors.blueHighlight;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
// "Open Instructions" button
HifiControlsUit.Button {
id: openInstructionsButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: keyFilePath.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 30;
anchors.right: parent.right;
anchors.rightMargin: 30;
height: 40;
text: "Open Instructions for Later";
onClicked: {
instructions01Container.visible = false;
instructions02Container.visible = true;
keysReadyPageFinishButton.visible = true;
Qt.openUrlExternally("https://www.highfidelity.com/");
}
}
}
Item {
id: instructions02Container;
anchors.fill: parent;
visible: false;
RalewayRegular {
text: "All set!<br>Instructions for backing up your keys have been opened on your desktop. " +
"Be sure to look them over after your session.";
size: 22;
anchors.fill: parent;
anchors.leftMargin: 30;
anchors.rightMargin: 30;
wrapMode: Text.WordWrap;
color: hifi.colors.white;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 50;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 50;
// "Finish" button
HifiControlsUit.Button {
id: keysReadyPageFinishButton;
visible: false;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 200;
text: "Finish";
onClicked: {
root.visible = false;
sendSignalToWallet({method: 'walletSetup_finished', referrer: root.referrer ? root.referrer : ""});
}
}
}
}
//
// PRIVATE KEYS READY END
//
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

View file

@ -1,502 +0,0 @@
//
// WalletSetupLightbox.qml
// qml/hifi/commerce/wallet
//
// WalletSetupLightbox
//
// Created by Zach Fox on 2017-08-17
// Copyright 2017 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 as Hifi
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../../../styles-uit"
import "../../../controls-uit" as HifiControlsUit
import "../../../controls" as HifiControls
// references XXX from root context
Rectangle {
HifiConstants { id: hifi; }
id: root;
property string lastPage: "initialize";
// Style
color: hifi.colors.baseGray;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
if (!exists && root.lastPage === "securityImage") {
// ERROR! Invalid security image.
securityImageContainer.visible = true;
choosePassphraseContainer.visible = false;
}
}
onWalletAuthenticatedStatusResult: {
securityImageContainer.visible = false;
if (isAuthenticated) {
privateKeysReadyContainer.visible = true;
} else {
choosePassphraseContainer.visible = true;
}
}
onKeyFilePathIfExistsResult: {
keyFilePath.text = path;
}
}
//
// SECURITY IMAGE SELECTION START
//
Item {
id: securityImageContainer;
// Anchors
anchors.fill: parent;
Item {
id: securityImageTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 1 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: securityImageTitleHelper;
text: "Choose a Security Picture:";
// Text size
size: 24;
// Anchors
anchors.top: securityImageTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
height: 50;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
SecurityImageSelection {
id: securityImageSelection;
// Anchors
anchors.top: securityImageTitleHelper.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 280;
Connections {
onSendSignalToWallet: {
sendSignalToWallet(msg);
}
}
}
// Text below security images
RalewayRegular {
text: "<b>Your security picture shows you that the service asking for your passphrase is authorized.</b> You can change your secure picture at any time.";
// Text size
size: 18;
// Anchors
anchors.top: securityImageSelection.bottom;
anchors.topMargin: 40;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Cancel" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Cancel"
onClicked: {
sendSignalToWallet({method: 'walletSetup_cancelClicked'});
}
}
// "Next" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
root.lastPage = "securityImage";
var securityImagePath = securityImageSelection.getImagePathFromImageID(securityImageSelection.getSelectedImageIndex())
commerce.chooseSecurityImage(securityImagePath);
securityImageContainer.visible = false;
choosePassphraseContainer.visible = true;
passphraseSelection.clearPassphraseFields();
}
}
}
}
//
// SECURITY IMAGE SELECTION END
//
//
// SECURE PASSPHRASE SELECTION START
//
Item {
id: choosePassphraseContainer;
visible: false;
// Anchors
anchors.fill: parent;
onVisibleChanged: {
if (visible) {
commerce.getWalletAuthenticatedStatus();
}
}
Item {
id: passphraseTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 2 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: passphraseTitleHelper;
text: "Choose a Secure Passphrase";
// Text size
size: 24;
// Anchors
anchors.top: passphraseTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
PassphraseSelection {
id: passphraseSelection;
anchors.top: passphraseTitleHelper.bottom;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.right: parent.right;
anchors.bottom: passphraseNavBar.top;
Connections {
onSendMessageToLightbox: {
if (msg.method === 'statusResult') {
} else {
sendSignalToWallet(msg);
}
}
}
}
// Navigation Bar
Item {
id: passphraseNavBar;
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Back" button
HifiControlsUit.Button {
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.left: parent.left;
anchors.leftMargin: 20;
width: 100;
text: "Back"
onClicked: {
root.lastPage = "choosePassphrase";
choosePassphraseContainer.visible = false;
securityImageContainer.visible = true;
}
}
// "Next" button
HifiControlsUit.Button {
id: passphrasePageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Next";
onClicked: {
if (passphraseSelection.validateAndSubmitPassphrase()) {
root.lastPage = "choosePassphrase";
commerce.generateKeyPair();
choosePassphraseContainer.visible = false;
privateKeysReadyContainer.visible = true;
}
}
}
}
}
//
// SECURE PASSPHRASE SELECTION END
//
//
// PRIVATE KEYS READY START
//
Item {
id: privateKeysReadyContainer;
visible: false;
// Anchors
anchors.fill: parent;
Item {
id: keysReadyTitle;
// Size
width: parent.width;
height: 50;
// Anchors
anchors.left: parent.left;
anchors.top: parent.top;
// Title Bar text
RalewaySemiBold {
text: "WALLET SETUP - STEP 3 OF 3";
// Text size
size: hifi.fontSizes.overlayTitle;
// Anchors
anchors.top: parent.top;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.bottom: parent.bottom;
width: paintedWidth;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
}
// Text below title bar
RalewaySemiBold {
id: keysReadyTitleHelper;
text: "Your Private Keys are Ready";
// Text size
size: 24;
// Anchors
anchors.top: keysReadyTitle.bottom;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: 50;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
// Text below checkbox
RalewayRegular {
id: explanationText;
text: "Your money and purchases are secured with private keys that only you have access to. " +
"<b>If they are lost, you will not be able to access your money or purchases.</b><br><br>" +
"<b>To protect your privacy, High Fidelity has no access to your private keys and cannot " +
"recover them for any reason.<br><br>To safeguard your private keys, backup this file on a regular basis:</b>";
// Text size
size: 16;
// Anchors
anchors.top: keysReadyTitleHelper.bottom;
anchors.topMargin: 16;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: parent.right;
anchors.rightMargin: 16;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
wrapMode: Text.WordWrap;
// Alignment
horizontalAlignment: Text.AlignHLeft;
verticalAlignment: Text.AlignVCenter;
}
HifiControlsUit.TextField {
id: keyFilePath;
anchors.top: explanationText.bottom;
anchors.topMargin: 10;
anchors.left: parent.left;
anchors.leftMargin: 16;
anchors.right: clipboardButton.left;
height: 40;
readOnly: true;
onVisibleChanged: {
if (visible) {
commerce.getKeyFilePathIfExists();
}
}
}
HifiControlsUit.Button {
id: clipboardButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.right: parent.right;
anchors.rightMargin: 16;
anchors.top: keyFilePath.top;
anchors.bottom: keyFilePath.bottom;
width: height;
HiFiGlyphs {
text: hifi.glyphs.question;
// Size
size: parent.height*1.3;
// Anchors
anchors.fill: parent;
// Style
horizontalAlignment: Text.AlignHCenter;
color: enabled ? hifi.colors.white : hifi.colors.faintGray;
}
onClicked: {
Window.copyToClipboard(keyFilePath.text);
}
}
// Navigation Bar
Item {
// Size
width: parent.width;
height: 100;
// Anchors:
anchors.left: parent.left;
anchors.bottom: parent.bottom;
// "Next" button
HifiControlsUit.Button {
id: keysReadyPageNextButton;
color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
anchors.topMargin: 3;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 3;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: 100;
text: "Finish";
onClicked: {
root.visible = false;
sendSignalToWallet({method: 'walletSetup_finished'});
}
}
}
}
//
// PRIVATE KEYS READY END
//
//
// FUNCTION DEFINITIONS START
//
signal sendSignalToWallet(var msg);
//
// FUNCTION DEFINITIONS END
//
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -138,6 +138,7 @@ Item {
id: colorSchemes
readonly property int light: 0
readonly property int dark: 1
readonly property int faintGray: 2
}
Item {
@ -178,7 +179,7 @@ Item {
readonly property real tableHeading: dimensions.largeScreen ? 12 : 10
readonly property real tableHeadingIcon: dimensions.largeScreen ? 60 : 33
readonly property real tableText: dimensions.largeScreen ? 15 : 12
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
readonly property real buttonLabel: dimensions.largeScreen ? 14 : 9
readonly property real iconButton: dimensions.largeScreen ? 13 : 9
readonly property real listItem: dimensions.largeScreen ? 15 : 11
readonly property real tabularData: dimensions.largeScreen ? 15 : 11
@ -209,11 +210,15 @@ Item {
readonly property int blue: 1
readonly property int red: 2
readonly property int black: 3
readonly property var textColor: [ colors.darkGray, colors.white, colors.white, colors.white ]
readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434" ]
readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black ]
readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black] ]
readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black] ]
readonly property int none: 4
readonly property int noneBorderless: 5
readonly property int noneBorderlessWhite: 6
readonly property int noneBorderlessGray: 7
readonly property var textColor: [ colors.darkGray, colors.white, colors.white, colors.white, colors.white, colors.blueAccent, colors.white, colors.darkGray ]
readonly property var colorStart: [ colors.white, colors.primaryHighlight, "#d42043", "#343434", Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ]
readonly property var colorFinish: [ colors.lightGrayText, colors.blueAccent, "#94132e", colors.black, Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0), Qt.rgba(0, 0, 0, 0) ]
readonly property var hoveredColor: [ colorStart[white], colorStart[blue], colorStart[red], colorFinish[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ]
readonly property var pressedColor: [ colorFinish[white], colorFinish[blue], colorFinish[red], colorStart[black], colorStart[none], colorStart[noneBorderless], colorStart[noneBorderlessWhite], colorStart[noneBorderlessGray] ]
readonly property var disabledColorStart: [ colorStart[white], colors.baseGrayHighlight]
readonly property var disabledColorFinish: [ colorFinish[white], colors.baseGrayShadow]
readonly property var disabledTextColor: [ colors.lightGrayText, colors.baseGrayShadow]
@ -338,6 +343,15 @@ Item {
readonly property string stop_square: "\ue01e"
readonly property string avatarTPose: "\ue01f"
readonly property string lock: "\ue006"
readonly property string check_2_01: "\ue020"
readonly property string checkmark: "\ue020"
readonly property string leftRightArrows: "\ue021"
readonly property string hfc: "\ue022"
readonly property string home2: "\ue023"
readonly property string walletKey: "\ue024"
readonly property string lightning: "\ue025"
readonly property string securityImage: "\ue026"
readonly property string wallet: "\ue027"
readonly property string paperPlane: "\ue028"
readonly property string passphrase: "\ue029"
}
}

View file

@ -8,7 +8,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4

View file

@ -17,6 +17,7 @@
#include "Wallet.h"
#include "Ledger.h"
#include "CommerceLogging.h"
#include <NetworkingConstants.h>
// inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}}
// balance answers {status: 'success', data: {balance: integer}}
@ -59,12 +60,16 @@ void Ledger::send(const QString& endpoint, const QString& success, const QString
QJsonDocument(request).toJson());
}
void Ledger::signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail) {
void Ledger::signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure) {
auto wallet = DependencyManager::get<Wallet>();
QString signature = key.isEmpty() ? "" : wallet->signWithKey(text, key);
QJsonObject request;
request[propertyName] = QString(text);
request["signature"] = signature;
if (!controlled_failure) {
request["signature"] = signature;
} else {
request["signature"] = QString("controlled failure!");
}
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, request);
}
@ -75,16 +80,15 @@ void Ledger::keysQuery(const QString& endpoint, const QString& success, const QS
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, request);
}
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername) {
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) {
QJsonObject transaction;
transaction["hfc_key"] = hfc_key;
transaction["cost"] = cost;
transaction["asset_id"] = asset_id;
transaction["inventory_key"] = inventory_key;
transaction["inventory_buyer_username"] = buyerUsername;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure");
signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure);
}
bool Ledger::receiveAt(const QString& hfc_key, const QString& old_key) {
@ -110,14 +114,15 @@ void Ledger::inventory(const QStringList& keys) {
QString nameFromKey(const QString& key, const QStringList& publicKeys) {
if (key.isNull() || key.isEmpty()) {
return "<b>Marketplace</b>";
return "Marketplace";
}
if (publicKeys.contains(key)) {
return "You";
}
return "<b>Someone</b>";
return "Someone";
}
static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
void Ledger::historySuccess(QNetworkReply& reply) {
// here we send a historyResult with some extra stuff in it
// Namely, the styled text we'd like to show. The issue is the
@ -135,10 +140,27 @@ void Ledger::historySuccess(QNetworkReply& reply) {
QJsonArray newHistoryArray;
// TODO: do this with 0 copies if possible
for(auto it = historyArray.begin(); it != historyArray.end(); it++) {
for (auto it = historyArray.begin(); it != historyArray.end(); it++) {
auto valueObject = (*it).toObject();
QString from = nameFromKey(valueObject["sender_key"].toString(), keys);
QString to = nameFromKey(valueObject["recipient_key"].toString(), keys);
bool isHfc = valueObject["asset_title"].toString() == "HFC";
bool iAmReceiving = to == "You";
QString coloredQuantityAndAssetTitle = QString::number(valueObject["quantity"].toInt()) + " " + valueObject["asset_title"].toString();
if (isHfc) {
if (iAmReceiving) {
coloredQuantityAndAssetTitle = QString("<font color='#1FC6A6'>") + coloredQuantityAndAssetTitle + QString("</font>");
} else {
coloredQuantityAndAssetTitle = QString("<font color='#EA4C5F'>") + coloredQuantityAndAssetTitle + QString("</font>");
}
} else {
coloredQuantityAndAssetTitle = QString("\"<font color='#0093C5'><a href='") +
MARKETPLACE_ITEMS_BASE_URL +
valueObject["asset_id"].toString() +
QString("'>") +
coloredQuantityAndAssetTitle +
QString("</a></font>\"");
}
// turns out on my machine, toLocalTime convert to some weird timezone, yet the
// systemTimeZone is correct. To avoid a strange bug with other's systems too, lets
// be explicit
@ -148,8 +170,8 @@ void Ledger::historySuccess(QNetworkReply& reply) {
QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC);
#endif
QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone());
valueObject["text"] = QString("%1 sent %2 <b>%3 %4</b> on %5 with message \"%6\"").
arg(from, to, QString::number(valueObject["quantity"].toInt()), valueObject["asset_title"].toString(), localCreatedAt.toString(Qt::SystemLocaleShortDate), valueObject["message"].toString());
valueObject["text"] = QString("%1 sent %2 %3 with message \"%4\"").
arg(from, to, coloredQuantityAndAssetTitle, valueObject["message"].toString());
newHistoryArray.push_back(valueObject);
}
// now copy the rest of the json -- this is inefficient
@ -201,3 +223,18 @@ void Ledger::accountFailure(QNetworkReply& reply) {
void Ledger::account() {
send("hfc_account", "accountSuccess", "accountFailure", QNetworkAccessManager::PutOperation, QJsonObject());
}
// The api/failResponse is called just for the side effect of logging.
void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("reset", reply); }
void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) {
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
QString key = keys[0];
QJsonObject transaction;
transaction["asset_id"] = asset_id;
transaction["location"] = location;
QJsonDocument transactionDoc{ transaction };
auto transactionString = transactionDoc.toJson(QJsonDocument::Compact);
signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure);
}

View file

@ -24,13 +24,14 @@ class Ledger : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const QString& buyerUsername = "");
void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false);
bool receiveAt(const QString& hfc_key, const QString& old_key);
void balance(const QStringList& keys);
void inventory(const QStringList& keys);
void history(const QStringList& keys);
void account();
void reset();
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
signals:
void buyResult(QJsonObject result);
@ -39,6 +40,7 @@ signals:
void inventoryResult(QJsonObject result);
void historyResult(QJsonObject result);
void accountResult(QJsonObject result);
void locationUpdateResult(QJsonObject result);
public slots:
void buySuccess(QNetworkReply& reply);
@ -55,13 +57,15 @@ public slots:
void resetFailure(QNetworkReply& reply);
void accountSuccess(QNetworkReply& reply);
void accountFailure(QNetworkReply& reply);
void updateLocationSuccess(QNetworkReply& reply);
void updateLocationFailure(QNetworkReply& reply);
private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
QJsonObject failResponse(const QString& label, QNetworkReply& reply);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail);
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail);
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false);
};
#endif // hifi_Ledger_h

View file

@ -54,7 +54,7 @@ void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
void QmlCommerce::buy(const QString& assetId, int cost, const bool controlledFailure) {
auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>();
QStringList keys = wallet->listPublicKeys();
@ -64,7 +64,7 @@ void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUser
}
QString key = keys[0];
// For now, we receive at the same key that pays for it.
ledger->buy(key, cost, assetId, key, buyerUsername);
ledger->buy(key, cost, assetId, key, controlledFailure);
}
void QmlCommerce::balance() {

View file

@ -50,7 +50,7 @@ protected:
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void buy(const QString& assetId, int cost, const QString& buyerUsername = "");
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
Q_INVOKABLE void balance();
Q_INVOKABLE void inventory();
Q_INVOKABLE void history();

View file

@ -280,6 +280,13 @@ void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArra
memcpy(ckey, wallet->getCKey(), 32);
}
Wallet::Wallet() {
auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "verifyOwnerChallenge");
}
Wallet::~Wallet() {
if (_securityImage) {
delete _securityImage;
@ -645,3 +652,30 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
qCDebug(commerce) << "changing passphrase";
return writeWallet(newPassphrase);
}
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
QString decryptedText;
quint64 encryptedTextSize;
quint64 publicKeySize;
if (verifyOwnerChallenge(packet->read(packet->readPrimitive(&encryptedTextSize)), packet->read(packet->readPrimitive(&publicKeySize)), decryptedText)) {
auto nodeList = DependencyManager::get<NodeList>();
// setup the packet
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + decryptedText.size(), true);
// write the decrypted text to the packet
decryptedTextPacket->write(decryptedText.toUtf8());
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text";
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
} else {
qCDebug(commerce) << "verifyOwnerChallenge() returned false";
}
}
bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) {
// I have no idea how to do this yet, so here's some dummy code that may not even work.
decryptedText = QString("hello");
return true;
}

View file

@ -15,6 +15,8 @@
#define hifi_Wallet_h
#include <DependencyManager.h>
#include <Node.h>
#include <ReceivedMessage.h>
#include <QPixmap>
@ -23,7 +25,7 @@ class Wallet : public QObject, public Dependency {
SINGLETON_DEPENDENCY
public:
Wallet();
~Wallet();
// These are currently blocking calls, although they might take a moment.
bool generateKeyPair();
@ -52,6 +54,9 @@ signals:
void securityImageResult(bool exists);
void keyFilePathIfExistsResult(const QString& path);
private slots:
void handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
private:
QStringList _publicKeys{};
QPixmap* _securityImage { nullptr };
@ -64,6 +69,8 @@ private:
void updateImageProvider();
bool writeSecurityImage(const QPixmap* pixmap, const QString& outputFilePath);
bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen);
bool verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText);
};
#endif // hifi_Wallet_h

View file

@ -137,7 +137,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID&
boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal);
float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE;
if (event.getID() == LEFT_HAND_HW_ID) {
offsetAngle *= -1;
offsetAngle *= -1.0f;
}
contextOverlayPosition = (glm::quat(glm::radians(glm::vec3(0.0f, offsetAngle, 0.0f)))) *
((cameraPosition + direction * (distance - CONTEXT_OVERLAY_OFFSET_DISTANCE)));
@ -201,8 +201,12 @@ bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityIt
void ContextOverlayInterface::contextOverlays_mousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
if (overlayID == _contextOverlayID && event.getButton() == PointerEvent::PrimaryButton) {
qCDebug(context_overlay) << "Clicked Context Overlay. Entity ID:" << _currentEntityWithContextOverlay << "Overlay ID:" << overlayID;
openMarketplace();
destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent());
if (_commerceSettingSwitch.get()) {
openInspectionCertificate();
} else {
openMarketplace();
}
emit contextOverlayClicked(_currentEntityWithContextOverlay);
_contextOverlayJustClicked = true;
}
}
@ -239,6 +243,16 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI
}
}
static const QString INSPECTION_CERTIFICATE_QML_PATH = qApp->applicationDirPath() + "../../../qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml";
void ContextOverlayInterface::openInspectionCertificate() {
// lets open the tablet to the inspection certificate QML
if (!_currentEntityWithContextOverlay.isNull() && _entityMarketplaceID.length() > 0) {
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
_hmdScriptingInterface->openTablet();
}
}
static const QString MARKETPLACE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace/items/";
void ContextOverlayInterface::openMarketplace() {

View file

@ -56,6 +56,9 @@ public:
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; }
signals:
void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay);
public slots:
bool createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
bool destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event);
@ -76,6 +79,9 @@ private:
bool _isInMarketplaceInspectionMode { false };
Setting::Handle<bool> _commerceSettingSwitch{ "commerce", false };
void openInspectionCertificate();
void openMarketplace();
void enableEntityHighlight(const EntityItemID& entityItemID);
void disableEntityHighlight(const EntityItemID& entityItemID);

View file

@ -306,9 +306,6 @@ public:
QString getMarketplaceID() const;
void setMarketplaceID(const QString& value);
bool getShouldHighlight() const;
void setShouldHighlight(const bool value);
// TODO: get rid of users of getRadius()...
float getRadius() const;

View file

@ -47,6 +47,8 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership
connect(nodeList.data(), &NodeList::isAllowedEditorChanged, this, &EntityScriptingInterface::canAdjustLocksChanged);
connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);
connect(nodeList.data(), &NodeList::canRezTmpChanged, this, &EntityScriptingInterface::canRezTmpChanged);
connect(nodeList.data(), &NodeList::canRezCertifiedChanged, this, &EntityScriptingInterface::canRezCertifiedChanged);
connect(nodeList.data(), &NodeList::canRezTmpCertifiedChanged, this, &EntityScriptingInterface::canRezTmpCertifiedChanged);
connect(nodeList.data(), &NodeList::canWriteAssetsChanged, this, &EntityScriptingInterface::canWriteAssetsChanged);
}
@ -76,6 +78,16 @@ bool EntityScriptingInterface::canRezTmp() {
return nodeList->getThisNodeCanRezTmp();
}
bool EntityScriptingInterface::canRezCertified() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanRezCertified();
}
bool EntityScriptingInterface::canRezTmpCertified() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanRezTmpCertified();
}
bool EntityScriptingInterface::canWriteAssets() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanWriteAssets();

View file

@ -126,6 +126,18 @@ public slots:
*/
Q_INVOKABLE bool canRezTmp();
/**jsdoc
* @function Entities.canRezCertified
* @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new certified entities
*/
Q_INVOKABLE bool canRezCertified();
/**jsdoc
* @function Entities.canRezTmpCertified
* @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new temporary certified entities
*/
Q_INVOKABLE bool canRezTmpCertified();
/**jsdoc
* @function Entities.canWriteAsseets
* @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server
@ -380,6 +392,8 @@ signals:
void canAdjustLocksChanged(bool canAdjustLocks);
void canRezChanged(bool canRez);
void canRezTmpChanged(bool canRez);
void canRezCertifiedChanged(bool canRez);
void canRezTmpCertifiedChanged(bool canRez);
void canWriteAssetsChanged(bool canWriteAssets);
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);

View file

@ -350,7 +350,8 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
}
if (!properties.getClientOnly() && getIsClient() &&
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp()) {
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified()) {
return nullptr;
}
@ -1076,7 +1077,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
if ((isAdd || properties.lifetimeChanged()) &&
!senderNode->getCanRez() && senderNode->getCanRezTmp()) {
((!senderNode->getCanRez() && senderNode->getCanRezTmp()) ||
(!senderNode->getCanRezCertified() && senderNode->getCanRezTmpCertified()))) {
// this node is only allowed to rez temporary entities. if need be, cap the lifetime.
if (properties.getLifetime() == ENTITY_ITEM_IMMORTAL_LIFETIME ||
properties.getLifetime() > _maxTmpEntityLifetime) {
@ -1146,8 +1148,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
} else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) {
failedAdd = true;
qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID()
<< "] attempted to add an entity ID:" << entityItemID;
<< "] attempted to add an entity ID:" << entityItemID;
// FIXME after Cert ID property integrated
} else if (/*!properties.getCertificateID().isNull() && */!senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) {
qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID()
<< "] attempted to add a certified entity with ID:" << entityItemID;
} else {
// this is a new entity... assign a new entityID
properties.setCreated(properties.getLastEdited());

View file

@ -159,6 +159,14 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
newPermissions.can(NodePermissions::Permission::canRezTemporaryEntities)) {
emit canRezTmpChanged(_permissions.can(NodePermissions::Permission::canRezTemporaryEntities));
}
if (originalPermissions.can(NodePermissions::Permission::canRezPermanentCertifiedEntities) !=
newPermissions.can(NodePermissions::Permission::canRezPermanentCertifiedEntities)) {
emit canRezCertifiedChanged(_permissions.can(NodePermissions::Permission::canRezPermanentCertifiedEntities));
}
if (originalPermissions.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities) !=
newPermissions.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities)) {
emit canRezTmpCertifiedChanged(_permissions.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities));
}
if (originalPermissions.can(NodePermissions::Permission::canWriteToAssetServer) !=
newPermissions.can(NodePermissions::Permission::canWriteToAssetServer)) {
emit canWriteAssetsChanged(_permissions.can(NodePermissions::Permission::canWriteToAssetServer));

View file

@ -113,6 +113,8 @@ public:
bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); }
bool getThisNodeCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); }
bool getThisNodeCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); }
bool getThisNodeCanRezCertified() const { return _permissions.can(NodePermissions::Permission::canRezPermanentCertifiedEntities); }
bool getThisNodeCanRezTmpCertified() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities); }
bool getThisNodeCanWriteAssets() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getThisNodeCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getThisNodeCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }
@ -330,6 +332,8 @@ signals:
void isAllowedEditorChanged(bool isAllowedEditor);
void canRezChanged(bool canRez);
void canRezTmpChanged(bool canRezTmp);
void canRezCertifiedChanged(bool canRez);
void canRezTmpCertifiedChanged(bool canRezTmp);
void canWriteAssetsChanged(bool canWriteAssets);
void canKickChanged(bool canKick);
void canReplaceContentChanged(bool canReplaceContent);

View file

@ -72,6 +72,8 @@ public:
bool isAllowedEditor() const { return _permissions.can(NodePermissions::Permission::canAdjustLocks); }
bool getCanRez() const { return _permissions.can(NodePermissions::Permission::canRezPermanentEntities); }
bool getCanRezTmp() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryEntities); }
bool getCanRezCertified() const { return _permissions.can(NodePermissions::Permission::canRezPermanentCertifiedEntities); }
bool getCanRezTmpCertified() const { return _permissions.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities); }
bool getCanWriteToAssetServer() const { return _permissions.can(NodePermissions::Permission::canWriteToAssetServer); }
bool getCanKick() const { return _permissions.can(NodePermissions::Permission::canKick); }
bool getCanReplaceContent() const { return _permissions.can(NodePermissions::Permission::canReplaceDomainContent); }

View file

@ -60,6 +60,8 @@ NodePermissions::NodePermissions(QMap<QString, QVariant> perms) {
permissions |= perms["id_can_adjust_locks"].toBool() ? Permission::canAdjustLocks : Permission::none;
permissions |= perms["id_can_rez"].toBool() ? Permission::canRezPermanentEntities : Permission::none;
permissions |= perms["id_can_rez_tmp"].toBool() ? Permission::canRezTemporaryEntities : Permission::none;
permissions |= perms["id_can_rez_certified"].toBool() ? Permission::canRezPermanentCertifiedEntities : Permission::none;
permissions |= perms["id_can_rez_tmp_certified"].toBool() ? Permission::canRezTemporaryCertifiedEntities : Permission::none;
permissions |= perms["id_can_write_to_asset_server"].toBool() ? Permission::canWriteToAssetServer : Permission::none;
permissions |= perms["id_can_connect_past_max_capacity"].toBool() ?
Permission::canConnectPastMaxCapacity : Permission::none;
@ -86,6 +88,8 @@ QVariant NodePermissions::toVariant(QHash<QUuid, GroupRank> groupRanks) {
values["id_can_adjust_locks"] = can(Permission::canAdjustLocks);
values["id_can_rez"] = can(Permission::canRezPermanentEntities);
values["id_can_rez_tmp"] = can(Permission::canRezTemporaryEntities);
values["id_can_rez_certified"] = can(Permission::canRezPermanentCertifiedEntities);
values["id_can_rez_tmp_certified"] = can(Permission::canRezTemporaryCertifiedEntities);
values["id_can_write_to_asset_server"] = can(Permission::canWriteToAssetServer);
values["id_can_connect_past_max_capacity"] = can(Permission::canConnectPastMaxCapacity);
values["id_can_kick"] = can(Permission::canKick);
@ -144,6 +148,12 @@ QDebug operator<<(QDebug debug, const NodePermissions& perms) {
if (perms.can(NodePermissions::Permission::canRezTemporaryEntities)) {
debug << " rez-tmp";
}
if (perms.can(NodePermissions::Permission::canRezPermanentCertifiedEntities)) {
debug << " rez-certified";
}
if (perms.can(NodePermissions::Permission::canRezTemporaryCertifiedEntities)) {
debug << " rez-tmp-certified";
}
if (perms.can(NodePermissions::Permission::canWriteToAssetServer)) {
debug << " asset-server";
}

View file

@ -73,7 +73,9 @@ public:
canWriteToAssetServer = 16,
canConnectPastMaxCapacity = 32,
canKick = 64,
canReplaceDomainContent = 128
canReplaceDomainContent = 128,
canRezPermanentCertifiedEntities = 256,
canRezTemporaryCertifiedEntities = 512
};
Q_DECLARE_FLAGS(Permissions, Permission)
Permissions permissions;

View file

@ -122,6 +122,7 @@ public:
ReplicatedKillAvatar,
ReplicatedBulkAvatarData,
OctreeFileReplacementFromUrl,
ChallengeOwnership,
NUM_PACKET_TYPE
};

View file

@ -25,6 +25,7 @@
// -WALLET_QML_SOURCE: The path to the Wallet QML
// -onWalletScreen: true/false depending on whether we're looking at the app.
var WALLET_QML_SOURCE = Script.resourcesPath() + "qml/hifi/commerce/wallet/Wallet.qml";
var MARKETPLACE_PURCHASES_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/purchases/Purchases.qml";
var onWalletScreen = false;
function onButtonClicked() {
if (!tablet) {
@ -54,6 +55,7 @@
// -Called when a message is received from SpectatorCamera.qml. The "message" argument is what is sent from the QML
// in the format "{method, params}", like json-rpc. See also sendToQml().
var isHmdPreviewDisabled = true;
var MARKETPLACES_INJECT_SCRIPT_URL = Script.resolvePath("../html/js/marketplacesInject.js");
function fromQml(message) {
switch (message.method) {
case 'passphrasePopup_cancelClicked':
@ -79,6 +81,12 @@
onButtonClicked();
onButtonClicked();
break;
case 'transactionHistory_linkClicked':
tablet.gotoWebScreen(message.marketplaceLink, MARKETPLACES_INJECT_SCRIPT_URL);
break;
case 'goToPurchases':
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
break;
default:
print('Unrecognized message from QML:', JSON.stringify(message));
}

View file

@ -27,6 +27,7 @@
var isPreparing = false; // Explicitly track download request status.
var confirmAllPurchases = false; // Set this to "true" to cause Checkout.qml to popup for all items, even if free
var userIsLoggedIn = false;
function injectCommonCode(isDirectoryPage) {
@ -90,23 +91,77 @@
});
}
function addPurchasesButton() {
// Why isn't this an id?! This really shouldn't be a class on the website, but it is.
var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0];
var purchasesElement = document.createElement('a');
purchasesElement.classList.add("btn");
purchasesElement.classList.add("btn-default");
purchasesElement.id = "purchasesButton";
purchasesElement.setAttribute('href', "#");
purchasesElement.innerHTML = "PURCHASES";
purchasesElement.style = "height:100%;margin-top:0;padding:15px 15px;";
navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement);
$('#purchasesButton').on('click', function () {
EventBridge.emitWebEvent(JSON.stringify({
type: "PURCHASES",
referrerURL: window.location.href
}));
});
function maybeAddLogInButton() {
if (!userIsLoggedIn) {
var resultsElement = document.getElementById('results');
var logInElement = document.createElement('div');
logInElement.classList.add("row");
logInElement.id = "logInDiv";
logInElement.style = "height:60px;margin:20px 10px 10px 10px;padding:5px;" +
"background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;";
var button = document.createElement('a');
button.classList.add("btn");
button.classList.add("btn-default");
button.id = "logInButton";
button.setAttribute('href', "#");
button.innerHTML = "LOG IN";
button.style = "width:80px;height:100%;margin-top:0;margin-left:10px;padding:13px;font-weight:bold;background:linear-gradient(white, #ccc);";
button.onclick = function () {
EventBridge.emitWebEvent(JSON.stringify({
type: "LOGIN"
}));
};
var span = document.createElement('span');
span.style = "margin:10px;color:#1b6420;font-size:15px;";
span.innerHTML = "to purchase items from the Marketplace.";
var xButton = document.createElement('a');
xButton.id = "xButton";
xButton.setAttribute('href', "#");
xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;";
xButton.innerHTML = "X";
xButton.onclick = function () {
logInElement.remove();
dummyRow.remove();
};
logInElement.appendChild(button);
logInElement.appendChild(span);
logInElement.appendChild(xButton);
resultsElement.insertBefore(logInElement, resultsElement.firstChild);
// Dummy row for padding
var dummyRow = document.createElement('div');
dummyRow.classList.add("row");
dummyRow.style = "height:15px;";
resultsElement.insertBefore(dummyRow, resultsElement.firstChild);
}
}
function maybeAddPurchasesButton() {
if (userIsLoggedIn) {
// Why isn't this an id?! This really shouldn't be a class on the website, but it is.
var navbarBrandElement = document.getElementsByClassName('navbar-brand')[0];
var purchasesElement = document.createElement('a');
var dropDownElement = document.getElementById('user-dropdown');
purchasesElement.id = "purchasesButton";
purchasesElement.setAttribute('href', "#");
purchasesElement.innerHTML = "MY PURCHASES";
// FRONTEND WEBDEV RANT: The username dropdown should REALLY not be programmed to be on the same
// line as the search bar, overlaid on top of the search bar, floated right, and then relatively bumped up using "top:-50px".
purchasesElement.style = "height:100%;margin-top:18px;font-weight:bold;float:right;margin-right:" + (dropDownElement.offsetWidth + 30) +
"px;position:relative;z-index:999;";
navbarBrandElement.parentNode.insertAdjacentElement('beforeend', purchasesElement);
$('#purchasesButton').on('click', function () {
EventBridge.emitWebEvent(JSON.stringify({
type: "PURCHASES",
referrerURL: window.location.href
}));
});
}
}
function buyButtonClicked(id, name, author, price, href) {
@ -123,6 +178,14 @@
function injectBuyButtonOnMainPage() {
var cost;
// Unbind original mouseenter and mouseleave behavior
$('body').off('mouseenter', '#price-or-edit .price');
$('body').off('mouseleave', '#price-or-edit .price');
$('.grid-item').find('#price-or-edit').each(function () {
$(this).css({ "margin-top": "0" });
});
$('.grid-item').find('#price-or-edit').find('a').each(function() {
$(this).attr('data-href', $(this).attr('href'));
$(this).attr('href', '#');
@ -131,14 +194,36 @@
$(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6');
$(this).closest('.col-xs-3').attr("class", 'col-xs-6');
var priceElement = $(this).find('.price')
priceElement.css({
"padding": "3px 5px",
"height": "40px",
"background": "linear-gradient(#00b4ef, #0093C5)",
"color": "#FFF",
"font-weight": "600",
"line-height": "34px"
});
if (parseInt(cost) > 0) {
var priceElement = $(this).find('.price')
priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" });
priceElement.text(cost + ' HFC');
priceElement.css({ "min-width": priceElement.width() + 10 });
priceElement.css({ "width": "auto" });
priceElement.html('<span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' +
'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost);
priceElement.css({ "min-width": priceElement.width() + 30 });
}
});
// change pricing to GET on button hover
$('body').on('mouseenter', '#price-or-edit .price', function () {
var $this = $(this);
$this.data('initialHtml', $this.html());
$this.text('GET');
});
$('body').on('mouseleave', '#price-or-edit .price', function () {
var $this = $(this);
$this.html($this.data('initialHtml'));
});
$('.grid-item').find('#price-or-edit').find('a').on('click', function () {
buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'),
@ -151,6 +236,9 @@
function injectHiFiCode() {
if (confirmAllPurchases) {
maybeAddLogInButton();
var target = document.getElementById('templated-items');
// MutationObserver is necessary because the DOM is populated after the page is loaded.
// We're searching for changes to the element whose ID is '#templated-items' - this is
@ -167,30 +255,41 @@
// Try this here in case it works (it will if the user just pressed the "back" button,
// since that doesn't trigger another AJAX request.
injectBuyButtonOnMainPage();
addPurchasesButton();
maybeAddPurchasesButton();
}
}
function injectHiFiItemPageCode() {
if (confirmAllPurchases) {
var href = $('#side-info').find('.btn').first().attr('href');
$('#side-info').find('.btn').first().attr('href', '#');
maybeAddLogInButton();
var purchaseButton = $('#side-info').find('.btn').first();
var href = purchaseButton.attr('href');
purchaseButton.attr('href', '#');
purchaseButton.css({
"background": "linear-gradient(#00b4ef, #0093C5)",
"color": "#FFF",
"font-weight": "600",
"padding-bottom": "10px"
});
var cost = $('.item-cost').text();
if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) {
$('#side-info').find('.btn').first().html('<span class="glyphicon glyphicon-download" id="buyItemButton"></span>Own Item: ' + cost + ' HFC');
purchaseButton.html('PURCHASE <span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' +
'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost);
}
$('#side-info').find('.btn').first().on('click', function () {
purchaseButton.on('click', function () {
buyButtonClicked(window.location.pathname.split("/")[3],
$('#top-center').find('h1').text(),
$('#creator').find('.value').text(),
cost,
href);
});
addPurchasesButton();
maybeAddPurchasesButton();
}
}
@ -451,7 +550,8 @@
if (parsedJsonMessage.type === "marketplaces") {
if (parsedJsonMessage.action === "commerceSetting") {
confirmAllPurchases = !!parsedJsonMessage.data;
confirmAllPurchases = !!parsedJsonMessage.data.commerceMode;
userIsLoggedIn = !!parsedJsonMessage.data.userIsLoggedIn
injectCode();
}
}

View file

@ -22,6 +22,7 @@
var MARKETPLACE_CHECKOUT_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/checkout/Checkout.qml";
var MARKETPLACE_PURCHASES_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/purchases/Purchases.qml";
var MARKETPLACE_WALLET_QML_PATH = Script.resourcesPath() + "qml/hifi/commerce/wallet/Wallet.qml";
var MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH = "commerce/inspectionCertificate/InspectionCertificate.qml";
var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png";
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png";
@ -56,10 +57,26 @@
Window.messageBoxClosed.connect(onMessageBoxClosed);
var onMarketplaceScreen = false;
var onCommerceScreen = false;
var debugCheckout = false;
function showMarketplace() {
UserActivityLogger.openedMarketplace();
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
if (!debugCheckout) {
UserActivityLogger.openedMarketplace();
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
} else {
tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH);
tablet.sendToQml({
method: 'updateCheckoutQML', params: {
itemId: '0d90d21c-ce7a-4990-ad18-e9d2cf991027',
itemName: 'Test Flaregun',
itemAuthor: 'hifiDave',
itemPrice: 17,
itemHref: 'http://mpassets.highfidelity.com/0d90d21c-ce7a-4990-ad18-e9d2cf991027-v1/flaregun.json',
},
canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified
});
}
}
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
@ -76,7 +93,7 @@
}
function onClick() {
if (onMarketplaceScreen) {
if (onMarketplaceScreen || onCommerceScreen) {
// for toolbar-mode: go back to home screen, this will close the window.
tablet.gotoHomeScreen();
} else {
@ -86,11 +103,24 @@
}
}
var referrerURL; // Used for updating Purchases QML
var filterText; // Used for updating Purchases QML
function onScreenChanged(type, url) {
onMarketplaceScreen = type === "Web" && url === MARKETPLACE_URL_INITIAL;
wireEventBridge(type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_PURCHASES_QML_PATH));
onCommerceScreen = type === "QML" && (url === MARKETPLACE_CHECKOUT_QML_PATH || url === MARKETPLACE_PURCHASES_QML_PATH || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1);
wireEventBridge(onCommerceScreen);
if (url === MARKETPLACE_PURCHASES_QML_PATH) {
tablet.sendToQml({
method: 'updatePurchases',
canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified,
referrerURL: referrerURL,
filterText: filterText
});
}
// for toolbar mode: change button to active when window is first openend, false otherwise.
marketplaceButton.editProperties({ isActive: onMarketplaceScreen });
marketplaceButton.editProperties({ isActive: onMarketplaceScreen || onCommerceScreen });
if (type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1) {
ContextOverlay.isInMarketplaceInspectionMode = true;
} else {
@ -98,9 +128,36 @@
}
}
function setCertificateInfo(currentEntityWithContextOverlay, itemMarketplaceId, closeGoesToPurchases) {
wireEventBridge(true);
tablet.sendToQml({
method: 'inspectionCertificate_setMarketplaceId',
marketplaceId: itemMarketplaceId || Entities.getEntityProperties(currentEntityWithContextOverlay, ['marketplaceID']).marketplaceID,
closeGoesToPurchases: closeGoesToPurchases
});
// ZRF FIXME! Make a call to the endpoint to get item info instead of this silliness
Script.setTimeout(function () {
var randomNumber = Math.floor((Math.random() * 150) + 1);
tablet.sendToQml({
method: 'inspectionCertificate_setItemInfo',
itemName: "The Greatest Item",
itemOwner: "ABCDEFG1234567",
itemEdition: (Math.floor(Math.random() * randomNumber) + " / " + randomNumber)
});
}, 500);
}
function onUsernameChanged() {
if (onMarketplaceScreen) {
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
}
}
marketplaceButton.clicked.connect(onClick);
tablet.screenChanged.connect(onScreenChanged);
Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged);
ContextOverlay.contextOverlayClicked.connect(setCertificateInfo);
GlobalServices.myUsernameChanged.connect(onUsernameChanged);
function onMessage(message) {
@ -133,20 +190,28 @@
} else {
var parsedJsonMessage = JSON.parse(message);
if (parsedJsonMessage.type === "CHECKOUT") {
wireEventBridge(true);
tablet.pushOntoStack(MARKETPLACE_CHECKOUT_QML_PATH);
tablet.sendToQml({ method: 'updateCheckoutQML', params: parsedJsonMessage });
tablet.sendToQml({
method: 'updateCheckoutQML',
params: parsedJsonMessage,
canRezCertifiedItems: Entities.canRezCertified || Entities.canRezTmpCertified
});
} else if (parsedJsonMessage.type === "REQUEST_SETTING") {
tablet.emitScriptEvent(JSON.stringify({
type: "marketplaces",
action: "commerceSetting",
data: Settings.getValue("commerce", false)
data: {
commerceMode: Settings.getValue("commerce", false),
userIsLoggedIn: Account.loggedIn
}
}));
} else if (parsedJsonMessage.type === "PURCHASES") {
referrerURL = parsedJsonMessage.referrerURL;
filterText = "";
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
tablet.sendToQml({
method: 'updatePurchases',
referrerURL: parsedJsonMessage.referrerURL
});
} else if (parsedJsonMessage.type === "LOGIN") {
openLoginWindow();
}
}
}
@ -154,13 +219,15 @@
tablet.webEventReceived.connect(onMessage);
Script.scriptEnding.connect(function () {
if (onMarketplaceScreen) {
if (onMarketplaceScreen || onCommerceScreen) {
tablet.gotoHomeScreen();
}
tablet.removeButton(marketplaceButton);
tablet.screenChanged.disconnect(onScreenChanged);
ContextOverlay.contextOverlayClicked.disconnect(setCertificateInfo);
tablet.webEventReceived.disconnect(onMessage);
Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged);
GlobalServices.myUsernameChanged.disconnect(onUsernameChanged);
});
@ -200,22 +267,33 @@
var isHmdPreviewDisabled = true;
function fromQml(message) {
switch (message.method) {
case 'purchases_openWallet':
case 'checkout_openWallet':
case 'checkout_setUpClicked':
tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH);
break;
case 'purchases_walletNotSetUp':
case 'checkout_walletNotSetUp':
wireEventBridge(true);
tablet.sendToQml({
method: 'updateWalletReferrer',
referrer: "purchases"
});
tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH);
break;
case 'checkout_cancelClicked':
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL);
// TODO: Make Marketplace a QML app that's a WebView wrapper so we can use the app stack.
// I don't think this is trivial to do since we also want to inject some JS into the DOM.
//tablet.popFromStack();
break;
case 'header_goToPurchases':
case 'checkout_goToPurchases':
referrerURL = MARKETPLACE_URL_INITIAL;
filterText = message.filterText;
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
tablet.sendToQml({
method: 'updatePurchases',
referrerURL: MARKETPLACE_URL_INITIAL
});
break;
case 'checkout_itemLinkClicked':
case 'checkout_continueShopping':
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL);
//tablet.popFromStack();
@ -226,6 +304,7 @@
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + itemId, MARKETPLACES_INJECT_SCRIPT_URL);
}
break;
case 'header_marketplaceImageClicked':
case 'purchases_backClicked':
tablet.gotoWebScreen(message.referrerURL, MARKETPLACES_INJECT_SCRIPT_URL);
break;
@ -246,6 +325,37 @@
case 'maybeEnableHmdPreview':
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
break;
case 'purchases_getIsFirstUse':
tablet.sendToQml({
method: 'purchases_getIsFirstUseResult',
isFirstUseOfPurchases: Settings.getValue("isFirstUseOfPurchases", true)
});
break;
case 'purchases_setIsFirstUse':
Settings.setValue("isFirstUseOfPurchases", false);
break;
case 'purchases_openGoTo':
tablet.loadQMLSource("TabletAddressDialog.qml");
break;
case 'purchases_itemCertificateClicked':
tablet.loadQMLSource("../commerce/inspectionCertificate/InspectionCertificate.qml");
setCertificateInfo("", message.itemMarketplaceId, true);
break;
case 'inspectionCertificate_closeClicked':
if (message.closeGoesToPurchases) {
referrerURL = MARKETPLACE_URL_INITIAL;
filterText = "";
tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH);
} else {
tablet.gotoHomeScreen();
}
break;
case 'inspectionCertificate_showInMarketplaceClicked':
tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL);
break;
case 'header_myItemsClicked':
tablet.gotoWebScreen(MARKETPLACE_URL + '?view=mine', MARKETPLACES_INJECT_SCRIPT_URL);
break;
default:
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message));
}