Merge branch 'master' into feature/ant-man

This commit is contained in:
Anthony J. Thibault 2017-09-05 16:27:41 -07:00
commit 74f974c68f
46 changed files with 1082 additions and 640 deletions

View file

@ -35,7 +35,7 @@ void ScriptableAvatar::startAnimation(const QString& url, float fps, float prior
return; return;
} }
_animation = DependencyManager::get<AnimationCache>()->getAnimation(url); _animation = DependencyManager::get<AnimationCache>()->getAnimation(url);
_animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame); _animationDetails = AnimationDetails("", QUrl(url), fps, 0, loop, hold, false, firstFrame, lastFrame, true, firstFrame, false);
_maskedJoints = maskedJoints; _maskedJoints = maskedJoints;
} }

View file

@ -6,7 +6,14 @@
<title>Welcome to Interface</title> <title>Welcome to Interface</title>
<style> <style>
body { @font-face {
font-family: 'Raleway Light';
src: url('../fonts/Raleway-Light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
body {
background: black; background: black;
width: 100%; width: 100%;
overflow-x: hidden; overflow-x: hidden;
@ -15,6 +22,14 @@
padding: 0; padding: 0;
} }
a:link {color:inherit}
a:active {color:inherit}
a:visited {color:inherit}
a:hover {
color:inherit;
cursor: pointer;
}
#left_button { #left_button {
position: absolute; position: absolute;
left: 70; left: 70;
@ -38,6 +53,15 @@
position: absolute; position: absolute;
top: 0; left: 0; bottom: 0; right: 0; top: 0; left: 0; bottom: 0; right: 0;
} }
#report_problem {
position: fixed;
top: 10;
right: 10;
font-family: "Raleway Light", sans-serif;
text-decoration: none;
color: #ddd;
}
</style> </style>
<script> <script>
var handControllerImageURL = null; var handControllerImageURL = null;
@ -152,6 +176,7 @@
<a href="#" id="left_button" onmousedown="cycleLeft()"></a> <a href="#" id="left_button" onmousedown="cycleLeft()"></a>
<a href="#" id="right_button" onmousedown="cycleRight()"></a> <a href="#" id="right_button" onmousedown="cycleRight()"></a>
</div> </div>
<a href="mailto:support@highfidelity.com" id="report_problem">Report Problem</a>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 643 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 909 B

View file

@ -29,7 +29,6 @@ Rectangle {
property bool purchasesReceived: false; property bool purchasesReceived: false;
property bool balanceReceived: false; property bool balanceReceived: false;
property bool securityImageResultReceived: false; property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property string itemId: ""; property string itemId: "";
property string itemHref: ""; property string itemHref: "";
property double balanceAfterPurchase: 0; property double balanceAfterPurchase: 0;
@ -46,10 +45,15 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
commerce.balance(); }
commerce.inventory(); }
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
} }
} }
@ -57,8 +61,8 @@ Rectangle {
securityImageResultReceived = true; securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
root.activeView = "checkoutMain"; commerce.getWalletAuthenticatedStatus();
} else if (exists) { } else if (exists) {
// just set the source again (to be sure the change was noticed) // just set the source again (to be sure the change was noticed)
securityImage.source = ""; securityImage.source = "";
@ -66,12 +70,17 @@ Rectangle {
} }
} }
onKeyFilePathIfExistsResult: { onWalletAuthenticatedStatusResult: {
keyFilePathIfExistsResultReceived = true; if (!isAuthenticated && !passphraseModal.visible) {
if (path === "" && root.activeView !== "notSetUp") { passphraseModal.visible = true;
root.activeView = "notSetUp"; } else if (isAuthenticated) {
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
root.activeView = "checkoutMain"; root.activeView = "checkoutMain";
if (!balanceReceived) {
commerce.balance();
}
if (!purchasesReceived) {
commerce.inventory();
}
} }
} }
@ -89,8 +98,8 @@ Rectangle {
console.log("Failed to get balance", result.data.message); console.log("Failed to get balance", result.data.message);
} else { } else {
root.balanceReceived = true; root.balanceReceived = true;
hfcBalanceText.text = (parseFloat(result.data.balance/100).toFixed(2)) + " HFC"; hfcBalanceText.text = result.data.balance + " HFC";
balanceAfterPurchase = parseFloat(result.data.balance/100) - root.itemPriceFull/100; balanceAfterPurchase = result.data.balance - root.itemPriceFull;
root.setBuyText(); root.setBuyText();
} }
} }
@ -110,10 +119,6 @@ Rectangle {
} }
} }
HifiWallet.SecurityImageModel {
id: securityImageModel;
}
// //
// TITLE BAR START // TITLE BAR START
// //
@ -195,7 +200,6 @@ Rectangle {
securityImageResultReceived = false; securityImageResultReceived = false;
purchasesReceived = false; purchasesReceived = false;
balanceReceived = false; balanceReceived = false;
keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
@ -221,7 +225,20 @@ Rectangle {
} }
} }
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
// //
// "WALLET NOT SET UP" START // "WALLET NOT SET UP" START
@ -553,7 +570,7 @@ Rectangle {
} }
RalewayRegular { RalewayRegular {
id: balanceAfterPurchaseText; id: balanceAfterPurchaseText;
text: balanceAfterPurchase.toFixed(2) + " HFC"; text: balanceAfterPurchase + " HFC";
// Text size // Text size
size: balanceAfterPurchaseTextLabel.size; size: balanceAfterPurchaseTextLabel.size;
// Anchors // Anchors
@ -648,7 +665,7 @@ Rectangle {
sendToScript({method: 'checkout_goToPurchases'}); sendToScript({method: 'checkout_goToPurchases'});
} }
} }
RalewayRegular { RalewayRegular {
id: buyText; id: buyText;
// Text size // Text size
@ -687,7 +704,7 @@ Rectangle {
anchors.bottom: root.bottom; anchors.bottom: root.bottom;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
RalewayRegular { RalewayRegular {
id: completeText; id: completeText;
text: "<b>Purchase Complete!</b><br><br>You bought <b>" + (itemNameText.text) + "</b> by <b>" + (itemAuthorText.text) + "</b>"; text: "<b>Purchase Complete!</b><br><br>You bought <b>" + (itemNameText.text) + "</b> by <b>" + (itemAuthorText.text) + "</b>";
@ -706,7 +723,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
Item { Item {
id: checkoutSuccessActionButtonsContainer; id: checkoutSuccessActionButtonsContainer;
// Size // Size
@ -756,7 +773,7 @@ Rectangle {
} }
} }
} }
Item { Item {
id: continueShoppingButtonContainer; id: continueShoppingButtonContainer;
// Size // Size
@ -799,7 +816,7 @@ Rectangle {
anchors.bottom: root.bottom; anchors.bottom: root.bottom;
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
RalewayRegular { RalewayRegular {
id: failureHeaderText; id: failureHeaderText;
text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed."; text: "<b>Purchase Failed.</b><br>Your Purchases and HFC balance haven't changed.";
@ -818,7 +835,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
RalewayRegular { RalewayRegular {
id: failureErrorText; id: failureErrorText;
// Text size // Text size
@ -836,7 +853,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter; horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
Item { Item {
id: backToMarketplaceButtonContainer; id: backToMarketplaceButtonContainer;
// Size // Size
@ -892,7 +909,7 @@ Rectangle {
itemNameText.text = message.params.itemName; itemNameText.text = message.params.itemName;
itemAuthorText.text = message.params.itemAuthor; itemAuthorText.text = message.params.itemAuthor;
root.itemPriceFull = message.params.itemPrice; root.itemPriceFull = message.params.itemPrice;
itemPriceText.text = root.itemPriceFull === 0 ? "Free" : "<b>" + (parseFloat(root.itemPriceFull/100).toFixed(2)) + " HFC</b>"; itemPriceText.text = root.itemPriceFull === 0 ? "Free" : "<b>" + root.itemPriceFull + " HFC</b>";
itemHref = message.params.itemHref; itemHref = message.params.itemHref;
if (itemHref.indexOf('.json') === -1) { if (itemHref.indexOf('.json') === -1) {
root.itemIsJson = false; root.itemIsJson = false;

View file

@ -28,7 +28,6 @@ Rectangle {
property string activeView: "initialize"; property string activeView: "initialize";
property string referrerURL: ""; property string referrerURL: "";
property bool securityImageResultReceived: false; property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property bool purchasesReceived: false; property bool purchasesReceived: false;
property bool punctuationMode: false; property bool punctuationMode: false;
// Style // Style
@ -41,9 +40,15 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
commerce.inventory(); }
}
onKeyFilePathIfExistsResult: {
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp";
} else if (path !== "" && root.activeView === "initialize") {
commerce.getSecurityImage();
} }
} }
@ -51,8 +56,8 @@ Rectangle {
securityImageResultReceived = true; securityImageResultReceived = true;
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up" if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
root.activeView = "purchasesMain"; commerce.getWalletAuthenticatedStatus();
} else if (exists) { } else if (exists) {
// just set the source again (to be sure the change was noticed) // just set the source again (to be sure the change was noticed)
securityImage.source = ""; securityImage.source = "";
@ -60,12 +65,12 @@ Rectangle {
} }
} }
onKeyFilePathIfExistsResult: { onWalletAuthenticatedStatusResult: {
keyFilePathIfExistsResultReceived = true; if (!isAuthenticated && !passphraseModal.visible) {
if (path === "" && root.activeView !== "notSetUp") { passphraseModal.visible = true;
root.activeView = "notSetUp"; } else if (isAuthenticated) {
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") {
root.activeView = "purchasesMain"; root.activeView = "purchasesMain";
commerce.inventory();
} }
} }
@ -166,7 +171,6 @@ Rectangle {
Component.onCompleted: { Component.onCompleted: {
securityImageResultReceived = false; securityImageResultReceived = false;
purchasesReceived = false; purchasesReceived = false;
keyFilePathIfExistsResultReceived = false;
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
@ -191,6 +195,21 @@ Rectangle {
commerce.getLoginStatus(); commerce.getLoginStatus();
} }
} }
HifiWallet.PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
// //
// "WALLET NOT SET UP" START // "WALLET NOT SET UP" START

View file

@ -47,11 +47,26 @@ Item {
HifiControlsUit.Button { HifiControlsUit.Button {
color: hifi.buttons.black; color: hifi.buttons.black;
colorScheme: hifi.colorSchemes.dark; colorScheme: hifi.colorSchemes.dark;
anchors.bottom: helpText.bottom; anchors.bottom: resetButton.top;
anchors.bottomMargin: 15;
anchors.horizontalCenter: parent.horizontalCenter; anchors.horizontalCenter: parent.horizontalCenter;
height: 50; height: 50;
width: 250; width: 250;
text: "Testing: Reset Wallet!"; text: "DEBUG: Clear Cached Passphrase";
onClicked: {
commerce.setPassphrase("");
}
}
HifiControlsUit.Button {
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!";
onClicked: { onClicked: {
commerce.reset(); commerce.reset();
sendSignalToWallet({method: 'walletReset'}); sendSignalToWallet({method: 'walletReset'});

View file

@ -0,0 +1,304 @@
//
// PassphraseModal.qml
// qml/hifi/commerce/wallet
//
// PassphraseModal
//
// Created by Zach Fox on 2017-08-31
// 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;
z: 998;
property bool keyboardRaised: false;
Hifi.QmlCommerce {
id: commerce;
onSecurityImageResult: {
passphraseModalSecurityImage.source = "";
passphraseModalSecurityImage.source = "image://security/securityImage";
}
onWalletAuthenticatedStatusResult: {
submitPassphraseInputButton.enabled = true;
if (!isAuthenticated) {
errorText.text = "Authentication failed - please try again.";
} else {
root.visible = false;
}
}
}
// 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;
}
// This will cause a bug -- if you bring up passphrase selection in HUD mode while
// in HMD while having HMD preview enabled, then move, then finish passphrase selection,
// HMD preview will stay off.
// TODO: Fix this unlikely bug
onVisibleChanged: {
if (visible) {
passphraseField.focus = true;
sendSignalToParent({method: 'disableHmdPreview'});
} else {
sendSignalToParent({method: 'maybeEnableHmdPreview'});
}
}
// Background rectangle
Rectangle {
anchors.fill: parent;
color: "black";
opacity: 0.9;
}
Rectangle {
anchors.top: parent.top;
anchors.left: parent.left;
anchors.right: parent.right;
height: 250;
color: hifi.colors.baseGray;
RalewaySemiBold {
id: instructionsText;
text: "Enter Wallet Passphrase";
size: 16;
anchors.top: parent.top;
anchors.topMargin: 30;
anchors.left: parent.left;
anchors.leftMargin: 16;
width: passphraseField.width;
height: paintedHeight;
// Style
color: hifi.colors.faintGray;
// Alignment
horizontalAlignment: Text.AlignLeft;
}
HifiControlsUit.TextField {
id: passphraseField;
anchors.top: instructionsText.bottom;
anchors.topMargin: 4;
anchors.left: instructionsText.left;
width: 280;
height: 50;
echoMode: TextInput.Password;
placeholderText: "passphrase";
onFocusChanged: {
root.keyboardRaised = focus;
}
MouseArea {
anchors.fill: parent;
onClicked: {
parent.focus = true;
root.keyboardRaised = true;
}
}
onAccepted: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
// Show passphrase text
HifiControlsUit.CheckBox {
id: showPassphrase;
colorScheme: hifi.colorSchemes.dark;
anchors.left: passphraseField.left;
anchors.top: passphraseField.bottom;
anchors.topMargin: 8;
height: 30;
text: "Show passphrase as plain text";
boxSize: 24;
onClicked: {
passphraseField.echoMode = checked ? TextInput.Normal : TextInput.Password;
}
}
// Security Image
Item {
id: securityImageContainer;
// Anchors
anchors.top: instructionsText.top;
anchors.left: passphraseField.right;
anchors.leftMargin: 12;
anchors.right: parent.right;
Image {
id: passphraseModalSecurityImage;
anchors.top: parent.top;
anchors.horizontalCenter: parent.horizontalCenter;
height: 75;
width: height;
fillMode: Image.PreserveAspectFit;
mipmap: true;
source: "image://security/securityImage";
cache: false;
onVisibleChanged: {
commerce.getSecurityImage();
}
}
Image {
id: passphraseModalSecurityImageOverlay;
source: "images/lockOverlay.png";
width: passphraseModalSecurityImage.width * 0.45;
height: passphraseModalSecurityImage.height * 0.45;
anchors.bottom: passphraseModalSecurityImage.bottom;
anchors.right: passphraseModalSecurityImage.right;
mipmap: true;
opacity: 0.9;
}
// "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;
}
//
// ACTION BUTTONS START
//
Item {
id: passphrasePopupActionButtonsContainer;
// Size
width: root.width;
height: 50;
// Anchors
anchors.left: parent.left;
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 {
id: submitPassphraseInputButton;
color: hifi.buttons.blue;
colorScheme: hifi.colorSchemes.dark;
anchors.top: parent.top;
height: 40;
anchors.right: parent.right;
anchors.rightMargin: 20;
width: parent.width/2 - anchors.rightMargin*2;
text: "Submit"
onClicked: {
submitPassphraseInputButton.enabled = false;
commerce.setPassphrase(passphraseField.text);
}
}
}
}
Item {
id: keyboardContainer;
z: 999;
visible: keyboard.raised;
property bool punctuationMode: false;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
Image {
id: lowerKeyboardButton;
source: "images/lowerKeyboard.png";
anchors.horizontalCenter: parent.horizontalCenter;
anchors.bottom: keyboard.top;
height: 30;
width: 120;
MouseArea {
anchors.fill: parent;
onClicked: {
root.keyboardRaised = false;
}
}
}
HifiControlsUit.Keyboard {
id: keyboard;
raised: HMD.mounted && root.keyboardRaised;
numeric: parent.punctuationMode;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
}
}
signal sendSignalToParent(var msg);
}

View file

@ -40,8 +40,8 @@ Item {
passphrasePageSecurityImage.source = "image://security/securityImage"; passphrasePageSecurityImage.source = "image://security/securityImage";
} }
onPassphraseSetupStatusResult: { onWalletAuthenticatedStatusResult: {
sendMessageToLightbox({method: 'statusResult', status: passphraseIsSetup}); sendMessageToLightbox({method: 'statusResult', status: isAuthenticated});
} }
} }
@ -58,10 +58,6 @@ Item {
} }
} }
SecurityImageModel {
id: gridModel;
}
HifiControlsUit.TextField { HifiControlsUit.TextField {
id: passphraseField; id: passphraseField;
anchors.top: parent.top; anchors.top: parent.top;
@ -199,7 +195,7 @@ Item {
// Text below TextFields // Text below TextFields
RalewaySemiBold { RalewaySemiBold {
id: passwordReqs; id: passwordReqs;
text: "Passphrase must be at least 4 characters"; text: "Passphrase must be at least 3 characters";
// Text size // Text size
size: 16; size: 16;
// Anchors // Anchors
@ -256,7 +252,7 @@ Item {
} }
function validateAndSubmitPassphrase() { function validateAndSubmitPassphrase() {
if (passphraseField.text.length < 4) { if (passphraseField.text.length < 3) {
setErrorText("Passphrase too short."); setErrorText("Passphrase too short.");
return false; return false;
} else if (passphraseField.text !== passphraseFieldAgain.text) { } else if (passphraseField.text !== passphraseFieldAgain.text) {

View file

@ -26,8 +26,6 @@ Rectangle {
id: root; id: root;
property string activeView: "initialize"; property string activeView: "initialize";
property bool securityImageResultReceived: false;
property bool keyFilePathIfExistsResultReceived: false;
property bool keyboardRaised: false; property bool keyboardRaised: false;
// Style // Style
@ -40,25 +38,30 @@ Rectangle {
root.activeView = "needsLogIn"; root.activeView = "needsLogIn";
} else if (isLoggedIn) { } else if (isLoggedIn) {
root.activeView = "initialize"; root.activeView = "initialize";
commerce.getSecurityImage();
commerce.getKeyFilePathIfExists(); commerce.getKeyFilePathIfExists();
} }
} }
onSecurityImageResult: { onKeyFilePathIfExistsResult: {
securityImageResultReceived = true; if (path === "" && root.activeView !== "notSetUp") {
if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && exists && root.keyFilePathIfExistsResultReceived && root.activeView === "initialize") { } else if (path !== "" && root.activeView === "initialize") {
root.activeView = "walletHome"; commerce.getSecurityImage();
} }
} }
onKeyFilePathIfExistsResult: { onSecurityImageResult: {
keyFilePathIfExistsResultReceived = true; if (!exists && root.activeView !== "notSetUp") { // "If security image is not set up"
if (path === "" && root.activeView !== "notSetUp") {
root.activeView = "notSetUp"; root.activeView = "notSetUp";
} else if (root.securityImageResultReceived && root.keyFilePathIfExistsResultReceived && path !== "" && root.activeView === "initialize") { } else if (exists && root.activeView === "initialize") {
commerce.getWalletAuthenticatedStatus();
}
}
onWalletAuthenticatedStatusResult: {
if (!isAuthenticated && passphraseModal && !passphraseModal.visible) {
passphraseModal.visible = true;
} else if (isAuthenticated) {
root.activeView = "walletHome"; root.activeView = "walletHome";
} }
} }
@ -89,7 +92,8 @@ Rectangle {
if (msg.method === 'walletSetup_cancelClicked') { if (msg.method === 'walletSetup_cancelClicked') {
walletSetupLightbox.visible = false; walletSetupLightbox.visible = false;
} else if (msg.method === 'walletSetup_finished') { } else if (msg.method === 'walletSetup_finished') {
root.activeView = "walletHome"; root.activeView = "initialize";
commerce.getLoginStatus();
} else if (msg.method === 'walletSetup_raiseKeyboard') { } else if (msg.method === 'walletSetup_raiseKeyboard') {
root.keyboardRaised = true; root.keyboardRaised = true;
} else if (msg.method === 'walletSetup_lowerKeyboard') { } else if (msg.method === 'walletSetup_lowerKeyboard') {
@ -218,6 +222,21 @@ Rectangle {
} }
} }
PassphraseModal {
id: passphraseModal;
visible: false;
anchors.top: titleBarContainer.bottom;
anchors.bottom: parent.bottom;
anchors.left: parent.left;
anchors.right: parent.right;
Connections {
onSendSignalToParent: {
sendToScript(msg);
}
}
}
NotSetUp { NotSetUp {
id: notSetUp; id: notSetUp;
visible: root.activeView === "notSetUp"; visible: root.activeView === "notSetUp";

View file

@ -38,7 +38,7 @@ Item {
} }
onBalanceResult : { onBalanceResult : {
balanceText.text = parseFloat(result.data.balance/100).toFixed(2); balanceText.text = result.data.balance;
} }
onHistoryResult : { onHistoryResult : {
@ -88,13 +88,14 @@ Item {
height: 60; height: 60;
Rectangle { Rectangle {
id: hfcBalanceField; id: hfcBalanceField;
color: hifi.colors.darkGray;
anchors.right: parent.right; anchors.right: parent.right;
anchors.left: parent.left; anchors.left: parent.left;
anchors.bottom: parent.bottom; anchors.bottom: parent.bottom;
height: parent.height - 15; height: parent.height - 15;
// "HFC" balance label // "HFC" balance label
RalewayRegular { FiraSansRegular {
id: balanceLabel; id: balanceLabel;
text: "HFC"; text: "HFC";
// Text size // Text size
@ -106,7 +107,7 @@ Item {
anchors.rightMargin: 4; anchors.rightMargin: 4;
width: paintedWidth; width: paintedWidth;
// Style // Style
color: hifi.colors.darkGray; color: hifi.colors.lightGrayText;
// Alignment // Alignment
horizontalAlignment: Text.AlignRight; horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
@ -121,7 +122,7 @@ Item {
} }
// Balance Text // Balance Text
FiraSansRegular { FiraSansSemiBold {
id: balanceText; id: balanceText;
text: "--"; text: "--";
// Text size // Text size
@ -133,7 +134,7 @@ Item {
anchors.right: balanceLabel.left; anchors.right: balanceLabel.left;
anchors.rightMargin: 4; anchors.rightMargin: 4;
// Style // Style
color: hifi.colors.darkGray; color: hifi.colors.lightGrayText;
// Alignment // Alignment
horizontalAlignment: Text.AlignRight; horizontalAlignment: Text.AlignRight;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
@ -258,7 +259,7 @@ Item {
delegate: Item { delegate: Item {
width: parent.width; width: parent.width;
height: transactionText.height + 30; height: transactionText.height + 30;
RalewayRegular { FiraSansRegular {
id: transactionText; id: transactionText;
text: model.text; text: model.text;
// Style // Style
@ -272,7 +273,7 @@ Item {
horizontalAlignment: Text.AlignLeft; horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignVCenter; verticalAlignment: Text.AlignVCenter;
} }
HifiControlsUit.Separator { HifiControlsUit.Separator {
anchors.left: parent.left; anchors.left: parent.left;
anchors.right: parent.right; anchors.right: parent.right;
@ -288,7 +289,7 @@ Item {
} }
// This should never be visible (since you immediately get 100 HFC) // This should never be visible (since you immediately get 100 HFC)
RalewayRegular { FiraSansRegular {
id: emptyTransationHistory; id: emptyTransationHistory;
size: 24; size: 24;
visible: !transactionHistory.visible && root.historyReceived; visible: !transactionHistory.visible && root.historyReceived;

View file

@ -39,9 +39,9 @@ Rectangle {
} }
} }
onPassphraseSetupStatusResult: { onWalletAuthenticatedStatusResult: {
securityImageContainer.visible = false; securityImageContainer.visible = false;
if (passphraseIsSetup) { if (isAuthenticated) {
privateKeysReadyContainer.visible = true; privateKeysReadyContainer.visible = true;
} else { } else {
choosePassphraseContainer.visible = true; choosePassphraseContainer.visible = true;
@ -117,7 +117,7 @@ Rectangle {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: 16; anchors.rightMargin: 16;
height: 280; height: 280;
Connections { Connections {
onSendSignalToWallet: { onSendSignalToWallet: {
sendSignalToWallet(msg); sendSignalToWallet(msg);
@ -210,7 +210,7 @@ Rectangle {
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
commerce.getPassphraseSetupStatus(); commerce.getWalletAuthenticatedStatus();
} }
} }

View file

@ -1,5 +1,6 @@
import QtQuick 2.5 import QtQuick 2.5
import QtGraphicalEffects 1.0 import QtGraphicalEffects 1.0
import QtQuick.Layouts 1.3
import "../../styles-uit" import "../../styles-uit"
import "../audio" as HifiAudio import "../audio" as HifiAudio
@ -109,15 +110,45 @@ Item {
} }
} }
RalewaySemiBold { Item {
id: usernameText width: 150
text: tabletRoot.username height: 50
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 20 anchors.rightMargin: 30
horizontalAlignment: Text.AlignRight anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 20
color: "#afafaf" ColumnLayout {
anchors.fill: parent
RalewaySemiBold {
text: Account.loggedIn ? qsTr("Log out") : qsTr("Log in")
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
RalewaySemiBold {
visible: Account.loggedIn
height: Account.loggedIn ? parent.height/2 - parent.spacing/2 : 0
text: Account.loggedIn ? "[" + tabletRoot.usernameShort + "]" : ""
horizontalAlignment: Text.AlignRight
anchors.right: parent.right
font.pixelSize: 20
color: "#afafaf"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!Account.loggedIn) {
DialogsManager.showLoginDialog()
} else {
Account.logOut()
}
}
}
} }
} }

View file

@ -8,6 +8,7 @@ Item {
id: tabletRoot id: tabletRoot
objectName: "tabletRoot" objectName: "tabletRoot"
property string username: "Unknown user" property string username: "Unknown user"
property string usernameShort: "Unknown user"
property var rootMenu; property var rootMenu;
property var openModal: null; property var openModal: null;
property var openMessage: null; property var openMessage: null;
@ -157,6 +158,11 @@ Item {
function setUsername(newUsername) { function setUsername(newUsername) {
username = newUsername; username = newUsername;
usernameShort = newUsername.substring(0, 8);
if (newUsername.length > 8) {
usernameShort = usernameShort + "..."
}
} }
ListModel { ListModel {

View file

@ -6337,11 +6337,11 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
bool Application::askToReplaceDomainContent(const QString& url) { bool Application::askToReplaceDomainContent(const QString& url) {
QString methodDetails; QString methodDetails;
const int MAX_CHARACTERS_PER_LINE = 90;
if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) { if (DependencyManager::get<NodeList>()->getThisNodeCanReplaceContent()) {
QUrl originURL { url }; QUrl originURL { url };
if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) { if (originURL.host().endsWith(MARKETPLACE_CDN_HOSTNAME)) {
// Create a confirmation dialog when this call is made // Create a confirmation dialog when this call is made
const int MAX_CHARACTERS_PER_LINE = 90;
static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. " static const QString infoText = simpleWordWrap("Your domain's content will be replaced with a new content set. "
"If you want to save what you have now, create a backup before proceeding. For more information about backing up " "If you want to save what you have now, create a backup before proceeding. For more information about backing up "
"and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) + "and restoring content, visit the documentation page at: ", MAX_CHARACTERS_PER_LINE) +
@ -6377,7 +6377,9 @@ bool Application::askToReplaceDomainContent(const QString& url) {
} }
} else { } else {
methodDetails = "UserDoesNotHavePermissionToReplaceContent"; methodDetails = "UserDoesNotHavePermissionToReplaceContent";
OffscreenUi::warning("Unable to replace content", "You do not have permissions to replace domain content", static const QString warningMessage = simpleWordWrap("The domain owner must enable 'Replace Content' "
"permissions for you in this domain's server settings before you can continue.", MAX_CHARACTERS_PER_LINE);
OffscreenUi::warning("You do not have permissions to replace domain content", warningMessage,
QMessageBox::Ok, QMessageBox::Ok); QMessageBox::Ok, QMessageBox::Ok);
} }
QJsonObject messageProperties = { QJsonObject messageProperties = {

View file

@ -1889,6 +1889,17 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
const bool shouldDrawHead = shouldRenderHead(renderArgs); const bool shouldDrawHead = shouldRenderHead(renderArgs);
if (shouldDrawHead != _prevShouldDrawHead) { if (shouldDrawHead != _prevShouldDrawHead) {
_skeletonModel->setEnableCauterization(!shouldDrawHead); _skeletonModel->setEnableCauterization(!shouldDrawHead);
for (int i = 0; i < _attachmentData.size(); i++) {
if (_attachmentData[i].jointName.compare("Head", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Neck", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("LeftEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene());
}
}
} }
_prevShouldDrawHead = shouldDrawHead; _prevShouldDrawHead = shouldDrawHead;
} }

View file

@ -11,6 +11,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QTimeZone>
#include <QJsonDocument> #include <QJsonDocument>
#include "AccountManager.h" #include "AccountManager.h"
#include "Wallet.h" #include "Wallet.h"
@ -45,7 +46,6 @@ Handler(buy)
Handler(receiveAt) Handler(receiveAt)
Handler(balance) Handler(balance)
Handler(inventory) Handler(inventory)
Handler(history)
void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) { void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, QJsonObject request) {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
@ -108,6 +108,64 @@ void Ledger::inventory(const QStringList& keys) {
keysQuery("inventory", "inventorySuccess", "inventoryFailure"); keysQuery("inventory", "inventorySuccess", "inventoryFailure");
} }
QString nameFromKey(const QString& key, const QStringList& publicKeys) {
if (key.isNull() || key.isEmpty()) {
return "<b>Marketplace</b>";
}
if (publicKeys.contains(key)) {
return "You";
}
return "<b>Someone</b>";
}
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
// QML cannot do that easily since it doesn't know what the wallet
// public key(s) are. Let's keep it that way
QByteArray response = reply.readAll();
QJsonObject data = QJsonDocument::fromJson(response).object();
// we will need the array of public keys from the wallet
auto wallet = DependencyManager::get<Wallet>();
auto keys = wallet->listPublicKeys();
// now we need to loop through the transactions and add fancy text...
auto historyArray = data.find("data").value().toObject().find("history").value().toArray();
QJsonArray newHistoryArray;
// TODO: do this with 0 copies if possible
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);
// 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
#ifdef Q_OS_MAC
QDateTime createdAt = QDateTime::fromTime_t(valueObject["created_at"].toInt(), Qt::UTC);
#else
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());
newHistoryArray.push_back(valueObject);
}
// now copy the rest of the json -- this is inefficient
// TODO: try to do this without making copies
QJsonObject newData;
newData["status"] = "success";
QJsonObject newDataData;
newDataData["history"] = newHistoryArray;
newData["data"] = newDataData;
emit historyResult(newData);
}
void Ledger::historyFailure(QNetworkReply& reply) {
failResponse("history", reply);
}
void Ledger::history(const QStringList& keys) { void Ledger::history(const QStringList& keys) {
keysQuery("history", "historySuccess", "historyFailure"); keysQuery("history", "historySuccess", "historyFailure");
} }
@ -117,4 +175,4 @@ void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); }
void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); } void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); }
void Ledger::reset() { void Ledger::reset() {
send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject()); send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, QJsonObject());
} }

View file

@ -29,6 +29,30 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) {
connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult);
} }
void QmlCommerce::getLoginStatus() {
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
}
void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get<Wallet>();
wallet->sendKeyFilePathIfExists();
}
void QmlCommerce::getWalletAuthenticatedStatus() {
auto wallet = DependencyManager::get<Wallet>();
emit walletAuthenticatedStatusResult(wallet->walletIsAuthenticatedWithPassphrase());
}
void QmlCommerce::getSecurityImage() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getSecurityImage();
}
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
auto wallet = DependencyManager::get<Wallet>();
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) { void QmlCommerce::buy(const QString& assetId, int cost, const QString& buyerUsername) {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
@ -60,30 +84,14 @@ void QmlCommerce::history() {
ledger->history(wallet->listPublicKeys()); ledger->history(wallet->listPublicKeys());
} }
void QmlCommerce::chooseSecurityImage(const QString& imageFile) {
auto wallet = DependencyManager::get<Wallet>();
wallet->chooseSecurityImage(imageFile);
}
void QmlCommerce::getSecurityImage() {
auto wallet = DependencyManager::get<Wallet>();
wallet->getSecurityImage();
}
void QmlCommerce::getLoginStatus() {
emit loginStatusResult(DependencyManager::get<AccountManager>()->isLoggedIn());
}
void QmlCommerce::setPassphrase(const QString& passphrase) { void QmlCommerce::setPassphrase(const QString& passphrase) {
emit passphraseSetupStatusResult(true);
}
void QmlCommerce::getPassphraseSetupStatus() {
emit passphraseSetupStatusResult(false);
}
void QmlCommerce::getKeyFilePathIfExists() {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
wallet->sendKeyFilePathIfExists(); if (wallet->getPassphrase() && !wallet->getPassphrase()->isEmpty()) {
wallet->changePassphrase(passphrase);
} else {
wallet->setPassphrase(passphrase);
}
getWalletAuthenticatedStatus();
} }
void QmlCommerce::reset() { void QmlCommerce::reset() {
@ -91,4 +99,4 @@ void QmlCommerce::reset() {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
ledger->reset(); ledger->reset();
wallet->reset(); wallet->reset();
} }

View file

@ -28,28 +28,32 @@ public:
QmlCommerce(QQuickItem* parent = nullptr); QmlCommerce(QQuickItem* parent = nullptr);
signals: signals:
void loginStatusResult(bool isLoggedIn);
void keyFilePathIfExistsResult(const QString& path);
void securityImageResult(bool exists);
void walletAuthenticatedStatusResult(bool isAuthenticated);
void buyResult(QJsonObject result); void buyResult(QJsonObject result);
// Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and // Balance and Inventory are NOT properties, because QML can't change them (without risk of failure), and
// because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain). // because we can't scalably know of out-of-band changes (e.g., another machine interacting with the block chain).
void balanceResult(QJsonObject result); void balanceResult(QJsonObject result);
void inventoryResult(QJsonObject result); void inventoryResult(QJsonObject result);
void securityImageResult(bool exists);
void loginStatusResult(bool isLoggedIn);
void passphraseSetupStatusResult(bool passphraseIsSetup);
void historyResult(QJsonObject result); void historyResult(QJsonObject result);
void keyFilePathIfExistsResult(const QString& path);
protected: protected:
Q_INVOKABLE void getLoginStatus();
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void getSecurityImage();
Q_INVOKABLE void getWalletAuthenticatedStatus();
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 QString& buyerUsername = "");
Q_INVOKABLE void balance(); Q_INVOKABLE void balance();
Q_INVOKABLE void inventory(); Q_INVOKABLE void inventory();
Q_INVOKABLE void history(); Q_INVOKABLE void history();
Q_INVOKABLE void chooseSecurityImage(const QString& imageFile);
Q_INVOKABLE void getSecurityImage();
Q_INVOKABLE void getLoginStatus();
Q_INVOKABLE void setPassphrase(const QString& passphrase);
Q_INVOKABLE void getPassphraseSetupStatus();
Q_INVOKABLE void getKeyFilePathIfExists();
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
}; };

View file

@ -56,16 +56,72 @@ QString imageFilePath() {
int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
// just return a hardcoded pwd for now // just return a hardcoded pwd for now
auto passphrase = DependencyManager::get<Wallet>()->getPassphrase(); auto passphrase = DependencyManager::get<Wallet>()->getPassphrase();
if (passphrase) { if (passphrase && !passphrase->isEmpty()) {
strcpy(password, passphrase->toLocal8Bit().constData()); strcpy(password, passphrase->toLocal8Bit().constData());
return static_cast<int>(passphrase->size()); return static_cast<int>(passphrase->size());
} else { } else {
// ok gotta bring up modal dialog... But right now lets just // this shouldn't happen - so lets log it to tell us we have
// just keep it empty // a problem with the flow...
qCCritical(commerce) << "no cached passphrase while decrypting!";
return 0; return 0;
} }
} }
RSA* readKeys(const char* filename) {
FILE* fp;
RSA* key = NULL;
if ((fp = fopen(filename, "rt"))) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
// now read private key
qCDebug(commerce) << "read public key";
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
qCDebug(commerce) << "read private key";
fclose(fp);
return key;
}
qCDebug(commerce) << "failed to read private key";
} else {
qCDebug(commerce) << "failed to read public key";
}
fclose(fp);
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
return key;
}
bool writeKeys(const char* filename, RSA* keys) {
FILE* fp;
bool retval = false;
if ((fp = fopen(filename, "wt"))) {
if (!PEM_write_RSAPublicKey(fp, keys)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
QFile(QString(filename)).remove();
return retval;
}
if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCDebug(commerce) << "failed to write private key";
QFile(QString(filename)).remove();
return retval;
}
retval = true;
qCDebug(commerce) << "wrote keys successfully";
fclose(fp);
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
return retval;
}
// BEGIN copied code - this will be removed/changed at some point soon // BEGIN copied code - this will be removed/changed at some point soon
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp. // copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
// We will have a different implementation in practice, but this gives us a start for now // We will have a different implementation in practice, but this gives us a start for now
@ -124,25 +180,9 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
} }
if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
// now lets persist them to files qCDebug(commerce) << "couldn't save keys!";
// FIXME: for now I'm appending to the file if it exists. As long as we always put return retval;
// the keys in the same order, this works fine. TODO: verify this will skip over
// anything else (like an embedded image)
FILE* fp;
if ((fp = fopen(keyFilePath().toStdString().c_str(), "at"))) {
if (!PEM_write_RSAPublicKey(fp, keyPair)) {
fclose(fp);
qCDebug(commerce) << "failed to write public key";
return retval;
}
if (!PEM_write_RSAPrivateKey(fp, keyPair, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCDebug(commerce) << "failed to write private key";
return retval;
}
fclose(fp);
} }
RSA_free(keyPair); RSA_free(keyPair);
@ -201,9 +241,6 @@ RSA* readPrivateKey(const char* filename) {
// file opened successfully // file opened successfully
qCDebug(commerce) << "opened key file" << filename; qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) { if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
// cleanup
fclose(fp);
qCDebug(commerce) << "parsed private key file successfully"; qCDebug(commerce) << "parsed private key file successfully";
} else { } else {
@ -215,7 +252,6 @@ RSA* readPrivateKey(const char* filename) {
} }
return key; return key;
} }
static const unsigned char IVEC[16] = "IAmAnIVecYay123"; static const unsigned char IVEC[16] = "IAmAnIVecYay123";
void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) { void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArray& salt) {
@ -236,6 +272,10 @@ void Wallet::setPassphrase(const QString& passphrase) {
delete _passphrase; delete _passphrase;
} }
_passphrase = new QString(passphrase); _passphrase = new QString(passphrase);
// no matter what, we now need to clear the keys as they
// need to be read using this passphrase
_publicKeys.clear();
} }
// encrypt some stuff // encrypt some stuff
@ -341,6 +381,38 @@ bool Wallet::decryptFile(const QString& inputFilePath, unsigned char** outputBuf
return true; return true;
} }
bool Wallet::walletIsAuthenticatedWithPassphrase() {
// try to read existing keys if they exist...
// FIXME: initialize OpenSSL elsewhere soon
initialize();
// this should always be false if we don't have a passphrase
// cached yet
if (!_passphrase || _passphrase->isEmpty()) {
return false;
}
if (_publicKeys.count() > 0) {
// we _must_ be authenticated if the publicKeys are there
return true;
}
// otherwise, we have a passphrase but no keys, so we have to check
auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
if (publicKey.size() > 0) {
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
RSA_free(key);
// be sure to add the public key so we don't do this over and over
_publicKeys.push_back(publicKey.toBase64());
return true;
}
}
return false;
}
bool Wallet::createIfNeeded() { bool Wallet::createIfNeeded() {
if (_publicKeys.count() > 0) return false; if (_publicKeys.count() > 0) return false;
@ -512,3 +584,30 @@ void Wallet::reset() {
keyFile.remove(); keyFile.remove();
imageFile.remove(); imageFile.remove();
} }
bool Wallet::changePassphrase(const QString& newPassphrase) {
qCDebug(commerce) << "changing passphrase";
RSA* keys = readKeys(keyFilePath().toStdString().c_str());
if (keys) {
// we read successfully, so now write to a new temp file
// save old passphrase just in case
// TODO: force re-enter?
QString oldPassphrase = *_passphrase;
setPassphrase(newPassphrase);
QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp"));
if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
// ok, now move the temp file to the correct spot
QFile(QString(keyFilePath())).remove();
QFile(tempFileName).rename(QString(keyFilePath()));
qCDebug(commerce) << "passphrase changed successfully";
return true;
} else {
qCDebug(commerce) << "couldn't write keys";
QFile(tempFileName).remove();
setPassphrase(oldPassphrase);
return false;
}
}
qCDebug(commerce) << "couldn't read keys";
return false;
}

