AccountDeletePostV1
This commit is contained in:
58
index.html
58
index.html
@@ -11,7 +11,7 @@
|
||||
|
||||
<body>
|
||||
<!-- App Container -->
|
||||
<main class="login-container fade-in">
|
||||
<main id="login-view" class="login-container fade-in">
|
||||
<!-- Background Orbs for Vibe -->
|
||||
<div class="orb orb-1"></div>
|
||||
<div class="orb orb-2"></div>
|
||||
@@ -256,6 +256,62 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Verification View -->
|
||||
<div id="verification-view" class="login-container hidden">
|
||||
<div class="login-card">
|
||||
<div class="brand-section">
|
||||
<div class="logo-icon">V</div>
|
||||
<h1>Verify Email</h1>
|
||||
<p>We sent a code to your email</p>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="otp-input" placeholder=" " maxlength="6"
|
||||
style="text-align: center; letter-spacing: 5px; font-size: 24px;">
|
||||
<label for="otp-input">Enter 6-Digit Code</label>
|
||||
</div>
|
||||
<button id="verify-btn" class="btn-primary"><span>Verify</span></button>
|
||||
<div class="signup-link">
|
||||
<a href="#" id="resend-code">Resend Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Onboarding View -->
|
||||
<div id="onboarding-view" class="login-container hidden">
|
||||
<div class="login-card">
|
||||
<div class="brand-section">
|
||||
<h1>Setup Profile</h1>
|
||||
<p>Tell us a bit about yourself</p>
|
||||
</div>
|
||||
|
||||
<div class="profile-upload-section">
|
||||
<div class="avatar-upload-wrapper" id="avatar-upload-trigger">
|
||||
<img src="https://i.pravatar.cc/150?img=12" alt="Avatar Preview" id="avatar-preview">
|
||||
<div class="upload-overlay">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" stroke="white" fill="none">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" id="avatar-input" accept="image/*" class="hidden">
|
||||
<p style="font-size: 12px; color: var(--text-muted); margin-top: 10px;">Tap to change photo</p>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" id="display-name" placeholder=" " required>
|
||||
<label for="display-name">Display Name</label>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input type="text" id="bio-input" placeholder=" ">
|
||||
<label for="bio-input">Bio (Optional)</label>
|
||||
</div>
|
||||
|
||||
<button id="complete-setup-btn" class="btn-primary"><span>Enter App</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lightbox View -->
|
||||
<div id="lightbox-view" class="hidden">
|
||||
<button id="lightbox-close" class="icon-btn-lg">
|
||||
|
||||
146
script.js
146
script.js
@@ -68,7 +68,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// --- QUERY SELECTORS ---
|
||||
const loginForm = document.querySelector('.login-form');
|
||||
const loginContainer = document.querySelector('.login-container');
|
||||
const loginView = document.getElementById('login-view');
|
||||
const feedView = document.getElementById('feed-view');
|
||||
const emailInput = document.getElementById('email');
|
||||
const passwordInput = document.getElementById('password');
|
||||
@@ -80,6 +80,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const title = document.querySelector('.brand-section h1');
|
||||
const subtitle = document.querySelector('.brand-section p');
|
||||
|
||||
// Onboarding Elements
|
||||
const verificationView = document.getElementById('verification-view');
|
||||
const onboardingView = document.getElementById('onboarding-view');
|
||||
const otpInput = document.getElementById('otp-input');
|
||||
const verifyBtn = document.getElementById('verify-btn');
|
||||
const displayNameInput = document.getElementById('display-name');
|
||||
const bioInput = document.getElementById('bio-input');
|
||||
const avatarInput = document.getElementById('avatar-input');
|
||||
const avatarPreview = document.getElementById('avatar-preview');
|
||||
const avatarTrigger = document.getElementById('avatar-upload-trigger');
|
||||
const completeSetupBtn = document.getElementById('complete-setup-btn');
|
||||
|
||||
// Post Creation Elements
|
||||
const createPostView = document.getElementById('create-post-view');
|
||||
const cancelPostBtn = document.getElementById('cancel-post-btn');
|
||||
@@ -95,9 +107,71 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const lightboxView = document.getElementById('lightbox-view');
|
||||
const lightboxClose = document.getElementById('lightbox-close');
|
||||
const lightboxCarousel = document.querySelector('.lightbox-carousel');
|
||||
// We check for counter inside helper, as it might be common class
|
||||
|
||||
let isLoginMode = true;
|
||||
let pendingUser = null;
|
||||
let generatedOTP = null;
|
||||
let newAvatarBase64 = null;
|
||||
|
||||
// --- ONBOARDING LOGIC ---
|
||||
// Avatar Upload Handler
|
||||
if (avatarTrigger && avatarInput) {
|
||||
avatarTrigger.addEventListener('click', () => avatarInput.click());
|
||||
avatarInput.addEventListener('change', (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
newAvatarBase64 = e.target.result;
|
||||
avatarPreview.src = newAvatarBase64;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Verify OTP Handler
|
||||
if (verifyBtn) {
|
||||
verifyBtn.addEventListener('click', () => {
|
||||
if (otpInput.value === generatedOTP) {
|
||||
verificationView.classList.add('hidden');
|
||||
onboardingView.classList.remove('hidden');
|
||||
onboardingView.classList.add('fade-in');
|
||||
} else {
|
||||
alert('Invalid Code');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Complete Setup Handler
|
||||
if (completeSetupBtn) {
|
||||
completeSetupBtn.addEventListener('click', async () => {
|
||||
const name = displayNameInput.value.trim();
|
||||
if (!name) { alert('Name is required'); return; }
|
||||
|
||||
const bio = bioInput.value.trim();
|
||||
|
||||
// Finalize User
|
||||
const finalUser = {
|
||||
...pendingUser,
|
||||
username: name,
|
||||
bio: bio,
|
||||
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
|
||||
|
||||
// Transition
|
||||
onboardingView.classList.add('hidden');
|
||||
feedView.classList.remove('hidden');
|
||||
feedView.classList.add('fade-in');
|
||||
initFeed();
|
||||
});
|
||||
}
|
||||
|
||||
// --- LIGHTBOX LOGIC ---
|
||||
function openLightbox(media, startIndex) {
|
||||
@@ -120,7 +194,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
// Scroll to index
|
||||
const width = window.innerWidth;
|
||||
// Need brief timeout for layout to stabilise
|
||||
setTimeout(() => {
|
||||
lightboxCarousel.scrollLeft = width * startIndex;
|
||||
}, 10);
|
||||
@@ -208,11 +281,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
function navigateToFeed() {
|
||||
if (loginContainer && feedView) {
|
||||
loginContainer.style.opacity = '0';
|
||||
if (loginView && feedView) {
|
||||
loginView.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
loginContainer.classList.add('hidden');
|
||||
loginContainer.style.display = 'none';
|
||||
loginView.classList.add('hidden');
|
||||
loginView.style.display = 'none';
|
||||
feedView.classList.remove('hidden');
|
||||
void feedView.offsetWidth;
|
||||
feedView.classList.add('fade-in');
|
||||
@@ -234,6 +307,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
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);
|
||||
} else {
|
||||
@@ -248,11 +322,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
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);
|
||||
|
||||
// Start Verification Flow
|
||||
pendingUser = { email, password, joinedAt: timestamp };
|
||||
generatedOTP = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
|
||||
alert(`[MOCK EMAIL] Your verification code is: ${generatedOTP}`);
|
||||
|
||||
loginView.classList.add('hidden');
|
||||
verificationView.classList.remove('hidden');
|
||||
verificationView.classList.add('fade-in');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -270,7 +349,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
for (const post of postsToRender) {
|
||||
if (!isPostDeleted(post.id) && !document.querySelector(`[data-post-id="${post.id}"]`)) {
|
||||
let mediaItems = post.media;
|
||||
// Backwards Compatibility implementation
|
||||
if (!mediaItems && post.imageSrc) {
|
||||
mediaItems = [{ type: 'image', src: post.imageSrc }];
|
||||
}
|
||||
@@ -323,13 +401,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
function renderPost(post, prepend = false) {
|
||||
if (document.querySelector(`[data-post-id="${post.id}"]`)) return;
|
||||
|
||||
const feedContainer = document.querySelector('.feed-container');
|
||||
const container = document.querySelector('.feed-container'); // Renamed variable
|
||||
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 ? `
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
const isOwner = post.userEmail && post.userEmail === currentUser.email;
|
||||
|
||||
// Only show options if owner
|
||||
const optionsHTML = isOwner ? `
|
||||
<button class="icon-btn-sm options-trigger">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" fill="none" stroke-width="2"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></svg>
|
||||
</button>
|
||||
@@ -339,7 +420,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
Delete Post
|
||||
</button>
|
||||
</div>
|
||||
` : `<button class="icon-btn-sm">...</button>`;
|
||||
` : '';
|
||||
|
||||
let slidesHTML = '';
|
||||
let dotsHTML = '';
|
||||
@@ -370,7 +451,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
article.innerHTML = `
|
||||
<div class="post-header">
|
||||
<div class="post-user">
|
||||
<img src="https://i.pravatar.cc/150?img=12" alt="User" class="avatar-sm">
|
||||
<img src="${post.userAvatar || 'https://i.pravatar.cc/150?img=12'}" alt="User" class="avatar-sm">
|
||||
<span class="username">${post.username}</span>
|
||||
</div>
|
||||
${optionsHTML}
|
||||
@@ -403,11 +484,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
`;
|
||||
|
||||
if (prepend) {
|
||||
if (!feedContainer.querySelector(`[data-post-id="${post.id}"]`)) {
|
||||
feedContainer.insertBefore(article, feedContainer.firstChild);
|
||||
if (!container.querySelector(`[data-post-id="${post.id}"]`)) {
|
||||
container.insertBefore(article, container.firstChild);
|
||||
}
|
||||
}
|
||||
else feedContainer.appendChild(article);
|
||||
else container.appendChild(article);
|
||||
|
||||
// Attach Drag-to-Scroll & Lightbox Click
|
||||
const carousel = article.querySelector('.media-carousel');
|
||||
@@ -487,15 +568,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const card = deleteBtn.closest('.post-card');
|
||||
const id = card.getAttribute('data-post-id');
|
||||
if (id && id.startsWith('post_')) {
|
||||
const userPosts = JSON.parse(localStorage.getItem('socialAppUserPosts') || '[]');
|
||||
const postToDelete = userPosts.find(p => p.id === id);
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
|
||||
if (!postToDelete || postToDelete.userEmail !== currentUser.email) {
|
||||
alert('You can only delete your own posts.');
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
const updatedPosts = userPosts.filter(p => p.id !== id);
|
||||
localStorage.setItem('socialAppUserPosts', JSON.stringify(updatedPosts));
|
||||
|
||||
deleteImageFromDB(id);
|
||||
|
||||
const deletedPosts = JSON.parse(localStorage.getItem('socialAppDeletedPosts') || '[]');
|
||||
if (!deletedPosts.includes(id)) {
|
||||
deletedPosts.push(id);
|
||||
@@ -687,12 +779,15 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const caption = captionInput.value.trim();
|
||||
const id = 'post_' + Date.now();
|
||||
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
|
||||
const displayPost = {
|
||||
id,
|
||||
media: stagedMedia,
|
||||
caption,
|
||||
timestamp: new Date().toISOString(),
|
||||
username: 'you',
|
||||
username: currentUser.username || 'You',
|
||||
userAvatar: currentUser.avatar,
|
||||
userEmail: currentUser.email, // Secure Owner ID
|
||||
likes: 0
|
||||
};
|
||||
renderPost(displayPost, true);
|
||||
@@ -703,6 +798,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
caption,
|
||||
timestamp: displayPost.timestamp,
|
||||
username: displayPost.username,
|
||||
userEmail: currentUser.email, // Secure Owner ID
|
||||
likes: 0
|
||||
};
|
||||
|
||||
|
||||
@@ -952,3 +952,73 @@ p {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
/* --- ONBOARDING STYLES --- */
|
||||
.profile-upload-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.avatar-upload-wrapper {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: 2px dashed var(--glass-border);
|
||||
padding: 2px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.avatar-upload-wrapper:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.avatar-upload-wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.upload-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.4);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.avatar-upload-wrapper:hover .upload-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/* Fix Visibility for Onboarding Views */
|
||||
#verification-view.hidden,
|
||||
#onboarding-view.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
/* FINAL FIX: Ensure ALL login containers hide correctly */
|
||||
.login-container.hidden,
|
||||
#login-view.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
/* Fix Bottom Nav Z-Index and Visibility */
|
||||
.bottom-nav {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user