mirror of
https://github.com/lubosz/overte.git
synced 2025-04-16 09:29:16 +02:00
Merge pull request #10253 from howard-stearns/new-address-bar
New address bar
This commit is contained in:
commit
5d6e24b495
3 changed files with 309 additions and 298 deletions
|
@ -35,6 +35,7 @@ Rectangle {
|
|||
property string timePhrase: pastTime(timestamp);
|
||||
property int onlineUsers: 0;
|
||||
property bool isConcurrency: action === 'concurrency';
|
||||
property bool isAnnouncement: action === 'announcement';
|
||||
property bool isStacked: !isConcurrency && drillDownToPlace;
|
||||
|
||||
property int textPadding: 10;
|
||||
|
@ -80,7 +81,7 @@ Rectangle {
|
|||
id: lobby;
|
||||
visible: !hasGif || (animation.status !== Image.Ready);
|
||||
width: parent.width - (isConcurrency ? 0 : (2 * smallMargin));
|
||||
height: parent.height - (isConcurrency ? 0 : smallMargin);
|
||||
height: parent.height - messageHeight - (isConcurrency ? 0 : smallMargin);
|
||||
source: thumbnail || defaultThumbnail;
|
||||
fillMode: Image.PreserveAspectCrop;
|
||||
anchors {
|
||||
|
@ -129,7 +130,7 @@ Rectangle {
|
|||
property int dropSamples: 9;
|
||||
property int dropSpread: 0;
|
||||
DropShadow {
|
||||
visible: true;
|
||||
visible: showPlace; // Do we have to check for whatever the modern equivalent is for desktop.gradientsSupported?
|
||||
source: place;
|
||||
anchors.fill: place;
|
||||
horizontalOffset: dropHorizontalOffset;
|
||||
|
@ -139,12 +140,12 @@ Rectangle {
|
|||
color: hifi.colors.black;
|
||||
spread: dropSpread;
|
||||
}
|
||||
RalewayLight {
|
||||
RalewaySemiBold {
|
||||
id: place;
|
||||
visible: showPlace;
|
||||
text: placeName;
|
||||
color: hifi.colors.white;
|
||||
size: 38;
|
||||
size: textSize;
|
||||
elide: Text.ElideRight; // requires constrained width
|
||||
anchors {
|
||||
top: parent.top;
|
||||
|
@ -153,57 +154,44 @@ Rectangle {
|
|||
margins: textPadding;
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: rectRow
|
||||
z: 1
|
||||
width: message.width + (users.visible ? users.width + bottomRow.spacing : 0)
|
||||
+ (icon.visible ? icon.width + bottomRow.spacing: 0) + bottomRow.spacing;
|
||||
height: messageHeight + 1;
|
||||
radius: 25
|
||||
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
leftMargin: textPadding
|
||||
bottomMargin: textPadding
|
||||
Row {
|
||||
FiraSansRegular {
|
||||
id: users;
|
||||
visible: isConcurrency || isAnnouncement;
|
||||
text: onlineUsers;
|
||||
size: textSize;
|
||||
color: messageColor;
|
||||
anchors.verticalCenter: message.verticalCenter;
|
||||
}
|
||||
|
||||
Row {
|
||||
id: bottomRow
|
||||
FiraSansRegular {
|
||||
id: users;
|
||||
visible: isConcurrency;
|
||||
text: onlineUsers;
|
||||
size: textSize;
|
||||
color: messageColor;
|
||||
anchors.verticalCenter: message.verticalCenter;
|
||||
}
|
||||
Image {
|
||||
id: icon;
|
||||
source: "../../images/snap-icon.svg"
|
||||
width: 40;
|
||||
height: 40;
|
||||
visible: action === 'snapshot';
|
||||
}
|
||||
RalewayRegular {
|
||||
id: message;
|
||||
text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (drillDownToPlace ? "snapshots" : ("by " + userName));
|
||||
size: textSizeSmall;
|
||||
color: messageColor;
|
||||
elide: Text.ElideRight; // requires a width to be specified`
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
bottomMargin: parent.spacing;
|
||||
}
|
||||
}
|
||||
spacing: textPadding;
|
||||
height: messageHeight;
|
||||
Image {
|
||||
id: icon;
|
||||
source: "../../images/snap-icon.svg"
|
||||
width: 40;
|
||||
height: 40;
|
||||
visible: action === 'snapshot';
|
||||
}
|
||||
RalewayRegular {
|
||||
id: message;
|
||||
text: isConcurrency ? ((onlineUsers === 1) ? "person" : "people") : (isAnnouncement ? "connections" : (drillDownToPlace ? "snapshots" : ("by " + userName)));
|
||||
size: textSizeSmall;
|
||||
color: messageColor;
|
||||
elide: Text.ElideRight; // requires a width to be specified`
|
||||
width: root.width - textPadding
|
||||
- (users.visible ? users.width + parent.spacing : 0)
|
||||
- (icon.visible ? icon.width + parent.spacing : 0)
|
||||
- (actionIcon.width + (2 * smallMargin));
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
leftMargin: 4
|
||||
bottomMargin: parent.spacing;
|
||||
}
|
||||
}
|
||||
spacing: textPadding;
|
||||
height: messageHeight;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
left: parent.left;
|
||||
leftMargin: textPadding;
|
||||
}
|
||||
}
|
||||
// These two can be supplied to provide hover behavior.
|
||||
// For example, AddressBarDialog provides functions that set the current list view item
|
||||
|
@ -218,37 +206,22 @@ Rectangle {
|
|||
onEntered: hoverThunk();
|
||||
onExited: unhoverThunk();
|
||||
}
|
||||
Rectangle {
|
||||
id: rectIcon
|
||||
z: 1
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 15
|
||||
StateImage {
|
||||
id: actionIcon;
|
||||
imageURL: "../../images/info-icon-2-state.svg";
|
||||
size: 32;
|
||||
buttonState: messageArea.containsMouse ? 1 : 0;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
right: parent.right;
|
||||
bottomMargin: textPadding;
|
||||
rightMargin: textPadding;
|
||||
}
|
||||
|
||||
StateImage {
|
||||
id: actionIcon;
|
||||
imageURL: "../../images/info-icon-2-state.svg";
|
||||
size: 32;
|
||||
buttonState: messageArea.containsMouse ? 1 : 0;
|
||||
anchors {
|
||||
bottom: parent.bottom;
|
||||
right: parent.right;
|
||||
//margins: smallMargin;
|
||||
}
|
||||
margins: smallMargin;
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: messageArea;
|
||||
width: rectIcon.width;
|
||||
height: rectIcon.height;
|
||||
anchors.fill: rectIcon
|
||||
width: parent.width;
|
||||
height: messageHeight;
|
||||
anchors.top: lobby.bottom;
|
||||
acceptedButtons: Qt.LeftButton;
|
||||
onClicked: goFunction(drillDownToPlace ? ("/places/" + placeName) : ("/user_stories/" + storyId));
|
||||
hoverEnabled: true;
|
||||
|
|
201
interface/resources/qml/hifi/Feed.qml
Normal file
201
interface/resources/qml/hifi/Feed.qml
Normal file
|
@ -0,0 +1,201 @@
|
|||
//
|
||||
// Feed.qml
|
||||
// qml/hifi
|
||||
//
|
||||
// Displays a particular type of feed
|
||||
//
|
||||
// Created by Howard Stearns on 4/18/2017
|
||||
// Copyright 2016 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
|
||||
import QtQuick 2.5
|
||||
import QtGraphicalEffects 1.0
|
||||
import "toolbars"
|
||||
import "../styles-uit"
|
||||
|
||||
Column {
|
||||
id: root;
|
||||
visible: false;
|
||||
|
||||
property int cardWidth: 212;
|
||||
property int cardHeight: 152;
|
||||
property int stackedCardShadowHeight: 10;
|
||||
property string metaverseServerUrl: '';
|
||||
property string actions: 'snapshot';
|
||||
onActionsChanged: fillDestinations();
|
||||
Component.onCompleted: fillDestinations();
|
||||
property string labelText: actions;
|
||||
property string filter: '';
|
||||
onFilterChanged: filterChoicesByText();
|
||||
property var goFunction: null;
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
ListModel { id: suggestions; }
|
||||
|
||||
function resolveUrl(url) {
|
||||
return (url.indexOf('/') === 0) ? (metaverseServerUrl + url) : url;
|
||||
}
|
||||
function makeModelData(data) { // create a new obj from data
|
||||
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
|
||||
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
|
||||
var name = data.place_name,
|
||||
tags = data.tags || [data.action, data.username],
|
||||
description = data.description || "",
|
||||
thumbnail_url = data.thumbnail_url || "";
|
||||
if (actions === 'concurrency,snapshot') {
|
||||
// A temporary hack for simulating announcements. We won't use this in production, but if requested, we'll use this data like announcements.
|
||||
data.details.connections = 4;
|
||||
data.action = 'announcement';
|
||||
}
|
||||
return {
|
||||
place_name: name,
|
||||
username: data.username || "",
|
||||
path: data.path || "",
|
||||
created_at: data.created_at || "",
|
||||
action: data.action || "",
|
||||
thumbnail_url: resolveUrl(thumbnail_url),
|
||||
image_url: resolveUrl(data.details && data.details.image_url),
|
||||
|
||||
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||
|
||||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.details.connections || data.details.concurrency || 0,
|
||||
drillDownToPlace: false,
|
||||
|
||||
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
|
||||
}
|
||||
}
|
||||
property var allStories: [];
|
||||
property var placeMap: ({}); // Used for making stacks.
|
||||
property int requestId: 0;
|
||||
function getUserStoryPage(pageNumber, cb, cb1) { // cb(error) after all pages of domain data have been added to model
|
||||
// If supplied, cb1 will be run after the first page IFF it is not the last, for responsiveness.
|
||||
var options = [
|
||||
'now=' + new Date().toISOString(),
|
||||
'include_actions=' + actions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
var thisRequestId = ++requestId;
|
||||
getRequest(url, function (error, data) {
|
||||
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
|
||||
return; // abandon stale requests
|
||||
}
|
||||
allStories = allStories.concat(data.user_stories.map(makeModelData));
|
||||
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
|
||||
if ((pageNumber === 1) && cb1) {
|
||||
cb1();
|
||||
}
|
||||
return getUserStoryPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
function fillDestinations() { // Public
|
||||
var filter = makeFilteredStoryProcessor(), counter = 0;
|
||||
allStories = [];
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
getUserStoryPage(1, function (error) {
|
||||
allStories.slice(counter).forEach(filter);
|
||||
console.log('user stories query', actions, error || 'ok', allStories.length, 'filtered to', suggestions.count);
|
||||
root.visible = !!suggestions.count;
|
||||
}, function () { // If there's more than a page, put what we have in the model right away, keeping track of how many are processed.
|
||||
allStories.forEach(function (story) {
|
||||
counter++;
|
||||
filter(story);
|
||||
root.visible = !!suggestions.count;
|
||||
});
|
||||
});
|
||||
}
|
||||
function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches
|
||||
var words = filter.toUpperCase().split(/\s+/).filter(identity);
|
||||
function suggestable(story) { // fixme add to makeFilteredStoryProcessor
|
||||
if (story.action === 'snapshot') {
|
||||
return true;
|
||||
}
|
||||
return (story.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
|
||||
}
|
||||
function matches(story) {
|
||||
if (!words.length) {
|
||||
return suggestable(story);
|
||||
}
|
||||
return words.every(function (word) {
|
||||
return story.searchText.indexOf(word) >= 0;
|
||||
});
|
||||
}
|
||||
function addToSuggestions(place) {
|
||||
var collapse = ((actions === 'concurrency,snapshot') && (place.action !== 'concurrency')) || (place.action === 'announcement');
|
||||
if (collapse) {
|
||||
var existing = placeMap[place.place_name];
|
||||
if (existing) {
|
||||
existing.drillDownToPlace = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
suggestions.append(place);
|
||||
if (collapse) {
|
||||
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
|
||||
} else if (place.action === 'concurrency') {
|
||||
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
|
||||
}
|
||||
}
|
||||
return function (story) {
|
||||
if (matches(story)) {
|
||||
addToSuggestions(story);
|
||||
}
|
||||
};
|
||||
}
|
||||
function filterChoicesByText() {
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
allStories.forEach(makeFilteredStoryProcessor());
|
||||
root.visible = !!suggestions.count;
|
||||
}
|
||||
|
||||
RalewayLight {
|
||||
id: label;
|
||||
text: labelText;
|
||||
color: hifi.colors.white;
|
||||
size: 28;
|
||||
}
|
||||
ListView {
|
||||
id: scroll;
|
||||
clip: true;
|
||||
model: suggestions;
|
||||
orientation: ListView.Horizontal;
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
|
||||
|
||||
spacing: 14;
|
||||
width: parent.width;
|
||||
height: cardHeight + stackedCardShadowHeight;
|
||||
delegate: Card {
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: root.goFunction;
|
||||
userName: model.username;
|
||||
placeName: model.place_name;
|
||||
hifiUrl: model.place_name + model.path;
|
||||
thumbnail: model.thumbnail_url;
|
||||
imageUrl: model.image_url;
|
||||
action: model.action;
|
||||
timestamp: model.created_at;
|
||||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
drillDownToPlace: model.drillDownToPlace;
|
||||
shadowHeight: stackedCardShadowHeight;
|
||||
hoverThunk: function () { scroll.currentIndex = index; }
|
||||
unhoverThunk: function () { scroll.currentIndex = -1; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,18 +30,15 @@ StackView {
|
|||
width: parent !== null ? parent.width : undefined
|
||||
height: parent !== null ? parent.height : undefined
|
||||
property var eventBridge;
|
||||
property var allStories: [];
|
||||
property int cardWidth: 460;
|
||||
property int cardHeight: 320;
|
||||
property int cardWidth: 212;
|
||||
property int cardHeight: 152;
|
||||
property string metaverseBase: addressBarDialog.metaverseServerUrl + "/api/v1/";
|
||||
|
||||
property var tablet: null;
|
||||
|
||||
Component { id: tabletWebView; TabletWebView {} }
|
||||
Component.onCompleted: {
|
||||
fillDestinations();
|
||||
updateLocationText(false);
|
||||
fillDestinations();
|
||||
addressLine.focus = !HMD.active;
|
||||
root.parentChanged.connect(center);
|
||||
center();
|
||||
|
@ -190,7 +187,6 @@ StackView {
|
|||
}
|
||||
font.pixelSize: hifi.fonts.pixelSize * 0.75
|
||||
onTextChanged: {
|
||||
filterChoicesByText();
|
||||
updateLocationText(text.length > 0);
|
||||
}
|
||||
onAccepted: {
|
||||
|
@ -225,109 +221,68 @@ StackView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: topBar
|
||||
height: 37
|
||||
color: hifiStyleConstants.colors.white
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.topMargin: 0
|
||||
anchors.top: addressBar.bottom
|
||||
|
||||
Row {
|
||||
id: thing
|
||||
spacing: 5 * hifi.layout.spacing
|
||||
|
||||
anchors {
|
||||
top: parent.top;
|
||||
left: parent.left
|
||||
leftMargin: 25
|
||||
}
|
||||
|
||||
TabletTextButton {
|
||||
id: allTab;
|
||||
text: "ALL";
|
||||
property string includeActions: 'snapshot,concurrency';
|
||||
selected: allTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
|
||||
TabletTextButton {
|
||||
id: placeTab;
|
||||
text: "PLACES";
|
||||
property string includeActions: 'concurrency';
|
||||
selected: placeTab === selectedTab;
|
||||
action: tabSelect;
|
||||
|
||||
}
|
||||
|
||||
TabletTextButton {
|
||||
id: snapTab;
|
||||
text: "SNAP";
|
||||
property string includeActions: 'snapshot';
|
||||
selected: snapTab === selectedTab;
|
||||
action: tabSelect;
|
||||
}
|
||||
id: bgMain;
|
||||
color: hifiStyleConstants.colors.faintGray50;
|
||||
anchors {
|
||||
top: addressBar.bottom;
|
||||
bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom;
|
||||
left: parent.left;
|
||||
right: parent.right;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bgMain
|
||||
color: hifiStyleConstants.colors.white
|
||||
anchors.bottom: parent.keyboardEnabled ? keyboard.top : parent.bottom
|
||||
anchors.bottomMargin: 0
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 0
|
||||
anchors.top: topBar.bottom
|
||||
anchors.topMargin: 0
|
||||
|
||||
ListModel { id: suggestions }
|
||||
|
||||
ListView {
|
||||
id: scroll
|
||||
|
||||
property int stackedCardShadowHeight: 0;
|
||||
clip: true
|
||||
spacing: 14
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: 10
|
||||
ScrollView {
|
||||
anchors.fill: bgMain;
|
||||
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff;
|
||||
verticalScrollBarPolicy: Qt.ScrollBarAsNeeded;
|
||||
Rectangle { // Column margins require QtQuick 2.7, which we don't use yet.
|
||||
id: column;
|
||||
property real pad: 10;
|
||||
width: bgMain.width - column.pad;
|
||||
height: stack.height;
|
||||
color: "transparent";
|
||||
anchors {
|
||||
left: parent.left;
|
||||
leftMargin: column.pad;
|
||||
topMargin: column.pad;
|
||||
}
|
||||
Column {
|
||||
id: stack;
|
||||
width: column.width;
|
||||
spacing: column.pad;
|
||||
Feed {
|
||||
id: happeningNow;
|
||||
width: parent.width;
|
||||
property real cardScale: 1.5;
|
||||
cardWidth: places.cardWidth * happeningNow.cardScale;
|
||||
cardHeight: places.cardHeight * happeningNow.cardScale;
|
||||
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
|
||||
labelText: 'Happening Now';
|
||||
//actions: 'concurrency,snapshot'; // uncomment this line instead of next to produce fake announcement data for testing.
|
||||
actions: 'announcement';
|
||||
filter: addressLine.text;
|
||||
goFunction: goCard;
|
||||
}
|
||||
Feed {
|
||||
id: places;
|
||||
width: parent.width;
|
||||
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
|
||||
labelText: 'Places';
|
||||
actions: 'concurrency';
|
||||
filter: addressLine.text;
|
||||
goFunction: goCard;
|
||||
}
|
||||
Feed {
|
||||
id: snapshots;
|
||||
width: parent.width;
|
||||
metaverseServerUrl: addressBarDialog.metaverseServerUrl;
|
||||
labelText: 'Recent Activity';
|
||||
actions: 'snapshot';
|
||||
filter: addressLine.text;
|
||||
goFunction: goCard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: suggestions
|
||||
orientation: ListView.Vertical
|
||||
|
||||
delegate: Card {
|
||||
width: cardWidth;
|
||||
height: cardHeight;
|
||||
goFunction: goCard;
|
||||
userName: model.username;
|
||||
placeName: model.place_name;
|
||||
hifiUrl: model.place_name + model.path;
|
||||
thumbnail: model.thumbnail_url;
|
||||
imageUrl: model.image_url;
|
||||
action: model.action;
|
||||
timestamp: model.created_at;
|
||||
onlineUsers: model.online_users;
|
||||
storyId: model.metaverseId;
|
||||
drillDownToPlace: model.drillDownToPlace;
|
||||
shadowHeight: scroll.stackedCardShadowHeight;
|
||||
hoverThunk: function () { scroll.currentIndex = index; }
|
||||
unhoverThunk: function () { scroll.currentIndex = -1; }
|
||||
}
|
||||
|
||||
highlightMoveDuration: -1;
|
||||
highlightMoveVelocity: -1;
|
||||
highlight: Rectangle { color: "transparent"; border.width: 4; border.color: hifiStyleConstants.colors.blueHighlight; z: 1; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,124 +364,6 @@ StackView {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
function resolveUrl(url) {
|
||||
return (url.indexOf('/') === 0) ? (addressBarDialog.metaverseServerUrl + url) : url;
|
||||
}
|
||||
|
||||
function makeModelData(data) { // create a new obj from data
|
||||
// ListModel elements will only ever have those properties that are defined by the first obj that is added.
|
||||
// So here we make sure that we have all the properties we need, regardless of whether it is a place data or user story.
|
||||
var name = data.place_name,
|
||||
tags = data.tags || [data.action, data.username],
|
||||
description = data.description || "",
|
||||
thumbnail_url = data.thumbnail_url || "";
|
||||
return {
|
||||
place_name: name,
|
||||
username: data.username || "",
|
||||
path: data.path || "",
|
||||
created_at: data.created_at || "",
|
||||
action: data.action || "",
|
||||
thumbnail_url: resolveUrl(thumbnail_url),
|
||||
image_url: resolveUrl(data.details.image_url),
|
||||
|
||||
metaverseId: (data.id || "").toString(), // Some are strings from server while others are numbers. Model objects require uniformity.
|
||||
|
||||
tags: tags,
|
||||
description: description,
|
||||
online_users: data.details.concurrency || 0,
|
||||
drillDownToPlace: false,
|
||||
|
||||
searchText: [name].concat(tags, description || []).join(' ').toUpperCase()
|
||||
}
|
||||
}
|
||||
function suggestable(place) {
|
||||
if (place.action === 'snapshot') {
|
||||
return true;
|
||||
}
|
||||
return (place.place_name !== AddressManager.placename); // Not our entry, but do show other entry points to current domain.
|
||||
}
|
||||
property var selectedTab: allTab;
|
||||
function tabSelect(textButton) {
|
||||
selectedTab = textButton;
|
||||
fillDestinations();
|
||||
}
|
||||
property var placeMap: ({});
|
||||
function addToSuggestions(place) {
|
||||
var collapse = allTab.selected && (place.action !== 'concurrency');
|
||||
if (collapse) {
|
||||
var existing = placeMap[place.place_name];
|
||||
if (existing) {
|
||||
existing.drillDownToPlace = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
suggestions.append(place);
|
||||
if (collapse) {
|
||||
placeMap[place.place_name] = suggestions.get(suggestions.count - 1);
|
||||
} else if (place.action === 'concurrency') {
|
||||
suggestions.get(suggestions.count - 1).drillDownToPlace = true; // Don't change raw place object (in allStories).
|
||||
}
|
||||
}
|
||||
property int requestId: 0;
|
||||
function getUserStoryPage(pageNumber, cb) { // cb(error) after all pages of domain data have been added to model
|
||||
var options = [
|
||||
'now=' + new Date().toISOString(),
|
||||
'include_actions=' + selectedTab.includeActions,
|
||||
'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'),
|
||||
'require_online=true',
|
||||
'protocol=' + encodeURIComponent(AddressManager.protocolVersion()),
|
||||
'page=' + pageNumber
|
||||
];
|
||||
var url = metaverseBase + 'user_stories?' + options.join('&');
|
||||
var thisRequestId = ++requestId;
|
||||
getRequest(url, function (error, data) {
|
||||
if ((thisRequestId !== requestId) || handleError(url, error, data, cb)) {
|
||||
return;
|
||||
}
|
||||
var stories = data.user_stories.map(function (story) { // explicit single-argument function
|
||||
return makeModelData(story, url);
|
||||
});
|
||||
allStories = allStories.concat(stories);
|
||||
stories.forEach(makeFilteredPlaceProcessor());
|
||||
if ((data.current_page < data.total_pages) && (data.current_page <= 10)) { // just 10 pages = 100 stories for now
|
||||
return getUserStoryPage(pageNumber + 1, cb);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
function makeFilteredPlaceProcessor() { // answer a function(placeData) that adds it to suggestions if it matches
|
||||
var words = addressLine.text.toUpperCase().split(/\s+/).filter(identity),
|
||||
data = allStories;
|
||||
function matches(place) {
|
||||
if (!words.length) {
|
||||
return suggestable(place);
|
||||
}
|
||||
return words.every(function (word) {
|
||||
return place.searchText.indexOf(word) >= 0;
|
||||
});
|
||||
}
|
||||
return function (place) {
|
||||
if (matches(place)) {
|
||||
addToSuggestions(place);
|
||||
}
|
||||
};
|
||||
}
|
||||
function filterChoicesByText() {
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
allStories.forEach(makeFilteredPlaceProcessor());
|
||||
}
|
||||
|
||||
function fillDestinations() {
|
||||
allStories = [];
|
||||
suggestions.clear();
|
||||
placeMap = {};
|
||||
getUserStoryPage(1, function (error) {
|
||||
console.log('user stories query', error || 'ok', allStories.length);
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocationText(enteringAddress) {
|
||||
if (enteringAddress) {
|
||||
notice.text = "Go To a place, @user, path, or network address:";
|
||||
|
|
Loading…
Reference in a new issue