View file

@ -39,18 +39,21 @@ public:
void setPassphrase(const QString& passphrase); void setPassphrase(const QString& passphrase);
QString* getPassphrase() { return _passphrase; } QString* getPassphrase() { return _passphrase; }
bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); }
bool walletIsAuthenticatedWithPassphrase();
bool changePassphrase(const QString& newPassphrase);
void reset(); void reset();
signals: signals:
void securityImageResult(bool exists) ; void securityImageResult(bool exists);
void keyFilePathIfExistsResult(const QString& path); void keyFilePathIfExistsResult(const QString& path);
private: private:
QStringList _publicKeys{}; QStringList _publicKeys{};
QPixmap* _securityImage { nullptr }; QPixmap* _securityImage { nullptr };
QByteArray _salt {"iamsalt!"}; QByteArray _salt {"iamsalt!"};
QString* _passphrase { new QString("pwd") }; QString* _passphrase { new QString("") };
void updateImageProvider(); void updateImageProvider();
bool encryptFile(const QString& inputFilePath, const QString& outputFilePath); bool encryptFile(const QString& inputFilePath, const QString& outputFilePath);

View file

@ -18,6 +18,8 @@ AccountScriptingInterface* AccountScriptingInterface::getInstance() {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
QObject::connect(accountManager.data(), &AccountManager::profileChanged, QObject::connect(accountManager.data(), &AccountManager::profileChanged,
&sharedInstance, &AccountScriptingInterface::usernameChanged); &sharedInstance, &AccountScriptingInterface::usernameChanged);
QObject::connect(accountManager.data(), &AccountManager::usernameChanged,
&sharedInstance, &AccountScriptingInterface::onUsernameChanged);
return &sharedInstance; return &sharedInstance;
} }
@ -31,6 +33,21 @@ bool AccountScriptingInterface::checkAndSignalForAccessToken() {
return accountManager->checkAndSignalForAccessToken(); return accountManager->checkAndSignalForAccessToken();
} }
void AccountScriptingInterface::logOut() {
auto accountManager = DependencyManager::get<AccountManager>();
return accountManager->logout();
}
AccountScriptingInterface::AccountScriptingInterface(QObject *parent): QObject(parent) {
m_loggedIn = isLoggedIn();
emit loggedInChanged(m_loggedIn);
}
void AccountScriptingInterface::onUsernameChanged(QString username) {
m_loggedIn = (username != QString());
emit loggedInChanged(m_loggedIn);
}
QString AccountScriptingInterface::getUsername() { QString AccountScriptingInterface::getUsername() {
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
if (accountManager->isLoggedIn()) { if (accountManager->isLoggedIn()) {

View file

@ -18,6 +18,7 @@ class AccountScriptingInterface : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged)
Q_PROPERTY(bool loggedIn READ loggedIn NOTIFY loggedInChanged)
/**jsdoc /**jsdoc
* @namespace Account * @namespace Account
@ -32,6 +33,7 @@ signals:
* @return {Signal} * @return {Signal}
*/ */
void usernameChanged(); void usernameChanged();
void loggedInChanged(bool loggedIn);
public slots: public slots:
static AccountScriptingInterface* getInstance(); static AccountScriptingInterface* getInstance();
@ -50,6 +52,20 @@ public slots:
*/ */
bool isLoggedIn(); bool isLoggedIn();
bool checkAndSignalForAccessToken(); bool checkAndSignalForAccessToken();
void logOut();
public:
AccountScriptingInterface(QObject* parent = nullptr);
bool loggedIn() const {
return m_loggedIn;
}
private slots:
void onUsernameChanged(QString username);
private:
bool m_loggedIn { false };
}; };
#endif // hifi_AccountScriptingInterface_h #endif // hifi_AccountScriptingInterface_h

View file

@ -33,7 +33,12 @@ void DialogsManagerScriptingInterface::showAddressBar() {
void DialogsManagerScriptingInterface::hideAddressBar() { void DialogsManagerScriptingInterface::hideAddressBar() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(), QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"hideAddressBar", Qt::QueuedConnection); "hideAddressBar", Qt::QueuedConnection);
}
void DialogsManagerScriptingInterface::showLoginDialog() {
QMetaObject::invokeMethod(DependencyManager::get<DialogsManager>().data(),
"showLoginDialog", Qt::QueuedConnection);
} }
void DialogsManagerScriptingInterface::showFeed() { void DialogsManagerScriptingInterface::showFeed() {

View file

@ -24,6 +24,7 @@ public:
public slots: public slots:
void showAddressBar(); void showAddressBar();
void hideAddressBar(); void hideAddressBar();
void showLoginDialog();
signals: signals:
void addressBarShown(bool visible); void addressBarShown(bool visible);

View file

@ -910,6 +910,8 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
auto& fbxJoints = _animation->getGeometry().joints; auto& fbxJoints = _animation->getGeometry().joints;
auto& originalFbxJoints = _model->getFBXGeometry().joints;
bool allowTranslation = entity->getAnimationAllowTranslation();
int frameCount = frames.size(); int frameCount = frames.size();
if (frameCount <= 0) { if (frameCount <= 0) {
return; return;
@ -952,7 +954,9 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
int index = _jointMapping[j]; int index = _jointMapping[j];
if (index >= 0) { if (index >= 0) {
glm::mat4 translationMat; glm::mat4 translationMat;
if (index < translations.size()) { if (!allowTranslation){
translationMat = glm::translate(originalFbxJoints[index].translation);
} else if (index < translations.size()) {
translationMat = glm::translate(translations[index]); translationMat = glm::translate(translations[index]);
} }
glm::mat4 rotationMat; glm::mat4 rotationMat;

View file

@ -33,6 +33,7 @@ bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b
void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FPS, Animation, animation, FPS, fps);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_FRAME_INDEX, Animation, animation, CurrentFrame, currentFrame);
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_PLAYING, Animation, animation, Running, running);
@ -46,6 +47,7 @@ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desire
void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, url, QString, setURL);
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(animation, allowTranslation, bool, setAllowTranslation);
// legacy property support // legacy property support
COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(animationURL, QString, setURL, getURL);
@ -67,6 +69,7 @@ void AnimationPropertyGroup::copyFromScriptValue(const QScriptValue& object, boo
void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) { void AnimationPropertyGroup::merge(const AnimationPropertyGroup& other) {
COPY_PROPERTY_IF_CHANGED(url); COPY_PROPERTY_IF_CHANGED(url);
COPY_PROPERTY_IF_CHANGED(allowTranslation);
COPY_PROPERTY_IF_CHANGED(fps); COPY_PROPERTY_IF_CHANGED(fps);
COPY_PROPERTY_IF_CHANGED(currentFrame); COPY_PROPERTY_IF_CHANGED(currentFrame);
COPY_PROPERTY_IF_CHANGED(running); COPY_PROPERTY_IF_CHANGED(running);
@ -88,6 +91,7 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
float lastFrame = getLastFrame(); float lastFrame = getLastFrame();
bool loop = getLoop(); bool loop = getLoop();
bool hold = getHold(); bool hold = getHold();
bool allowTranslation = getAllowTranslation();
QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8()); QJsonDocument settingsAsJson = QJsonDocument::fromJson(value.toUtf8());
QJsonObject settingsAsJsonObject = settingsAsJson.object(); QJsonObject settingsAsJsonObject = settingsAsJson.object();
@ -122,6 +126,11 @@ void AnimationPropertyGroup::setFromOldAnimationSettings(const QString& value) {
running = settingsMap["hold"].toBool(); running = settingsMap["hold"].toBool();
} }
if (settingsMap.contains("allowTranslation")) {
allowTranslation = settingsMap["allowTranslation"].toBool();
}
setAllowTranslation(allowTranslation);
setFPS(fps); setFPS(fps);
setCurrentFrame(currentFrame); setCurrentFrame(currentFrame);
setRunning(running); setRunning(running);
@ -137,6 +146,7 @@ void AnimationPropertyGroup::debugDump() const {
qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged(); qCDebug(entities) << " url:" << getURL() << " has changed:" << urlChanged();
qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged(); qCDebug(entities) << " fps:" << getFPS() << " has changed:" << fpsChanged();
qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged(); qCDebug(entities) << "currentFrame:" << getCurrentFrame() << " has changed:" << currentFrameChanged();
qCDebug(entities) << "allowTranslation:" << getAllowTranslation() << " has changed:" << allowTranslationChanged();
} }
void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) { void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
@ -149,6 +159,9 @@ void AnimationPropertyGroup::listChangedProperties(QList<QString>& out) {
if (currentFrameChanged()) { if (currentFrameChanged()) {
out << "animation-currentFrame"; out << "animation-currentFrame";
} }
if (allowTranslationChanged()) {
out << "animation-allowTranslation";
}
} }
@ -162,6 +175,7 @@ bool AnimationPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
bool successPropertyFits = true; bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
@ -181,6 +195,7 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
bool somethingChanged = false; bool somethingChanged = false;
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
@ -198,7 +213,8 @@ bool AnimationPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyF
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_FIRST_FRAME, FirstFrame);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_LAST_FRAME, LastFrame);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_HOLD, Hold);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation);
processedBytes += bytesRead; processedBytes += bytesRead;
Q_UNUSED(somethingChanged); Q_UNUSED(somethingChanged);
@ -215,6 +231,7 @@ void AnimationPropertyGroup::markAllChanged() {
_firstFrameChanged = true; _firstFrameChanged = true;
_lastFrameChanged = true; _lastFrameChanged = true;
_holdChanged = true; _holdChanged = true;
_allowTranslationChanged = true;
} }
EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const { EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
@ -228,12 +245,14 @@ EntityPropertyFlags AnimationPropertyGroup::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FIRST_FRAME, firstFrame);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_LAST_FRAME, lastFrame);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold); CHECK_PROPERTY_CHANGE(PROP_ANIMATION_HOLD, hold);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_ALLOW_TRANSLATION, allowTranslation);
return changedProperties; return changedProperties;
} }
void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const { void AnimationPropertyGroup::getProperties(EntityItemProperties& properties) const {
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, URL, getURL);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, AllowTranslation, getAllowTranslation);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, FPS, getFPS);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, CurrentFrame, getCurrentFrame);
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Animation, Running, getRunning);
@ -247,6 +266,7 @@ bool AnimationPropertyGroup::setProperties(const EntityItemProperties& propertie
bool somethingChanged = false; bool somethingChanged = false;
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, URL, url, setURL);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, AllowTranslation, allowTranslation, setAllowTranslation);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, FPS, fps, setFPS);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, CurrentFrame, currentFrame, setCurrentFrame);
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Animation, Running, running, setRunning);
@ -268,6 +288,7 @@ EntityPropertyFlags AnimationPropertyGroup::getEntityProperties(EncodeBitstreamP
requestedProperties += PROP_ANIMATION_FIRST_FRAME; requestedProperties += PROP_ANIMATION_FIRST_FRAME;
requestedProperties += PROP_ANIMATION_LAST_FRAME; requestedProperties += PROP_ANIMATION_LAST_FRAME;
requestedProperties += PROP_ANIMATION_HOLD; requestedProperties += PROP_ANIMATION_HOLD;
requestedProperties += PROP_ANIMATION_ALLOW_TRANSLATION;
return requestedProperties; return requestedProperties;
} }
@ -283,6 +304,7 @@ void AnimationPropertyGroup::appendSubclassData(OctreePacketData* packetData, En
bool successPropertyFits = true; bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, getURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, getAllowTranslation());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, getFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, getCurrentFrame());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning()); APPEND_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, getRunning());
@ -301,6 +323,7 @@ int AnimationPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char
const unsigned char* dataAt = data; const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL); READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setURL);
READ_ENTITY_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, bool, setAllowTranslation);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setFPS);
READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setCurrentFrame);
READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setRunning);

