Commit 40762d43 authored by Vlad Gaydukov's avatar Vlad Gaydukov
Browse files

Merge branch 'new-user-blocks' into 'master'

New user blocks

See merge request avvy/blocks-admin!12
parents 10a54d89 03a3e2ad
Showing with 1233 additions and 5 deletions
+1233 -5
from base import controller
from models.dbQuery import db_query
import requests, json
from random import randrange
import trimesh
import numpy as np
from settings import STORAGE_URL
from controllers.block import MANIFEST_TEMP
from controllers.view import UPLOAD_PATH
from controllers.view import compute_cels
class UserBlockNew(controller.Controller):
def get(self):
self.render("user_block_new.html")
def post(self):
children = []
obj_path = ''
data = MANIFEST_TEMP
data["_id"] = str(randrange(0, 1<<63))
data["manifest"]["meta"]["id"] = str(data["_id"])
# save assets
file = self.request.files["asset"][0]
files_data = {"file": (file["filename"], file["body"], file["content_type"])}
r = requests.post(UPLOAD_PATH, data={'path': "avvy_assets"}, files=files_data)
filename = r.json()["files"]["file"]
obj_path = STORAGE_URL + "storage/" + filename
print(obj_path)
children = compute_cels(obj_path)
data["manifest"]["properties"]["children"] = children
data["manifest"]["meta"]["views"]["0"] = obj_path
data["manifest"]["meta"]["name"] = self.args.pop("name")
data["manifest"]["meta"]["description"] = self.args.pop("desc")
data["manifest"]["meta"]["tags"] = self.args.pop("tags")
# data["draft"] = True
db_query.add_block(data)
self.write(json.dumps({'data': data}))
from base import controller
from models.dbQuery import db_query
import requests, json
from random import randrange
import trimesh
import numpy as np
from settings import STORAGE_URL
UPLOAD_PATH = STORAGE_URL + 'upload'
class UserBlockNewView(controller.Controller):
def post(self, bclass, view_id):
data = db_query.get_block(bclass)
obj_path = ''
# save assets
file = self.request.files["asset"][0]
files_data = {"file": (file["filename"], file["body"], file["content_type"])}
r = requests.post(UPLOAD_PATH, data={'path': "avvy_assets"}, files=files_data)
filename = r.json()["files"]["file"]
obj_path = STORAGE_URL + "storage/" + filename
print(obj_path)
data["manifest"]["meta"]["views"][self.args["id"]] = obj_path
db_query.add_block(data)
self.write(json.dumps({'data': data}))
def delete(self, bclass, view_id):
data = db_query.get_block(bclass)
del data["manifest"]["meta"]["views"][view_id]
db_query.add_block(data)
self.write(json.dumps({'data': data}))
......@@ -5,6 +5,8 @@ from controllers.logout import Logout
from controllers.inventory import Inventory
from controllers.inventory_web import InventoryWeb
from controllers.view import View
from controllers.user_block_new import UserBlockNew
from controllers.user_block_new_view import UserBlockNewView
url_map = [
(r"/", Dashboard),
......@@ -15,4 +17,7 @@ url_map = [
(r"/block/(?P<bclass>[^\/]+)?/view/(?P<view_id>[^\/]+)?", View),
(r"/inventory", Inventory),
(r"/inventoryWeb", InventoryWeb),
(r"/user/block/new", UserBlockNew),
(r"/user/block/(?P<bclass>[^\/]+)?/view/(?P<view_id>[^\/]+)?", UserBlockNewView)
]
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@600&display=swap');
:root {
--primary-color: #4A72DB;
--light-color: #FFFFFF;
--dark-light-color: #F9F9F9;
--light-dark-color: #262626;
--dark-color: #000000;
--secondary: rgba(38, 38, 38, 0.5);
--border: 1px solid rgba(0, 0, 0, 0.1);
--border-color: rgba(0, 0, 0, 0.1);
}
* {
font-family: 'Roboto', sans-serif;
box-sizing: border-box;
}
#threeview {
height: 100vh;
}
table.dataTable tbody tr.selected {
background-color: #b0bed9;
}
.drop-container {
width: 50%;
display: flex;
justify-content: center;
padding: 60px;
background: #FFFFFF;
border-radius: 16px;
/* border: 2px solid rgba(38, 38, 38, 0.04); */
border: 2px solid #4A72DB;
}
.drop-desc {
margin-top: 8px;
color: rgba(0, 0, 0, 0.5);
}
.form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
}
.grid {
width: 100%;
display: grid;
grid-template-rows: repeat(var(--bs-rows, 1), 1fr);
grid-template-columns: repeat(var(--bs-columns, 12), 1fr);
gap: var(--bs-gap, 1rem);
}
.g-col-4 {
grid-column: auto/span 4;
}
.g-col-8 {
grid-column: auto/span 8;
}
.g-col-12 {
grid-column: auto/span 12;
}
#drop-item {
display: none;
}
#block-name, #block-tags, #block-desc {
padding: 22px;
background: #FFFFFF;
border: 1.6px solid #4A72DB;
outline: none;
border-radius: 12px;
}
#block-name::placeholder, #block-tags::placeholder, #block-desc::placeholder {
color: #4A72DB;
}
.next-button {
margin-top: 1rem;
width: 88px;
justify-content: space-evenly;
align-items: center;
display: flex;
padding: 12px;
border-radius: 12px;
background: #4A72DB;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 16px;
text-align: center;
color: #FFFFFF;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0.1);
}
.next-button.disabled {
background: #FFFFFF;
color: #cfcfcf;
cursor: not-allowed;
border: 1px solid rgba(0, 0, 0, 0.1);
}
.next-button.disabled path{
stroke: #cfcfcf;
}
#next-view {
display: none;
}
#threeview {
padding: 0;
}
.header {
font-family: Roboto;
font-style: normal;
font-weight: 500;
font-size: 32px;
line-height: 37px;
text-align: left;
color: var(--light-dark-color);
}
.button-wrapper {
display: flex;
flex-wrap: wrap;
gap: 22px;
}
.button {
min-width: 140px;
max-width: 25%;
display: inline-block;
padding: 12px;
border: 1px solid var(--primary-color);
border-radius: 12px;
background: var(--light-color);
font-family: Roboto;
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 19px;
text-align: center;
color: var(--primary-color);
cursor: pointer;
}
.file-button {
min-width: 140px;
max-width: 50%;
display: inline-block;
padding: 12px;
border: 1px solid var(--primary-color);
border-radius: 12px;
background: var(--light-color);
font-family: Roboto;
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 19px;
text-align: center;
color: var(--primary-color);
cursor: pointer;
outline: none;
}
.active-button {
background: var(--primary-color);
color: var(--light-color);
}
.button:hover, .file-button:hover {
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: var(--primary-color);
color: var(--light-color);
text-decoration: none;
}
.button:focus, .file-button:focus {
outline: none;
}
.button.active-button:hover {
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: #688AE1;
text-decoration: none;
}
.card-views {
position: relative;
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
word-wrap: break-word;
background-color: #fff;
background-clip: border-box;
border-radius: 0.25rem;
}
.card-view__wrapper {
padding: 12px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 10px;
border-radius: 12px;
border: var(--border);
cursor: pointer;
}
.card-view__wrapper.active {
background: rgba(74, 114, 219, 0.3);
}
.card-view__wrapper:hover {
border-color: var(--primary-color);
}
.card-view__item {
width: 100%;
min-width: 100px;
padding: 8px 16px;
display: flex;
flex-direction: column;
gap: 4px;
text-align: left;
background: var(--dark-light-color);
border-radius: 12px;
}
.card-view__wrapper .card-view__item{
width: 48%;
}
.card-view__item-name {
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 16px;
color: var(--secondary);
}
.card-view__item-value {
font-family: Roboto;
font-style: normal;
font-weight: 500;
font-size: 18px;
line-height: 21px;
color: var(--light-dark-color);
}
.preview-image {
width: 80px;
height: 80px;
}
#inputBlockName, #inputBlockDescription, #inputNewBlockId {
padding: 22px;
background: #FFFFFF;
border: 1.6px solid var(--primary-color);
outline: none;
border-radius: 12px;
}
#inputBlockName::placeholder, #inputBlockDescription::placeholder, #inputBlockTags::placeholder, #inputNewBlockId::placeholder {
color: var(--primary-color);
}
.bootstrap-tagsinput {
padding: 22px;
height: auto;
border-color: var(--primary-color);
border-radius: 12px;
}
.bootstrap-tagsinput input::placeholder {
color: var(--primary-color);
}
.bootstrap-tagsinput:focus-within {
box-shadow: 0 0 0 0.2rem rgb(0 123 255 / 25%);
}
.error-container {
width: 50%;
display: flex;
justify-content: center;
padding: 60px;
background: #FFFFFF;
border-radius: 16px;
/* border: 2px solid rgba(38, 38, 38, 0.04); */
border: 2px solid #dd3d52;
}
.error-icon {
font-size: 100px;
color: #dd3d52;
}
.error-content {
width: 80%;
margin: 32px auto;
display: flex;
justify-content: space-around;
align-content: center;
align-items: center;
padding: 32px;
background: #FFFFFF;
border-radius: 16px;
/* border: 2px solid rgba(38, 38, 38, 0.04); */
border: 2px solid #dd3d52;
font-size: 32px;
color: #dd3d52;
}
.error-link {
margin-left: 12px;
font-size: 32px;
color: #dd3d52;
text-decoration: none;
}
.error-link:hover {
color: #e98290;
text-decoration: none;
}
.error-section {
margin-top: 10%;
display: flex;
flex-direction: column;
align-items: center;
}
\ No newline at end of file
......@@ -3,6 +3,67 @@
const STORAGE_PATH = "https://storage.avvyland.com/storage";
const delCellBtn = document.getElementById("delete-cell");
const submitBtn = document.getElementById("submit-button");
const approveForm = document.getElementById("approve-form");
const inputBlockProps = document.getElementById("inputBlockProps");
const btnState = {
active: false
};
let newBlockProps = {};
let mesh;
const changeStateBtn = function(e) {
btnState.active = !btnState.active;
if (btnState.active) {
delCellBtn.classList.add("active-button");
mesh.material.depthTest = false;
} else {
delCellBtn.classList.remove("active-button");
mesh.material.depthTest = true;
}
};
const raycast = function() {
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
let intersects, mesh, result, camera, renderer, context;
return function(event) {
const points = [];
context = this;
camera = engine.camera;
renderer = engine.renderer;
// mouse.x = (event.x / renderer.domElement.clientWidth) * 2 - 1;
// mouse.y = -(event.y / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(event, camera);
intersects = raycaster.intersectObject(engine.view, true);
for (var i = 0; i < intersects.length; i++) {
mesh = intersects[i].object;
if (mesh.ignoreRaycaster || mesh.parent.ignoreRaycaster)
continue;
else {
result = intersects[i];
result.object = mesh;
return result;
}
}
}
}();
delCellBtn.addEventListener("click", changeStateBtn);
var engine = new VG.Engine("threeview");
var mouse = new VG.MouseEventsHandler(engine.renderer.domElement);
......@@ -38,12 +99,19 @@
});
var matrix = new THREE.Matrix4();
for (let i = 0; i < AvvY.blockProps.properties.children.length; i++) {
newBlockProps[i] = AvvY.blockProps.properties.children[i];
}
for (var ch in AvvY.blockProps.properties.children) {
var cell = AvvY.blockProps.properties.children[ch];
var mesh = new THREE.Mesh();
let mesh = new THREE.Mesh();
mesh.geometry = cellGeometry;
mesh.material = cellMaterial;
mesh.userData.id = ch;
mesh.userData.cell = cell;
matrix.fromArray(cell.props.transformation);
mesh.position.setFromMatrixPosition(matrix);
mesh.rotation.setFromRotationMatrix(matrix);
......@@ -68,9 +136,10 @@
loader.load(
objUrl,
function (gltf) {
var mesh = findMesh(gltf.scene.children);
mesh = findMesh(gltf.scene.children);
mesh.rotation.set(0, 0, 0);
mesh.scale.set(100, 100, 100);
mesh.ignoreRaycaster = true;
scene.add(mesh);
},
......@@ -82,4 +151,38 @@
}
);
}
function deleteOneCell(e) {
if (btnState.active) {
let cell;
cell = raycast(mouse.mousePosition);
if (cell) {
engine.view.remove(cell.object);
delete newBlockProps[cell.object.userData.id];
} else {
return null;
}
}
}
function saveCells() {
const _newBlockProps = [];
for (const id in newBlockProps) {
_newBlockProps.push(newBlockProps[id]);
}
return _newBlockProps;
}
VG.EventDispatcher.bind("mouse.click", {}, deleteOneCell);
submitBtn.addEventListener("click", e => {
e.preventDefault();
const newCells = saveCells();
AvvY.blockProps["properties"]["children"] = newCells;
inputBlockProps.value = JSON.stringify(AvvY.blockProps);
approveForm.submit();
});
})(jQuery);
const dropContainer = document.getElementById("drop-container");
const dropInput = document.getElementById("drop-input");
const dropIcon = document.getElementById("drop-icon");
const dropItem = document.getElementById("drop-item");
const dropDesc = document.getElementById("drop-desc");
const blockNameInput = document.getElementById("block-name");
const blockTagsInput = document.getElementById("block-tags");
const blockDescInput = document.getElementById("block-desc");
const nextBtn = document.getElementById("next-button");
const submitBtn = document.getElementById("submit-button");
const viewsContainer = document.getElementById("views");
const errorSection = document.getElementById("error-section");
const delCellBtn = document.getElementById("delete-cell");
const btnState = {
active: false
};
const previewBtn = document.getElementById("preview-button");
const newViewBtn = document.getElementById("new-view-button");
const inputNewBlockId = document.getElementById("inputNewBlockId");
const inputBlockAssetPreview = document.getElementById("inputBlockAssetPreview");
const newViewForm = document.getElementById("new-view-form");
const saveNewViewBtn = document.getElementById("save-new-view");
const inputBlockAsset = document.getElementById("inputBlockAsset");
const inputBlockName = document.getElementById("inputBlockName");
const previewImage = document.getElementById("preview-image");
const inputBlockDescription = document.getElementById("inputBlockDescription");
const inputBlockTags = document.getElementById("inputBlockTags");
const inputBlockProps = document.getElementById("inputBlockProps");
const inputBlockId = document.getElementById("inputBlockId");
const inputBlockSize = document.getElementById("inputBlockSize");
const inputIsPrefab = document.getElementById("inputIsPrefab");
const approveForm = document.getElementById("approve-form");
const addNewViewModal = document.getElementById("add-view-modal");
const fileNameField = document.getElementById("file-name");
const viewFileNameField = document.getElementById("file-name-view");
const nextView = document.getElementById("next-view");
const deleteViewBtn = document.getElementById("remove-view")
const dropWrapper = document.getElementById("drop-wrapper");
const formWrapper = document.getElementById("form-wrapper");
nextBtn.disabled = true;
let formData = new FormData();
const newBlockState = {
file: false,
name: false,
tags: false,
desc: false,
}
let newBlockProps = {};
let activeView = "0";
const viewMeshes = {};
let mouse;
let engine;
let scene;
const loader = new THREE.GLTFLoader();
validateState();
const raycast = function() {
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects, mesh, result, camera, renderer, context;
return function(event) {
const points = [];
context = this;
camera = engine.camera;
renderer = engine.renderer;
// mouse.x = (event.x / renderer.domElement.clientWidth) * 2 - 1;
// mouse.y = -(event.y / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(event, camera);
intersects = raycaster.intersectObject(engine.view, true);
for (var i = 0; i < intersects.length; i++) {
mesh = intersects[i].object;
if (mesh.ignoreRaycaster || mesh.parent.ignoreRaycaster)
continue;
else {
result = intersects[i];
result.object = mesh;
return result;
}
}
}
}();
function validateState() {
if (newBlockState.file && newBlockState.name && newBlockState.tags && newBlockState.desc) {
nextBtn.classList.remove("disabled");
nextBtn.disabled = false;
} else {
nextBtn.classList.add("disabled");
nextBtn.disabled = true;
}
}
dropContainer.addEventListener("click", (e) => {
dropInput.click();
});
dropContainer.addEventListener("dragenter", e => {
e.preventDefault();
e.stopPropagation();
// add style
});
dropContainer.addEventListener("dragover", e => {
e.preventDefault();
e.stopPropagation();
// add style
});
dropContainer.addEventListener("dragleave", e => {
e.preventDefault();
e.stopPropagation();
// remove style
});
dropContainer.addEventListener("drop", e => {
e.preventDefault();
e.stopPropagation();
// remove style
let file = e.dataTransfer.files[0];
addFile(file);
});
dropInput.addEventListener("change", e => {
if (dropInput.files[0]) {
addFile(dropInput.files[0]);
}
});
function addFile(file) {
dropIcon.style.display = "none";
dropItem.style.display = "block";
dropContainer.style.background = "#e8f0fe";
dropDesc.textContent = file.name;
newBlockState.file = true;
validateState();
formData.append('asset', file);
}
function checkState(el, key) {
if (el.value) {
newBlockState[key] = true;
} else {
newBlockState[key] = false;
}
validateState();
}
blockNameInput.addEventListener("input", e => {
checkState(blockNameInput, "name");
});
blockTagsInput.addEventListener("input", e => {
checkState(blockTagsInput, "tags");
});
blockDescInput.addEventListener("input", e => {
if (blockDescInput.value.length) {
blockDescInput.style.background = "#e8f0fe";
} else {
blockDescInput.style.background = "#ffffff";
}
checkState(blockDescInput, "desc");
});
nextBtn.addEventListener("click", e => {
e.preventDefault();
formData.append("name", blockNameInput.value);
formData.append("tags", blockTagsInput.value.trim().split(" ").toString());
formData.append("desc", blockDescInput.value);
const _xsrf = document.querySelector("input[name='_xsrf']").value;
formData.append("_xsrf",_xsrf);
fetch(`/user/block/new`, {
method: 'POST',
body: formData,
})
.then((res) => {
return res.json();
})
.then(response => {
dropWrapper.remove();
formWrapper.remove();
const data = response.data;
AvvY.deleteLink = `/block/${data["manifest"]["meta"]["id"]}`;
AvvY.redirectLink = "/";
AvvY.blockProps = data["manifest"];
AvvY.class_id = data["manifest"]["meta"]["id"];
for (let i = 0; i < AvvY.blockProps.properties.children.length; i++) {
newBlockProps[i] = AvvY.blockProps.properties.children[i];
}
inputBlockName.value = data["manifest"]["meta"]["name"];
inputBlockDescription.value = data["manifest"]["meta"]["description"];
inputBlockProps.value = JSON.stringify(data["manifest"]);
inputBlockId.value = data["manifest"]["meta"]["id"];
inputBlockSize.value = data["manifest"]["meta"]["size"];
inputIsPrefab.value = false;
for (let i = 0; i < data["manifest"]["meta"]["tags"].length; i++) {
const tag = data["manifest"]["meta"]["tags"][i];
$("#inputBlockTags").tagsinput('add', tag);
}
nextView.style.display = "block";
renderViewsId(data["manifest"]["meta"]["views"]);
renderView();
})
.catch(() => {
dropWrapper.remove();
formWrapper.remove();
errorSection.classList.remove("d-none");
});
});
previewBtn.addEventListener("click", (e) => {
e.preventDefault();
inputBlockAssetPreview.click();
});
newViewBtn.addEventListener("click", (e) => {
e.preventDefault();
inputBlockAsset.click();
});
inputBlockAssetPreview.addEventListener("change", () => {
fileNameField.textContent = inputBlockAssetPreview.files[0].name.length > 30 ? `...${inputBlockAssetPreview.files[0].name.slice(-30)}` : inputBlockAssetPreview.files[0].name;
let reader = new FileReader()
reader.readAsDataURL(inputBlockAssetPreview.files[0]);
reader.onloadend = function() {
previewImage.src = reader.result;
}
});
inputBlockAsset.addEventListener("change", () => {
viewFileNameField.textContent = inputBlockAsset.files[0].name.length > 30 ? `...${inputBlockAsset.files[0].name.slice(-30)}` : inputBlockAsset.files[0].name;
});
deleteViewBtn.addEventListener("click", (e) => {
e.preventDefault();
deleteViewBtn.disabled = true;
let formData = new FormData();
const _xsrf = document.querySelector("input[name='_xsrf']").value;
formData.append("_xsrf",_xsrf);
fetch( `/user${AvvY.deleteLink}/view/${activeView}`, {
method: 'DELETE',
body: formData,
})
.then((res) => {
return res.json();
})
.then(response => {
const data = response.data;
scene.remove(viewMeshes[activeView]);
delete viewMeshes[activeView];
renderViewsId(data["manifest"]["meta"]["views"]);
AvvY.blockProps["meta"]["views"] = data["manifest"]["meta"]["views"];
})
.catch(() => {
nextView.style.display = "none";
errorSection.classList.remove("d-none");
})
});
function loadView(objUrl) {
if (objUrl) {
loader.load(
objUrl,
function (gltf) {
viewMeshes[activeView] = findMesh(gltf.scene.children);
viewMeshes[activeView].rotation.set(0, 0, 0);
viewMeshes[activeView].scale.set(100, 100, 100);
viewMeshes[activeView].ignoreRaycaster = true;
scene.add(viewMeshes[activeView]);
},
function (xhr) {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
},
function (error) {
console.log("An error happened");
}
);
}
}
function renderView () {
engine = new VG.Engine("threeview");
mouse = new VG.MouseEventsHandler(engine.renderer.domElement);
const cameraController = new VG.CameraControllerOrbit(
engine.camera,
engine.renderer.domElement
);
cameraController.maxPolarAngle = Math.PI / 2;
cameraController.minDistance = 1;
cameraController.maxDistance = 100;
const light = new THREE.DirectionalLight(0xffffffff, 0.7);
light.position.set(500, 300, 100);
engine.view.add(light);
const alight = new THREE.AmbientLight(0xffffffff, 1.2);
engine.view.add(alight);
scene = new THREE.Object3D();
scene.scale.set(0.1, 0.1, 0.1);
engine.view.add(scene);
// Load a glTF resource
const cellGeometry = new THREE.BoxGeometry(0.95, 0.95, 0.95);
const cellMaterial = new THREE.MeshLambertMaterial({
color: "green",
transparent: true,
opacity: 0.2,
});
var matrix = new THREE.Matrix4();
for (let ch in AvvY.blockProps.properties.children) {
let cell = AvvY.blockProps.properties.children[ch];
let mesh = new THREE.Mesh();
mesh.geometry = cellGeometry;
mesh.material = cellMaterial;
mesh.userData.id = ch;
mesh.userData.cell = cell;
matrix.fromArray(cell.props.transformation);
mesh.position.setFromMatrixPosition(matrix);
mesh.rotation.setFromRotationMatrix(matrix);
engine.view.add(mesh);
}
let objUrl = AvvY.blockProps?.meta?.views?.[0];
loadView(objUrl);
}
function changeActiveView(view) {
const views = document.querySelectorAll(".card-view__wrapper");
for (let i = 0; i < views.length; i++) {
views[i].classList.remove("active");
}
scene.remove(viewMeshes[activeView]);
activeView = view;
if (viewMeshes[activeView]) {
scene.add(viewMeshes[activeView]);
} else {
loadView(AvvY.blockProps["meta"]["views"][activeView]);
}
}
function renderViewsId(views) {
const template = document.getElementById("temp__view");
viewsContainer.innerHTML = "";
for (const viewId in views) {
const viewWrapper = template.content.getElementById("view__wrapper").cloneNode(true);
if (activeView === viewId) {
viewWrapper.classList.add("active");
}
viewWrapper.addEventListener("click", e => {
changeActiveView(viewId);
viewWrapper.classList.add("active");
});
const viewIdEl = viewWrapper.querySelector("#view-id");
const viewUrlEl = viewWrapper.querySelector("#view-url");
viewIdEl.textContent = viewId;
viewUrlEl.textContent = views[viewId];
viewsContainer.append(viewWrapper);
}
}
function findMesh(children) {
for (let i = 0; i < children.length; i++) {
let child = children[i];
if (child instanceof THREE.Mesh) {
return child;
} else {
child = findMesh(child.children);
if (child) return child;
else continue;
}
}
}
function deleteOneCell(e) {
if (btnState.active) {
let cell;
cell = raycast(mouse.mousePosition);
if (cell) {
engine.view.remove(cell.object);
delete newBlockProps[cell.object.userData.id];
} else {
return null;
}
}
}
function saveCells() {
const _newBlockProps = [];
for (const id in newBlockProps) {
_newBlockProps.push(newBlockProps[id]);
}
return _newBlockProps;
}
const changeStateBtn = function(e) {
btnState.active = !btnState.active;
if (btnState.active) {
delCellBtn.classList.add("active-button");
viewMeshes[activeView].material.depthTest = false;
} else {
delCellBtn.classList.remove("active-button");
viewMeshes[activeView].material.depthTest = true;
}
};
VG.EventDispatcher.bind("mouse.click", {}, deleteOneCell);
submitBtn.addEventListener("click", e => {
e.preventDefault();
const newCells = saveCells();
AvvY.blockProps["properties"]["children"] = newCells;
inputBlockProps.value = JSON.stringify(AvvY.blockProps);
approveForm.submit();
});
saveNewViewBtn.addEventListener("click", e => {
e.preventDefault();
$('#add-view-modal').modal('hide');
let formData = new FormData();
formData.append("id", inputNewBlockId.value);
formData.append('asset', inputBlockAsset.files[0]);
const _xsrf = document.querySelector("input[name='_xsrf']").value;
formData.append("_xsrf",_xsrf);
fetch(`/user${AvvY.deleteLink}/view/${inputNewBlockId.value}`, {
method: 'POST',
body: formData,
})
.then((res) => {
return res.json();
})
.then(response => {
newViewForm.reset();
const data = response.data;
renderViewsId(data["manifest"]["meta"]["views"]);
AvvY.blockProps["meta"]["views"] = data["manifest"]["meta"]["views"];
})
.catch(() => {
nextView.style.display = "none";
errorSection.classList.remove("d-none");
})
});
delCellBtn.addEventListener("click", changeStateBtn);
<link href="{{ static_url('css/styles.css') }}" rel="stylesheet" />
<link href="{{ static_url('css/main.css') }}" rel="stylesheet" />
<link href="{{ static_url('css/jsoneditor.min.css') }}" rel="stylesheet" />
<link href="{{ static_url('css/bootstrap-tagsinput.css') }}" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
<link href="{{ static_url('css/bootstrap-multiselect.css') }}" type="text/css" rel="stylesheet" crossorigin="anonymous">
<link href="{{ static_url('css/main.css') }}" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/js/all.min.js" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" crossorigin="anonymous"></script>
......
......@@ -23,6 +23,7 @@
</ol>
<div class="row">
<div id="threeview" class="col-sm-6">
<button id="delete-cell" type="button" class="button" style="position: absolute; top: 20px; left: 30px;">Delete cell mode</button>
</div>
<div class="col-sm-6 text-black bg-white pt-2 pb-2 mb-4 pl-3">
{% if not is_new %}
......@@ -61,7 +62,7 @@
{% else %}
<a id="create-block" class="col-sm-3 btn btn-primary" href="#">Create Block</a>
{% end %}
<form action="/block/{{ data["manifest"]["meta"]["name"] }}" method="post" enctype="multipart/form-data">
<form id="approve-form" action="/block/{{ data["manifest"]["meta"]["name"] }}" method="post" enctype="multipart/form-data">
<div class="form-group"><label class="small mb-1" for="inputBlockPreview">Preview</label><img class=" py-4" style="width: 64px;" id="inputBlockId" name="preview" src={{ data["manifest"]["meta"].get("preview", "") }}></img></div>
<div class="form-group"><label class="small mb-1" for="inputBlockId">Block Class ID</label><input class="form-control py-4" id="inputBlockId" type="text" name="_id" placeholder="Enter block class id" value={{ data["_id"] }}></div>
<div class="form-group"><label class="small mb-1" for="inputBlockName">Block Name</label><input class="form-control py-4" id="inputBlockName" type="text" name="name" placeholder="Enter block name" value={{ data["manifest"]["meta"]["name"] }}></div>
......@@ -82,7 +83,7 @@
<div class="form-group"><label for="inputBlockAsset">Asset Bundle IOS</label><input type="file" name="ios" class="btn btn-success form-control-file" id="inputBlockAssetIOS" multiple data-show-upload="true" data-show-caption="true"></div>
<div class="form-group"><label for="inputBlockAsset">Asset Bundle Windows</label><input type="file" name="win" class="btn btn-success form-control-file" id="inputBlockAssetWin" multiple data-show-upload="true" data-show-caption="true"></div>
<div class="form-group"><label for="inputBlockAsset">Block Asset Preview</label><input type="file" name="preview" class="btn btn-info form-control-file" id="inputBlockAssetPreview" multiple data-show-upload="true" data-show-caption="true"></div>
<div class="form-group d-flex align-items-center justify-content-between mt-4 mb-0"><button type="submit" class="btn btn-primary">Save</button></div>
<div class="form-group d-flex align-items-center justify-content-between mt-4 mb-0"><button id="submit-button" type="submit" class="btn btn-primary">Save</button></div>
{% raw xsrf_form_html() %}
</form>
</div>
......
......@@ -7,6 +7,9 @@
<li class="breadcrumb-item active">
<a class="btn btn-primary" href="block/new" role="button">New Block</a>
</li>
<li class="active ml-2">
<a class="btn btn-info" href="user/block/new" role="button">New User Block</a>
</li>
</ol>
<div class="card mb-12" style="margin: 20px 0;">
<div class="card mb-4">
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="" />
<meta name="author" content="" />
<title>Avvy New Block</title>
{% module Template("_head_styles_scripts.html") %}
<script src="{{ static_url('js/three.js') }}"></script>
<script src="{{ static_url('js/vg.js') }}"></script>
<script src="{{ static_url('js/GLTFLoader.js') }}"></script>
</head>
<body>
<nav class="navbar" style="background: #4A72DB;">
<div class="container">
<a class="navbar-brand" href="#">
<img src="https://avvyland.com/wp-content/uploads/2021/06/logo.png" alt="Avvyland logo">
</a>
</div>
</nav>
<div id="drop-wrapper" class="container d-flex flex-column align-items-center justify-content-center mt-3">
<div id="drop-container" class="drop-container">
<input type="file" style="display: none;" id="drop-input">
<svg id="drop-icon" width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M125 5V245M5 125H245" stroke="#4A72DB" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg id="drop-item" width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M44 76.5L127.097 125M44 76.5V173.5L127.097 222M44 76.5L121.903 28L205 76.5M127.097 125V222M127.097 125L205 76.5M127.097 222L205 173.5V76.5" stroke="#4A72DB" stroke-width="4"/>
</svg>
</div>
<h3 id="drop-desc" class="drop-desc">Drag your model here</h3>
</div>
<div id="form-wrapper" class="container mt-3">
<form action="/user/block/new" method="post" enctype="multipart/form-data" class="form">
<div class="grid">
<input id="block-name" class="form-control g-col-4" type="text" placeholder="Block name" aria-label="Block name">
<input id="block-tags" class="form-control g-col-8" type="text" placeholder="Tags" aria-label="Tags">
<textarea id="block-desc" class="form-control g-col-12" placeholder="Description" rows="3"></textarea>
</div>
<button id="next-button" class="next-button">
<span>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 21L16 12L7 3" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</span>
<span>Next</span>
</button>
{% raw xsrf_form_html() %}
</form>
</div>
<main id="next-view">
<div class="container-fluid">
<h1 class="mt-4 header">Block : </h1>
<div class="row">
<div id="threeview" class="col-sm-6" style="position: relative;">
<button id="delete-cell" type="button" class="button" style="position: absolute; top: 20px; left: 30px;">Delete cell mode</button>
</div>
<div class="col-sm-6 text-black bg-white pt-2 pb-2 mb-4 pl-3">
<div class="button-wrapper">
<a id="new-view" class="button active-button" href="#" role="button" data-toggle="modal" data-target="#add-view-modal">New View</a>
<a id="remove-view" class="button" href="#" role="button">Remove View</a>
</div>
<div id="views" class="card-views mb-12" style="margin: 20px 0;">
</div>
<form id="approve-form" action="/block/" method="post" enctype="multipart/form-data">
<div class="form-group">
<div class="card-view__item">
<span class="card-view__item-name">Preview</span>
<span class="card-view__item-value">
<img id="preview-image" class="preview-image" src="https://storage.avvyland.com/storage/avvy_assets/82022908755d6dd7a1a6a347185a7e16.png" alt="Block preview">
</span>
</div>
</div>
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="inputBlockName">Block Name</label>
<span class="card-view__item-value">
<input class="form-control py-4" id="inputBlockName" type="text" name="name" placeholder="Enter block name" value="">
</span>
</div>
</div>
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="inputBlockDescription">Block Description</label>
<span class="card-view__item-value">
<textarea class="form-control py-4" id="inputBlockDescription" type="text" name="description" placeholder="Enter block description">
</textarea>
</span>
</div>
</div>
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="inputBlockTags">Block Tags</label>
<div class="card-view__item-value">
<input class="form-control py-4" id="inputBlockTags" type="text" name="tags" placeholder="Enter block tags" data-role="tagsinput" value="">
</div>
</div>
</div>
<div class="form-group d-none"><label class="small mb-1" for="inputBlockProps">Block Props</label><input class="form-control py-4" id="inputBlockProps" type="text" name="props" placeholder="Enter block props" value=""></div>
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="preview">Block Asset Preview</label>
<div class="card-view__item-value">
<input type="file" name="preview" class="d-none" id="inputBlockAssetPreview" multiple data-show-upload="true" data-show-caption="true">
<button id="preview-button" class="file-button">Choose preview</button>
<span id="file-name" class="card-view__item-name"></span>
</div>
</div>
</div>
<input class="d-none" id="inputBlockId" type="text" name="_id" placeholder="Enter block class id" value="">
<input class="d-none" id="inputBlockSize" type="text" name="size" placeholder="Enter block size" value="">
<input class="d-none" id="inputIsPrefab" type="checkbox" name="is_prefab" placeholder="If block is prefab">
<div class="form-group d-flex align-items-center justify-content-end mt-4 mb-0"><button id="submit-button" type="submit" class="button active-button">Approve block</button></div>
{% raw xsrf_form_html() %}
</form>
</div>
</div>
</div>
</main>
<div id="add-view-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content" style="border-radius: 12px;">
<div class="modal-header">
<h5 class="modal-title">Add view</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="new-view-form" action="/block/class_id/view" method="post" enctype="multipart/form-data">
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="inputNewBlockId">Block View ID</label>
<span class="card-view__item-value">
<input class="form-control py-4" id="inputNewBlockId" type="text" name="id" placeholder="Enter block view id" value="">
</span>
</div>
</div>
<div class="form-group">
<div class="card-view__item">
<label class="card-view__item-name" for="inputBlockAsset">Block View Asset</label>
<div class="card-view__item-value">
<input type="file" name="asset" class="d-none" id="inputBlockAsset" multiple data-show-upload="true" data-show-caption="true">
<button type="button" id="new-view-button" class="file-button">Choose file</button>
<span id="file-name-view" class="card-view__item-name"></span>
</div>
</div>
</div>
<div class="form-group d-flex align-items-center justify-content-end mt-4 mb-0"><button type="button" id="save-new-view" class="button active-button">Save</button></div>
{% raw xsrf_form_html() %}
</form>
</div>
</div>
</div>
</div>
<section id="error-section" class="container error-section d-none">
<div class="error-container">
<h2 class="error-icon">!</h2>
</div>
<div class="error-content">
Something went wrong! <a class="error-link" href="/user/block/new">Return</a>
</div>
</section>
<template id="temp__view">
<div id="view__wrapper" class="card-view__wrapper">
<div class="card-view__item">
<span class="card-view__item-name">View Id</span>
<span id="view-id" class="card-view__item-value">0</span>
</div>
<div class="card-view__item">
<span class="card-view__item-name">Url</span>
<span id="view-url" class="card-view__item-value">https://storage.avvyland.com/storage/avvy_assets/31ce6583cd8960b676ca73600a3c5a84.glb
</span>
</div>
</div>
</template>
<script src="{{ static_url('js/user_block_new.js') }}"></script>
<script src="{{ static_url('js/view_popup.js') }}"></script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment