AccountsAndCrossPlatformLogin
This commit is contained in:
219
script.js
219
script.js
@@ -1,4 +1,30 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// --- API CONFIG ---
|
||||
const API = {
|
||||
async getUsers() {
|
||||
const res = await fetch('/api/users');
|
||||
if (!res.ok) throw new Error(`Server Error: ${res.status}`);
|
||||
return await res.json();
|
||||
},
|
||||
async addUser(user) {
|
||||
await fetch('/api/users/add', { method: 'POST', body: JSON.stringify(user) });
|
||||
},
|
||||
async getPosts() {
|
||||
const res = await fetch('/api/posts');
|
||||
if (!res.ok) throw new Error(`Server Error: ${res.status}`);
|
||||
return await res.json();
|
||||
},
|
||||
async addPost(post) {
|
||||
await fetch('/api/posts/add', { method: 'POST', body: JSON.stringify(post) });
|
||||
},
|
||||
async updatePost(post) {
|
||||
await fetch('/api/posts/update', { method: 'POST', body: JSON.stringify(post) });
|
||||
},
|
||||
async deletePost(id) {
|
||||
await fetch('/api/posts/delete', { method: 'POST', body: JSON.stringify({ id }) });
|
||||
}
|
||||
};
|
||||
|
||||
// --- INDEXED DB HELPERS (Multi-Media Support) ---
|
||||
const DB_NAME = 'SocialAppDB';
|
||||
const DB_VERSION = 1;
|
||||
@@ -106,24 +132,44 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// Lightbox Elements
|
||||
const lightboxView = document.getElementById('lightbox-view');
|
||||
const lightboxClose = document.getElementById('lightbox-close');
|
||||
const lightboxCarousel = document.querySelector('.lightbox-carousel');
|
||||
const lightboxCarousel = lightboxView?.querySelector('.lightbox-carousel');
|
||||
|
||||
// State
|
||||
let isLoginMode = true;
|
||||
let pendingUser = null;
|
||||
let generatedOTP = null;
|
||||
let newAvatarBase64 = null;
|
||||
|
||||
// --- CHECK SESSION & LOGOUT ---
|
||||
function checkSession() {
|
||||
async function checkSession() {
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
if (currentUser.email) {
|
||||
if (loginView) {
|
||||
loginView.classList.add('hidden');
|
||||
loginView.style.display = 'none';
|
||||
}
|
||||
if (feedView) {
|
||||
feedView.classList.remove('hidden');
|
||||
initFeed();
|
||||
try {
|
||||
// Verify user actually exists in DB (fixes "Ghost User" after DB wipe)
|
||||
const users = await API.getUsers();
|
||||
const validUser = users.find(u => u.email === currentUser.email);
|
||||
|
||||
if (!validUser) {
|
||||
console.log('Session invalid: User not found on server.');
|
||||
localStorage.removeItem('currentUser');
|
||||
if (loginView && !loginView.classList.contains('hidden')) return; // Already on login
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginView) {
|
||||
loginView.classList.add('hidden');
|
||||
loginView.style.display = 'none';
|
||||
}
|
||||
if (feedView) {
|
||||
feedView.classList.remove('hidden');
|
||||
initFeed();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Session check failed:', err);
|
||||
alert('Connection to Server Failed.\n\nReason: ' + err.message + '\n\nLogging out for security.');
|
||||
localStorage.removeItem('currentUser');
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +186,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
// --- ONBOARDING LOGIC ---
|
||||
// Avatar Upload Handler
|
||||
if (avatarTrigger && avatarInput) {
|
||||
avatarTrigger.addEventListener('click', () => avatarInput.click());
|
||||
avatarInput.addEventListener('change', (e) => {
|
||||
@@ -193,11 +238,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
avatar: newAvatarBase64 || 'https://i.pravatar.cc/150?img=12'
|
||||
};
|
||||
|
||||
// Save to DB
|
||||
const existingUsers = JSON.parse(localStorage.getItem('socialAppUsers') || '[]');
|
||||
existingUsers.push(finalUser);
|
||||
localStorage.setItem('socialAppUsers', JSON.stringify(existingUsers));
|
||||
localStorage.setItem('currentUser', JSON.stringify(finalUser)); // Log them in
|
||||
// --- ASYNC SAVE ---
|
||||
await API.addUser(finalUser);
|
||||
localStorage.setItem('currentUser', JSON.stringify(finalUser)); // Session persistence
|
||||
|
||||
// Transition
|
||||
onboardingView.classList.add('hidden');
|
||||
@@ -206,7 +249,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
initFeed();
|
||||
} catch (err) {
|
||||
console.error('Setup Error:', err);
|
||||
alert('Something went wrong during setup: ' + err.message);
|
||||
alert('Connection Error: ' + err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -334,42 +377,49 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
if (loginForm) {
|
||||
loginForm.addEventListener('submit', (e) => {
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const email = emailInput.value.trim();
|
||||
const email = emailInput.value.trim().toLowerCase();
|
||||
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) {
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
showFeedback(true, 'Success!');
|
||||
setTimeout(navigateToFeed, 1000);
|
||||
if (!email || !password) { showFeedback(false, 'Missing fields'); return; }
|
||||
|
||||
try {
|
||||
const users = await API.getUsers();
|
||||
|
||||
if (isLoginMode) {
|
||||
const user = users.find(u => u.email === email && u.password === password);
|
||||
if (user) {
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
showFeedback(true, 'Success!');
|
||||
setTimeout(navigateToFeed, 1000);
|
||||
} else {
|
||||
showFeedback(false, 'Incorrect details');
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
if (users.find(u => u.email === email)) {
|
||||
showFeedback(false, 'User exists');
|
||||
return;
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
showFeedback(false, 'Passwords mismatch');
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Verification Flow
|
||||
pendingUser = { email, password, joinedAt: timestamp };
|
||||
generatedOTP = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
// Start Verification Flow
|
||||
pendingUser = { email, password, joinedAt: timestamp };
|
||||
generatedOTP = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
alert(`[SERVER SYNC] Verification code: ${generatedOTP}`);
|
||||
|
||||
alert(`[MOCK EMAIL] Your verification code is: ${generatedOTP}`);
|
||||
|
||||
loginView.classList.add('hidden');
|
||||
verificationView.classList.remove('hidden');
|
||||
verificationView.classList.add('fade-in');
|
||||
loginView.classList.add('hidden');
|
||||
verificationView.classList.remove('hidden');
|
||||
verificationView.classList.add('fade-in');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
showFeedback(false, 'Server Error');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -379,76 +429,31 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
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 LOAD FEED ---
|
||||
async function initFeed() {
|
||||
const savedUserPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]');
|
||||
const postsToRender = [...savedUserPosts].reverse();
|
||||
const container = document.querySelector('.posts-container');
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
|
||||
for (const post of postsToRender) {
|
||||
if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) {
|
||||
let mediaItems = post.media;
|
||||
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);
|
||||
}
|
||||
try {
|
||||
const posts = await API.getPosts();
|
||||
if (posts.length === 0) {
|
||||
container.innerHTML = '<div style="text-align:center; padding:40px; color:#666;">No posts yet. Be the first!</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
for (const post of posts) {
|
||||
renderPost(post, false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Feed Error:', err);
|
||||
container.innerHTML = '<div style="text-align:center; color:red;">Connection Failed</div>';
|
||||
}
|
||||
|
||||
// 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 currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
const userEmail = (currentUser.email || '').toLowerCase(); // Fix missing email check
|
||||
|
||||
const likeIcon = card.querySelector('.heart-icon');
|
||||
const likesText = card.querySelector('.likes');
|
||||
if (likeIcon && likesText) {
|
||||
// Check if current user is in `likedBy` array
|
||||
let likedBy = data.likedBy || [];
|
||||
// Migration: if old `liked` boolean exists, legacy support (or reset)
|
||||
if (data.liked && likedBy.length === 0) { likedBy = []; }
|
||||
|
||||
if (likedBy.includes(userEmail)) {
|
||||
likeIcon.classList.add('liked');
|
||||
likeIcon.style.fill = '#EF4444';
|
||||
likeIcon.style.stroke = '#EF4444';
|
||||
}
|
||||
likesText.innerText = (data.likesCount || likedBy.length) + ' likes';
|
||||
}
|
||||
const commentsContainer = card.querySelector('.added-comments');
|
||||
if (commentsContainer) {
|
||||
commentsContainer.innerHTML = '';
|
||||
if (data.comments) {
|
||||
data.comments.forEach(comment => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('comment-item');
|
||||
// Handle both legacy strings and new objects
|
||||
const author = (typeof comment === 'object') ? comment.username : 'someone';
|
||||
const text = (typeof comment === 'object') ? comment.text : comment;
|
||||
// Highlight own comments
|
||||
const displayUser = ((typeof comment === 'object') && comment.email === userEmail) ? 'you' : author;
|
||||
|
||||
el.innerHTML = `<span class="comment-user">${displayUser}</span> ${text}`;
|
||||
commentsContainer.appendChild(el);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --- RENDER POST ---
|
||||
function renderPost(post, prepend = false) {
|
||||
if (document.querySelector(`[data-post-id="${post.id}"]`)) return;
|
||||
|
||||
Reference in New Issue
Block a user