overte-thingvellir/scripts/communityScripts/explore/explore.html

629 lines
28 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<link href="../libraries/materialdesignicons/css/materialdesignicons.min.css" rel="stylesheet" />
<link href="../libraries/vuetify/vuetify-v2.3.9.min.css" rel="stylesheet" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
html {
overflow: hidden;
background: black;
}
#component-templates {
display: none;
}
.thumbnailItem .d-flex {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(5px);
text-shadow: 1px 1px 2px black, 0 0 25px black, 0 0 5px black;
}
.thumbnailItemHover .d-flex {
background: rgba(100, 100, 100, 0.2);
}
</style>
<!--<script src="../Explore/js/TestEventBridge.js"></script>-->
</head>
<body>
<div id="component-templates">
<div id="add-location-template">
<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">
<template v-slot:activator="{ on, attrs }">
<v-btn color="grey darken-3" class="ml-5" v-show="permission" value="animations" v-bind="attrs" v-on="on" fab small>
<v-icon color="purple lighten-2">mdi-map-plus</v-icon>
</v-btn>
</template>
<v-card>
<v-toolbar dark color="purple">
<v-btn icon dark @click="closeDialog();">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>Add Location Beacon</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<v-btn dark text @click="addLocation()">Save</v-btn>
</v-toolbar-items>
</v-toolbar>
<v-card-text>
<v-container>
<v-form ref="form"
v-model="valid"
lazy-validation>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="domainName" :rules="domainNameRules" label="Location Name *" required hint="This is the name that shows on the listing" persistent-hint></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="owner" :rules="ownerRules" label="Owner *" hint="Owner name to show in listing" required persistent-hint></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field v-model="port" :rules="portRules" label="Port *" hint="Domain server port number (default 40102)" required persistent-hint></v-text-field>
</v-col>
<v-col cols="12">
<v-select v-model="locationProvider" :rules="locationProviderRules" :items="locationProviders"
label="Location Provider *" required persistent-hint hint="The location provider to use when adding domain."></v-select>
</v-col>
</v-row>
</v-form>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-text>
When you add a location, you are actually creating a location beacon wherever you are currently standing. By default this will show up as a red cube.
If you need to amend the details of your listing at a later date, simply open the Create app in your domain, find the entity with the name "Explore Marker (Your domain name)"
and modify its userData values.
You can also hide the marker easily by toggling the entity's visibility. It will still function when hidden.
</v-card-text>
</v-card>
</v-dialog>
</div>
<div id="add-bookmark-template"></div>
<div id="explore-template">
<div>
<v-container fluid class="pa-0">
<v-data-iterator :items="items"
:items-per-page.sync="itemsPerPage"
:page.sync="page"
:search="visit"
:sort-by="sortBy"
:sort-desc="sortDesc"
:loading="loading"
@page-count="pageCount = $event"
hide-default-footer>
<template v-slot:header>
<v-row dense>
<v-text-field v-model="visit" clearable
flat
solo-inverted
hide-details
label="Visit"
class="pb-3"
append-icon="mdi-location-enter"
v-on:keyup.enter="visitIconCallback"
@click:append="visitIconCallback">
</v-text-field>
</v-row>
</template>
<template v-slot:default="props">
<v-row dense>
<v-col v-for="item in props.items"
:key="item.index"
cols="12"
sm="6"
md="4"
lg="3">
<v-hover v-slot:default="{ hover }">
<v-card @click.native="openLocation(item['location'])"
:color="hover ? 'grey darken-3' : 'grey darken-4'"
:style="item.thumbnail ? 'background-size: cover; background-position: center; background-image: url(' + item.thumbnail +');' : '' "
:class="item.thumbnail ? (hover ? 'thumbnailItem thumbnailItemHover' : 'thumbnailItem') : '' "
>
<div class="d-flex flex-no-wrap justify-space-between">
<div>
<v-card-title class="text-h6 d-block text-truncate pa-2" v-text="item['name']" style="max-width: 400px;"></v-card-title>
<v-card-subtitle class="pa-2 d-block text-truncate" style="max-width: 400px;" v-text="item.owner"></v-card-subtitle>
</div>
<div style="text-align: right;">
<v-card-title align-right class="align-right" v-text="item['userCount']"></v-card-title>
</div>
</div>
</v-card>
</v-hover>
</v-col>
</v-row>
</template>
<template v-slot:footer>
<v-footer absolute dense class="font-weight-medium " color="black">
<v-col class="text-center pa-0"
cols="12">
<v-pagination v-model="page"
color="purple"
:total-visible="8"
:length="pageCount"
prev-icon="mdi-menu-left"
next-icon="mdi-menu-right"></v-pagination>
</v-col>
</v-footer>
</template>
</v-data-iterator>
</v-container>
</div>
</div>
<div id="bookmarks-template">
<div>
<v-container fluid class="pa-0">
<v-data-iterator :items="items"
:items-per-page.sync="itemsPerPage"
:page.sync="page"
:search="search"
:sort-by="sortBy"
:sort-desc="sortDesc"
:loading="loading"
@page-count="pageCount = $event"
hide-default-footer>
<template v-slot:header>
<v-row dense>
<v-col cols="12"
sm="6"
md="4"
lg="3">
<v-text-field v-model="search"
clearable
flat
solo-inverted
hide-details
label="Search"
class="pb-3"></v-text-field>
</v-col>
</v-row>
</template>
<template v-slot:default="props">
<v-row dense>
<v-col v-for="item in props.items"
:key="item.name"
cols="12"
sm="6"
md="4"
lg="3">
<v-card hover @click.native="openLocation(item['location'])">
<div class="d-flex flex-no-wrap justify-space-between">
<div>
<v-card-title class="text-h6 d-block text-truncate pa-2" v-text="item['name']" style="max-width: 400px;"></v-card-title>
</div>
</div>
</v-card>
</v-col>
</v-row>
</template>
<template v-slot:footer>
<v-footer absolute dense class="font-weight-medium " color="black">
<v-col class="text-center pa-0"
cols="12">
<v-pagination v-model="page"
color="purple"
:total-visible="8"
:length="pageCount"
prev-icon="mdi-menu-left"
next-icon="mdi-menu-right"></v-pagination>
</v-col>
</v-footer>
</template>
</v-data-iterator>
</v-container>
</div>
</div>
<div id="events-template">
<div>events from template</div>
</div>
<div id="settings-template">
<div>settings from template</div>
</div>
</div>
<div id="app">
<v-app>
<div>
<v-app-bar dense>
<v-toolbar-title>Explore</v-toolbar-title>
<v-spacer></v-spacer>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" color="grey darken-3" class="mx-1" v-show="isHomeSet" value="animations" fab small @click="navigateHome()">
<v-icon color="purple lighten-2">mdi-home</v-icon>
</v-btn>
</template>
<span>Go Home</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" color="grey darken-3" class="mx-1" value="animations" fab small @click="navigateBack()">
<v-icon color="purple lighten-2">mdi-arrow-left-bold</v-icon>
</v-btn>
</template>
<span>Go Back</span>
</v-tooltip>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" color="grey darken-3" class="mx-1" value="animations" fab small @click="navigateForward()">
<v-icon color="purple lighten-2">mdi-arrow-right-bold</v-icon>
</v-btn>
</template>
<span>Go Forward</span>
</v-tooltip>
<!--<v-btn value="animations" fab small>
<v-icon color="purple">mdi-map-marker-plus</v-icon>
</v-btn>-->
<add-location ref="addLocation" :permission="permission"></add-location>
</v-app-bar>
</div>
<v-main>
<v-container>
<router-view></router-view>
</v-container>
</v-main>
<v-bottom-navigation grow color="purple lighten-1" shift v-model="activeItem">
<v-btn value="explore" :to="{ path: '/'}">
<span>Explore</span>
<v-icon>mdi-map-search-outline</v-icon>
</v-btn>
<!--<v-btn value="bookmarks" :to="{ path: '/bookmarks'}">
<span>Bookmarks</span>
<v-icon>mdi-map-marker</v-icon>
</v-btn>-->
<!--<v-btn value="events" :to="{ path: '/events'}" >
<span>Events</span>
<v-icon>mdi-calendar-month</v-icon>
</v-btn>-->
<!--<v-btn value="settings" :to="{ path: '/settings'}">
<span>Settings</span>
<v-icon>mdi-cog</v-icon>
</v-btn>-->
</v-bottom-navigation>
</v-app>
</div>
<script src="../libraries/vue/vue.min.js"></script>
<script src="../libraries/vue-router/vue-router.js"></script>
<script src="../libraries/vuetify/vuetify-v2.3.9.js"></script>
<script src="../libraries/axios/axios.min.js"></script>
<script>
var exploreComponent = null;
var locationData = null;
var placesData = null;
function blendedData() {
var joinedData = [];
if (locationData != null) {
for (var i = 0; i < locationData.length; i++) {
joinedData.push({
"index": joinedData.length,
"location": locationData[i].Visit,
"name": locationData[i]['Domain Name'],
"owner": locationData[i].Owner,
"userCount": locationData[i].People,
"description": "",
"thumbnail": ""
});
}
}
if (placesData != null) {
var activeThresholdTime = new Date();
activeThresholdTime.setMinutes(activeThresholdTime.getMinutes() - 15);
// while blending directory server data ensure it is cleaned for active domains
for (var i = 0; i < placesData.length; i++) {
if (placesData[i].domain.active && placesData[i].visibility == "open" && new Date(placesData[i].last_activity_update) > activeThresholdTime) {
joinedData.push({
"index": joinedData.length,
"location": placesData[i].name,
"name": placesData[i].name,
"owner": placesData[i].managers[0],
"userCount": placesData[i].current_attendance,
"description": placesData[i].description,
"thumbnail": placesData[i].thumbnail
});
}
}
}
return joinedData;
}
function getAddressListData(component) {
exploreComponent = component;
// if json data not loaded fire emti event for it
if (locationData == null) {
var readyEvent = {
"action": "requestAddressList",
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
}
// if directory server data not loaded fire a request for it
if (placesData == null) {
// fire off data get against the directory server
axios.get("https://mv.overte.org/server/api/v1/places")
.then((response) => {
placesData = response.data.data.places;
exploreComponent.updateData(blendedData());
})
.catch(function (error) {
console.log(error);
});
}
}
var bookmarksComponent = null;
var bookmarksData = null;
function getBookmarkListData(component) {
bookmarksComponent = component;
if (bookmarksData != null) {
bookmarksComponent.updateData(bookmarksData);
return;
}
var readyEvent = {
"action": "requestBookmarksList",
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
}
var addLocationComponent = null;
var addLocationData = null;
function getAddLocationData(component) {
addLocationComponent = component;
if (addLocationData != null) {
addLocationComponent.updateData(addLocationData);
return;
}
var readyEvent = {
"action": "retrievePortInformation",
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
}
const Explore = {
data() {
return {
loading: true,
visit: '',
filter: {},
sortDesc: true,
page: 1,
pageCount: 0,
itemsPerPage: 6,
sortBy: 'userCount',
items: [],
}
},
template: document.getElementById("explore-template").innerHTML,
created() {
this.fetchData();
},
methods: {
openLocation(url) {
var readyEvent = {
"action": "goToUrl",
"visit": url,
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
},
visitIconCallback() {
if (this.visit !== "") {
this.openLocation(this.visit);
}
},
fetchData() {
this.error = this.post = null;
this.loading = true;
getAddressListData(this);
},
updateData(data) {
this.items = data;
this.loading = false;
}
}
};
const Bookmarks =
{
data() {
return {
loading: true,
search: '',
visit: '',
filter: {},
sortDesc: true,
page: 1,
pageCount: 0,
itemsPerPage: 6,
sortBy: 'name',
items: [], //testLocationData,
}
},
template: document.getElementById("bookmarks-template").innerHTML,
created() {
this.fetchData();
},
methods: {
openLocation(url) {
var readyEvent = {
"action": "goToUrl",
"visit": url,
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
},
fetchData() {
this.error = this.post = null;
this.loading = true;
getBookmarkListData(this);
},
updateData(data) {
this.items = data;
this.loading = false;
}
}
};
const Events = { template: document.getElementById("events-template").innerHTML };
const Settings = { template: document.getElementById("settings-template").innerHTML }
const AddLocation = {
data() {
return {
valid: true,
//permission: false,
dialog: false,
domainName: "",
domainNameRules: [
v => !!v || 'Domain Display Name is required',
],
owner: "",
ownerRules: [
v => !!v || 'Owner is required',
],
port: "40102",
portRules: [
v => !!v || 'Port is required',
v => /^[0-9]{1,5}$/.test(v) || 'Must be a valid port number'
],
locationProvider: "",
locationProviderRules: [
v => !!v || 'Location Provider is required',
],
locationProviders: []
}
},
watch: {
dialog: function (val) {
if (val) {
this.opening();
}
},
},
props: {
permission: false
},
template: document.getElementById("add-location-template").innerHTML,
methods: {
opening() {
getAddLocationData(this);
},
updateData(data) {
this.locationProviders = data;
this.locationProvider = data[0];
},
addLocation() {
if (this.$refs.form.validate()) {
var readyEvent = {
"action": "addLocation",
"domainName": this.domainName,
"owner": this.owner,
"Port": this.port,
"script": this.locationProvider
};
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
this.closeDialog();
}
},
closeDialog() {
this.domainName = "";
this.owner = "";
this.port = "40102";
this.locationProvider = "Current";
this.dialog = false;
this.$refs.form.resetValidation();
}
}
}
const routes = [
{ path: '/', component: Explore },
{ path: '/bookmarks', component: Bookmarks },
{ path: '/events', component: Events },
{ path: '/settings', component: Settings }
];
const router = new VueRouter({ routes });
const vm = new Vue({
router,
el: '#app',
data: {
activeItem: string = 'explore',
permission: false,
isHomeSet: false,
},
vuetify: new Vuetify({
theme: {
dark: true,
},
}),
components: {
'add-location': AddLocation
},
methods: {
navigateHome() {
var readyEvent = {
"action": "navigateHome"
}
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
},
navigateBack() {
var readyEvent = {
"action": "navigateBack"
}
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
},
navigateForward() {
var readyEvent = {
"action": "navigateForward"
}
EventBridge.emitWebEvent(JSON.stringify(readyEvent));
}
},
created: function () {
const self = this;
EventBridge.scriptEventReceived.connect(function (message) {
var messageData = JSON.parse(message);
if (messageData.action == "addressListv2") {
self.permission = messageData.permission;
locationData = messageData.myAddress;
exploreComponent.updateData(blendedData());
self.isHomeSet = messageData.isHomeSet;
}
if (messageData.action == "bookmarksList") {
bookmarksData = messageData.bookmarks;
bookmarksComponent.updateData(bookmarksData);
}
if (messageData.action == "retrievePortInformationResponse") {
addLocationData = messageData.goToAddresses;
for (let i = 0; i < addLocationData.length; i++) {
var metaverseProvider = addLocationData[i].split("/")[2].split(":")[0];
addLocationData[i] = "https://" + metaverseProvider + "/interim/d-goto/app/decentralizedGoToServerScript.js";
}
addLocationComponent.updateData(addLocationData);
}
});
}
});
</script>
</body>
</html>