From 6262023aa9374109027d64bb026f2f99ba1e31b9 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 28 Jan 2026 21:22:51 +1100 Subject: [PATCH] DeletePost --- script.js | 716 ++++++++++++++++++++++++++---------------------- styles/main.css | 51 ++++ 2 files changed, 443 insertions(+), 324 deletions(-) diff --git a/script.js b/script.js index f02be57..1854f1c 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,5 @@ document.addEventListener('DOMContentLoaded', () => { + // --- QUERY SELECTORS --- const loginForm = document.querySelector('.login-form'); const loginContainer = document.querySelector('.login-container'); const feedView = document.getElementById('feed-view'); @@ -7,15 +8,25 @@ document.addEventListener('DOMContentLoaded', () => { const confirmPasswordInput = document.getElementById('confirm-password'); const confirmPasswordGroup = document.getElementById('confirm-password-group'); const submitBtn = document.querySelector('.btn-primary'); - const btnText = submitBtn.querySelector('span'); - const helperText = document.querySelector('.signup-link'); + 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; - // Toggle between Login and Signup + // --- AUTH LOGIC --- if (toggleLink) { toggleLink.addEventListener('click', (e) => { e.preventDefault(); @@ -25,47 +36,53 @@ document.addEventListener('DOMContentLoaded', () => { } function updateUI() { + if (!title || !subtitle || !btnText || !confirmPasswordGroup) return; + if (isLoginMode) { title.innerText = 'Welcome Back'; subtitle.innerText = 'Enter your details below'; btnText.innerText = 'Sign In'; - helperText.innerHTML = 'Don\'t have an account? Create One'; + 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'; - helperText.innerHTML = 'Already have an account? Sign In'; + const helper = document.querySelector('.signup-link'); + if (helper) helper.innerHTML = 'Already have an account? Sign In'; confirmPasswordGroup.classList.remove('hidden'); confirmPasswordInput.required = true; } - // Re-attach listener to the new link element const newLink = document.querySelector('.signup-link a'); - newLink.addEventListener('click', (e) => { - e.preventDefault(); - isLoginMode = !isLoginMode; - updateUI(); - }); + if (newLink) { + newLink.addEventListener('click', (e) => { + e.preventDefault(); + isLoginMode = !isLoginMode; + updateUI(); + }); + } - // Reset form and styles - loginForm.reset(); + 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%)'; // Success Green + submitBtn.style.background = 'linear-gradient(135deg, #10B981 0%, #059669 100%)'; } else { - submitBtn.style.background = 'linear-gradient(135deg, #EF4444 0%, #B91C1C 100%)'; // Error Red + submitBtn.style.background = 'linear-gradient(135deg, #EF4444 0%, #B91C1C 100%)'; submitBtn.animate([ { transform: 'translateX(0)' }, { transform: 'translateX(-5px)' }, @@ -83,77 +100,54 @@ document.addEventListener('DOMContentLoaded', () => { function navigateToFeed() { if (loginContainer && feedView) { - // Fade out login loginContainer.style.opacity = '0'; - setTimeout(() => { loginContainer.classList.add('hidden'); - loginContainer.style.display = 'none'; // Ensure it's gone + loginContainer.style.display = 'none'; - // Show Feed feedView.classList.remove('hidden'); - // Trigger reflow - void feedView.offsetWidth; + void feedView.offsetWidth; // Reflow feedView.classList.add('fade-in'); - // Load persisted state for posts - loadPostState(); + loadPostState(); // Load data - // Remove fade-in class after animation to fix fixed positioning context setTimeout(() => { feedView.classList.remove('fade-in'); }, 600); - }, 600); // Wait for transition + }, 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(); - - // Get existing users const existingUsers = JSON.parse(localStorage.getItem('socialAppUsers') || '[]'); if (isLoginMode) { - // --- LOGIN LOGIC --- const user = existingUsers.find(u => u.email === email && u.password === password); - if (user) { - console.log('Login Successful:', user); showFeedback(true, 'Success!'); setTimeout(navigateToFeed, 1000); } else { - console.warn('Login Failed: Invalid credentials'); showFeedback(false, 'Incorrect details'); } - } else { - // --- SIGNUP LOGIC --- - // 1. Check if user already exists if (existingUsers.some(u => u.email === email)) { showFeedback(false, 'User exists'); return; } - - // 2. Validate Password Match 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)); - - console.log('New User Registered:', newUser); showFeedback(true, 'Account Created!'); - - // Switch to login mode after success setTimeout(() => { isLoginMode = true; updateUI(); @@ -163,290 +157,150 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // --- PERSISTENCE LOGIC --- + // --- 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); + } function loadPostState() { const state = getPostState(); - const posts = document.querySelectorAll('.post-card'); - - posts.forEach(card => { + // 1. Remove deleted static posts + document.querySelectorAll('.post-card').forEach(card => { const id = card.getAttribute('data-post-id'); - if (id && state[id]) { + if (id && isPostDeleted(id)) { + card.remove(); + } else if (id && state[id]) { + // Restore Likes/Comments const data = state[id]; - - // Restore Likes const likeIcon = card.querySelector('.heart-icon'); const likesText = card.querySelector('.likes'); - - if (data.liked) { - likeIcon.classList.add('liked'); - likeIcon.style.fill = '#EF4444'; - likeIcon.style.stroke = '#EF4444'; - } else { - likeIcon.classList.remove('liked'); - likeIcon.style.fill = 'none'; - likeIcon.style.stroke = 'white'; + if (likeIcon && likesText) { + if (data.liked) { + likeIcon.classList.add('liked'); + likeIcon.style.fill = '#EF4444'; + likeIcon.style.stroke = '#EF4444'; + } else { + likeIcon.classList.remove('liked'); + likeIcon.style.fill = 'none'; + likeIcon.style.stroke = 'white'; + } + likesText.innerText = data.likesCount + ' likes'; } - likesText.innerText = data.likesCount + ' likes'; - // Restore Comments const commentsContainer = card.querySelector('.added-comments'); - commentsContainer.innerHTML = ''; // Clear existing to prevent dupes on re-run - if (data.comments && data.comments.length > 0) { - data.comments.forEach(text => { - const commentEl = document.createElement('div'); - commentEl.classList.add('comment-item'); - commentEl.innerHTML = `you ${text}`; - commentsContainer.appendChild(commentEl); - }); + if (commentsContainer) { + commentsContainer.innerHTML = ''; + if (data.comments && data.comments.length > 0) { + data.comments.forEach(text => { + const commentEl = document.createElement('div'); + commentEl.classList.add('comment-item'); + commentEl.innerHTML = `you ${text}`; + commentsContainer.appendChild(commentEl); + }); + } } } }); + + // 2. Render User Posts (if not already present) + const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); + // We iterate correctly to put newest on top + [...savedUserPosts].reverse().forEach(post => { + if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) { + renderPost(post, true); + // Re-apply state if needed? create function handles it mostly, + // but checking `state` for these dynamic posts is also good practice if we tracked likes on them. + // For now, let's assume they load fresh or we'd need to run the state logic on them too. + // Actually, logic is: render then apply state. + } + }); + + // Re-run state application for newly added dynamic posts + // Optimization: splitting this logic explicitly would be better, but re-querying all is fine for small apps. + // Let's just do a second pass for dynamic posts to support likes on them? + // Existing loop `document.querySelectorAll` covers them if they are in DOM. + // So this order (Filter Static -> Render Dynamic -> (Ideally re-check state on all)) is slightly off. + // Better: Render Dynamic First, THEN apply state to ALL. } - // --- FEED INTERACTIONS --- - if (feedView) { - feedView.addEventListener('click', (e) => { - const target = e.target; + // Improve Load Flow + function initFeed() { + const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); + [...savedUserPosts].reverse().forEach(post => { + if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) { + renderPost(post, true); + } + }); - // 1. LIKE ACTION - const likeBtn = target.closest('.heart-icon')?.closest('.icon-btn'); - if (likeBtn) { - const icon = likeBtn.querySelector('.heart-icon'); - const postCard = likeBtn.closest('.post-card'); - const likesText = postCard.querySelector('.likes'); - const postId = postCard.getAttribute('data-post-id'); - - // Toggle State - icon.classList.toggle('liked'); - - // Update Count (Simple parsing) - let count = parseInt(likesText.innerText); - let isLiked = false; - - if (icon.classList.contains('liked')) { - icon.style.fill = '#EF4444'; - icon.style.stroke = '#EF4444'; - count++; - isLiked = true; - } else { - icon.style.fill = 'none'; - icon.style.stroke = 'white'; - count--; - isLiked = false; - } - likesText.innerText = count + ' likes'; - - // Save State - if (postId) { - const state = getPostState(); - if (!state[postId]) state[postId] = { comments: [] }; - state[postId].liked = isLiked; - state[postId].likesCount = count; - // Preserve existing comments if we didn't just load them - if (!state[postId].comments) state[postId].comments = []; - savePostState(state); - } + const state = getPostState(); + document.querySelectorAll('.post-card').forEach(card => { + const id = card.getAttribute('data-post-id'); + if (id && isPostDeleted(id)) { + card.remove(); return; } - - // 2. COMMENT TOGGLE ACTION - const commentBtn = target.closest('.icon-btn'); - if (commentBtn && !commentBtn.querySelector('.heart-icon')) { - // Check if it's the comment button (has specific path) - const path = commentBtn.querySelector('path'); - if (path && (path.getAttribute('d').startsWith('M21 11.5') || path.closest('svg')?.innerHTML.includes('M21 11.5'))) { - const postCard = commentBtn.closest('.post-card'); - const commentSection = postCard.querySelector('.comment-section'); - commentSection.classList.toggle('active'); - if (commentSection.classList.contains('active')) { - commentSection.querySelector('input').focus(); + 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'; } - return; + likesText.innerText = data.likesCount + ' likes'; } - } - - // 3. POST COMMENT ACTION - if (target.classList.contains('post-btn')) { - const wrapper = target.closest('.comment-input-wrapper'); - const input = wrapper.querySelector('.comment-input'); - const text = input.value.trim(); - const postCard = wrapper.closest('.post-card'); - const postId = postCard.getAttribute('data-post-id'); - - if (text) { - addComment(wrapper.closest('.comment-section'), text); - - if (postId) { - const state = getPostState(); - if (!state[postId]) state[postId] = { liked: false, likesCount: parseInt(postCard.querySelector('.likes').innerText) || 0, comments: [] }; - if (!state[postId].comments) state[postId].comments = []; - state[postId].comments.push(text); - savePostState(state); + 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); + }); } - input.value = ''; - } - } - }); - - // Enter key for comments - feedView.addEventListener('keydown', (e) => { - if (e.key === 'Enter' && e.target.classList.contains('comment-input')) { - const text = e.target.value.trim(); - const wrapper = e.target.closest('.comment-input-wrapper'); - const postCard = wrapper.closest('.post-card'); - const postId = postCard.getAttribute('data-post-id'); - - if (text) { - addComment(e.target.closest('.comment-section'), text); - - if (postId) { - const state = getPostState(); - if (!state[postId]) state[postId] = { liked: false, likesCount: parseInt(postCard.querySelector('.likes').innerText) || 0, comments: [] }; - if (!state[postId].comments) state[postId].comments = []; - state[postId].comments.push(text); - savePostState(state); - } - e.target.value = ''; } } }); } - function addComment(section, text) { - const container = section.querySelector('.added-comments'); - const commentEl = document.createElement('div'); - commentEl.classList.add('comment-item'); - commentEl.innerHTML = `you ${text}`; - container.appendChild(commentEl); - } - - // --- CREATE POST LOGIC --- - 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)'); // Center Post Button - const navHomeBtn = document.querySelector('.nav-btn:first-child'); - - // 1. Navigation - if (navPostBtn) { - navPostBtn.addEventListener('click', () => { - feedView.classList.add('hidden'); - createPostView.classList.remove('hidden'); - createPostView.classList.add('fade-in'); - }); - } - - if (cancelPostBtn) { - cancelPostBtn.addEventListener('click', () => { - resetPostForm(); - createPostView.classList.add('hidden'); - feedView.classList.remove('hidden'); - }); - } - - if (navHomeBtn && feedView.classList.contains('hidden') && !createPostView.classList.contains('hidden')) { - // Logic handled by cancel mostly, but good to have explicit nav - navHomeBtn.addEventListener('click', () => { - if (!createPostView.classList.contains('hidden')) { - resetPostForm(); - createPostView.classList.add('hidden'); - feedView.classList.remove('hidden'); - } - }); - } - - // 2. Image Selection - if (uploadTrigger) { - uploadTrigger.addEventListener('click', () => imageInput.click()); - } - - if (imageInput) { - imageInput.addEventListener('change', (e) => { - const file = e.target.files[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - imagePreview.src = e.target.result; - imagePreview.classList.remove('hidden'); - document.querySelector('.upload-placeholder').classList.add('hidden'); - }; - reader.readAsDataURL(file); - } - }); - } - - // 3. Share Post - if (sharePostBtn) { - sharePostBtn.addEventListener('click', () => { - if (!imagePreview.src || imagePreview.classList.contains('hidden')) { - alert('Please select an image first.'); - return; - } - - const caption = captionInput.value.trim(); - const imageSrc = imagePreview.src; - const id = 'post_' + Date.now(); - - const newPost = { - id, - imageSrc, - caption, - timestamp: new Date().toISOString(), - username: 'you', // Mock user - likes: 0 - }; - - // Save to LocalStorage - const userPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); - userPosts.unshift(newPost); // Add to top - localStorage.setItem('socialAppUserPosts', JSON.stringify(userPosts)); - - // Render to Feed - renderPost(newPost, true); // true = prepend - - // Reset and Go Back - resetPostForm(); - createPostView.classList.add('hidden'); - feedView.classList.remove('hidden'); - - // Re-bind listeners isn't needed strictly due to delegation, but state refresh might be - loadPostState(); - }); - } - - function resetPostForm() { - imageInput.value = ''; - captionInput.value = ''; - imagePreview.src = ''; - imagePreview.classList.add('hidden'); - document.querySelector('.upload-placeholder').classList.remove('hidden'); - } - + // --- RENDER POST --- function renderPost(post, prepend = false) { 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 = `
User ${post.username}
- + ${optionsHTML}
Post @@ -471,49 +325,263 @@ document.addEventListener('DOMContentLoaded', () => {
`; - // This is a bit tricky with existing static posts. - // Ideally we keep static posts static and just inject dynamic ones. - if (prepend) { - feedContainer.insertBefore(article, feedContainer.firstChild); - // Note: index.html has static articles directly. `firstChild` might be text node or first article. - // A safer insert would be after the first spacer if I had one, or simply `prepend`. - // Let's use `prepend` on the container, but strict ordering might be weird with existing static HTML. - // Actually, `prepend` puts it before the first child, which IS the first post card. Perfect. - } else { - feedContainer.appendChild(article); - } + if (prepend) feedContainer.insertBefore(article, feedContainer.firstChild); + else feedContainer.appendChild(article); } - // Load User Posts on Init - const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); - savedUserPosts.forEach(post => { - // Check if already in DOM to avoid dupes (simple check) - if (!document.querySelector(`[data-post-id="${post.id}"]`)) { - renderPost(post, true); // Keep order inverted? - // If we loop saved posts [newest, ..., oldest], we should Append? - // Actually saved as [newest, oldest]. - // If we prepend one by one: - // 1. Newest -> Prepend -> Top. - // 2. Oldest -> Prepend -> Top (Wrong). - // So we should iterate in reverse if we prepend, OR just append at top once properly sorted. - // Let's keep it simple: Iterate in reverse order [oldest ... newest] and prepend each, so newest ends up on top. - } - }); - // Fix order: Reverse iteration for prepend logic - // savedUserPosts.reverse().forEach(...) -> Wait, `unshift` adds to start. So [0] is newest. - // Loop: - // Post 0 (Newest) -> Prepend (become first). - // Post 1 (Older) -> Prepend (become first). - // Result: Post 1 is above Post 0. WRONG. - // Correct: Iterate from last to first (reverse) and prepend. - // Post Last (Oldest) -> Prepend. - // ... - // Post 0 (Newest) -> Prepend (Top). CORRECT. - // So: - [...savedUserPosts].reverse().forEach(post => { - if (!document.querySelector(`[data-post-id="${post.id}"]`)) { - renderPost(post, true); - } - }); + // --- FEED LISTENERS --- + if (feedView) { + initFeed(); // Initial Load logic + feedView.addEventListener('click', (e) => { + const target = e.target; + + // 1. OPTIONS MENU + const trigger = target.closest('.options-trigger'); + if (trigger) { + const header = trigger.closest('.post-header'); + const menu = header.querySelector('.options-menu'); + if (menu) { + document.querySelectorAll('.options-menu.active').forEach(m => { + if (m !== menu) m.classList.remove('active'); + }); + menu.classList.toggle('active'); + e.stopPropagation(); + } + return; + } + + // 2. DELETE ACTION + const deleteBtn = target.closest('.delete'); + if (deleteBtn) { + const card = deleteBtn.closest('.post-card'); + const id = card.getAttribute('data-post-id'); + + if (id && id.startsWith('post_')) { // Double check ownership + if (confirm('Delete this post?')) { + card.style.transition = 'opacity 0.3s, transform 0.3s'; + card.style.opacity = '0'; + card.style.transform = 'scale(0.9)'; + setTimeout(() => card.remove(), 300); + + let userPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); + userPosts = userPosts.filter(p => p.id !== id); + localStorage.setItem('socialAppUserPosts', JSON.stringify(userPosts)); + + // Also ban ID to be safe + const deletedPosts = JSON.parse(localStorage.getItem('socialAppDeletedPosts') || '[]'); + if (!deletedPosts.includes(id)) { + deletedPosts.push(id); + localStorage.setItem('socialAppDeletedPosts', JSON.stringify(deletedPosts)); + } + } + } + return; + } + + // 3. LIKE ACTION + const likeBtn = target.closest('.heart-icon')?.closest('.icon-btn'); + if (likeBtn) { + const icon = likeBtn.querySelector('.heart-icon'); + const postCard = likeBtn.closest('.post-card'); + const likesText = postCard.querySelector('.likes'); + const postId = postCard.getAttribute('data-post-id'); + + icon.classList.toggle('liked'); + let count = parseInt(likesText.innerText); + let isLiked = false; + + if (icon.classList.contains('liked')) { + icon.style.fill = '#EF4444'; + icon.style.stroke = '#EF4444'; + count++; + isLiked = true; + } else { + icon.style.fill = 'none'; + icon.style.stroke = 'white'; + count--; + isLiked = false; + } + likesText.innerText = count + ' likes'; + + if (postId) { + const state = getPostState(); + if (!state[postId]) state[postId] = { comments: [] }; + state[postId].liked = isLiked; + state[postId].likesCount = count; + if (!state[postId].comments) state[postId].comments = []; + savePostState(state); + } + return; + } + + // 4. COMMENT TOGGLE + const commentBtn = target.closest('.icon-btn'); + if (commentBtn && !commentBtn.querySelector('.heart-icon') && !commentBtn.closest('.post-actions').querySelector('.action-left').contains(commentBtn) === false) { + // The comment button is the second one in action-left. + // Easier check: + const svgs = commentBtn.innerHTML; + if (svgs.includes('M21 11.5')) { + const postCard = commentBtn.closest('.post-card'); + const commentSection = postCard.querySelector('.comment-section'); + commentSection.classList.toggle('active'); + if (commentSection.classList.contains('active')) { + commentSection.querySelector('input').focus(); + } + return; + } + } + + // 5. POST COMMENT + if (target.classList.contains('post-btn')) { + const wrapper = target.closest('.comment-input-wrapper'); + const input = wrapper.querySelector('.comment-input'); + const text = input.value.trim(); + const postCard = wrapper.closest('.post-card'); + const postId = postCard.getAttribute('data-post-id'); + + if (text) { + addComment(wrapper.closest('.comment-section'), text); + if (postId) { + const state = getPostState(); + if (!state[postId]) state[postId] = { liked: false, likesCount: parseInt(postCard.querySelector('.likes').innerText) || 0, comments: [] }; + if (!state[postId].comments) state[postId].comments = []; + state[postId].comments.push(text); + savePostState(state); + } + input.value = ''; + } + } + }); + + // Enter key for comments + feedView.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && e.target.classList.contains('comment-input')) { + const text = e.target.value.trim(); + const wrapper = e.target.closest('.comment-input-wrapper'); + const postCard = wrapper.closest('.post-card'); + const postId = postCard.getAttribute('data-post-id'); + + if (text) { + addComment(e.target.closest('.comment-section'), text); + if (postId) { + const state = getPostState(); + if (!state[postId]) state[postId] = { liked: false, likesCount: parseInt(postCard.querySelector('.likes').innerText) || 0, comments: [] }; + if (!state[postId].comments) state[postId].comments = []; + state[postId].comments.push(text); + savePostState(state); + } + e.target.value = ''; + } + } + }); + + // Close menus + document.addEventListener('click', (e) => { + if (!e.target.closest('.options-menu') && !e.target.closest('.options-trigger')) { + document.querySelectorAll('.options-menu.active').forEach(m => m.classList.remove('active')); + } + }); + } + + function addComment(section, text) { + const container = section.querySelector('.added-comments'); + const commentEl = document.createElement('div'); + commentEl.classList.add('comment-item'); + commentEl.innerHTML = `you ${text}`; + container.appendChild(commentEl); + } + + // --- CREATE POST LOGIC --- + // Navigation + if (navPostBtn) { + navPostBtn.addEventListener('click', () => { + if (feedView && createPostView) { + feedView.classList.add('hidden'); + createPostView.classList.remove('hidden'); + createPostView.classList.add('fade-in'); + } + }); + } + + // Cancel + if (cancelPostBtn) { + cancelPostBtn.addEventListener('click', () => { + resetPostForm(); + createPostView.classList.add('hidden'); + feedView.classList.remove('hidden'); + }); + } + // Home Nav + if (navHomeBtn) { + navHomeBtn.addEventListener('click', () => { + if (createPostView && !createPostView.classList.contains('hidden')) { + resetPostForm(); + createPostView.classList.add('hidden'); + feedView.classList.remove('hidden'); + } + }); + } + + // Upload + if (uploadTrigger && imageInput) { + uploadTrigger.addEventListener('click', () => imageInput.click()); + imageInput.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + imagePreview.src = e.target.result; + imagePreview.classList.remove('hidden'); + document.querySelector('.upload-placeholder').classList.add('hidden'); + }; + reader.readAsDataURL(file); + } + }); + } + + // Share + if (sharePostBtn) { + sharePostBtn.addEventListener('click', () => { + if (!imagePreview.src || imagePreview.classList.contains('hidden')) { + alert('Please select an image first.'); + return; + } + + const caption = captionInput.value.trim(); + const imageSrc = imagePreview.src; + const id = 'post_' + Date.now(); + + const newPost = { + id, + imageSrc, + caption, + timestamp: new Date().toISOString(), + username: 'you', + likes: 0 + }; + + const userPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]'); + userPosts.unshift(newPost); + localStorage.setItem('socialAppUserPosts', JSON.stringify(userPosts)); + + renderPost(newPost, true); + resetPostForm(); + createPostView.classList.add('hidden'); + feedView.classList.remove('hidden'); + + // Note: Don't need full reload, but let's ensure state is clean + }); + } + + function resetPostForm() { + if (imageInput) imageInput.value = ''; + if (captionInput) captionInput.value = ''; + if (imagePreview) { + imagePreview.src = ''; + imagePreview.classList.add('hidden'); + } + const ph = document.querySelector('.upload-placeholder'); + if (ph) ph.classList.remove('hidden'); + } }); diff --git a/styles/main.css b/styles/main.css index 51b702c..baac6e6 100644 --- a/styles/main.css +++ b/styles/main.css @@ -600,6 +600,57 @@ p { margin-right: 5px; } +/* Options Menu */ +.post-header { + position: relative; + /* Context for menu */ +} + +.options-menu { + position: absolute; + top: 40px; + right: 15px; + background: rgba(20, 20, 22, 0.95); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--glass-border); + border-radius: var(--radius-sm); + padding: 5px; + z-index: 10; + min-width: 120px; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5); + display: none; +} + +.options-menu.active { + display: block; + animation: fadeIn 0.2s ease-out; +} + +.menu-btn { + width: 100%; + background: none; + border: none; + padding: 10px 15px; + text-align: left; + color: var(--text-main); + font-size: 14px; + cursor: pointer; + border-radius: 8px; + transition: background 0.2s; + display: flex; + align-items: center; + gap: 8px; +} + +.menu-btn:hover { + background: rgba(255, 255, 255, 0.05); +} + +.menu-btn.delete { + color: #EF4444; +} + /* Bottom Navigation */ .bottom-nav { position: fixed;