View file

@ -85,6 +85,7 @@ public:
DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame DEFINE_PROPERTY(PROP_ANIMATION_FIRST_FRAME, FirstFrame, firstFrame, float, 0.0f); // was animationSettings.firstFrame
DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame DEFINE_PROPERTY(PROP_ANIMATION_LAST_FRAME, LastFrame, lastFrame, float, MAXIMUM_POSSIBLE_FRAME); // was animationSettings.lastFrame
DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold DEFINE_PROPERTY(PROP_ANIMATION_HOLD, Hold, hold, bool, false); // was animationSettings.hold
DEFINE_PROPERTY(PROP_ANIMATION_ALLOW_TRANSLATION, AllowTranslation, allowTranslation, bool, true);
protected: protected:
friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b); friend bool operator==(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b);

View file

@ -1033,6 +1033,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_FIRST_FRAME, Animation, animation, FirstFrame, firstFrame);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_LAST_FRAME, Animation, animation, LastFrame, lastFrame);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold); ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_HOLD, Animation, animation, Hold, hold);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color);
ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url);

View file

@ -40,6 +40,7 @@ enum EntityPropertyList {
PROP_ANIMATION_FPS, PROP_ANIMATION_FPS,
PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_FRAME_INDEX,
PROP_ANIMATION_PLAYING, PROP_ANIMATION_PLAYING,
PROP_ANIMATION_ALLOW_TRANSLATION,
// these properties are supported by the EntityItem base class // these properties are supported by the EntityItem base class
PROP_REGISTRATION_POINT, PROP_REGISTRATION_POINT,

View file

@ -330,6 +330,10 @@ void ModelEntityItem::setAnimationSettings(const QString& value) {
setAnimationHold(hold); setAnimationHold(hold);
} }
if (settingsMap.contains("allowTranslation")) {
bool allowTranslation = settingsMap["allowTranslation"].toBool();
setAnimationAllowTranslation(allowTranslation);
}
_dirtyFlags |= Simulation::DIRTY_UPDATEABLE; _dirtyFlags |= Simulation::DIRTY_UPDATEABLE;
} }

