mirror of
https://github.com/overte-org/overte.git
synced 2025-07-10 21:18:54 +02:00
Merge pull request #8463 from howard-stearns/html-user-story
html drill-down user story
This commit is contained in:
commit
01fcaa024d
10 changed files with 95 additions and 19 deletions
|
@ -15,6 +15,7 @@ import "styles"
|
||||||
import "windows"
|
import "windows"
|
||||||
import "hifi"
|
import "hifi"
|
||||||
import "hifi/toolbars"
|
import "hifi/toolbars"
|
||||||
|
import "controls-uit" as HifiControls
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: root
|
id: root
|
||||||
|
@ -46,15 +47,23 @@ Window {
|
||||||
anchors.centerIn = parent;
|
anchors.centerIn = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetAfterTeleport() {
|
||||||
|
storyCardFrame.shown = root.shown = false;
|
||||||
|
}
|
||||||
function goCard(card) {
|
function goCard(card) {
|
||||||
if (addressBarDialog.useFeed) {
|
if (addressBarDialog.useFeed) {
|
||||||
storyCard.imageUrl = card.imageUrl;
|
if (useHTML) {
|
||||||
storyCard.userName = card.userName;
|
storyCardHTML.url = metaverseBase + "user_stories/" + card.storyId + ".html";
|
||||||
storyCard.placeName = card.placeName;
|
storyCardFrame.shown = true;
|
||||||
storyCard.actionPhrase = card.actionPhrase;
|
} else {
|
||||||
storyCard.timePhrase = card.timePhrase;
|
storyCardQML.imageUrl = card.imageUrl;
|
||||||
storyCard.hifiUrl = card.hifiUrl;
|
storyCardQML.userName = card.userName;
|
||||||
storyCard.visible = true;
|
storyCardQML.placeName = card.placeName;
|
||||||
|
storyCardQML.actionPhrase = card.actionPhrase;
|
||||||
|
storyCardQML.timePhrase = card.timePhrase;
|
||||||
|
storyCardQML.hifiUrl = card.hifiUrl;
|
||||||
|
storyCardQML.visible = true;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addressLine.text = card.hifiUrl;
|
addressLine.text = card.hifiUrl;
|
||||||
|
@ -65,6 +74,8 @@ Window {
|
||||||
property int cardWidth: 200;
|
property int cardWidth: 200;
|
||||||
property int cardHeight: 152;
|
property int cardHeight: 152;
|
||||||
property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/";
|
property string metaverseBase: "https://metaverse.highfidelity.com/api/v1/";
|
||||||
|
//property string metaverseBase: "http://10.0.0.241:3000/api/v1/";
|
||||||
|
property bool useHTML: false; // fixme: remove this and all false branches after the server is updated
|
||||||
|
|
||||||
AddressBarDialog {
|
AddressBarDialog {
|
||||||
id: addressBarDialog
|
id: addressBarDialog
|
||||||
|
@ -74,6 +85,7 @@ Window {
|
||||||
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
onBackEnabledChanged: backArrow.buttonState = addressBarDialog.backEnabled ? 1 : 0;
|
||||||
onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
|
onForwardEnabledChanged: forwardArrow.buttonState = addressBarDialog.forwardEnabled ? 1 : 0;
|
||||||
onUseFeedChanged: { updateFeedState(); }
|
onUseFeedChanged: { updateFeedState(); }
|
||||||
|
onReceivedHifiSchemeURL: resetAfterTeleport();
|
||||||
|
|
||||||
ListModel { id: suggestions }
|
ListModel { id: suggestions }
|
||||||
|
|
||||||
|
@ -102,6 +114,7 @@ Window {
|
||||||
action: model.action;
|
action: model.action;
|
||||||
timestamp: model.created_at;
|
timestamp: model.created_at;
|
||||||
onlineUsers: model.online_users;
|
onlineUsers: model.online_users;
|
||||||
|
storyId: model.metaverseId;
|
||||||
hoverThunk: function () { ListView.view.currentIndex = index; }
|
hoverThunk: function () { ListView.view.currentIndex = index; }
|
||||||
unhoverThunk: function () { ListView.view.currentIndex = -1; }
|
unhoverThunk: function () { ListView.view.currentIndex = -1; }
|
||||||
}
|
}
|
||||||
|
@ -114,6 +127,7 @@ Window {
|
||||||
Image { // Just a visual indicator that the user can swipe the cards over to see more.
|
Image { // Just a visual indicator that the user can swipe the cards over to see more.
|
||||||
source: "../images/Swipe-Icon-single.svg"
|
source: "../images/Swipe-Icon-single.svg"
|
||||||
width: 50;
|
width: 50;
|
||||||
|
visible: suggestions.count > 3;
|
||||||
anchors {
|
anchors {
|
||||||
right: scroll.right;
|
right: scroll.right;
|
||||||
verticalCenter: scroll.verticalCenter;
|
verticalCenter: scroll.verticalCenter;
|
||||||
|
@ -215,10 +229,10 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserStoryCard {
|
UserStoryCard {
|
||||||
id: storyCard;
|
id: storyCardQML;
|
||||||
visible: false;
|
visible: false;
|
||||||
visitPlace: function (hifiUrl) {
|
visitPlace: function (hifiUrl) {
|
||||||
storyCard.visible = false;
|
storyCardQML.visible = false;
|
||||||
addressLine.text = hifiUrl;
|
addressLine.text = hifiUrl;
|
||||||
toggleOrGo(true);
|
toggleOrGo(true);
|
||||||
};
|
};
|
||||||
|
@ -228,6 +242,25 @@ Window {
|
||||||
verticalCenterOffset: 50;
|
verticalCenterOffset: 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Window {
|
||||||
|
width: 750;
|
||||||
|
height: 360;
|
||||||
|
HifiControls.WebView {
|
||||||
|
anchors.fill: parent;
|
||||||
|
id: storyCardHTML;
|
||||||
|
}
|
||||||
|
id: storyCardFrame;
|
||||||
|
|
||||||
|
shown: false;
|
||||||
|
destroyOnCloseButton: false;
|
||||||
|
pinnable: false;
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: scroll.verticalCenter;
|
||||||
|
horizontalCenter: scroll.horizontalCenter;
|
||||||
|
verticalCenterOffset: 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -357,6 +390,8 @@ Window {
|
||||||
thumbnail_url: thumbnail_url,
|
thumbnail_url: thumbnail_url,
|
||||||
image_url: image_url,
|
image_url: image_url,
|
||||||
|
|
||||||
|
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||||
|
|
||||||
tags: tags,
|
tags: tags,
|
||||||
description: description,
|
description: description,
|
||||||
online_users: data.online_users || 0,
|
online_users: data.online_users || 0,
|
||||||
|
|
|
@ -25,6 +25,7 @@ Rectangle {
|
||||||
property string thumbnail: defaultThumbnail;
|
property string thumbnail: defaultThumbnail;
|
||||||
property string imageUrl: "";
|
property string imageUrl: "";
|
||||||
property var goFunction: null;
|
property var goFunction: null;
|
||||||
|
property string storyId: "";
|
||||||
|
|
||||||
property string timePhrase: pastTime(timestamp);
|
property string timePhrase: pastTime(timestamp);
|
||||||
property string actionPhrase: makeActionPhrase(action);
|
property string actionPhrase: makeActionPhrase(action);
|
||||||
|
|
|
@ -4853,6 +4853,7 @@ bool Application::canAcceptURL(const QString& urlString) const {
|
||||||
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
|
||||||
if (urlString.startsWith(HIFI_URL_SCHEME)) {
|
if (urlString.startsWith(HIFI_URL_SCHEME)) {
|
||||||
// this is a hifi URL - have the AddressManager handle it
|
// this is a hifi URL - have the AddressManager handle it
|
||||||
|
emit receivedHifiSchemeURL(urlString);
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
|
||||||
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
Qt::AutoConnection, Q_ARG(const QString&, urlString));
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -266,6 +266,7 @@ signals:
|
||||||
void activeDisplayPluginChanged();
|
void activeDisplayPluginChanged();
|
||||||
|
|
||||||
void uploadRequest(QString path);
|
void uploadRequest(QString path);
|
||||||
|
void receivedHifiSchemeURL(QString path);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
QVector<EntityItemID> pasteEntities(float x, float y, float z);
|
||||||
|
|
|
@ -26,6 +26,11 @@ bool AccountScriptingInterface::isLoggedIn() {
|
||||||
return accountManager->isLoggedIn();
|
return accountManager->isLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AccountScriptingInterface::checkAndSignalForAccessToken() {
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
return accountManager->checkAndSignalForAccessToken();
|
||||||
|
}
|
||||||
|
|
||||||
QString AccountScriptingInterface::getUsername() {
|
QString AccountScriptingInterface::getUsername() {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
if (accountManager->isLoggedIn()) {
|
if (accountManager->isLoggedIn()) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ public slots:
|
||||||
static AccountScriptingInterface* getInstance();
|
static AccountScriptingInterface* getInstance();
|
||||||
QString getUsername();
|
QString getUsername();
|
||||||
bool isLoggedIn();
|
bool isLoggedIn();
|
||||||
|
bool checkAndSignalForAccessToken();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AccountScriptingInterface_h
|
#endif // hifi_AccountScriptingInterface_h
|
||||||
|
|
|
@ -39,6 +39,7 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
|
||||||
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
_backEnabled = !(DependencyManager::get<AddressManager>()->getBackStack().isEmpty());
|
||||||
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
_forwardEnabled = !(DependencyManager::get<AddressManager>()->getForwardStack().isEmpty());
|
||||||
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
|
||||||
|
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) {
|
void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ signals:
|
||||||
void backEnabledChanged();
|
void backEnabledChanged();
|
||||||
void forwardEnabledChanged();
|
void forwardEnabledChanged();
|
||||||
void useFeedChanged();
|
void useFeedChanged();
|
||||||
|
void receivedHifiSchemeURL(QString url);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void displayAddressOfflineMessage();
|
void displayAddressOfflineMessage();
|
||||||
|
|
|
@ -84,6 +84,9 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
function handleShareButtons(shareMsg) {
|
function handleShareButtons(shareMsg) {
|
||||||
|
var openFeed = document.getElementById('openFeed');
|
||||||
|
openFeed.checked = shareMsg.openFeedAfterShare;
|
||||||
|
openFeed.onchange = function () { EventBridge.emitWebEvent(openFeed.checked ? 'setOpenFeedTrue' : 'setOpenFeedFalse'); };
|
||||||
if (!shareMsg.canShare) {
|
if (!shareMsg.canShare) {
|
||||||
// this means you may or may not be logged in, but can't share
|
// this means you may or may not be logged in, but can't share
|
||||||
// because you are not in a public place.
|
// because you are not in a public place.
|
||||||
|
@ -116,10 +119,10 @@
|
||||||
};
|
};
|
||||||
// beware of bug: Cannot send objects at top level. (Nested in arrays is fine.)
|
// beware of bug: Cannot send objects at top level. (Nested in arrays is fine.)
|
||||||
shareSelected = function () {
|
shareSelected = function () {
|
||||||
EventBridge.emitWebEvent(paths.concat({openFeed: document.getElementById("openFeed").checked}));
|
EventBridge.emitWebEvent(paths);
|
||||||
};
|
};
|
||||||
doNotShare = function () {
|
doNotShare = function () {
|
||||||
EventBridge.emitWebEvent([{openFeed: document.getElementById("openFeed").checked}]);
|
EventBridge.emitWebEvent([]);
|
||||||
};
|
};
|
||||||
snapshotSettings = function () {
|
snapshotSettings = function () {
|
||||||
EventBridge.emitWebEvent("openSettings");
|
EventBridge.emitWebEvent("openSettings");
|
||||||
|
|
|
@ -20,39 +20,62 @@ var button = toolBar.addButton({
|
||||||
alpha: 0.9,
|
alpha: 0.9,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function shouldOpenFeedAfterShare() {
|
||||||
|
var persisted = Settings.getValue('openFeedAfterShare', true); // might answer true, false, "true", or "false"
|
||||||
|
return persisted && (persisted !== 'false');
|
||||||
|
}
|
||||||
function showFeedWindow() {
|
function showFeedWindow() {
|
||||||
DialogsManager.showFeed();
|
DialogsManager.showFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
var openFeed, outstanding;
|
var outstanding;
|
||||||
function confirmShare(data) {
|
function confirmShare(data) {
|
||||||
var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470);
|
var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/ShareSnapshot.html"), 800, 470);
|
||||||
function onMessage(message) {
|
function onMessage(message) {
|
||||||
|
// Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
|
||||||
|
// 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
|
||||||
|
// 2. Although we currently use a single image, we would like to take snapshot, a selfie, a 360 etc. all at the
|
||||||
|
// same time, show the user all of them, and have the user deselect any that they do not want to share.
|
||||||
|
// So we'll ultimately be receiving a set of objects, perhaps with different post processing for each.
|
||||||
|
var isLoggedIn, needsLogin = false;
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case 'ready':
|
case 'ready':
|
||||||
dialog.emitScriptEvent(data); // Send it.
|
dialog.emitScriptEvent(data); // Send it.
|
||||||
openFeed = false;
|
|
||||||
outstanding = 0;
|
outstanding = 0;
|
||||||
break;
|
break;
|
||||||
case 'openSettings':
|
case 'openSettings':
|
||||||
Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog");
|
Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog");
|
||||||
break;
|
break;
|
||||||
|
case 'setOpenFeedFalse':
|
||||||
|
Settings.setValue('openFeedAfterShare', false)
|
||||||
|
break;
|
||||||
|
case 'setOpenFeedTrue':
|
||||||
|
Settings.setValue('openFeedAfterShare', true)
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close?
|
dialog.webEventReceived.disconnect(onMessage); // I'm not certain that this is necessary. If it is, what do we do on normal close?
|
||||||
dialog.close();
|
dialog.close();
|
||||||
dialog.deleteLater();
|
dialog.deleteLater();
|
||||||
|
isLoggedIn = Account.isLoggedIn();
|
||||||
message.forEach(function (submessage) {
|
message.forEach(function (submessage) {
|
||||||
|
if (submessage.share && !isLoggedIn) {
|
||||||
|
needsLogin = true;
|
||||||
|
submessage.share = false;
|
||||||
|
}
|
||||||
if (submessage.share) {
|
if (submessage.share) {
|
||||||
print('sharing', submessage.localPath);
|
print('sharing', submessage.localPath);
|
||||||
outstanding++;
|
outstanding++;
|
||||||
Window.shareSnapshot(submessage.localPath);
|
Window.shareSnapshot(submessage.localPath);
|
||||||
} else if (submessage.openFeed) {
|
} else {
|
||||||
openFeed = true;
|
print('not sharing', submessage.localPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (openFeed && !outstanding) {
|
if (!outstanding && shouldOpenFeedAfterShare()) {
|
||||||
showFeedWindow();
|
showFeedWindow();
|
||||||
}
|
}
|
||||||
|
if (needsLogin) { // after the possible feed, so that the login is on top
|
||||||
|
Account.checkAndSignalForAccessToken();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialog.webEventReceived.connect(onMessage);
|
dialog.webEventReceived.connect(onMessage);
|
||||||
|
@ -67,7 +90,7 @@ function snapshotShared(success) {
|
||||||
// for now just print an error.
|
// for now just print an error.
|
||||||
print('snapshot upload/share failed');
|
print('snapshot upload/share failed');
|
||||||
}
|
}
|
||||||
if ((--outstanding <= 0) && openFeed) {
|
if ((--outstanding <= 0) && shouldOpenFeedAfterShare()) {
|
||||||
showFeedWindow();
|
showFeedWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,8 +137,12 @@ function resetButtons(path, notify) {
|
||||||
|
|
||||||
// last element in data array tells dialog whether we can share or not
|
// last element in data array tells dialog whether we can share or not
|
||||||
confirmShare([
|
confirmShare([
|
||||||
{ localPath: path },
|
{ localPath: path },
|
||||||
{ canShare: Boolean(Window.location.placename), isLoggedIn: Account.isLoggedIn() }
|
{
|
||||||
|
canShare: Boolean(Window.location.placename),
|
||||||
|
isLoggedIn: true, // Just have the dialog act as though we are. To be removed at both ends later.
|
||||||
|
openFeedAfterShare: shouldOpenFeedAfterShare()
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue