document.addEventListener('DOMContentLoaded', () => {
// --- INDEXED DB HELPERS (Multi-Media Support) ---
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' });
}
};
});
}
// Save Array of Blobs/Strings
async function saveMediaToDB(id, mediaArray) {
try {
const db = await openDB();
return new Promise((resolve, reject) => {
const tx = db.transaction('images', 'readwrite');
const store = tx.objectStore('images');
store.put({ id, media: mediaArray });
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
} catch (e) {
console.error('IndexedDB Save Error:', e);
throw e;
}
}
// Get Media Array
async function getMediaFromDB(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.media : 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.log('IndexedDB Delete Error or ID not found:', 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 mediaPreviewContainer = document.getElementById('media-preview-container');
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%)';
}
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;
feedView.classList.add('fade-in');
initFeed();
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() {
const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]');
const postsToRender = [...savedUserPosts].reverse();
for (const post of postsToRender) {
if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) {
let mediaItems = post.media;
// Backwards Compatibility
if (!mediaItems && post.imageSrc) {
mediaItems = [{ type: 'image', src: post.imageSrc }];
}
if (!mediaItems || (mediaItems.length > 0 && mediaItems[0].src && mediaItems[0].src.length < 100)) {
const dbMedia = await getMediaFromDB(post.id);
if (dbMedia) mediaItems = dbMedia;
}
if (mediaItems && mediaItems.length > 0) {
const postWithMedia = { ...post, media: mediaItems };
renderPost(postWithMedia, true);
}
}
}
// Restore State
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 = ` ${text}`;
commentsContainer.appendChild(el);
});
}
}
}
});
}
// --- RENDER POST ---
function renderPost(post, prepend = false) {
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 ? `
` : ``;
let slidesHTML = '';
let dotsHTML = '';
const media = post.media || (post.imageSrc ? [{ type: 'image', src: post.imageSrc }] : []);
if (media.length > 0) {
media.forEach((item, index) => {
if (item.type === 'video') {
slidesHTML += `
`;
} else {
slidesHTML += `
`;
}
if (media.length > 1) {
dotsHTML += ``;
}
});
}
const paginationHTML = media.length > 1 ? `${dotsHTML}
` : '';
const counterHTML = media.length > 1 ? `1/${media.length}
` : '';
article.innerHTML = `
${slidesHTML}
${counterHTML}
${paginationHTML}
0 likes
${post.username} ${post.caption}
`;
if (prepend) {
if (!feedContainer.querySelector(`[data-post-id="${post.id}"]`)) {
feedContainer.insertBefore(article, feedContainer.firstChild);
}
}
else feedContainer.appendChild(article);
// Attach Drag-to-Scroll for Desktop
if (media.length > 1) {
const carousel = article.querySelector('.media-carousel');
if (carousel) enableDragScroll(carousel);
}
}
// --- DRAG TO SCROLL HELPER ---
function enableDragScroll(container) {
let isDown = false;
let startX;
let scrollLeft;
container.addEventListener('mousedown', (e) => {
isDown = true;
container.style.cursor = 'grabbing';
startX = e.pageX - container.offsetLeft;
scrollLeft = container.scrollLeft;
});
container.addEventListener('mouseleave', () => { isDown = false; container.style.cursor = 'grab'; });
container.addEventListener('mouseup', () => { isDown = false; container.style.cursor = 'grab'; });
container.addEventListener('mousemove', (e) => {
if (!isDown) return;
e.preventDefault();
const x = e.pageX - container.offsetLeft;
const walk = (x - startX) * 2; // Scroll-fast
container.scrollLeft = scrollLeft - walk;
});
}
// Global helper for dots and counter
window.updateCarouselState = function (carousel) {
const index = Math.round(carousel.scrollLeft / carousel.offsetWidth);
// Update Dots
const dots = carousel.parentElement.querySelectorAll('.dot');
dots.forEach((dot, i) => {
if (i === index) dot.classList.add('active');
else dot.classList.remove('active');
});
// Update Counter
const counter = carousel.parentElement.querySelector('.media-counter');
if (counter) {
counter.innerText = `${index + 1}/${dots.length}`;
}
}
// --- FEED LISTENERS ---
if (feedView) {
initFeed();
feedView.addEventListener('click', (e) => {
const target = e.target;
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 => m.classList.remove('active'));
menu.classList.toggle('active');
e.stopPropagation();
}
return;
}
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_')) {
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));
deleteImageFromDB(id);
const deletedPosts = JSON.parse(localStorage.getItem('socialAppDeletedPosts') || '[]');
if (!deletedPosts.includes(id)) {
deletedPosts.push(id);
localStorage.setItem('socialAppDeletedPosts', JSON.stringify(deletedPosts));
}
}
}
return;
}
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;
}
const commentBtn = target.closest('.icon-btn');
if (commentBtn) {
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')) {
const input = commentSection.querySelector('input');
if (input) input.focus();
}
return;
}
}
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 = '';
}
}
});
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 = '';
}
}
});
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 = ` ${text}`;
container.appendChild(commentEl);
}
// --- CREATE POST LOGIC ---
if (navPostBtn) {
navPostBtn.addEventListener('click', () => { if (feedView && createPostView) { 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) {
navHomeBtn.addEventListener('click', () => { if (createPostView && !createPostView.classList.contains('hidden')) { resetPostForm(); createPostView.classList.add('hidden'); feedView.classList.remove('hidden'); } });
}
// Staging Area for Selected Files
let stagedMedia = [];
// Upload Handler (Multiple Files)
if (uploadTrigger && imageInput) {
uploadTrigger.addEventListener('click', () => imageInput.click());
imageInput.addEventListener('change', async (e) => {
const files = Array.from(e.target.files);
if (files.length > 0) {
stagedMedia = [];
mediaPreviewContainer.innerHTML = '';
mediaPreviewContainer.classList.remove('hidden');
document.querySelector('.upload-placeholder').classList.add('hidden');
for (const file of files) {
if (!file.type.startsWith('image/') && !file.type.startsWith('video/')) {
alert('Skipping invalid file: ' + file.name);
continue;
}
const isVideo = file.type.startsWith('video/');
const dataUrl = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.readAsDataURL(file);
});
stagedMedia.push({ type: isVideo ? 'video' : 'image', src: dataUrl });
const slide = document.createElement('div');
slide.className = 'media-slide';
if (isVideo) {
slide.innerHTML = ``;
} else {
slide.innerHTML = `
`;
}
mediaPreviewContainer.appendChild(slide);
}
// Add Dots for Preview
const wrapper = document.querySelector('#create-post-view .image-upload-wrapper');
const oldDots = wrapper.querySelector('.carousel-dots');
if (oldDots) oldDots.remove();
if (stagedMedia.length > 1) {
const dotsContainer = document.createElement('div');
dotsContainer.className = 'carousel-dots';
stagedMedia.forEach((_, i) => {
const dot = document.createElement('div');
dot.className = `dot ${i === 0 ? 'active' : ''}`;
dotsContainer.appendChild(dot);
});
wrapper.appendChild(dotsContainer);
// Attach Listeners
mediaPreviewContainer.onscroll = () => window.updateCarouselState(mediaPreviewContainer);
enableDragScroll(mediaPreviewContainer); // Desktop Swipe Support
}
}
});
}
// Share Handler
if (sharePostBtn) {
sharePostBtn.addEventListener('click', async () => {
if (stagedMedia.length === 0) {
alert('Please select at least one photo or video.');
return;
}
const caption = captionInput.value.trim();
const id = 'post_' + Date.now();
const displayPost = {
id,
media: stagedMedia,
caption,
timestamp: new Date().toISOString(),
username: 'you',
likes: 0
};
renderPost(displayPost, true);
const storagePost = {
id,
media: null,
caption,
timestamp: displayPost.timestamp,
username: displayPost.username,
likes: 0
};
const userPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]');
userPosts.unshift(storagePost);
localStorage.setItem('socialAppUserPosts', JSON.stringify(userPosts));
try {
await saveMediaToDB(id, stagedMedia);
} catch (e) {
console.error('Failed to save media array:', e);
alert('Uploaded, but media could not be saved permanently due to storage error.');
}
resetPostForm();
createPostView.classList.add('hidden');
feedView.classList.remove('hidden');
});
}
function resetPostForm() {
if (imageInput) imageInput.value = '';
if (captionInput) captionInput.value = '';
stagedMedia = [];
if (mediaPreviewContainer) {
mediaPreviewContainer.innerHTML = '';
mediaPreviewContainer.classList.add('hidden');
mediaPreviewContainer.onscroll = null;
}
const dots = document.querySelector('#create-post-view .carousel-dots');
if (dots) dots.remove();
const ph = document.querySelector('.upload-placeholder');
if (ph) ph.classList.remove('hidden');
}
});