Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
urlpatterns = [
path('build', views.build, name='build'),
path('download', views.download, name='download'),
path('install_block', views.install_block, name='install_block'),
path('installed_blocks', views.installed_blocks, name='installed_blocks'),
]
44 changes: 44 additions & 0 deletions backend/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from django.views.decorators.csrf import csrf_exempt

import json
import os
from django.conf import settings

@csrf_exempt
def build(request):
Expand All @@ -20,3 +22,45 @@ def build(request):

def download(request):
return JsonResponse({'status': 'request received'})


@csrf_exempt
def install_block(request):
try:
data = json.loads(request.body)
block_name = data.get('package', {}).get('name') or data.get('name', 'Untitled')

custom_blocks_dir = os.path.join(settings.BASE_DIR, 'custom_blocks')
if not os.path.exists(custom_blocks_dir):
os.makedirs(custom_blocks_dir)

safe_filename = "".join([c for c in block_name if c.isalpha() or c.isdigit() or c in (' ')]).rstrip()
filepath = os.path.join(custom_blocks_dir, f"{safe_filename}.vc3")

with open(filepath, 'w') as f:
json.dump(data, f, indent=4)

return JsonResponse({'status': 'success', 'saved_as': os.path.basename(filepath)})
except Exception as e:
return HttpResponseBadRequest(str(e))

def installed_blocks(request):
try:
custom_blocks_dir = os.path.join(settings.BASE_DIR, 'custom_blocks')
if not os.path.exists(custom_blocks_dir):
return JsonResponse({'blocks': []})

blocks = []
for filename in os.listdir(custom_blocks_dir):
if filename.endswith('.vc3'):
filepath = os.path.join(custom_blocks_dir, filename)
try:
with open(filepath, 'r') as f:
block_data = json.load(f)
blocks.append(block_data)
except Exception as e:
print(f"Failed to load block {filename}: {e}")

return JsonResponse({'blocks': blocks})
except Exception as e:
return HttpResponseBadRequest(str(e))
36 changes: 17 additions & 19 deletions frontend/src/components/marketplace/MarketplacePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,29 +96,27 @@ const MarketplacePanel: React.FC<MarketplacePanelProps> = ({ open, onClose, edit
const isValid = validateMarketplaceBlock(blockData);

if (isValid) {
// Save to Local Storage
let installedBlocks = [];
try {
const stored = localStorage.getItem('vc_marketplace_blocks');
if (stored) {
installedBlocks = JSON.parse(stored);
}
} catch (e) {
console.warn("Could not read local storage", e);
}

// Avoid duplicates by checking package name
const exists = installedBlocks.find((b: any) => b.package && b.package.name === blockData.package.name);
if (!exists) {
installedBlocks.push(blockData);
localStorage.setItem('vc_marketplace_blocks', JSON.stringify(installedBlocks));
const backendHost = process.env.REACT_APP_BACKEND_HOST || 'http://localhost:8000/api/';
const endpoint = backendHost.endsWith('/') ? `${backendHost}install_block` : `${backendHost}/install_block`;

// Dispatch event so the MenuBar updates
const installResponse = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(blockData),
});

if (!installResponse.ok) {
throw new Error(`Backend error: ${installResponse.status}`);
}
window.dispatchEvent(new Event('vc_marketplace_updated'));

alert(`Successfully added ${block.name} to your Downloads Menu!`);
} else {
alert(`${block.name} is already in your Downloads!`);
alert(`Successfully added ${block.name} to your Custom Blocks!`);
} catch (e) {
console.error("Failed to save to backend", e);
alert(`Failed to save ${block.name} to the backend.`);
}
} else {
alert(`Failed to validate block ${block.name}.`);
Expand Down
14 changes: 9 additions & 5 deletions frontend/src/components/menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ function MenuBar(props: MenuBarProps) {
const [marketplaceOpen, setMarketplaceOpen] = useState(false);
const [downloads, setDownloads] = useState<any[]>([]);

const loadDownloads = () => {
const loadDownloads = async () => {
try {
const stored = localStorage.getItem('vc_marketplace_blocks');
if (stored) {
setDownloads(JSON.parse(stored));
const backendHost = process.env.REACT_APP_BACKEND_HOST || 'http://localhost:8000/api/';
const endpoint = backendHost.endsWith('/') ? `${backendHost}installed_blocks` : `${backendHost}/installed_blocks`;

const response = await fetch(endpoint);
if (response.ok) {
const data = await response.json();
setDownloads(data.blocks || []);
}
} catch (e) {
console.error("Failed to load downloads", e);
console.error("Failed to load downloads from backend", e);
}
};

Expand Down
Loading