${post.username} ${post.caption}
document.addEventListener('DOMContentLoaded', () => { // --- INDEXED DB HELPERS (For >5MB Storage) --- const DB_NAME = 'SocialAppDB'; const DB_VERSION = 1; function openDB() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result); request.onupgradeneeded = (e) => { const db = e.target.result; if (!db.objectStoreNames.contains('images')) { db.createObjectStore('images', { keyPath: 'id' }); } }; }); } async function saveImageToDB(id, dataUrl) { try { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction('images', 'readwrite'); const store = tx.objectStore('images'); store.put({ id, data: dataUrl }); tx.oncomplete = () => resolve(); tx.onerror = () => reject(tx.error); }); } catch (e) { console.error('IndexedDB Save Error:', e); throw e; } } async function getImageFromDB(id) { try { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction('images', 'readonly'); const store = tx.objectStore('images'); const request = store.get(id); request.onsuccess = () => resolve(request.result ? request.result.data : null); request.onerror = () => reject(request.error); }); } catch (e) { console.error('IndexedDB Read Error:', e); return null; } } async function deleteImageFromDB(id) { try { const db = await openDB(); return new Promise((resolve, reject) => { const tx = db.transaction('images', 'readwrite'); const store = tx.objectStore('images'); store.delete(id); tx.oncomplete = () => resolve(); tx.onerror = () => reject(tx.error); }); } catch (e) { console.error('IndexedDB Delete Error:', e); } } // --- QUERY SELECTORS --- const loginForm = document.querySelector('.login-form'); const loginContainer = document.querySelector('.login-container'); const feedView = document.getElementById('feed-view'); const emailInput = document.getElementById('email'); const passwordInput = document.getElementById('password'); const confirmPasswordInput = document.getElementById('confirm-password'); const confirmPasswordGroup = document.getElementById('confirm-password-group'); const submitBtn = document.querySelector('.btn-primary'); const btnText = submitBtn ? submitBtn.querySelector('span') : null; const toggleLink = document.querySelector('.signup-link a'); const title = document.querySelector('.brand-section h1'); const subtitle = document.querySelector('.brand-section p'); // Post Creation Elements const createPostView = document.getElementById('create-post-view'); const cancelPostBtn = document.getElementById('cancel-post-btn'); const sharePostBtn = document.getElementById('share-post-btn'); const uploadTrigger = document.getElementById('upload-trigger'); const imageInput = document.getElementById('post-image-input'); const imagePreview = document.getElementById('image-preview'); const captionInput = document.getElementById('post-caption-input'); const navPostBtn = document.querySelector('.nav-btn:nth-child(2)'); const navHomeBtn = document.querySelector('.nav-btn:first-child'); let isLoginMode = true; // --- AUTH LOGIC --- if (toggleLink) { toggleLink.addEventListener('click', (e) => { e.preventDefault(); isLoginMode = !isLoginMode; updateUI(); }); } function updateUI() { if (!title || !subtitle || !btnText || !confirmPasswordGroup) return; if (isLoginMode) { title.innerText = 'Welcome Back'; subtitle.innerText = 'Enter your details below'; btnText.innerText = 'Sign In'; const helper = document.querySelector('.signup-link'); if (helper) helper.innerHTML = 'Don\'t have an account? Create One'; confirmPasswordGroup.classList.add('hidden'); confirmPasswordInput.required = false; } else { title.innerText = 'Create Account'; subtitle.innerText = 'Start your journey with us'; btnText.innerText = 'Sign Up'; const helper = document.querySelector('.signup-link'); if (helper) helper.innerHTML = 'Already have an account? Sign In'; confirmPasswordGroup.classList.remove('hidden'); confirmPasswordInput.required = true; } const newLink = document.querySelector('.signup-link a'); if (newLink) { newLink.addEventListener('click', (e) => { e.preventDefault(); isLoginMode = !isLoginMode; updateUI(); }); } if (loginForm) loginForm.reset(); resetButton(); } function resetButton() { if (!submitBtn || !btnText) return; submitBtn.style.background = ''; submitBtn.style.transform = ''; btnText.innerText = isLoginMode ? 'Sign In' : 'Sign Up'; } function showFeedback(isSuccess, message) { if (!btnText || !submitBtn) return; btnText.innerText = message; if (isSuccess) { submitBtn.style.background = 'linear-gradient(135deg, #10B981 0%, #059669 100%)'; } else { submitBtn.style.background = 'linear-gradient(135deg, #EF4444 0%, #B91C1C 100%)'; submitBtn.animate([ { transform: 'translateX(0)' }, { transform: 'translateX(-5px)' }, { transform: 'translateX(5px)' }, { transform: 'translateX(0)' } ], { duration: 300 }); } setTimeout(() => { if (!message.includes('Success')) { resetButton(); } }, 2000); } function navigateToFeed() { if (loginContainer && feedView) { loginContainer.style.opacity = '0'; setTimeout(() => { loginContainer.classList.add('hidden'); loginContainer.style.display = 'none'; feedView.classList.remove('hidden'); void feedView.offsetWidth; // Reflow feedView.classList.add('fade-in'); initFeed(); // Load data (Async) setTimeout(() => { feedView.classList.remove('fade-in'); }, 600); }, 600); } } if (loginForm) { loginForm.addEventListener('submit', (e) => { e.preventDefault(); const email = emailInput.value.trim(); const password = passwordInput.value.trim(); const confirmPassword = confirmPasswordInput.value.trim(); const timestamp = new Date().toISOString(); const existingUsers = JSON.parse(localStorage.getItem('socialAppUsers') || '[]'); if (isLoginMode) { const user = existingUsers.find(u => u.email === email && u.password === password); if (user) { showFeedback(true, 'Success!'); setTimeout(navigateToFeed, 1000); } else { showFeedback(false, 'Incorrect details'); } } else { if (existingUsers.some(u => u.email === email)) { showFeedback(false, 'User exists'); return; } if (password !== confirmPassword) { showFeedback(false, 'Passwords do not match'); return; } const newUser = { email, password, joinedAt: timestamp }; existingUsers.push(newUser); localStorage.setItem('socialAppUsers', JSON.stringify(existingUsers)); showFeedback(true, 'Account Created!'); setTimeout(() => { isLoginMode = true; updateUI(); setTimeout(navigateToFeed, 1000); }, 1500); } }); } // --- DATA HELPERS --- function getPostState() { return JSON.parse(localStorage.getItem('socialAppPostState') || '{}'); } function savePostState(state) { localStorage.setItem('socialAppPostState', JSON.stringify(state)); } function isPostDeleted(id) { const deletedPosts = JSON.parse(localStorage.getItem('socialAppDeletedPosts') || '[]'); return deletedPosts.includes(id); } // --- ASYNC LOAD FEED --- async function initFeed() { // 1. Render User Posts (Fetch images from DB) const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); // Use a loop to handle async properly const postsToRender = [...savedUserPosts].reverse(); for (const post of postsToRender) { if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) { // Try to get image from DB, fallback to post.imageSrc (legacy support) let imgSrc = post.imageSrc; if (!imgSrc || imgSrc.length < 100) { // If it's a placeholder (null) const dbImg = await getImageFromDB(post.id); if (dbImg) imgSrc = dbImg; } if (imgSrc) { const postWithImg = { ...post, imageSrc: imgSrc }; renderPost(postWithImg, true); } } } // 2. Restore Interaction State (Likes/Comments) // Give a small delay to ensure rendering catches up or run immediately const state = getPostState(); document.querySelectorAll('.post-card').forEach(card => { const id = card.getAttribute('data-post-id'); if (id && isPostDeleted(id)) { card.remove(); return; } if (id && state[id]) { const data = state[id]; const likeIcon = card.querySelector('.heart-icon'); const likesText = card.querySelector('.likes'); if (likeIcon && likesText) { if (data.liked) { likeIcon.classList.add('liked'); likeIcon.style.fill = '#EF4444'; likeIcon.style.stroke = '#EF4444'; } likesText.innerText = data.likesCount + ' likes'; } const commentsContainer = card.querySelector('.added-comments'); if (commentsContainer) { commentsContainer.innerHTML = ''; if (data.comments) { data.comments.forEach(text => { const el = document.createElement('div'); el.classList.add('comment-item'); el.innerHTML = `you ${text}`; commentsContainer.appendChild(el); }); } } } }); } // --- RENDER POST --- function renderPost(post, prepend = false) { // Avoid duplicates if (document.querySelector(`[data-post-id="${post.id}"]`)) return; const feedContainer = document.querySelector('.feed-container'); const article = document.createElement('article'); article.className = 'post-card'; article.setAttribute('data-post-id', post.id); const isUserPost = post.id && post.id.startsWith('post_'); const optionsHTML = isUserPost ? `
` : ``; article.innerHTML = `${post.username} ${post.caption}