overte/scripts/system/inventory/inventory.html
2020-04-04 06:42:19 -04:00

805 lines
No EOL
30 KiB
HTML

<!--
//
// inventory.html
//
// Created by kasenvr@gmail.com on 2 Mar 2020
// Copyright 2020 Vircadia Contributors
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
-->
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<!-- <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet"> -->
<link href="./styles/vuetify.css" rel="stylesheet">
<link href="./styles/styles.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="inventoryApp">
<v-app>
<v-app-bar
app
>
<v-app-bar-nav-icon @click="drawer = true"></v-app-bar-nav-icon>
<v-toolbar-title>Inventory</v-toolbar-title>
</v-app-bar>
<v-navigation-drawer
v-model="drawer"
fixed
temporary
>
<v-list
nav
>
<v-list-item-group
>
<v-list-item @click="addDialog.show = true">
<v-list-item-icon>
<v-icon>mdi-plus</v-icon>
</v-list-item-icon>
<v-list-item-title>Add Item</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-navigation-drawer>
<v-content>
<v-container fluid>
<v-data-iterator
:items="items"
hide-default-footer
>
<template>
<v-row>
<v-col
v-for="item in items"
v-bind:key="item.url"
cols="12"
sm="6"
md="4"
lg="3"
>
<v-card
class="mx-auto"
max-width="344"
outlined
>
<v-list-item three-line>
<v-list-item-content class="pb-1 pt-2">
<div class="overline" style="font-size: 0.825rem !important;">{{item.type}}</div>
<v-list-item-title class="subtitle-1 mb-1">{{item.name}}</v-list-item-title>
<v-list-item-subtitle>{{item.url}}</v-list-item-subtitle>
</v-list-item-content>
<v-menu bottom left>
<template v-slot:activator="{ on }">
<v-btn
:style="{backgroundColor: (getIconColor(item.type)) }"
fab
x-large
dark
v-on="on"
>
<v-icon>{{displayIcon(item.type)}}</v-icon>
</v-btn>
</template>
<v-list color="grey darken-3">
<v-list-item
@click="useItem(item.type, item.url)"
>
<v-list-item-title>Use</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-play</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="
editDialog.show = true;
editDialog.url = item.url;
editDialog.data.type = item.type;
editDialog.data.name = item.name;
editDialog.data.url = item.url;
"
>
<v-list-item-title>Edit</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-pencil</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="shareDialog.show = true; shareDialog.data.url = item.url; sendAppMessage('web-to-script-request-nearby-users', '')"
>
<v-list-item-title>Share</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-share</v-icon>
</v-list-item-action>
</v-list-item>
<v-list-item
@click="removeDialog.show = true; removeDialog.url = item.url;"
color="red darken-1"
>
<v-list-item-title>Remove</v-list-item-title>
<v-list-item-action>
<v-icon>mdi-minus</v-icon>
</v-list-item-action>
</v-list-item>
</v-list>
</v-menu>
</v-list-item>
</v-card>
</v-col>
</v-row>
</template>
</v-data-iterator>
</v-container>
</v-content>
<v-dialog
v-model="removeDialog.show"
max-width="290"
>
<v-card>
<v-card-title class="headline">Remove Item</v-card-title>
<v-card-text>
Are you sure you want to delete this item from your inventory?
</v-card-text>
<v-card-actions>
<v-btn
color="blue"
class="px-3"
@click="removeDialog.show = false"
>
No
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="red"
class="px-3"
@click="removeDialog.show = false; removeItem(removeDialog.url);"
>
Yes
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog
v-model="editDialog.show"
max-width="380"
>
<v-card>
<v-card-title class="headline">Edit Item</v-card-title>
<v-form
ref="editForm"
v-model="editDialog.valid"
:lazy-validation="false"
>
<v-text-field
class="px-2"
label="Type"
v-model="editDialog.data.type"
:rules="[v => !!v || 'Type is required.']"
required
></v-text-field>
<v-text-field
class="px-2"
label="Name"
v-model="editDialog.data.name"
:rules="[v => !!v || 'Name is required.']"
required
></v-text-field>
<v-text-field
class="px-2"
label="URL"
v-model="editDialog.data.url"
:rules="[v => !!v || 'URL is required.']"
required
></v-text-field>
<v-card-actions>
<v-btn
color="red"
class="px-3"
@click="editDialog.show = false"
>
Cancel
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="blue"
class="px-3"
:disabled="!editDialog.valid"
@click="editDialog.show = false; editItem(editDialog.url);"
>
Done
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
<v-dialog
v-model="addDialog.show"
max-width="380"
>
<v-card>
<v-card-title class="headline">Add Item</v-card-title>
<v-card-text>
Enter the name of the item.
</v-card-text>
<v-form
ref="addForm"
v-model="addDialog.valid"
:lazy-validation="false"
>
<v-text-field
class="px-2"
label="Name"
v-model="addDialog.data.name"
:rules="[v => !!v || 'Name is required.']"
required
></v-text-field>
<v-card-text>
Enter the URL of the item.
</v-card-text>
<v-text-field
class="px-2"
label="URL"
v-model="addDialog.data.url"
:rules="[v => !!v || 'URL is required.']"
required
></v-text-field>
<v-card-actions>
<v-btn
color="red"
class="px-3"
@click="addDialog.show = false"
>
Cancel
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="blue"
class="px-3"
:disabled="!addDialog.valid"
@click="addDialog.show = false; addItem(addDialog.data.name, addDialog.data.url)"
>
Add
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
<v-dialog
v-model="receiveDialog.show"
max-width="380"
persistent
>
<v-card>
<v-card-title class="headline">Receiving Item</v-card-title>
<v-card-text>
{{receiveDialog.data.user}} is sending you an item.
</v-card-text>
<v-form
ref="receiveForm"
v-model="receiveDialog.valid"
:lazy-validation="false"
>
<v-text-field
class="px-2"
label="Type"
:rules="[v => !!v || 'Type is required.']"
v-model="receiveDialog.data.type"
required
></v-text-field>
<v-text-field
class="px-2"
label="Name"
:rules="[v => !!v || 'Name is required.']"
v-model="receiveDialog.data.name"
required
></v-text-field>
<v-text-field
class="px-2"
label="URL"
:rules="[v => !!v || 'URL is required.']"
v-model="receiveDialog.data.url"
required
></v-text-field>
<v-card-actions>
<v-btn
color="red"
class="px-3"
@click="receiveDialog.show = false"
>
Reject
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="blue"
class="px-3"
:disabled="!receiveDialog.valid"
@click="receiveDialog.show = false; acceptItem();"
>
Accept
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
<v-dialog
v-model="shareDialog.show"
max-width="380"
persistent
>
<v-card>
<v-card-title class="headline">Share Item</v-card-title>
<v-card-text>
Select a user to send this item to.
</v-card-text>
<v-form
ref="shareForm"
v-model="shareDialog.valid"
:lazy-validation="false"
class="px-2"
>
<!-- <v-list>
<v-list-item-group v-model="shareDialog.data.recipient" color="primary">
<v-list-item
v-for="user in nearbyUsers"
v-bind:key="user.uuid"
>
<v-list-item-content>
<v-list-item-title v-text="user.name"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list> -->
<v-select
v-model="shareDialog.data.recipient"
:items="nearbyUsers"
item-text="name"
item-value="uuid"
:rules="[v => !!v || 'A recipient is required']"
label="Nearby Users"
required
></v-select>
<v-text-field
class="px-2"
label="URL"
:rules="[v => !!v || 'URL is required.']"
v-model="shareDialog.data.url"
required
></v-text-field>
<v-card-actions>
<v-btn
color="red"
class="px-3"
@click="shareDialog.show = false"
>
Cancel
</v-btn>
<v-spacer></v-spacer>
<v-btn
color="blue"
class="px-3"
:disabled="!shareDialog.valid"
@click="shareDialog.show = false; shareItem(shareDialog.data.url);"
>
Send
</v-btn>
</v-card-actions>
</v-form>
</v-card>
</v-dialog>
</v-app>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script>
var vue_this;
EventBridge.scriptEventReceived.connect(function(receivedCommand) {
receivedCommand = JSON.parse(receivedCommand);
// alert("RECEIVED COMMAND:" + receivedCommand.command)
if (receivedCommand.app == "inventory") {
// We route the data based on the command given.
if (receivedCommand.command == 'script-to-web-inventory') {
// alert("INVENTORY RECEIVED ON APP:" + JSON.stringify(receivedCommand.data));
vue_this.receiveInventory(receivedCommand.data);
}
if (receivedCommand.command == 'script-to-web-receiving-item') {
// alert("RECEIVING ITEM OFFER:" + JSON.stringify(receivedCommand.data));
vue_this.receivingItem(receivedCommand.data);
}
if (receivedCommand.command == 'script-to-web-nearby-users') {
// alert("RECEIVING NEARBY USERS:" + JSON.stringify(receivedCommand.data));
vue_this.receiveNearbyUsers(receivedCommand.data);
}
}
});
new Vue({
el: '#inventoryApp',
vuetify: new Vuetify(),
data: () => ({
items: [
{
"type": "script",
"name": "VRGrabScale",
"url": "https://gooawefaweawfgle.com/vr.js",
},
{
"type": "script",
"name": "VRGrabScale",
"url": "https://googfdafsgaergale.com/vr.js",
},
{
"type": "script",
"name": "TEST",
"url": "https://gooadfdagle.com/vr.js",
},
{
"type": "script",
"name": "TESTLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"url": "https://googfdaffle.com/vrLONGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG.js",
},
{
"type": "avatar",
"name": "AVI",
"url": "https://googlfadfe.com/vr.fst",
},
{
"type": "avatar",
"name": "AVI",
"url": "https://googlefdaf.com/vr.fst",
},
{
"type": "model",
"name": "3D MODEL",
"url": "https://googlee.com/vr.fbx",
},
{
"type": "model",
"name": "3D MODEL",
"url": "https://googleee.com/vr.fbx",
},
],
iconType: {
"script": {
"icon": "mdi-code-tags",
"color": "red",
},
"model": {
"icon": "mdi-video-3d",
"color": "green",
},
"avatar": {
"icon": "mdi-account-convert",
"color": "purple",
},
"unknown": {
"icon": "mdi-help",
"color": "grey",
}
},
// The URL is the key (to finding the item we want) so we want to keep track of that.
removeDialog: {
show: false,
url: null,
},
addDialog: {
show: false,
valid: false,
data: {
"name": null,
"url": null,
},
},
editDialog: {
show: false,
valid: false,
url: null, // This is the key, the URL in data is what will overwrite this key.
data: {
"type": null,
"name": null,
"url": null,
},
},
receiveDialog: {
show: false,
valid: false,
data: {
"user": null,
"name": null,
"type": null,
"url": null,
},
},
shareDialog: {
show: false,
valid: false,
data: {
"url": null, // The item you want to share.
"recipient": null,
}
},
nearbyUsers: [
{
name: "Who",
uuid: "{4131531653652562}",
},
{
name: "Is",
uuid: "{4131531653756756576543652562}",
},
{
name: "This?",
uuid: "{4131531676575653652562}",
},
],
sortBy: "alphabetical",
darkTheme: true,
drawer: false,
}),
created: function () {
vue_this = this;
this.$vuetify.theme.dark = this.darkTheme;
this.sendAppMessage("ready", "");
},
methods: {
checkFileType: function(fileType) {
var detectedItemType = null;
switch (fileType) {
// Model Cases
case ".fbx":
detectedItemType = "model";
break;
case ".gltf":
detectedItemType = "model";
break;
// Script Cases
case ".js":
detectedItemType = "script";
break;
// Avatar Cases
case ".fst":
detectedItemType = "avatar";
break;
}
if (detectedItemType == null) {
// This is not a known item...
detectedItemType = "unknown";
}
return detectedItemType;
},
checkItemType: function(itemType) {
var detectedItemType = null;
switch (itemType) {
case "model":
detectedItemType = "model";
break;
case "avatar":
detectedItemType = "avatar";
break;
case "script":
detectedItemType = "script";
break;
}
if (detectedItemType == null) {
// This is not a known item type...
detectedItemType = "unknown";
}
return detectedItemType;
}
addItem: function(name, url) {
var extensionRegex = /\.[0-9a-z]+$/i; // to detect the file type based on extension in the URL.
var detectedFileType = url.match(extensionRegex);
var itemType;
if (detectedFileType == null || detectedFileType[0] == null) {
itemType = "unknown";
} else {
itemType = this.checkFileType(detectedFileType[0]);
}
var itemToPush =
{
"type": itemType,
"name": name,
"url": url,
};
this.items.push(itemToPush);
this.addDialog.data.name = null;
this.addDialog.data.url = null;
},
removeItem: function(url) {
for (i = 0; i < this.items.length; i++) {
if (this.items[i].url == url) {
this.items.splice(i, 1);
}
}
},
editItem: function(url) {
for (i = 0; i < this.items.length; i++) {
if (this.items[i].url == url) {
this.items[i].type = this.checkFileType(this.editDialog.data.type);
this.items[i].name = this.editDialog.data.name;
this.items[i].url = this.editDialog.data.url;
}
}
},
receivingItem: function(data) {
if (this.receiveDialog.show != true) { // Do not accept offers if the user is already receiving an offer.
this.receiveDialog.data.user = data.data.user;
this.receiveDialog.data.type = data.data.type;
this.receiveDialog.data.name = data.data.name;
this.receiveDialog.data.url = data.data.url;
this.receiveDialog.show = true;
}
},
shareItem: function(url) {
var typeToShare;
var nameToShare;
for (i = 0; i < this.items.length; i++) {
if (this.items[i].url == url) {
typeToShare = this.items[i].type;
nameToShare = this.items[i].name;
}
}
// alert("type" + typeToShare + "name" + nameToShare);
this.sendAppMessage("share-item", {
"type": typeToShare,
"name": nameToShare,
"url": this.shareDialog.data.url,
"recipient": this.shareDialog.data.recipient,
});
},
acceptItem: function() {
var itemToPush =
{
"type": this.checkItemType(this.receiveDialog.data.type),
"name": this.receiveDialog.data.name,
"url": this.receiveDialog.data.url,
};
this.items.push(itemToPush);
},
useItem: function(type, url) {
this.sendAppMessage("use-item", {
"type": type,
"url": url
});
},
sendInventory: function() {
this.sendAppMessage("web-to-script-inventory", this.items );
},
receiveInventory: function(receivedInventory) {
if (!receivedInventory) {
this.items = [];
} else {
this.items = receivedInventory;
}
},
displayIcon: function(itemType) {
return this.iconType[itemType].icon;
},
getIconColor: function(itemType) {
return this.iconType[itemType].color;
},
receiveNearbyUsers: function(receivedUsers) {
if (!receivedUsers) {
this.nearbyUsers = [];
} else {
this.nearbyUsers = receivedUsers;
}
},
sendAppMessage: function(command, data) {
var JSONtoSend = {
"app": "inventory",
"command": command,
"data": data
};
// alert(JSON.stringify(JSONtoSend));
EventBridge.emitWebEvent(JSON.stringify(JSONtoSend));
},
},
watch: {
// Whenever the item list changes, this will notice and then send it to the script to be saved.
items: {
deep: true,
handler() {
this.sendInventory();
}
}
},
computed: {
}
})
</script>