AccountDeletePostV1

This commit is contained in:
2026-01-29 16:41:37 +11:00
parent 36ff34c893
commit bcd6d7c0ef
3 changed files with 248 additions and 26 deletions

View File

@@ -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
View File

@@ -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
};

View File

@@ -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;
}