View file

@ -83,7 +83,10 @@ public:
void setAnimationCurrentFrame(float value); void setAnimationCurrentFrame(float value);
void setAnimationIsPlaying(bool value); void setAnimationIsPlaying(bool value);
void setAnimationFPS(float value); void setAnimationFPS(float value);
void setAnimationAllowTranslation(bool value) { _animationProperties.setAllowTranslation(value); };
bool getAnimationAllowTranslation() const { return _animationProperties.getAllowTranslation(); };
void setAnimationLoop(bool loop); void setAnimationLoop(bool loop);
bool getAnimationLoop() const; bool getAnimationLoop() const;

View file

@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityEdit: case PacketType::EntityEdit:
case PacketType::EntityData: case PacketType::EntityData:
case PacketType::EntityPhysics: case PacketType::EntityPhysics:
return VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE; return VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES;
case PacketType::EntityQuery: case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree); return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
case PacketType::AvatarIdentity: case PacketType::AvatarIdentity:

View file

@ -260,6 +260,7 @@ const PacketVersion VERSION_ENTITIES_HINGE_CONSTRAINT = 69;
const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70; const PacketVersion VERSION_ENTITIES_BULLET_DYNAMICS = 70;
const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71;
const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72; const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72;
const PacketVersion VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES = 73;
enum class EntityQueryPacketVersion: PacketVersion { enum class EntityQueryPacketVersion: PacketVersion {
JSONFilter = 18, JSONFilter = 18,

View file

@ -92,6 +92,7 @@ void EntityMotionState::updateServerPhysicsVariables() {
Transform localTransform; Transform localTransform;
_entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity); _entity->getLocalTransformAndVelocities(localTransform, _serverVelocity, _serverAngularVelocity);
_serverVariablesSet = true;
_serverPosition = localTransform.getTranslation(); _serverPosition = localTransform.getTranslation();
_serverRotation = localTransform.getRotation(); _serverRotation = localTransform.getRotation();
_serverAcceleration = _entity->getAcceleration(); _serverAcceleration = _entity->getAcceleration();
@ -99,18 +100,19 @@ void EntityMotionState::updateServerPhysicsVariables() {
} }
void EntityMotionState::handleDeactivation() { void EntityMotionState::handleDeactivation() {
// copy _server data to entity if (_serverVariablesSet) {
bool success; // copy _server data to entity
_entity->setPosition(_serverPosition, success, false); Transform localTransform = _entity->getLocalTransform();
_entity->setOrientation(_serverRotation, success, false); localTransform.setTranslation(_serverPosition);
_entity->setVelocity(ENTITY_ITEM_ZERO_VEC3); localTransform.setRotation(_serverRotation);
_entity->setAngularVelocity(ENTITY_ITEM_ZERO_VEC3); _entity->setLocalTransformAndVelocities(localTransform, ENTITY_ITEM_ZERO_VEC3, ENTITY_ITEM_ZERO_VEC3);
// and also to RigidBody // and also to RigidBody
btTransform worldTrans; btTransform worldTrans;
worldTrans.setOrigin(glmToBullet(_serverPosition)); worldTrans.setOrigin(glmToBullet(_entity->getPosition()));
worldTrans.setRotation(glmToBullet(_serverRotation)); worldTrans.setRotation(glmToBullet(_entity->getRotation()));
_body->setWorldTransform(worldTrans); _body->setWorldTransform(worldTrans);
// no need to update velocities... should already be zero // no need to update velocities... should already be zero
}
} }
// virtual // virtual
@ -339,6 +341,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
// if we've never checked before, our _lastStep will be 0, and we need to initialize our state // if we've never checked before, our _lastStep will be 0, and we need to initialize our state
if (_lastStep == 0) { if (_lastStep == 0) {
btTransform xform = _body->getWorldTransform(); btTransform xform = _body->getWorldTransform();
_serverVariablesSet = true;
_serverPosition = worldToLocal.transform(bulletToGLM(xform.getOrigin())); _serverPosition = worldToLocal.transform(bulletToGLM(xform.getOrigin()));
_serverRotation = worldToLocal.getRotation() * bulletToGLM(xform.getRotation()); _serverRotation = worldToLocal.getRotation() * bulletToGLM(xform.getRotation());
_serverVelocity = worldVelocityToLocal.transform(getBodyLinearVelocityGTSigma()); _serverVelocity = worldVelocityToLocal.transform(getBodyLinearVelocityGTSigma());

View file

@ -107,6 +107,7 @@ protected:
// Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid. // Meanwhile we also keep a raw EntityItem* for internal stuff where the pointer is guaranteed valid.
EntityItem* _entity; EntityItem* _entity;
bool _serverVariablesSet { false };
glm::vec3 _serverPosition; // in simulation-frame (not world-frame) glm::vec3 _serverPosition; // in simulation-frame (not world-frame)
glm::quat _serverRotation; glm::quat _serverRotation;
glm::vec3 _serverVelocity; glm::vec3 _serverVelocity;

View file

@ -827,10 +827,10 @@ AnimationDetails::AnimationDetails() :
} }
AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop, AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame) : bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame, bool allowTranslation) :
role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold), role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold),
startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame), startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame),
running(running), currentFrame(currentFrame) { running(running), currentFrame(currentFrame), allowTranslation(allowTranslation) {
} }
@ -847,6 +847,7 @@ QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const Animatio
obj.setProperty("lastFrame", details.lastFrame); obj.setProperty("lastFrame", details.lastFrame);
obj.setProperty("running", details.running); obj.setProperty("running", details.running);
obj.setProperty("currentFrame", details.currentFrame); obj.setProperty("currentFrame", details.currentFrame);
obj.setProperty("allowTranslation", details.allowTranslation);
return obj; return obj;
} }

