-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommunity-projects.html
More file actions
161 lines (148 loc) · 6.93 KB
/
community-projects.html
File metadata and controls
161 lines (148 loc) · 6.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>MyScratchBlocks - A Scratch & TurboWarp Modification</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc;
color: #334155;
}
.loading-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
display: inline-block;
vertical-align: middle;
margin-left: 8px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="antialiased">
<header class="bg-white shadow-sm py-4">
<div class="container mx-auto px-4 flex justify-between items-center">
<a href="#" class="flex items-center space-x-2 text-2xl font-bold text-indigo-600">
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h2v-6h-2v6zm0-8h2V7h-2v2z"/>
</svg>
<span>MyScratchBlocks</span>
</a>
<nav class="hidden md:flex space-x-6">
<a href="/" class="text-gray-600 hover:text-indigo-600 font-medium">Home</a>
<a href="/#features" class="text-gray-600 hover:text-indigo-600 font-medium">Features</a>
<a href="/#block-ideas" class="text-gray-600 hover:text-indigo-600 font-medium">Block Ideas</a>
<a href="/#project-ideas" class="text-gray-600 hover:text-indigo-600 font-medium">Project Ideas</a>
<a href="/#featured-projects" class="text-gray-600 hover:text-indigo-600 font-medium">Featured Projects</a>
<a href="/#access" class="text-gray-600 hover:text-indigo-600 font-medium">Access</a>
<a href="/#community" class="text-gray-600 hover:text-indigo-600 font-medium">Community</a>
<a href="/#about" class="text-gray-600 hover:text-indigo-600 font-medium">About</a>
<a id="account" href="account" class="text-gray-600 hover:text-indigo-600 font-medium">Login</a>
</nav>
</div>
</header>
<section id="home" class="bg-gradient-to-r from-indigo-500 to-purple-600 text-white py-20 px-4 text-center rounded-b-lg shadow-lg">
<div class="container mx-auto max-w-4xl">
<h1 class="text-4xl sm:text-5xl md:text-6xl font-extrabold leading-tight mb-6">
Unleash Your Creativity with <br class="hidden sm:inline"> MyScratchBlocks
</h1>
</div>
</section>
<section id="featured-projects" class="py-16 bg-gray-100">
<div class="container mx-auto px-4">
<h2 class="text-3xl font-bold text-gray-800 mb-8 text-center">Uploaded Projects</h2>
<div id="projects-loading" class="flex justify-center mb-6 hidden">
<span class="loading-spinner"></span>
</div>
<div id="projects-error" class="text-center text-red-500 mb-6 hidden"></div>
<div id="projects-container" class="grid sm:grid-cols-2 md:grid-cols-3 gap-6"></div>
</div>
</section>
<script>
async function fetchFeaturedProjects() {
const projectsContainer = document.getElementById('projects-container');
const projectsLoading = document.getElementById('projects-loading');
const projectsError = document.getElementById('projects-error');
projectsLoading.classList.remove('hidden');
projectsError.classList.add('hidden');
projectsContainer.innerHTML = '';
const PROJECTS_API_URL = 'https://corsproxy.io/?url=https://editor-compiler.onrender.com/api/projects';
try {
const response = await fetch(PROJECTS_API_URL);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
const projects = data.projects;
if (!Array.isArray(projects) || projects.length === 0) {
projectsContainer.innerHTML = '<p class="col-span-full text-gray-600">No featured projects available at the moment.</p>';
return;
}
// Use for...of to handle async-await properly
for (const project of projects) {
const projectCard = document.createElement('div');
projectCard.className = 'bg-white p-6 rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 transform hover:-translate-y-1';
// Safely extract id from the link
let id = project.link.split("#")[1];
let author = project.author;
if (id) {
try {
const res = await fetch(`https://corsproxy.io/?url=https://editor-compiler.onrender.com/api/projects/${id}/meta/${localStorage.getItem('username')}`);
if (res.ok) {
const jsonData = await res.json();
}
} catch (err) {
console.warn('Failed to fetch author meta for project', id, err);
}
}
const link = project.link.replace("projects", "projects/");
projectCard.innerHTML = `
<img src="https://editor-compiler.onrender.com${escapeHTML(project.image) || '/images/No%20Cover%20Available.png'}" alt="${escapeHTML(project.name)} thumbnail" class="w-full h-40 object-cover rounded-md mb-4" onerror="this.onerror=null;this.src='/images/No%20Cover%20Available.png';" />
<h3 class="text-xl font-semibold text-gray-700 mb-2">${escapeHTML(project.name)}</h3>
<p class="text-gray-600 text-sm mb-4">Author: ${author}</p>
<a href="${link}" class="text-indigo-600 hover:text-indigo-800 font-medium inline-flex items-center">
View Project
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path>
</svg>
</a>
`;
projectsContainer.appendChild(projectCard);
}
} catch (error) {
console.error('Error fetching featured projects:', error);
projectsError.classList.remove('hidden');
projectsError.textContent = `Failed to load projects. Please try again later. (Error: ${error.message})`;
} finally {
projectsLoading.classList.add('hidden');
}
}
document.addEventListener('DOMContentLoaded', () => {
fetchFeaturedProjects();
const username = localStorage.getItem('username');
if (username) {
const accountElement = document.getElementById('account');
if (accountElement) {
accountElement.textContent = username;
}
}
});
function escapeHTML(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
</script>
</body>
</html>