Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>WallpaperHub - Beautiful Phone Wallpapers</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); | |
body { | |
font-family: 'Poppins', sans-serif; | |
background-color: #f8fafc; | |
} | |
.wallpaper-card { | |
transition: all 0.3s ease; | |
transform: scale(1); | |
} | |
.wallpaper-card:hover { | |
transform: scale(1.02); | |
box-shadow: 0 10px 25px rgba(0,0,0,0.1); | |
} | |
.favorite-btn { | |
transition: all 0.2s ease; | |
} | |
.favorite-btn.active { | |
color: #ef4444; | |
} | |
.modal { | |
transition: all 0.3s ease; | |
opacity: 0; | |
visibility: hidden; | |
} | |
.modal.active { | |
opacity: 1; | |
visibility: visible; | |
} | |
.wallpaper-image { | |
background-size: cover; | |
background-position: center; | |
transition: transform 0.5s ease; | |
} | |
.wallpaper-image:hover { | |
transform: scale(1.05); | |
} | |
.category-chip { | |
transition: all 0.2s ease; | |
} | |
.category-chip.active { | |
background-color: #3b82f6; | |
color: white; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(20px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.fade-in { | |
animation: fadeIn 0.5s ease forwards; | |
} | |
.loading-spinner { | |
border-top-color: #3b82f6; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
to { transform: rotate(360deg); } | |
} | |
</style> | |
</head> | |
<body class="min-h-screen"> | |
<!-- Header --> | |
<header class="bg-white shadow-sm sticky top-0 z-10"> | |
<div class="container mx-auto px-4 py-3 flex justify-between items-center"> | |
<div class="flex items-center"> | |
<i class="fas fa-image text-blue-500 text-2xl mr-2"></i> | |
<h1 class="text-xl font-bold text-gray-800">WallpaperHub</h1> | |
</div> | |
<div class="flex items-center space-x-4"> | |
<button id="login-btn" class="px-4 py-2 text-gray-700 hover:text-blue-500">Login</button> | |
<button id="register-btn" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Register</button> | |
<div id="user-menu" class="hidden relative"> | |
<button id="user-btn" class="flex items-center space-x-2"> | |
<img id="user-avatar" src="https://ui-avatars.com/api/?name=User&background=random" alt="User" class="w-8 h-8 rounded-full"> | |
<span id="username" class="font-medium"></span> | |
</button> | |
<div id="dropdown-menu" class="hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-20"> | |
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Profile</a> | |
<a href="#" id="favorites-link" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Favorites</a> | |
<a href="#" id="logout-btn" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Logout</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
</header> | |
<!-- Main Content --> | |
<main class="container mx-auto px-4 py-8"> | |
<!-- Search and Filters --> | |
<div class="mb-8"> | |
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6 gap-4"> | |
<div class="relative flex-grow max-w-xl"> | |
<input type="text" id="search-input" placeholder="Search wallpapers..." class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i> | |
</div> | |
<div class="flex items-center space-x-2"> | |
<button id="grid-view" class="p-2 text-gray-700 hover:text-blue-500 active"> | |
<i class="fas fa-th-large"></i> | |
</button> | |
<button id="list-view" class="p-2 text-gray-700 hover:text-blue-500"> | |
<i class="fas fa-list"></i> | |
</button> | |
</div> | |
</div> | |
<!-- Categories --> | |
<div class="flex flex-wrap gap-2 mb-6"> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm active">All</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Nature</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Abstract</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Minimal</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Space</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Animals</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Cities</button> | |
<button class="category-chip px-3 py-1 bg-gray-200 text-gray-800 rounded-full text-sm">Dark</button> | |
</div> | |
</div> | |
<!-- Wallpapers Grid --> | |
<div id="wallpapers-container" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"> | |
<!-- Wallpapers will be loaded here --> | |
</div> | |
<!-- Loading Spinner --> | |
<div id="loading-spinner" class="flex justify-center py-8 hidden"> | |
<div class="w-12 h-12 border-4 border-gray-300 rounded-full loading-spinner"></div> | |
</div> | |
<!-- Load More Button --> | |
<div class="text-center mt-8"> | |
<button id="load-more" class="px-6 py-3 bg-blue-500 text-white rounded-md hover:bg-blue-600">Load More</button> | |
</div> | |
</main> | |
<!-- Wallpaper Detail Modal --> | |
<div id="wallpaper-modal" class="modal fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center p-4 z-50"> | |
<div class="bg-white rounded-xl p-6 w-full max-w-4xl relative max-h-[90vh] overflow-y-auto"> | |
<button onclick="closeModal()" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700 text-2xl"> | |
<i class="fas fa-times"></i> | |
</button> | |
<div class="flex flex-col lg:flex-row gap-6"> | |
<div class="lg:w-2/3"> | |
<div id="wallpaper-detail-image" class="wallpaper-image w-full h-64 lg:h-96 rounded-lg bg-gray-200"></div> | |
</div> | |
<div class="lg:w-1/3"> | |
<h2 id="wallpaper-title" class="text-2xl font-bold mb-2">Wallpaper Title</h2> | |
<p id="wallpaper-author" class="text-gray-600 mb-4">By <span class="font-medium">Author Name</span></p> | |
<div class="flex items-center mb-6"> | |
<button id="favorite-btn" class="favorite-btn mr-4 text-2xl text-gray-400 hover:text-red-500"> | |
<i class="far fa-heart"></i> | |
</button> | |
<span id="download-count" class="text-gray-600"><i class="fas fa-download mr-1"></i> 0 downloads</span> | |
</div> | |
<div class="mb-6"> | |
<h3 class="font-semibold mb-2">Available Resolutions</h3> | |
<div class="grid grid-cols-2 gap-2"> | |
<button class="resolution-btn px-3 py-2 border border-gray-300 rounded hover:bg-gray-100">1080x1920</button> | |
<button class="resolution-btn px-3 py-2 border border-gray-300 rounded hover:bg-gray-100">1440x2560</button> | |
<button class="resolution-btn px-3 py-2 border border-gray-300 rounded hover:bg-gray-100">720x1280</button> | |
<button class="resolution-btn px-3 py-2 border border-gray-300 rounded hover:bg-gray-100">Original</button> | |
</div> | |
</div> | |
<div> | |
<h3 class="font-semibold mb-2">Tags</h3> | |
<div class="flex flex-wrap gap-2"> | |
<span class="px-2 py-1 bg-gray-200 text-gray-800 rounded-full text-xs">Nature</span> | |
<span class="px-2 py-1 bg-gray-200 text-gray-800 rounded-full text-xs">Mountain</span> | |
<span class="px-2 py-1 bg-gray-200 text-gray-800 rounded-full text-xs">Sunset</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Login Modal --> | |
<div id="login-modal" class="modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"> | |
<div class="bg-white rounded-xl p-6 w-full max-w-md relative"> | |
<button onclick="closeModal()" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
<h2 class="text-2xl font-bold mb-6">Login</h2> | |
<form id="login-form" class="space-y-4"> | |
<div> | |
<label for="login-email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> | |
<input type="email" id="login-email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div> | |
<label for="login-password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> | |
<input type="password" id="login-password" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div class="pt-2"> | |
<button type="submit" class="w-full py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition">Login</button> | |
</div> | |
<div class="text-center text-sm text-gray-600"> | |
Don't have an account? <button type="button" onclick="switchToRegister()" class="text-blue-500 hover:underline">Register</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<!-- Register Modal --> | |
<div id="register-modal" class="modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50"> | |
<div class="bg-white rounded-xl p-6 w-full max-w-md relative"> | |
<button onclick="closeModal()" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
<h2 class="text-2xl font-bold mb-6">Register</h2> | |
<form id="register-form" class="space-y-4"> | |
<div> | |
<label for="register-name" class="block text-sm font-medium text-gray-700 mb-1">Name</label> | |
<input type="text" id="register-name" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div> | |
<label for="register-email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> | |
<input type="email" id="register-email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div> | |
<label for="register-password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> | |
<input type="password" id="register-password" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div> | |
<label for="register-confirm-password" class="block text-sm font-medium text-gray-700 mb-1">Confirm Password</label> | |
<input type="password" id="register-confirm-password" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div class="pt-2"> | |
<button type="submit" class="w-full py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition">Register</button> | |
</div> | |
<div class="text-center text-sm text-gray-600"> | |
Already have an account? <button type="button" onclick="switchToLogin()" class="text-blue-500 hover:underline">Login</button> | |
</div> | |
</form> | |
</div> | |
</div> | |
<script> | |
// DOM Elements | |
const wallpapersContainer = document.getElementById('wallpapers-container'); | |
const wallpaperModal = document.getElementById('wallpaper-modal'); | |
const loginModal = document.getElementById('login-modal'); | |
const registerModal = document.getElementById('register-modal'); | |
const loginForm = document.getElementById('login-form'); | |
const registerForm = document.getElementById('register-form'); | |
const loginBtn = document.getElementById('login-btn'); | |
const registerBtn = document.getElementById('register-btn'); | |
const userMenu = document.getElementById('user-menu'); | |
const userBtn = document.getElementById('user-btn'); | |
const dropdownMenu = document.getElementById('dropdown-menu'); | |
const logoutBtn = document.getElementById('logout-btn'); | |
const favoritesLink = document.getElementById('favorites-link'); | |
const loadMoreBtn = document.getElementById('load-more'); | |
const loadingSpinner = document.getElementById('loading-spinner'); | |
const searchInput = document.getElementById('search-input'); | |
const categoryChips = document.querySelectorAll('.category-chip'); | |
const gridViewBtn = document.getElementById('grid-view'); | |
const listViewBtn = document.getElementById('list-view'); | |
// Sample wallpapers data (in a real app, this would come from an API) | |
const sampleWallpapers = [ | |
{ | |
id: 1, | |
title: "Mountain Sunset", | |
imageUrl: "https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "NatureLover", | |
downloads: 1245, | |
tags: ["Nature", "Mountain", "Sunset"], | |
category: "Nature", | |
resolutions: ["1080x1920", "1440x2560", "720x1280", "Original"] | |
}, | |
{ | |
id: 2, | |
title: "Abstract Waves", | |
imageUrl: "https://images.unsplash.com/photo-1534972195531-d630b249794a?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "ArtDesigner", | |
downloads: 892, | |
tags: ["Abstract", "Art", "Colorful"], | |
category: "Abstract", | |
resolutions: ["1080x1920", "1440x2560", "Original"] | |
}, | |
{ | |
id: 3, | |
title: "Minimal Geometry", | |
imageUrl: "https://images.unsplash.com/photo-1517486808906-6ca8b3f8e5c0?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "Minimalist", | |
downloads: 756, | |
tags: ["Minimal", "Geometry", "Simple"], | |
category: "Minimal", | |
resolutions: ["1080x1920", "720x1280", "Original"] | |
}, | |
{ | |
id: 4, | |
title: "Galaxy View", | |
imageUrl: "https://images.unsplash.com/photo-1462331940025-496dfbfc7564?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "SpaceExplorer", | |
downloads: 1532, | |
tags: ["Space", "Galaxy", "Stars"], | |
category: "Space", | |
resolutions: ["1080x1920", "1440x2560", "Original"] | |
}, | |
{ | |
id: 5, | |
title: "Wild Tiger", | |
imageUrl: "https://images.unsplash.com/photo-1547407139-3c921a66005c?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "WildlifePhotographer", | |
downloads: 2103, | |
tags: ["Animals", "Tiger", "Wildlife"], | |
category: "Animals", | |
resolutions: ["1080x1920", "Original"] | |
}, | |
{ | |
id: 6, | |
title: "City Lights", | |
imageUrl: "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "UrbanExplorer", | |
downloads: 1789, | |
tags: ["Cities", "Night", "Lights"], | |
category: "Cities", | |
resolutions: ["1080x1920", "1440x2560", "720x1280", "Original"] | |
}, | |
{ | |
id: 7, | |
title: "Dark Forest", | |
imageUrl: "https://images.unsplash.com/photo-1448375240586-882707db888b?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "MysteryHunter", | |
downloads: 987, | |
tags: ["Dark", "Forest", "Mystery"], | |
category: "Dark", | |
resolutions: ["1080x1920", "Original"] | |
}, | |
{ | |
id: 8, | |
title: "Ocean Waves", | |
imageUrl: "https://images.unsplash.com/photo-1505118380757-91f5f5632de0?ixlib=rb-1.2.1&auto=format&fit=crop&w=1080&q=80", | |
author: "OceanLover", | |
downloads: 1345, | |
tags: ["Nature", "Ocean", "Water"], | |
category: "Nature", | |
resolutions: ["1080x1920", "1440x2560", "Original"] | |
} | |
]; | |
// App state | |
let currentUser = null; | |
let currentWallpapers = [...sampleWallpapers]; | |
let filteredWallpapers = [...sampleWallpapers]; | |
let currentCategory = "All"; | |
let currentView = "grid"; | |
let currentSearchQuery = ""; | |
let currentPage = 1; | |
const wallpapersPerPage = 8; | |
// Initialize the app | |
document.addEventListener('DOMContentLoaded', () => { | |
// Check if user is logged in | |
checkAuthStatus(); | |
// Load initial wallpapers | |
loadWallpapers(); | |
// Event listeners | |
loginBtn.addEventListener('click', () => loginModal.classList.add('active')); | |
registerBtn.addEventListener('click', () => registerModal.classList.add('active')); | |
userBtn.addEventListener('click', () => { | |
dropdownMenu.classList.toggle('hidden'); | |
}); | |
logoutBtn.addEventListener('click', logout); | |
favoritesLink.addEventListener('click', showFavorites); | |
loginForm.addEventListener('submit', handleLogin); | |
registerForm.addEventListener('submit', handleRegister); | |
loadMoreBtn.addEventListener('click', loadMoreWallpapers); | |
searchInput.addEventListener('input', handleSearch); | |
categoryChips.forEach(chip => { | |
chip.addEventListener('click', () => filterByCategory(chip.textContent)); | |
}); | |
gridViewBtn.addEventListener('click', () => switchView('grid')); | |
listViewBtn.addEventListener('click', () => switchView('list')); | |
// Close dropdown when clicking outside | |
document.addEventListener('click', (e) => { | |
if (!userMenu.contains(e.target)) { | |
dropdownMenu.classList.add('hidden'); | |
} | |
}); | |
}); | |
// Check authentication status | |
function checkAuthStatus() { | |
const user = JSON.parse(localStorage.getItem('currentUser')); | |
if (user) { | |
currentUser = user; | |
updateAuthUI(); | |
} | |
} | |
// Update UI based on auth status | |
function updateAuthUI() { | |
if (currentUser) { | |
loginBtn.classList.add('hidden'); | |
registerBtn.classList.add('hidden'); | |
userMenu.classList.remove('hidden'); | |
document.getElementById('username').textContent = currentUser.name; | |
document.getElementById('user-avatar').src = currentUser.avatar || "https://ui-avatars.com/api/?name=" + currentUser.name + "&background=random"; | |
} else { | |
loginBtn.classList.remove('hidden'); | |
registerBtn.classList.remove('hidden'); | |
userMenu.classList.add('hidden'); | |
} | |
} | |
// Handle login | |
function handleLogin(e) { | |
e.preventDefault(); | |
const email = document.getElementById('login-email').value; | |
const password = document.getElementById('login-password').value; | |
// Simple validation | |
if (!email || !password) { | |
alert('Please fill in all fields'); | |
return; | |
} | |
// In a real app, this would be an API call | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const user = users.find(u => u.email === email && u.password === password); | |
if (user) { | |
currentUser = { | |
id: user.id, | |
name: user.name, | |
email: user.email, | |
avatar: user.avatar | |
}; | |
localStorage.setItem('currentUser', JSON.stringify(currentUser)); | |
updateAuthUI(); | |
closeModal(); | |
} else { | |
alert('Invalid email or password'); | |
} | |
} | |
// Handle registration | |
function handleRegister(e) { | |
e.preventDefault(); | |
const name = document.getElementById('register-name').value; | |
const email = document.getElementById('register-email').value; | |
const password = document.getElementById('register-password').value; | |
const confirmPassword = document.getElementById('register-confirm-password').value; | |
// Validation | |
if (!name || !email || !password || !confirmPassword) { | |
alert('Please fill in all fields'); | |
return; | |
} | |
if (password !== confirmPassword) { | |
alert('Passwords do not match'); | |
return; | |
} | |
if (password.length < 6) { | |
alert('Password must be at least 6 characters'); | |
return; | |
} | |
// Check if user already exists | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const userExists = users.some(u => u.email === email); | |
if (userExists) { | |
alert('Email already registered'); | |
return; | |
} | |
// Create new user | |
const newUser = { | |
id: Date.now(), | |
name, | |
email, | |
password, // In a real app, passwords should be hashed | |
avatar: "https://ui-avatars.com/api/?name=" + name + "&background=random", | |
favorites: [] | |
}; | |
users.push(newUser); | |
localStorage.setItem('users', JSON.stringify(users)); | |
// Log the user in | |
currentUser = { | |
id: newUser.id, | |
name: newUser.name, | |
email: newUser.email, | |
avatar: newUser.avatar | |
}; | |
localStorage.setItem('currentUser', JSON.stringify(currentUser)); | |
updateAuthUI(); | |
closeModal(); | |
} | |
// Logout | |
function logout() { | |
currentUser = null; | |
localStorage.removeItem('currentUser'); | |
updateAuthUI(); | |
dropdownMenu.classList.add('hidden'); | |
} | |
// Show favorites | |
function showFavorites(e) { | |
e.preventDefault(); | |
if (!currentUser) return; | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const user = users.find(u => u.id === currentUser.id); | |
if (user && user.favorites && user.favorites.length > 0) { | |
filteredWallpapers = sampleWallpapers.filter(wp => user.favorites.includes(wp.id)); | |
currentCategory = "Favorites"; | |
currentPage = 1; | |
updateActiveCategory(); | |
loadWallpapers(); | |
} else { | |
filteredWallpapers = []; | |
currentCategory = "Favorites"; | |
currentPage = 1; | |
updateActiveCategory(); | |
loadWallpapers(); | |
alert('You have no favorites yet'); | |
} | |
dropdownMenu.classList.add('hidden'); | |
} | |
// Load wallpapers | |
function loadWallpapers() { | |
wallpapersContainer.innerHTML = ''; | |
if (filteredWallpapers.length === 0) { | |
wallpapersContainer.innerHTML = ` | |
<div class="col-span-full text-center py-12 text-gray-500"> | |
<i class="fas fa-image text-4xl mb-4"></i> | |
<p class="text-lg">No wallpapers found</p> | |
<button onclick="resetFilters()" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600">Reset Filters</button> | |
</div> | |
`; | |
loadMoreBtn.classList.add('hidden'); | |
return; | |
} | |
const startIndex = (currentPage - 1) * wallpapersPerPage; | |
const endIndex = startIndex + wallpapersPerPage; | |
const wallpapersToShow = filteredWallpapers.slice(0, endIndex); | |
wallpapersToShow.forEach(wallpaper => { | |
const wallpaperElement = document.createElement('div'); | |
wallpaperElement.className = `wallpaper-card bg-white rounded-lg overflow-hidden shadow-md fade-in ${currentView === 'grid' ? '' : 'flex'}`; | |
if (currentView === 'grid') { | |
wallpaperElement.innerHTML = ` | |
<div class="relative"> | |
<div class="wallpaper-image w-full h-48" style="background-image: url('${wallpaper.imageUrl}')"></div> | |
<button class="favorite-btn absolute top-2 right-2 text-2xl text-white bg-black bg-opacity-40 rounded-full w-10 h-10 flex items-center justify-center"> | |
<i class="far fa-heart"></i> | |
</button> | |
</div> | |
<div class="p-4"> | |
<h3 class="font-semibold text-gray-800 truncate">${wallpaper.title}</h3> | |
<p class="text-sm text-gray-600">By ${wallpaper.author}</p> | |
<div class="flex justify-between items-center mt-3"> | |
<span class="text-xs text-gray-500"><i class="fas fa-download mr-1"></i> ${wallpaper.downloads}</span> | |
<button onclick="viewWallpaper(${wallpaper.id})" class="text-sm text-blue-500 hover:text-blue-700">View</button> | |
</div> | |
</div> | |
`; | |
} else { | |
wallpaperElement.innerHTML = ` | |
<div class="w-1/3"> | |
<div class="wallpaper-image w-full h-full" style="background-image: url('${wallpaper.imageUrl}')"></div> | |
</div> | |
<div class="w-2/3 p-4"> | |
<div class="flex justify-between items-start"> | |
<div> | |
<h3 class="font-semibold text-gray-800">${wallpaper.title}</h3> | |
<p class="text-sm text-gray-600">By ${wallpaper.author}</p> | |
</div> | |
<button class="favorite-btn text-xl"> | |
<i class="far fa-heart"></i> | |
</button> | |
</div> | |
<div class="mt-4"> | |
<span class="text-xs text-gray-500"><i class="fas fa-download mr-1"></i> ${wallpaper.downloads}</span> | |
</div> | |
<div class="mt-4 flex flex-wrap gap-2"> | |
${wallpaper.tags.map(tag => `<span class="px-2 py-1 bg-gray-200 text-gray-800 rounded-full text-xs">${tag}</span>`).join('')} | |
</div> | |
<button onclick="viewWallpaper(${wallpaper.id})" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 text-sm">View & Download</button> | |
</div> | |
`; | |
} | |
// Set favorite status | |
if (currentUser) { | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const user = users.find(u => u.id === currentUser.id); | |
if (user && user.favorites && user.favorites.includes(wallpaper.id)) { | |
const heartIcon = wallpaperElement.querySelector('.favorite-btn i'); | |
if (heartIcon) { | |
heartIcon.classList.remove('far'); | |
heartIcon.classList.add('fas'); | |
wallpaperElement.querySelector('.favorite-btn').classList.add('active'); | |
} | |
} | |
} | |
// Add favorite event | |
const favoriteBtn = wallpaperElement.querySelector('.favorite-btn'); | |
if (favoriteBtn) { | |
favoriteBtn.addEventListener('click', (e) => { | |
e.stopPropagation(); | |
toggleFavorite(wallpaper.id, favoriteBtn); | |
}); | |
} | |
// Add click event to view wallpaper | |
wallpaperElement.addEventListener('click', () => { | |
viewWallpaper(wallpaper.id); | |
}); | |
wallpapersContainer.appendChild(wallpaperElement); | |
}); | |
// Show/hide load more button | |
if (endIndex >= filteredWallpapers.length) { | |
loadMoreBtn.classList.add('hidden'); | |
} else { | |
loadMoreBtn.classList.remove('hidden'); | |
} | |
} | |
// Load more wallpapers | |
function loadMoreWallpapers() { | |
currentPage++; | |
loadWallpapers(); | |
} | |
// View wallpaper details | |
function viewWallpaper(id) { | |
const wallpaper = sampleWallpapers.find(wp => wp.id === id); | |
if (!wallpaper) return; | |
// Update modal content | |
document.getElementById('wallpaper-detail-image').style.backgroundImage = `url('${wallpaper.imageUrl}')`; | |
document.getElementById('wallpaper-title').textContent = wallpaper.title; | |
document.getElementById('wallpaper-author').innerHTML = `By <span class="font-medium">${wallpaper.author}</span>`; | |
document.getElementById('download-count').innerHTML = `<i class="fas fa-download mr-1"></i> ${wallpaper.downloads} downloads`; | |
// Update tags | |
const tagsContainer = document.querySelector('#wallpaper-modal .flex-wrap'); | |
tagsContainer.innerHTML = wallpaper.tags.map(tag => | |
`<span class="px-2 py-1 bg-gray-200 text-gray-800 rounded-full text-xs">${tag}</span>` | |
).join(''); | |
// Update resolutions | |
const resolutionBtns = document.querySelectorAll('.resolution-btn'); | |
resolutionBtns.forEach((btn, index) => { | |
if (index < wallpaper.resolutions.length) { | |
btn.textContent = wallpaper.resolutions[index]; | |
btn.classList.remove('hidden'); | |
btn.onclick = () => downloadWallpaper(wallpaper.id, wallpaper.resolutions[index]); | |
} else { | |
btn.classList.add('hidden'); | |
} | |
}); | |
// Update favorite button | |
const favoriteBtn = document.getElementById('favorite-btn'); | |
favoriteBtn.onclick = () => toggleFavorite(wallpaper.id, favoriteBtn); | |
if (currentUser) { | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const user = users.find(u => u.id === currentUser.id); | |
if (user && user.favorites && user.favorites.includes(wallpaper.id)) { | |
const heartIcon = favoriteBtn.querySelector('i'); | |
heartIcon.classList.remove('far'); | |
heartIcon.classList.add('fas'); | |
favoriteBtn.classList.add('active'); | |
} else { | |
const heartIcon = favoriteBtn.querySelector('i'); | |
heartIcon.classList.remove('fas'); | |
heartIcon.classList.add('far'); | |
favoriteBtn.classList.remove('active'); | |
} | |
} | |
// Open modal | |
wallpaperModal.classList.add('active'); | |
} | |
// Toggle favorite | |
function toggleFavorite(id, button) { | |
if (!currentUser) { | |
loginModal.classList.add('active'); | |
return; | |
} | |
const users = JSON.parse(localStorage.getItem('users')) || []; | |
const userIndex = users.findIndex(u => u.id === currentUser.id); | |
if (userIndex === -1) return; | |
if (!users[userIndex].favorites) { | |
users[userIndex].favorites = []; | |
} | |
const heartIcon = button.querySelector('i'); | |
const isFavorite = users[userIndex].favorites.includes(id); | |
if (isFavorite) { | |
// Remove from favorites | |
users[userIndex].favorites = users[userIndex].favorites.filter(favId => favId !== id); | |
heartIcon.classList.remove('fas'); | |
heartIcon.classList.add('far'); | |
button.classList.remove('active'); | |
} else { | |
// Add to favorites | |
users[userIndex].favorites.push(id); | |
heartIcon.classList.remove('far'); | |
heartIcon.classList.add('fas'); | |
button.classList.add('active'); | |
} | |
localStorage.setItem('users', JSON.stringify(users)); | |
// If viewing favorites, reload | |
if (currentCategory === "Favorites") { | |
showFavorites({ preventDefault: () => {} }); | |
} | |
} | |
// Download wallpaper | |
function downloadWallpaper(id, resolution) { | |
const wallpaper = sampleWallpapers.find(wp => wp.id === id); | |
if (!wallpaper) return; | |
// In a real app, this would download the correct resolution from the server | |
// For this demo, we'll just simulate a download | |
alert(`Downloading ${wallpaper.title} in ${resolution} resolution`); | |
// Update download count | |
const wallpaperIndex = sampleWallpapers.findIndex(wp => wp.id === id); | |
if (wallpaperIndex !== -1) { | |
sampleWallpapers[wallpaperIndex].downloads++; | |
loadWallpapers(); | |
} | |
} | |
// Handle search | |
function handleSearch() { | |
currentSearchQuery = searchInput.value.toLowerCase(); | |
filterWallpapers(); | |
} | |
// Filter by category | |
function filterByCategory(category) { | |
currentCategory = category; | |
currentPage = 1; | |
filterWallpapers(); | |
updateActiveCategory(); | |
} | |
// Update active category chip | |
function updateActiveCategory() { | |
categoryChips.forEach(chip => { | |
chip.classList.remove('active'); | |
if (chip.textContent === currentCategory) { | |
chip.classList.add('active'); | |
} | |
}); | |
} | |
// Filter wallpapers based on search and category | |
function filterWallpapers() { | |
filteredWallpapers = currentWallpapers.filter(wallpaper => { | |
const matchesSearch = wallpaper.title.toLowerCase().includes(currentSearchQuery) || | |
wallpaper.author.toLowerCase().includes(currentSearchQuery) || | |
wallpaper.tags.some(tag => tag.toLowerCase().includes(currentSearchQuery)); | |
const matchesCategory = currentCategory === "All" || | |
currentCategory === "Favorites" || | |
wallpaper.category === currentCategory; | |
return matchesSearch && matchesCategory; | |
}); | |
loadWallpapers(); | |
} | |
// Switch view between grid and list | |
function switchView(view) { | |
currentView = view; | |
if (view === 'grid') { | |
gridViewBtn.classList.add('active'); | |
listViewBtn.classList.remove('active'); | |
wallpapersContainer.classList.remove('grid-cols-1'); | |
wallpapersContainer.classList.add('grid-cols-1', 'sm:grid-cols-2', 'md:grid-cols-3', 'lg:grid-cols-4'); | |
} else { | |
gridViewBtn.classList.remove('active'); | |
listViewBtn.classList.add('active'); | |
wallpapersContainer.classList.remove('grid-cols-1', 'sm:grid-cols-2', 'md:grid-cols-3', 'lg:grid-cols-4'); | |
wallpapersContainer.classList.add('grid-cols-1'); | |
} | |
loadWallpapers(); | |
} | |
// Reset all filters | |
function resetFilters() { | |
currentCategory = "All"; | |
currentSearchQuery = ""; | |
searchInput.value = ""; | |
currentPage = 1; | |
filteredWallpapers = [...sampleWallpapers]; | |
updateActiveCategory(); | |
loadWallpapers(); | |
} | |
// Switch to register modal | |
function switchToRegister() { | |
loginModal.classList.remove('active'); | |
registerModal.classList.add('active'); | |
} | |
// Switch to login modal | |
function switchToLogin() { | |
registerModal.classList.remove('active'); | |
loginModal.classList.add('active'); | |
} | |
// Close modal | |
function closeModal() { | |
document.querySelectorAll('.modal').forEach(modal => { | |
modal.classList.remove('active'); | |
}); | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Hoof2/wallpaperhub" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |