diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e3df955ba..64b18ec522 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2526,6 +2526,11 @@ bool Application::event(QEvent* event) { isPaintingThrottled = false; + return true; + } else if ((int)event->type() == (int)Idle) { + float nsecsElapsed = (float)_lastTimeUpdated.nsecsElapsed(); + idle(nsecsElapsed); + return true; } @@ -6491,10 +6496,22 @@ void Application::activeChanged(Qt::ApplicationState state) { } void Application::windowMinimizedChanged(bool minimized) { + // initialize the _minimizedWindowTimer + static std::once_flag once; + std::call_once(once, [&] { + connect(&_minimizedWindowTimer, &QTimer::timeout, this, [] { + QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(static_cast(Idle)), Qt::HighEventPriority); + }); + }); + + // avoid rendering to the display plugin but continue posting Idle events, + // so that physics continues to simulate and the deadlock watchdog knows we're alive if (!minimized && !getActiveDisplayPlugin()->isActive()) { + _minimizedWindowTimer.stop(); getActiveDisplayPlugin()->activate(); } else if (minimized && getActiveDisplayPlugin()->isActive()) { getActiveDisplayPlugin()->deactivate(); + _minimizedWindowTimer.start(THROTTLED_SIM_FRAME_PERIOD_MS); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 041f1f8930..86b2335e62 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -138,6 +138,7 @@ public: enum Event { Present = DisplayPlugin::Present, Paint = Present + 1, + Idle = Paint + 1, Lambda = Paint + 1 }; @@ -536,6 +537,7 @@ private: RateCounter<> _avatarSimCounter; RateCounter<> _simCounter; + QTimer _minimizedWindowTimer; QElapsedTimer _timerStart; QElapsedTimer _lastTimeUpdated; diff --git a/interface/src/ui/SnapshotUploader.cpp b/interface/src/ui/SnapshotUploader.cpp index aa37608476..3408cb8512 100644 --- a/interface/src/ui/SnapshotUploader.cpp +++ b/interface/src/ui/SnapshotUploader.cpp @@ -31,6 +31,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { auto dataObject = doc.object().value("data").toObject(); QString thumbnailUrl = dataObject.value("thumbnail_url").toString(); QString imageUrl = dataObject.value("image_url").toString(); + QString snapshotID = dataObject.value("id").toString(); auto addressManager = DependencyManager::get(); QString placeName = _inWorldLocation.authority(); // We currently only upload shareable places, in which case this is just host. QString currentPath = _inWorldLocation.path(); @@ -43,6 +44,7 @@ void SnapshotUploader::uploadSuccess(QNetworkReply& reply) { if (dataObject.contains("shareable_url")) { detailsObject.insert("shareable_url", dataObject.value("shareable_url").toString()); } + detailsObject.insert("snapshot_id", snapshotID); QString pickledDetails = QJsonDocument(detailsObject).toJson(); userStoryObject.insert("details", pickledDetails); userStoryObject.insert("thumbnail_url", thumbnailUrl); diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 9b23b4e695..3b8ae508eb 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -324,11 +324,11 @@ public: void reset() override { } protected: - std::shared_ptr maybeOpenFile(); + std::shared_ptr maybeOpenFile() const; - std::mutex _cacheFileCreateMutex; - std::mutex _cacheFileWriteMutex; - std::weak_ptr _cacheFile; + mutable std::mutex _cacheFileCreateMutex; + mutable std::mutex _cacheFileWriteMutex; + mutable std::weak_ptr _cacheFile; std::string _filename; std::atomic _minMipLevelAvailable; diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index efff6c7afe..d2f93c0036 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -128,7 +128,7 @@ KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { } } -std::shared_ptr KtxStorage::maybeOpenFile() { +std::shared_ptr KtxStorage::maybeOpenFile() const { std::shared_ptr file = _cacheFile.lock(); if (file) { return file; @@ -154,7 +154,8 @@ PixelsPointer KtxStorage::getMipFace(uint16 level, uint8 face) const { auto faceOffset = _ktxDescriptor->getMipFaceTexelsOffset(level, face); auto faceSize = _ktxDescriptor->getMipFaceTexelsSize(level, face); if (faceSize != 0 && faceOffset != 0) { - result = std::make_shared(_filename.c_str())->createView(faceSize, faceOffset)->toMemoryStorage(); + auto file = maybeOpenFile(); + result = file->createView(faceSize, faceOffset)->toMemoryStorage(); } return result; } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index be3bfcc0e9..7745766177 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -334,6 +334,22 @@ private: int _maxNumPixels; }; +NetworkTexture::~NetworkTexture() { + if (_ktxHeaderRequest || _ktxMipRequest) { + if (_ktxHeaderRequest) { + _ktxHeaderRequest->disconnect(this); + _ktxHeaderRequest->deleteLater(); + _ktxHeaderRequest = nullptr; + } + if (_ktxMipRequest) { + _ktxMipRequest->disconnect(this); + _ktxMipRequest->deleteLater(); + _ktxMipRequest = nullptr; + } + TextureCache::requestCompleted(_self); + } +} + const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max(); void NetworkTexture::makeRequest() { if (!_sourceIsKTX) { @@ -398,13 +414,18 @@ void NetworkTexture::startRequestForNextMipLevel() { } if (_ktxResourceState == WAITING_FOR_MIP_REQUEST) { + auto self = _self.lock(); + if (!self) { + return; + } + _ktxResourceState = PENDING_MIP_REQUEST; init(); float priority = -(float)_originalKtxDescriptor->header.numberOfMipmapLevels + (float)_lowestKnownPopulatedMip; setLoadPriority(this, priority); _url.setFragment(QString::number(_lowestKnownPopulatedMip - 1)); - TextureCache::attemptRequest(_self); + TextureCache::attemptRequest(self); } } @@ -473,19 +494,16 @@ void NetworkTexture::ktxMipRequestFinished() { texture->assignStoredMip(_ktxMipLevelRangeInFlight.first, _ktxMipRequest->getData().size(), reinterpret_cast(_ktxMipRequest->getData().data())); _lowestKnownPopulatedMip = _textureSource->getGPUTexture()->minAvailableMipLevel(); - } - else { + } else { qWarning(networking) << "Trying to update mips but texture is null"; } finishedLoading(true); _ktxResourceState = WAITING_FOR_MIP_REQUEST; - } - else { + } else { finishedLoading(false); if (handleFailedRequest(_ktxMipRequest->getResult())) { _ktxResourceState = PENDING_MIP_REQUEST; - } - else { + } else { qWarning(networking) << "Failed to load mip: " << _url; _ktxResourceState = FAILED_TO_LOAD; } @@ -497,8 +515,7 @@ void NetworkTexture::ktxMipRequestFinished() { if (_ktxResourceState == WAITING_FOR_MIP_REQUEST && _lowestRequestedMipLevel < _lowestKnownPopulatedMip) { startRequestForNextMipLevel(); } - } - else { + } else { qWarning() << "Mip request finished in an unexpected state: " << _ktxResourceState; } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 1e61b9ecee..c7a7799216 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -46,6 +46,7 @@ class NetworkTexture : public Resource, public Texture { public: NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels); + ~NetworkTexture() override; QString getType() const override { return "NetworkTexture"; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d4c7d63ee5..51c8d8554a 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -344,7 +344,7 @@ class Resource : public QObject { public: Resource(const QUrl& url); - ~Resource(); + virtual ~Resource(); virtual QString getType() const { return "Resource"; } diff --git a/scripts/modules/request.js b/scripts/modules/request.js new file mode 100644 index 0000000000..c7bf98d815 --- /dev/null +++ b/scripts/modules/request.js @@ -0,0 +1,82 @@ +"use strict"; + +// request.js +// +// Created by Cisco Fresquet on 04/24/2017. +// 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 +// + +/* global module */ +// @module request +// +// This module contains the `request` module implementation + +// =========================================================================================== +module.exports = { + + // ------------------------------------------------------------------ + request: function (options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. + var httpRequest = new XMLHttpRequest(), key; + // QT bug: apparently doesn't handle onload. Workaround using readyState. + httpRequest.onreadystatechange = function () { + var READY_STATE_DONE = 4; + var HTTP_OK = 200; + if (httpRequest.readyState >= READY_STATE_DONE) { + var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, + response = !error && httpRequest.responseText, + contentType = !error && httpRequest.getResponseHeader('content-type'); + if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. + try { + response = JSON.parse(response); + } catch (e) { + error = e; + } + } + if (error) { + response = { statusCode: httpRequest.status }; + } + callback(error, response); + } + }; + if (typeof options === 'string') { + options = { uri: options }; + } + if (options.url) { + options.uri = options.url; + } + if (!options.method) { + options.method = 'GET'; + } + if (options.body && (options.method === 'GET')) { // add query parameters + var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; + for (key in options.body) { + if (options.body.hasOwnProperty(key)) { + params.push(key + '=' + options.body[key]); + } + } + options.uri += appender + params.join('&'); + delete options.body; + } + if (options.json) { + options.headers = options.headers || {}; + options.headers["Content-type"] = "application/json"; + options.body = JSON.stringify(options.body); + } + for (key in options.headers || {}) { + if (options.headers.hasOwnProperty(key)) { + httpRequest.setRequestHeader(key, options.headers[key]); + } + } + httpRequest.open(options.method, options.uri, true); + httpRequest.send(options.body); + } +}; + +// =========================================================================================== +// @function - debug logging +function debug() { + print('RequestModule | ' + [].slice.call(arguments).join(' ')); +} diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ec70b0b1c8..db0840b078 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -14,7 +14,7 @@ /* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings, Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, - setGrabCommunications, Menu, HMD, isInEditMode */ + setGrabCommunications, Menu, HMD, isInEditMode, AvatarList */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -75,7 +75,6 @@ var WEB_TOUCH_Y_OFFSET = 0.05; // how far forward (or back with a negative numbe // // distant manipulation // -var linearTimeScale = 0; var DISTANCE_HOLDING_RADIUS_FACTOR = 3.5; // multiplied by distance between hand and object var DISTANCE_HOLDING_ACTION_TIMEFRAME = 0.1; // how quickly objects move to their new position var DISTANCE_HOLDING_UNITY_MASS = 1200; // The mass at which the distance holding action timeframe is unmodified @@ -155,7 +154,6 @@ var INCHES_TO_METERS = 1.0 / 39.3701; // these control how long an abandoned pointer line or action will hang around var ACTION_TTL = 15; // seconds -var ACTION_TTL_ZERO = 0; // seconds var ACTION_TTL_REFRESH = 5; var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; @@ -193,7 +191,6 @@ var FORBIDDEN_GRAB_TYPES = ["Unknown", "Light", "PolyLine", "Zone"]; var holdEnabled = true; var nearGrabEnabled = true; var farGrabEnabled = true; -var farToNearGrab = false; var myAvatarScalingEnabled = true; var objectScalingEnabled = true; var mostRecentSearchingHand = RIGHT_HAND; @@ -300,14 +297,13 @@ function getFingerWorldLocation(hand) { // Object assign polyfill if (typeof Object.assign != 'function') { Object.assign = function(target, varArgs) { - 'use strict'; - if (target == null) { + if (target === null) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; - if (nextSource != null) { + if (nextSource !== null) { for (var nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; @@ -801,7 +797,7 @@ function calculateNearestStylusTarget(stylusTargets) { } return nearestStylusTarget; -}; +} // EntityPropertiesCache is a helper class that contains a cache of entity properties. // the hope is to prevent excess calls to Entity.getEntityProperties() @@ -1229,8 +1225,9 @@ function MyController(hand) { newState !== STATE_OVERLAY_LASER_TOUCHING)) { return; } - setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_DISTANCE_ROTATING) - || (newState === STATE_NEAR_GRABBING)); + setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || + (newState === STATE_DISTANCE_ROTATING) || + (newState === STATE_NEAR_GRABBING)); if (WANT_DEBUG || WANT_DEBUG_STATE) { var oldStateName = stateToName(this.state); var newStateName = stateToName(newState); @@ -1428,7 +1425,7 @@ function MyController(hand) { if (PICK_WITH_HAND_RAY) { this.overlayLineOff(); } - } + }; this.otherGrabbingLineOn = function(avatarPosition, entityPosition, color) { if (this.otherGrabbingLine === null) { @@ -1641,11 +1638,6 @@ function MyController(hand) { var tipPosition = this.stylusTip.position; - var candidates = { - entities: [], - overlays: [] - }; - // build list of stylus targets, near the stylusTip var stylusTargets = []; var candidateEntities = Entities.findEntities(tipPosition, WEB_DISPLAY_STYLUS_DISTANCE); @@ -1972,9 +1964,10 @@ function MyController(hand) { var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME); var otherHandControllerState = this.getOtherHandController().state; - var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING - || otherHandControllerState === STATE_DISTANCE_HOLDING || otherHandControllerState === STATE_DISTANCE_ROTATING) - && this.getOtherHandController().grabbedThingID === hotspot.entityID); + var okToEquipFromOtherHand = ((otherHandControllerState === STATE_NEAR_GRABBING || + otherHandControllerState === STATE_DISTANCE_HOLDING || + otherHandControllerState === STATE_DISTANCE_ROTATING) && + this.getOtherHandController().grabbedThingID === hotspot.entityID); var hasParent = true; if (props.parentID === NULL_UUID) { hasParent = false; @@ -1999,7 +1992,7 @@ function MyController(hand) { return entityProps.cloneable; } return false; - } + }; this.entityIsGrabbable = function(entityID) { var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID); var props = entityPropertiesCache.getProps(entityID); @@ -2720,7 +2713,8 @@ function MyController(hand) { var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); - var candidateHotSpotEntities = Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); + var candidateHotSpotEntities = + Entities.findEntities(controllerLocation.position,MAX_FAR_TO_NEAR_EQUIP_HOTSPOT_RADIUS); entityPropertiesCache.addEntities(candidateHotSpotEntities); var potentialEquipHotspot = this.chooseBestEquipHotspotForFarToNearEquip(candidateHotSpotEntities); @@ -2730,40 +2724,23 @@ function MyController(hand) { this.grabbedThingID = potentialEquipHotspot.entityID; this.grabbedIsOverlay = false; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO - }); - - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedThingID).name + "'"); return; } } var rayPositionOnEntity = Vec3.subtract(grabbedProperties.position, this.offsetPosition); //Far to Near Grab: If object is draw by user inside FAR_TO_NEAR_GRAB_MAX_DISTANCE, grab it - if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, controllerLocation.position, FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { + if (this.entityIsFarToNearGrabbable(rayPositionOnEntity, + controllerLocation.position, + FAR_TO_NEAR_GRAB_MAX_DISTANCE)) { this.farToNearGrab = true; - var success = Entities.updateAction(this.grabbedThingID, this.actionID, { - targetPosition: newTargetPosition, - linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - targetRotation: this.currentObjectRotation, - angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject), - ttl: ACTION_TTL_ZERO // Overriding ACTION_TTL,Assign ACTION_TTL_ZERO so that the object is dropped down immediately after the trigger is released. - }); - if (success) { - this.actionTimeout = now + (ACTION_TTL_ZERO * MSECS_PER_SEC); - } else { - print("continueDistanceHolding -- updateAction failed"); - } + Entities.deleteAction(this.grabbedThingID, this.actionID); + this.actionID = null; + this.setState(STATE_NEAR_GRABBING , "near grab entity '" + this.grabbedThingID + "'"); return; } @@ -2844,7 +2821,7 @@ function MyController(hand) { COLORS_GRAB_DISTANCE_HOLD, this.grabbedThingID); this.previousWorldControllerRotation = worldControllerRotation; - } + }; this.setupHoldAction = function() { this.actionID = Entities.addAction("hold", this.grabbedThingID, { @@ -3043,15 +3020,14 @@ function MyController(hand) { var worldEntities = Entities.findEntities(MyAvatar.position, 50); var count = 0; worldEntities.forEach(function(item) { - var item = Entities.getEntityProperties(item, ["name"]); - if (item.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { + var itemWE = Entities.getEntityProperties(item, ["name"]); + if (itemWE.name.indexOf('-clone-' + grabbedProperties.id) !== -1) { count++; } - }) + }); var limit = grabInfo.cloneLimit ? grabInfo.cloneLimit : 0; if (count >= limit && limit !== 0) { - delete limit; return; } @@ -3067,7 +3043,7 @@ function MyController(hand) { delete cUserData.grabbableKey.cloneable; delete cUserData.grabbableKey.cloneDynamic; delete cUserData.grabbableKey.cloneLimit; - delete cProperties.id + delete cProperties.id; cProperties.dynamic = dynamic; cProperties.locked = false; @@ -3133,7 +3109,7 @@ function MyController(hand) { _this.currentAngularVelocity = ZERO_VEC; _this.prevDropDetected = false; - } + }; if (isClone) { // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. @@ -3149,7 +3125,6 @@ function MyController(hand) { var ttl = ACTION_TTL; if (this.farToNearGrab) { - ttl = ACTION_TTL_ZERO; // farToNearGrab - Assign ACTION_TTL_ZERO so that, the object is dropped down immediately after the trigger is released. if(!this.triggerClicked){ this.farToNearGrab = false; } @@ -3322,7 +3297,7 @@ function MyController(hand) { this.maybeScale(props); } - if (this.actionID && this.actionTimeout - now < ttl * MSECS_PER_SEC) { + if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) { // if less than a 5 seconds left, refresh the actions ttl var success = Entities.updateAction(this.grabbedThingID, this.actionID, { hand: this.hand === RIGHT_HAND ? "right" : "left", @@ -3761,10 +3736,12 @@ function MyController(hand) { var TABLET_MAX_TOUCH_DISTANCE = 0.01; if (this.stylusTarget) { - if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { + if (this.stylusTarget.distance > TABLET_MIN_TOUCH_DISTANCE && + this.stylusTarget.distance < TABLET_MAX_TOUCH_DISTANCE) { var POINTER_PRESS_TO_MOVE_DELAY = 0.33; // seconds if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY || - distance2D(this.stylusTarget.position2D, this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { + distance2D(this.stylusTarget.position2D, + this.touchingEnterStylusTarget.position2D) > this.deadspotRadius) { sendTouchMoveEventToStylusTarget(this.hand, this.stylusTarget); this.deadspotExpired = true; } diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index ccc9386d13..29c5f465d7 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -104,97 +104,42 @@ input[type=button].naked:active { */ /* -// START styling of share bar +// START styling of share overlay */ .shareControls { display: flex; justify-content: space-between; flex-direction: row; align-items: center; - height: 45px; - line-height: 60px; + height: 65px; + line-height: 65px; width: calc(100% - 8px); position: absolute; bottom: 4px; left: 4px; right: 4px; } -.shareButtons { - display: flex; - align-items: center; - margin-left: 15px; - height: 100%; - width: 75%; -} -.blastToConnections { - text-align: left; - margin-right: 20px; - height: 29px; -} -.shareWithEveryone { - background: #DDDDDD url(../img/shareToFeed.png) no-repeat scroll center; - border-width: 0px; - text-align: left; - margin-right: 8px; - height: 29px; - width: 30px; - border-radius: 3px; -} -.facebookButton { - background-image: url(../img/fb_logo.png); - width: 29px; - height: 29px; - display: inline-block; - margin-right: 8px; -} -.twitterButton { - background-image: url(../img/twitter_logo.png); - width: 29px; - height: 29px; - display: inline-block; - margin-right: 8px; - border-radius: 3px; -} .showShareButtonsButtonDiv { display: inline-flex; align-items: center; font-family: Raleway-SemiBold; font-size: 14px; color: white; + width: 75px; height: 100%; - margin-right: 10px; + margin-bottom: 0px; +} +.showShareButtonsButtonDiv.active:hover { + background-color: rgba(0, 0, 0, 0.45); + background-size: 2px; } .showShareButtonsButtonDiv > label { text-shadow: 2px 2px 3px #000000; + margin-bottom: -14px; + margin-left: 12px; } -.showShareButton { - width: 40px; - height: 40px; - border-radius: 50%; - border-width: 0; - margin-left: 5px; - outline: none; -} -.showShareButton.active { - border-color: #00b4ef; - border-width: 3px; - background-color: white; -} -.showShareButton.active:hover { - background-color: #afafaf; -} -.showShareButton.active:active { - background-color: white; -} -.showShareButton.inactive { - border-width: 0; - background-color: white; -} -.showShareButton.inactive:hover { - background-color: #afafaf; -} -.showShareButton.inactive:active { - background-color: white; +.showShareButtonsButtonDiv:hover > label { + text-shadow: none; } .showShareButtonDots { display: block; @@ -203,15 +148,81 @@ input[type=button].naked:active { font-family: HiFi-Glyphs; font-size: 60px; position: absolute; - right: 20px; - bottom: 12px; - color: #00b4ef; + left: 6px; + bottom: 32px; + color: white; pointer-events: none; } +.shareButtons { + display: flex; + align-items: flex-end; + height: 40px; + width: calc(100% - 60px); + margin-bottom: 25px; + margin-left: 0; +} +.shareButtons img { + width: 40px; + height: 40px; +} +.shareButton { + width: 40px; + height: 40px; + display: inline-block; +} +.shareButton.disabled { + background-color: #000000; + opacity: 0.5; +} +.shareControlsHelp { + height: 25px; + line-height: 25px; + position: absolute; + bottom: 0; + left: 73px; + right: 0; + font-family: Raleway-Regular; + font-weight: 500; + font-size: 16px; + padding-left: 8px; + color: white; +} /* // END styling of share overlay */ +/* +// START styling of confirmation message +*/ +.confirmationMessageContainer { + width: 100%; + height: 100%; + position: absolute; + background-color: rgba(0, 0, 0, 0.45); + text-align: center; + left: 0; + top: 0; + pointer-events: none; + color: white; + font-weight: bold; + font-size: 16px; +} +.confirmationMessage { + width: 130px; + height: 130px; + margin: 50px auto 0 auto; +} +.confirmationMessage > img { + width: 72px; + height: 72px; + display: block; + margin: 0 auto; + padding: 10px 0 0 0; +} +/* +// END styling of uploading message +*/ + /* // START styling of snapshot controls (bottom panel) and its contents */ diff --git a/scripts/system/html/img/blast_icon.svg b/scripts/system/html/img/blast_icon.svg new file mode 100644 index 0000000000..31df8e7f53 --- /dev/null +++ b/scripts/system/html/img/blast_icon.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/scripts/system/html/img/fb_icon.svg b/scripts/system/html/img/fb_icon.svg new file mode 100644 index 0000000000..6d67d17bb2 --- /dev/null +++ b/scripts/system/html/img/fb_icon.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/scripts/system/html/img/fb_logo.png b/scripts/system/html/img/fb_logo.png deleted file mode 100644 index 1de20bacd8..0000000000 Binary files a/scripts/system/html/img/fb_logo.png and /dev/null differ diff --git a/scripts/system/html/img/hifi_icon.svg b/scripts/system/html/img/hifi_icon.svg new file mode 100644 index 0000000000..acbb98a3b3 --- /dev/null +++ b/scripts/system/html/img/hifi_icon.svg @@ -0,0 +1,19 @@ + + + + + + + diff --git a/scripts/system/html/img/loader.gif b/scripts/system/html/img/loader.gif new file mode 100644 index 0000000000..c464703c84 Binary files /dev/null and b/scripts/system/html/img/loader.gif differ diff --git a/scripts/system/html/img/shareIcon.png b/scripts/system/html/img/shareIcon.png deleted file mode 100644 index 0486ac9202..0000000000 Binary files a/scripts/system/html/img/shareIcon.png and /dev/null differ diff --git a/scripts/system/html/img/shareToFeed.png b/scripts/system/html/img/shareToFeed.png deleted file mode 100644 index f681c49d8f..0000000000 Binary files a/scripts/system/html/img/shareToFeed.png and /dev/null differ diff --git a/scripts/system/html/img/twitter_icon.svg b/scripts/system/html/img/twitter_icon.svg new file mode 100644 index 0000000000..0393d963f2 --- /dev/null +++ b/scripts/system/html/img/twitter_icon.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/scripts/system/html/img/twitter_logo.png b/scripts/system/html/img/twitter_logo.png deleted file mode 100644 index 59fd027c2a..0000000000 Binary files a/scripts/system/html/img/twitter_logo.png and /dev/null differ diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index ed290e9756..6365c2be18 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -1,3 +1,5 @@ +/*jslint browser:true */ +/*jslint maxlen: 180*/ "use strict"; // // SnapshotReview.js @@ -13,27 +15,46 @@ var paths = []; var idCounter = 0; var imageCount = 0; +var blastShareText = "Blast to my Connections", + blastAlreadySharedText = "Already Blasted to Connections", + hifiShareText = "Share to Snaps Feed", + hifiAlreadySharedText = "Already Shared to Snaps Feed", + facebookShareText = "Share to Facebook", + twitterShareText = "Share to Twitter"; function showSetupInstructions() { var snapshotImagesDiv = document.getElementById("snapshot-images"); snapshotImagesDiv.className = "snapshotInstructions"; snapshotImagesDiv.innerHTML = 'Snapshot Instructions' + - '
' + - '

This app lets you take and share snaps and GIFs with your connections in High Fidelity.

' + - "

Setup Instructions

" + - "

Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:

" + - '
' + - '
' + - '' + - '
'; + '
' + + '

Take and share snaps and GIFs with people in High Fidelity, Facebook, and Twitter.

' + + "

Setup Instructions

" + + "

Before you can begin taking snaps, please choose where you'd like to save snaps on your computer:

" + + '
' + + '
' + + '' + + '
'; document.getElementById("snap-button").disabled = true; } function showSetupComplete() { var snapshotImagesDiv = document.getElementById("snapshot-images"); snapshotImagesDiv.className = "snapshotInstructions"; snapshotImagesDiv.innerHTML = 'Snapshot Instructions' + - '
' + - "

You're all set!

" + - '

Try taking a snapshot by pressing the red button below.

'; + '
' + + '
' + + '

Snapshot location set.

' + + '

Press the big red button to take a snap!

' + + '
'; +} +function showSnapshotInstructions() { + var snapshotImagesDiv = document.getElementById("snapshot-images"); + snapshotImagesDiv.className = "snapshotInstructions"; + snapshotImagesDiv.innerHTML = 'Snapshot Instructions' + + '
' + + '

Take and share snaps and GIFs with people in High Fidelity, Facebook, and Twitter.

' + + '
' + + '
' + + '

Press the big red button to take a snap!

' + + '
'; } function chooseSnapshotLocation() { EventBridge.emitWebEvent(JSON.stringify({ @@ -52,13 +73,120 @@ function clearImages() { imageCount = 0; idCounter = 0; } -function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled) { + +function selectImageToShare(selectedID, isSelected) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var imageContainer = document.getElementById(selectedID), + image = document.getElementById(selectedID + 'img'), + shareBar = document.getElementById(selectedID + "shareBar"), + shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), + itr, + containers = document.getElementsByClassName("shareControls"); + + if (isSelected) { + showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, false); }; + showShareButtonsButtonDiv.classList.remove("inactive"); + showShareButtonsButtonDiv.classList.add("active"); + + image.onclick = function () { selectImageToShare(selectedID, false); }; + imageContainer.style.outline = "4px solid #00b4ef"; + imageContainer.style.outlineOffset = "-4px"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; + + shareButtonsDiv.style.visibility = "visible"; + shareBarHelp.style.visibility = "visible"; + + for (itr = 0; itr < containers.length; itr += 1) { + var parentID = containers[itr].id.slice(0, 2); + if (parentID !== selectedID) { + selectImageToShare(parentID, false); + } + } + } else { + showShareButtonsButtonDiv.onclick = function () { selectImageToShare(selectedID, true); }; + showShareButtonsButtonDiv.classList.remove("active"); + showShareButtonsButtonDiv.classList.add("inactive"); + + image.onclick = function () { selectImageToShare(selectedID, true); }; + imageContainer.style.outline = "none"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + + shareButtonsDiv.style.visibility = "hidden"; + shareBarHelp.style.visibility = "hidden"; + } +} +function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { + var shareBar = document.createElement("div"), + shareBarHelpID = parentID + "shareBarHelp", + shareButtonsDivID = parentID + "shareButtonsDiv", + showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv", + showShareButtonsLabelID = parentID + "showShareButtonsLabel", + blastToConnectionsButtonID = parentID + "blastToConnectionsButton", + shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", + facebookButtonID = parentID + "facebookButton", + twitterButtonID = parentID + "twitterButton"; + + shareBar.id = parentID + "shareBar"; + shareBar.className = "shareControls"; + var shareBarInnerHTML = '
' + + '' + + '' + + '' + + '
' + + '' + + ''; + + shareBar.innerHTML = shareBarInnerHTML; + + shareBar.innerHTML += ''; + + // Add onclick handler to parent DIV's img to toggle share buttons + document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; + + return shareBar; +} +function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { + if (divID.id) { + divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); + if (divID === "p0") { + selectImageToShare(divID, true); + if (canBlast) { + shareButtonHovered('blast', divID); + } else { + shareButtonHovered('hifi', divID); + } + } +} +function shareForUrl(selectedID) { + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "shareSnapshotForUrl", + data: paths[parseInt(selectedID.substring(1), 10)] + })); +} +function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (!image_data.localPath) { return; } - var id = "p" + idCounter++; - // imageContainer setup - var imageContainer = document.createElement("DIV"); + var id = "p" + (idCounter++), + imageContainer = document.createElement("DIV"), + img = document.createElement("IMG"), + isGif; imageContainer.id = id; imageContainer.style.width = "95%"; imageContainer.style.height = "240px"; @@ -67,160 +195,264 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b imageContainer.style.justifyContent = "center"; imageContainer.style.alignItems = "center"; imageContainer.style.position = "relative"; - // img setup - var img = document.createElement("IMG"); img.id = id + "img"; - if (imageCount > 1) { - img.setAttribute("class", "multiple"); - } img.src = image_data.localPath; + isGif = img.src.split('.').pop().toLowerCase() === "gif"; imageContainer.appendChild(img); document.getElementById("snapshot-images").appendChild(imageContainer); paths.push(image_data.localPath); - var isGif = img.src.split('.').pop().toLowerCase() === "gif"; if (isGif) { imageContainer.innerHTML += 'GIF'; } if (!isGifLoading && !isShowingPreviousImages && canShare) { + appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true); shareForUrl(id); - } else if (isShowingPreviousImages && canShare && image_data.story_id) { - appendShareBar(id, image_data.story_id, isGif, blastButtonDisabled, hifiButtonDisabled) + } + if (isShowingPreviousImages && image_data.story_id) { + appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + updateShareInfo(id, image_data.story_id); } } -function appendShareBar(divID, story_id, isGif, blastButtonDisabled, hifiButtonDisabled) { - var story_url = "https://highfidelity.com/user_stories/" + story_id; - var parentDiv = document.getElementById(divID); - parentDiv.setAttribute('data-story-id', story_id); - document.getElementById(divID).appendChild(createShareBar(divID, isGif, story_url, blastButtonDisabled, hifiButtonDisabled)); - if (divID === "p0") { - selectImageToShare(divID, true); +function showConfirmationMessage(selectedID, destination) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + + var opacity = 2.0, + fadeRate = 0.05, + timeBetweenFadesMS = 50, + confirmationMessageContainer = document.createElement("div"), + confirmationMessage = document.createElement("div"); + confirmationMessageContainer.className = "confirmationMessageContainer"; + + confirmationMessage.className = "confirmationMessage"; + + var socialIcon = document.createElement("img"); + switch (destination) { + case 'blast': + socialIcon.src = "img/blast_icon.svg"; + confirmationMessage.appendChild(socialIcon); + confirmationMessage.innerHTML += 'Blast Sent!'; + confirmationMessage.style.backgroundColor = "#EA4C5F"; + break; + case 'hifi': + socialIcon.src = "img/hifi_icon.svg"; + confirmationMessage.appendChild(socialIcon); + confirmationMessage.innerHTML += 'Snap Shared!'; + confirmationMessage.style.backgroundColor = "#1FC6A6"; + break; + } + + confirmationMessageContainer.appendChild(confirmationMessage); + document.getElementById(selectedID).appendChild(confirmationMessageContainer); + + setInterval(function () { + if (opacity <= fadeRate) { + confirmationMessageContainer.remove(); + } + opacity -= fadeRate; + confirmationMessageContainer.style.opacity = opacity; + }, timeBetweenFadesMS); +} +function showUploadingMessage(selectedID, destination) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + + shareBarHelp.innerHTML = 'Preparing to Share'; + shareBarHelp.setAttribute("data-destination", destination); +} +function hideUploadingMessageAndShare(selectedID, storyID) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + shareBarHelpDestination = shareBarHelp.getAttribute("data-destination"); + if (shareBarHelpDestination) { + switch (shareBarHelpDestination) { + case 'blast': + blastToConnections(selectedID, selectedID === "p1"); + shareBarHelp.innerHTML = blastAlreadySharedText; + break; + case 'hifi': + shareWithEveryone(selectedID, selectedID === "p1"); + shareBarHelp.innerHTML = hifiAlreadySharedText; + break; + case 'facebook': + var facebookButton = document.getElementById(selectedID + "facebookButton"); + window.open(facebookButton.getAttribute("href"), "_blank"); + shareBarHelp.innerHTML = facebookShareText; + break; + case 'twitter': + var twitterButton = document.getElementById(selectedID + "twitterButton"); + window.open(twitterButton.getAttribute("href"), "_blank"); + shareBarHelp.innerHTML = twitterShareText; + break; + } + + shareBarHelp.setAttribute("data-destination", ""); + + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "removeFromStoryIDsToMaybeDelete", + story_id: storyID + })); } } -function createShareBar(parentID, isGif, shareURL, blastButtonDisabled, hifiButtonDisabled) { - var shareBar = document.createElement("div"); - shareBar.id = parentID + "shareBar"; - shareBar.className = "shareControls"; - var shareButtonsDivID = parentID + "shareButtonsDiv"; - var showShareButtonsButtonDivID = parentID + "showShareButtonsButtonDiv"; - var showShareButtonsButtonID = parentID + "showShareButtonsButton"; - var showShareButtonsLabelID = parentID + "showShareButtonsLabel"; - var blastToConnectionsButtonID = parentID + "blastToConnectionsButton"; - var shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton"; - var facebookButtonID = parentID + "facebookButton"; - var twitterButtonID = parentID + "twitterButton"; - shareBar.innerHTML += '' + - '' + - '
' + - '' + - '' + - '
' + - '' + - '
' + - '
'; +function updateShareInfo(containerID, storyID) { + if (containerID.id) { + containerID = containerID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID + } + var shareBar = document.getElementById(containerID + "shareBar"), + parentDiv = document.getElementById(containerID), + shareURL = "https://highfidelity.com/user_stories/" + storyID, + facebookButton = document.getElementById(containerID + "facebookButton"), + twitterButton = document.getElementById(containerID + "twitterButton"); - // Add onclick handler to parent DIV's img to toggle share buttons - document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true) }; + parentDiv.setAttribute('data-story-id', storyID); - return shareBar; + facebookButton.setAttribute("target", "_blank"); + facebookButton.setAttribute("href", 'https://www.facebook.com/dialog/feed?app_id=1585088821786423&link=' + shareURL); + + twitterButton.setAttribute("target", "_blank"); + twitterButton.setAttribute("href", 'https://twitter.com/intent/tweet?text=I%20just%20took%20a%20snapshot!&url=' + shareURL + '&via=highfidelity&hashtags=VR,HiFi'); + + hideUploadingMessageAndShare(containerID, storyID); } -function selectImageToShare(selectedID, isSelected) { +function blastToConnections(selectedID, isGif) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } - var imageContainer = document.getElementById(selectedID); - var image = document.getElementById(selectedID + 'img'); - var shareBar = document.getElementById(selectedID + "shareBar"); - var shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv"); - var showShareButtonsButton = document.getElementById(selectedID + "showShareButtonsButton"); - if (isSelected) { - showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, false) }; - showShareButtonsButton.classList.remove("inactive"); - showShareButtonsButton.classList.add("active"); + var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"), + shareBar = document.getElementById(selectedID + "shareBar"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + blastToConnectionsButton.onclick = function () { }; - image.onclick = function () { selectImageToShare(selectedID, false) }; - imageContainer.style.outline = "4px solid #00b4ef"; - imageContainer.style.outlineOffset = "-4px"; + var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); - shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; - - shareButtonsDiv.style.visibility = "visible"; - - var containers = document.getElementsByClassName("shareControls"); - var parentID; - for (var i = 0; i < containers.length; ++i) { - parentID = containers[i].id.slice(0, 2); - if (parentID !== selectedID) { - selectImageToShare(parentID, false); - } - } + if (storyID) { + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "blastToConnections", + story_id: storyID, + isGif: isGif + })); + showConfirmationMessage(selectedID, 'blast'); + blastToConnectionsButton.classList.add("disabled"); + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; } else { - showShareButtonsButton.onclick = function () { selectImageToShare(selectedID, true) }; - showShareButtonsButton.classList.remove("active"); - showShareButtonsButton.classList.add("inactive"); - - image.onclick = function () { selectImageToShare(selectedID, true) }; - imageContainer.style.outline = "none"; - - shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; - - shareButtonsDiv.style.visibility = "hidden"; + showUploadingMessage(selectedID, 'blast'); } } -function shareForUrl(selectedID) { - EventBridge.emitWebEvent(JSON.stringify({ - type: "snapshot", - action: "shareSnapshotForUrl", - data: paths[parseInt(selectedID.substring(1))] - })); -} -function blastToConnections(selectedID, isGif) { - selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID - - document.getElementById(selectedID + "blastToConnectionsButton").disabled = true; - - EventBridge.emitWebEvent(JSON.stringify({ - type: "snapshot", - action: "blastToConnections", - story_id: document.getElementById(selectedID).getAttribute("data-story-id"), - isGif: isGif - })); -} function shareWithEveryone(selectedID, isGif) { - selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } - document.getElementById(selectedID + "shareWithEveryoneButton").disabled = true; + var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"), + shareBar = document.getElementById(selectedID + "shareBar"), + shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); + shareWithEveryoneButton.onclick = function () { }; - EventBridge.emitWebEvent(JSON.stringify({ - type: "snapshot", - action: "shareSnapshotWithEveryone", - story_id: document.getElementById(selectedID).getAttribute("data-story-id"), - isGif: isGif - })); + var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); + + if (storyID) { + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "shareSnapshotWithEveryone", + story_id: storyID, + isGif: isGif + })); + showConfirmationMessage(selectedID, 'hifi'); + shareWithEveryoneButton.classList.add("disabled"); + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + } else { + showUploadingMessage(selectedID, 'hifi'); + } } -function shareButtonClicked(selectedID) { - selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID - EventBridge.emitWebEvent(JSON.stringify({ - type: "snapshot", - action: "shareButtonClicked", - story_id: document.getElementById(selectedID).getAttribute("data-story-id") - })); -} -function cancelSharing(selectedID) { - selectedID = selectedID.id; // `selectedID` is passed as an HTML object to these functions; we just want the ID - var shareBar = document.getElementById(selectedID + "shareBar"); +function shareButtonHovered(destination, selectedID) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), + shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes, + itr; - shareBar.style.display = "inline"; + for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { + shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; + } + shareBarHelp.style.opacity = "1.0"; + + switch (destination) { + case 'blast': + var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"); + if (!blastToConnectionsButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#EA4C5F"; + shareBarHelp.style.opacity = "1.0"; + blastToConnectionsButton.style.backgroundColor = "#EA4C5F"; + blastToConnectionsButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = blastShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = blastAlreadySharedText; + } + break; + case 'hifi': + var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"); + if (!shareWithEveryoneButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#1FC6A6"; + shareBarHelp.style.opacity = "1.0"; + shareWithEveryoneButton.style.backgroundColor = "#1FC6A6"; + shareWithEveryoneButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = hifiShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = hifiAlreadySharedText; + } + break; + case 'facebook': + shareBarHelp.style.backgroundColor = "#3C58A0"; + shareBarHelp.innerHTML = facebookShareText; + document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; + break; + case 'twitter': + shareBarHelp.style.backgroundColor = "#00B4EE"; + shareBarHelp.innerHTML = twitterShareText; + document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; + break; + } +} +function shareButtonClicked(destination, selectedID) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var storyID = document.getElementById(selectedID).getAttribute("data-story-id"); + + if (!storyID) { + showUploadingMessage(selectedID, destination); + } } function handleCaptureSetting(setting) { - var stillAndGif = document.getElementById('stillAndGif'); - var stillOnly = document.getElementById('stillOnly'); + var stillAndGif = document.getElementById('stillAndGif'), + stillOnly = document.getElementById('stillOnly'); + stillAndGif.checked = setting; stillOnly.checked = !setting; @@ -229,19 +461,20 @@ function handleCaptureSetting(setting) { type: "snapshot", action: "captureStillAndGif" })); - } + }; + stillOnly.onclick = function () { EventBridge.emitWebEvent(JSON.stringify({ type: "snapshot", action: "captureStillOnly" })); - } + }; } window.onload = function () { // Uncomment the line below to test functionality in a browser. // See definition of "testInBrowser()" to modify tests. - //testInBrowser(false); + //testInBrowser(3); openEventBridge(function () { // Set up a handler for receiving the data, and tell the .js we are ready to receive it. EventBridge.scriptEventReceived.connect(function (message) { @@ -251,7 +484,9 @@ window.onload = function () { if (message.type !== "snapshot") { return; } - + + var messageOptions = message.options; + switch (message.action) { case 'showSetupInstructions': showSetupInstructions(); @@ -265,38 +500,41 @@ window.onload = function () { break; case 'showPreviousImages': clearImages(); - var messageOptions = message.options; imageCount = message.image_data.length; - message.image_data.forEach(function (element, idx, array) { - addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled); - }); + if (imageCount > 0) { + message.image_data.forEach(function (element, idx) { + addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); + }); + } else { + showSnapshotInstructions(); + } break; case 'addImages': // The last element of the message contents list contains a bunch of options, // including whether or not we can share stuff // The other elements of the list contain image paths. - var messageOptions = message.options; if (messageOptions.containsGif) { if (messageOptions.processingGif) { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon message.image_data.push({ localPath: messageOptions.loadingGifPath }); - message.image_data.forEach(function (element, idx, array) { + message.image_data.forEach(function (element, idx) { addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); }); } else { - var gifPath = message.image_data[0].localPath; - var p1img = document.getElementById('p1img'); + var gifPath = message.image_data[0].localPath, + p1img = document.getElementById('p1img'); p1img.src = gifPath; paths[1] = gifPath; if (messageOptions.canShare) { shareForUrl("p1"); + appendShareBar("p1", true, false, false, true); } } } else { imageCount = message.image_data.length; - message.image_data.forEach(function (element, idx, array) { + message.image_data.forEach(function (element) { addImage(element, false, messageOptions.canShare, false); }); } @@ -306,7 +544,7 @@ window.onload = function () { break; case 'snapshotUploadComplete': var isGif = message.image_url.split('.').pop().toLowerCase() === "gif"; - appendShareBar(isGif ? "p1" : "p0", message.story_id, isGif); + updateShareInfo(isGif ? "p1" : "p0", message.story_id); break; default: console.log("Unknown message action received in SnapshotReview.js."); @@ -333,13 +571,24 @@ function takeSnapshot() { })); } -function testInBrowser(isTestingSetupInstructions) { - if (isTestingSetupInstructions) { +function testInBrowser(test) { + if (test === 0) { showSetupInstructions(); - } else { - imageCount = 1; + } else if (test === 1) { + imageCount = 2; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.gif' }, false, true, true, false, false); - addImage({ localPath: 'C:/Users/valef/Desktop/hifi-snap-by-zfox-on-2017-05-01_15-48-15.jpg' }, false, true, true, false, false); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + } else if (test === 2) { + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + showConfirmationMessage("p0", 'blast'); + showConfirmationMessage("p1", 'hifi'); + } else if (test === 3) { + imageCount = 2; + //addImage({ localPath: 'http://lorempixel.com/553/255' }); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + showUploadingMessage("p0", 'hifi'); } } diff --git a/scripts/system/makeUserConnection.js b/scripts/system/makeUserConnection.js index 33d6b363ab..78be54f774 100644 --- a/scripts/system/makeUserConnection.js +++ b/scripts/system/makeUserConnection.js @@ -11,8 +11,11 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + (function () { // BEGIN LOCAL_SCOPE + var request = Script.require('request').request; + var LABEL = "makeUserConnection"; var MAX_AVATAR_DISTANCE = 0.2; // m var GRIP_MIN = 0.75; // goes from 0-1, so 75% pressed is pressed @@ -126,61 +129,6 @@ function cleanId(guidWithCurlyBraces) { return guidWithCurlyBraces.slice(1, -1); } - function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; - // QT bug: apparently doesn't handle onload. Workaround using readyState. - httpRequest.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (httpRequest.readyState >= READY_STATE_DONE) { - var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, - response = !error && httpRequest.responseText, - contentType = !error && httpRequest.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - if (error) { - response = {statusCode: httpRequest.status}; - } - callback(error, response); - } - }; - if (typeof options === 'string') { - options = {uri: options}; - } - if (options.url) { - options.uri = options.url; - } - if (!options.method) { - options.method = 'GET'; - } - if (options.body && (options.method === 'GET')) { // add query parameters - var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; - for (key in options.body) { - if (options.body.hasOwnProperty(key)) { - params.push(key + '=' + options.body[key]); - } - } - options.uri += appender + params.join('&'); - delete options.body; - } - if (options.json) { - options.headers = options.headers || {}; - options.headers["Content-type"] = "application/json"; - options.body = JSON.stringify(options.body); - } - for (key in options.headers || {}) { - if (options.headers.hasOwnProperty(key)) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); - } function handToString(hand) { if (hand === Controller.Standard.RightHand) { @@ -514,7 +462,7 @@ endHandshakeAnimation(); // No-op if we were successful, but this way we ensure that failures and abandoned handshakes don't leave us // in a weird state. - request({uri: requestUrl, method: 'DELETE'}, debug); + request({ uri: requestUrl, method: 'DELETE' }, debug); } function updateTriggers(value, fromKeyboard, hand) { diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 4a6b8d4142..9229ec772a 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -14,6 +14,8 @@ (function() { // BEGIN LOCAL_SCOPE + var request = Script.require('request').request; + var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL, @@ -331,55 +333,6 @@ function updateUser(data) { // // These are prototype versions that will be changed when the back end changes. var METAVERSE_BASE = location.metaverseServerUrl; -function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; - // QT bug: apparently doesn't handle onload. Workaround using readyState. - httpRequest.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (httpRequest.readyState >= READY_STATE_DONE) { - var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, - response = !error && httpRequest.responseText, - contentType = !error && httpRequest.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - callback(error, response); - } - }; - if (typeof options === 'string') { - options = {uri: options}; - } - if (options.url) { - options.uri = options.url; - } - if (!options.method) { - options.method = 'GET'; - } - if (options.body && (options.method === 'GET')) { // add query parameters - var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; - for (key in options.body) { - params.push(key + '=' + options.body[key]); - } - options.uri += appender + params.join('&'); - delete options.body; - } - if (options.json) { - options.headers = options.headers || {}; - options.headers["Content-type"] = "application/json"; - options.body = JSON.stringify(options.body); - } - for (key in options.headers || {}) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); -} - function requestJSON(url, callback) { // callback(data) if successfull. Logs otherwise. request({ diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index d0d1bd96f5..b93595f0b4 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -182,7 +182,6 @@ function onMessage(message) { break; case 'blastToConnections': isLoggedIn = Account.isLoggedIn(); - storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); if (message.isGif) { Settings.setValue("previousAnimatedSnapBlastingDisabled", true); } else { @@ -242,7 +241,6 @@ function onMessage(message) { break; case 'shareSnapshotWithEveryone': isLoggedIn = Account.isLoggedIn(); - storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); if (message.isGif) { Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); } else { @@ -283,8 +281,7 @@ function onMessage(message) { snapshotToShareAfterLogin = { path: message.data, href: message.href || href }; } break; - case 'shareButtonClicked': - print('Twitter or FB "Share" button clicked! Removing ID', message.story_id, 'from storyIDsToMaybeDelete[].'); + case 'removeFromStoryIDsToMaybeDelete': storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(message.story_id), 1); print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); break; @@ -314,7 +311,8 @@ function onButtonClicked() { snapshotOptions = { containsGif: previousAnimatedSnapPath !== "", processingGif: false, - shouldUpload: false + shouldUpload: false, + canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID") } imageData = []; if (previousStillSnapPath !== "") { @@ -395,7 +393,9 @@ function takeSnapshot() { resetOverlays = Menu.isOptionChecked("Overlays"); // For completeness. Certainly true if the button is visible to be clicked. reticleVisible = Reticle.visible; Reticle.visible = false; - Reticle.allowMouseCapture = false; + if (!HMD.active) { + Reticle.allowMouseCapture = false; + } var includeAnimated = Settings.getValue("alsoTakeAnimatedSnapshot", true); if (includeAnimated) { diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index fec7a6de90..4e023f5460 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -14,6 +14,9 @@ // (function () { // BEGIN LOCAL_SCOPE + + var request = Script.require('request').request; + var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; var onGotoScreen = false; @@ -30,54 +33,7 @@ text: buttonName, sortOrder: 8 }); - function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; - // QT bug: apparently doesn't handle onload. Workaround using readyState. - httpRequest.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (httpRequest.readyState >= READY_STATE_DONE) { - var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, - response = !error && httpRequest.responseText, - contentType = !error && httpRequest.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - callback(error, response); - } - }; - if (typeof options === 'string') { - options = {uri: options}; - } - if (options.url) { - options.uri = options.url; - } - if (!options.method) { - options.method = 'GET'; - } - if (options.body && (options.method === 'GET')) { // add query parameters - var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; - for (key in options.body) { - params.push(key + '=' + options.body[key]); - } - options.uri += appender + params.join('&'); - delete options.body; - } - if (options.json) { - options.headers = options.headers || {}; - options.headers["Content-type"] = "application/json"; - options.body = JSON.stringify(options.body); - } - for (key in options.headers || {}) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); - } + function fromQml(message) { var response = {id: message.id, jsonrpc: "2.0"}; switch (message.method) { @@ -99,6 +55,7 @@ // No need for a different activeIcon, because we issue messagesWaiting(false) when the button goes active anyway. }); } + var hasEventBridge = false; function wireEventBridge(on) { if (on) {