View file

@ -197,7 +197,7 @@ class AnimationDetails {
public: public:
AnimationDetails(); AnimationDetails();
AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop, AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop,
bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame); bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame, bool allowTranslation);
QString role; QString role;
QUrl url; QUrl url;
@ -210,6 +210,7 @@ public:
float lastFrame; float lastFrame;
bool running; bool running;
float currentFrame; float currentFrame;
bool allowTranslation;
}; };
Q_DECLARE_METATYPE(AnimationDetails); Q_DECLARE_METATYPE(AnimationDetails);
QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event); QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event);

View file

@ -706,7 +706,7 @@ void SpatiallyNestable::setSNScale(const glm::vec3& scale, bool& success) {
} }
} }
const Transform SpatiallyNestable::getLocalTransform() const { Transform SpatiallyNestable::getLocalTransform() const {
Transform result; Transform result;
_transformLock.withReadLock([&] { _transformLock.withReadLock([&] {
result =_transform; result =_transform;

View file

@ -122,7 +122,7 @@ public:
virtual glm::vec3 getSNScale(int jointIndex, bool& success) const; virtual glm::vec3 getSNScale(int jointIndex, bool& success) const;
// object's parent's frame // object's parent's frame
virtual const Transform getLocalTransform() const; virtual Transform getLocalTransform() const;
virtual void setLocalTransform(const Transform& transform); virtual void setLocalTransform(const Transform& transform);
virtual glm::vec3 getLocalPosition() const; virtual glm::vec3 getLocalPosition() const;

View file

@ -56,6 +56,7 @@
var isHmdPreviewDisabled = true; var isHmdPreviewDisabled = true;
function fromQml(message) { function fromQml(message) {
switch (message.method) { switch (message.method) {
case 'passphrasePopup_cancelClicked':
case 'walletSetup_cancelClicked': case 'walletSetup_cancelClicked':
case 'needsLogIn_cancelClicked': case 'needsLogIn_cancelClicked':
tablet.gotoHomeScreen(); tablet.gotoHomeScreen();

View file

@ -446,6 +446,10 @@
<input type="checkbox" id="property-model-animation-hold"> <input type="checkbox" id="property-model-animation-hold">
<label for="property-model-animation-hold">Animation hold</label> <label for="property-model-animation-hold">Animation hold</label>
</div> </div>
<div class="property checkbox indent">
<input type="checkbox" id="property-model-animation-allow-translation">
<label for="property-model-animation-allow-translation">Animation Allow Translation</label>
</div>
<div id="animation-fps" class="property number"> <div id="animation-fps" class="property number">
<label>Animation FPS</label> <label>Animation FPS</label>
<input type="number" id="property-model-animation-fps"> <input type="number" id="property-model-animation-fps">
@ -642,10 +646,6 @@
</div> </div>
</fieldset> </fieldset>
<fieldset id="polyvox" class="major"> <fieldset id="polyvox" class="major">
<legend class="section-header spatial-group poly-vox-section property xyz"> <legend class="section-header spatial-group poly-vox-section property xyz">
Voxel volume size <span>m</span> Voxel volume size <span>m</span>

View file

@ -611,6 +611,7 @@ function loaded() {
var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame"); var elModelAnimationLastFrame = document.getElementById("property-model-animation-last-frame");
var elModelAnimationLoop = document.getElementById("property-model-animation-loop"); var elModelAnimationLoop = document.getElementById("property-model-animation-loop");
var elModelAnimationHold = document.getElementById("property-model-animation-hold"); var elModelAnimationHold = document.getElementById("property-model-animation-hold");
var elModelAnimationAllowTranslation = document.getElementById("property-model-animation-allow-translation");
var elModelTextures = document.getElementById("property-model-textures"); var elModelTextures = document.getElementById("property-model-textures");
var elModelOriginalTextures = document.getElementById("property-model-original-textures"); var elModelOriginalTextures = document.getElementById("property-model-original-textures");
@ -926,6 +927,7 @@ function loaded() {
elModelAnimationLastFrame.value = properties.animation.lastFrame; elModelAnimationLastFrame.value = properties.animation.lastFrame;
elModelAnimationLoop.checked = properties.animation.loop; elModelAnimationLoop.checked = properties.animation.loop;
elModelAnimationHold.checked = properties.animation.hold; elModelAnimationHold.checked = properties.animation.hold;
elModelAnimationAllowTranslation.checked = properties.animation.allowTranslation;
elModelTextures.value = properties.textures; elModelTextures.value = properties.textures;
setTextareaScrolling(elModelTextures); setTextareaScrolling(elModelTextures);
elModelOriginalTextures.value = properties.originalTextures; elModelOriginalTextures.value = properties.originalTextures;
@ -1276,6 +1278,7 @@ function loaded() {
elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame')); elModelAnimationLastFrame.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('animation', 'lastFrame'));
elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop')); elModelAnimationLoop.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'loop'));
elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold')); elModelAnimationHold.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'hold'));
elModelAnimationAllowTranslation.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('animation', 'allowTranslation'));
elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures')); elModelTextures.addEventListener('change', createEmitTextPropertyUpdateFunction('textures'));

View file

@ -27,7 +27,7 @@
var isPreparing = false; // Explicitly track download request status. 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 confirmAllPurchases = false; // Set this to "true" to cause Checkout.qml to popup for all items, even if free
function injectCommonCode(isDirectoryPage) { function injectCommonCode(isDirectoryPage) {
// Supporting styles from marketplaces.css. // Supporting styles from marketplaces.css.
@ -122,7 +122,7 @@
function injectBuyButtonOnMainPage() { function injectBuyButtonOnMainPage() {
var cost; var cost;
$('.grid-item').find('#price-or-edit').find('a').each(function() { $('.grid-item').find('#price-or-edit').find('a').each(function() {
$(this).attr('data-href', $(this).attr('href')); $(this).attr('data-href', $(this).attr('href'));
$(this).attr('href', '#'); $(this).attr('href', '#');
@ -130,16 +130,16 @@
$(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6');
$(this).closest('.col-xs-3').attr("class", 'col-xs-6'); $(this).closest('.col-xs-3').attr("class", 'col-xs-6');
if (parseInt(cost) > 0) { if (parseInt(cost) > 0) {
var priceElement = $(this).find('.price') var priceElement = $(this).find('.price')
priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" }); priceElement.css({ "width": "auto", "padding": "3px 5px", "height": "26px" });
priceElement.text(parseFloat(cost / 100).toFixed(2) + ' HFC'); priceElement.text(cost + ' HFC');
priceElement.css({ "min-width": priceElement.width() + 10 }); priceElement.css({ "min-width": priceElement.width() + 10 });
} }
}); });
$('.grid-item').find('#price-or-edit').find('a').on('click', function () { $('.grid-item').find('#price-or-edit').find('a').on('click', function () {
buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'),
$(this).closest('.grid-item').find('.item-title').text(), $(this).closest('.grid-item').find('.item-title').text(),
@ -175,11 +175,12 @@
if (confirmAllPurchases) { if (confirmAllPurchases) {
var href = $('#side-info').find('.btn').first().attr('href'); var href = $('#side-info').find('.btn').first().attr('href');
$('#side-info').find('.btn').first().attr('href', '#'); $('#side-info').find('.btn').first().attr('href', '#');
var cost = $('.item-cost').text(); var cost = $('.item-cost').text();
if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { 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: ' + (parseFloat(cost / 100).toFixed(2)) + ' HFC'); $('#side-info').find('.btn').first().html('<span class="glyphicon glyphicon-download" id="buyItemButton"></span>Own Item: ' + cost + ' HFC');
} }
$('#side-info').find('.btn').first().on('click', function () { $('#side-info').find('.btn').first().on('click', function () {
@ -264,7 +265,7 @@
// Reference: https://clara.io/learn/sdk/api/export // Reference: https://clara.io/learn/sdk/api/export
//var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&centerScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true&centerScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL";
// 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to
// be successful in generating zip files. // be successful in generating zip files.
var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL";
@ -447,7 +448,7 @@
cancelClaraDownload(); cancelClaraDownload();
} else { } else {
var parsedJsonMessage = JSON.parse(message); var parsedJsonMessage = JSON.parse(message);
if (parsedJsonMessage.type === "marketplaces") { if (parsedJsonMessage.type === "marketplaces") {
if (parsedJsonMessage.action === "inspectionModeSetting") { if (parsedJsonMessage.action === "inspectionModeSetting") {
confirmAllPurchases = !!parsedJsonMessage.data; confirmAllPurchases = !!parsedJsonMessage.data;

View file

@ -197,6 +197,7 @@
// Description: // Description:
// -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML // -Called when a message is received from Checkout.qml. The "message" argument is what is sent from the Checkout QML
// in the format "{method, params}", like json-rpc. // in the format "{method, params}", like json-rpc.
var isHmdPreviewDisabled = true;
function fromQml(message) { function fromQml(message) {
switch (message.method) { switch (message.method) {
case 'checkout_setUpClicked': case 'checkout_setUpClicked':
@ -231,12 +232,20 @@
case 'purchases_goToMarketplaceClicked': case 'purchases_goToMarketplaceClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
case 'passphrasePopup_cancelClicked':
case 'needsLogIn_cancelClicked': case 'needsLogIn_cancelClicked':
tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL);
break; break;
case 'needsLogIn_loginClicked': case 'needsLogIn_loginClicked':
openLoginWindow(); openLoginWindow();
break; break;
case 'disableHmdPreview':
isHmdPreviewDisabled = Menu.isOptionChecked("Disable Preview");
Menu.setIsOptionChecked("Disable Preview", true);
break;
case 'maybeEnableHmdPreview':
Menu.setIsOptionChecked("Disable Preview", isHmdPreviewDisabled);
break;
default: default:
print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message)); print('Unrecognized message from Checkout.qml or Purchases.qml: ' + JSON.stringify(message));
} }

View file

@ -0,0 +1,168 @@
(function () {
var FORCE_DROP_CHANNEL = "Hifi-Hand-Drop";
var proxInterval,
proxTimeout;
var _entityID;
this.preload = function (entityID) {
_entityID = entityID;
Entities.editEntity(_entityID, {
userData: '{"grabbableKey": {"grabbable": true}'
});
};
var particleTrailEntity = null;
function particleTrail() {
var props = {
type: 'ParticleEffect',
name: 'Particle',
parentID: _entityID,
isEmitting: true,
lifespan: 2.0,
maxParticles: 100,
textures: 'https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png',
emitRate: 50,
emitSpeed: 0,
emitterShouldTrail: true,
particleRadius: 0,
radiusSpread: 0,
radiusStart: .2,
radiusFinish: 0.1,
color: {
red: 201,
blue: 201,
green: 34
},
accelerationSpread: {
x: 0,
y: 0,
z: 0
},
alpha: 0,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: 0,
polarStart: 0,
polarFinish: 0,
azimuthStart: -180,
azimuthFinish: 180
};
particleTrailEntity = Entities.addEntity(props);
}
function particleExplode() {
var entPos = Entities.getEntityProperties(_entityID, 'position').position;
var props = {
type: 'ParticleEffect',
name: 'Particle',
parentID: _entityID,
isEmitting: true,
lifespan: 2,
maxParticles: 10,
position: entPos,
textures: 'https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png',
emitRate: 1,
emitSpeed: 0,
emitterShouldTrail: false,
particleRadius: 1,
radiusSpread: 0,
radiusStart: 0,
radiusFinish: 1,
color: {
red: 232,
blue: 232,
green: 26
},
emitAcceleration: {
x: 0,
y: 0,
z: 0
},
alpha: 0,
alphaSpread: 0,
alphaStart: 1,
alphaFinish: .5,
polarStart: 0,
polarFinish: 0,
azimuthStart: -180,
azimuthFinish: 180
};
var explosionParticles = Entities.addEntity(props);
Entities.editEntity(_entityID, {
velocity: Vec3.ZERO,
dynamic: false
});
Script.setTimeout(function () {
Entities.deleteEntity(explosionParticles);
Entities.editEntity(_entityID, {
dynamic: true
})
}, 500);
}
function clearProxCheck() {
if (proxInterval) {
Script.clearInterval(proxInterval);
Entities.deleteEntity(particleTrailEntity);
particleTrailEntity = null;
}
if (proxTimeout) {
Script.clearTimeout(proxTimeout);
}
}
function proxCheck() {
var ballPos = Entities.getEntityProperties(_entityID, ['position']).position;
var isAnyAvatarInRange = AvatarList.isAvatarInRange(ballPos, 1);
if (isAnyAvatarInRange) {
clearProxCheck();
particleExplode();
}
}
this.startDistanceGrab = function (thisEntityID, triggerHandAndAvatarUUIDArray) {
clearProxCheck();
var triggerHand = triggerHandAndAvatarUUIDArray[0];
var avatarUUID = triggerHandAndAvatarUUIDArray[1];
var ballPos = Entities.getEntityProperties(_entityID, ['position']).position;
var MAX_DISTANCE_GRAB = 2; //meter
if (Vec3.distance(ballPos, AvatarList.getAvatar(avatarUUID).position) > MAX_DISTANCE_GRAB) {
Messages.sendMessage(FORCE_DROP_CHANNEL, triggerHand, true);
}
};
this.startNearGrab = function (thisEntityID, triggerHandAndAvatarUUIDArray) {
clearProxCheck();
};
this.releaseGrab = function (thisEntityID) {
if (particleTrailEntity === null) {
particleTrail();
}
Script.setTimeout(function () {
proxInterval = Script.setInterval(proxCheck, 50);
}, 200); // Setting a delay to give it time to leave initial avatar without proc.
proxTimeout = Script.setTimeout(function () {
clearProxCheck();
}, 10000)
};
this.collisionWithEntity = function (thisEntityID, collisionEntityID, collisionInfo) {
clearProxCheck();
};
});