This commit is contained in:
2026-01-28 21:06:16 +11:00
parent 109e463724
commit a7669d9014
3 changed files with 303 additions and 0 deletions

View File

@@ -226,6 +226,35 @@
</button>
</nav>
</div>
<!-- Create Post View -->
<div id="create-post-view" class="hidden">
<header class="feed-header">
<button class="text-btn" id="cancel-post-btn">Cancel</button>
<span class="header-title">New Post</span>
<button class="text-btn accent" id="share-post-btn">Share</button>
</header>
<div class="post-creation-area">
<div class="image-upload-wrapper" id="upload-trigger">
<input type="file" id="post-image-input" accept="image/*" hidden>
<div class="upload-placeholder">
<svg viewBox="0 0 24 24" width="48" height="48" stroke="white" fill="none" stroke-width="1">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<circle cx="8.5" cy="8.5" r="1.5"></circle>
<polyline points="21 15 16 10 5 21"></polyline>
</svg>
<span>Tap to Select Photo</span>
</div>
<img id="image-preview" class="hidden" src="" alt="Preview">
</div>
<div class="caption-wrapper">
<img src="https://i.pravatar.cc/150?img=12" alt="User" class="avatar-sm">
<textarea id="post-caption-input" placeholder="Write a caption..."></textarea>
</div>
</div>
</div>
<script src="script.js"></script>
</body>

189
script.js
View File

@@ -327,4 +327,193 @@ document.addEventListener('DOMContentLoaded', () => {
commentEl.innerHTML = `<span class="comment-user">you</span> ${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');
}
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);
article.innerHTML = `
<div class="post-header">
<div class="post-user">
<img src="https://i.pravatar.cc/150?img=12" alt="User" class="avatar-sm">
<span class="username">${post.username}</span>
</div>
<button class="icon-btn-sm">...</button>
</div>
<div class="post-image">
<img src="${post.imageSrc}" alt="Post">
</div>
<div class="post-actions">
<div class="action-left">
<button class="icon-btn"><svg viewBox="0 0 24 24" width="24" height="24" stroke="white" fill="none" class="heart-icon"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg></button>
<button class="icon-btn"><svg viewBox="0 0 24 24" width="24" height="24" stroke="white" fill="none"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"></path></svg></button>
</div>
<button class="icon-btn"><svg viewBox="0 0 24 24" width="24" height="24" stroke="white" fill="none"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"></path></svg></button>
</div>
<div class="post-content">
<span class="likes">0 likes</span>
<p><span class="username">${post.username}</span> ${post.caption}</p>
</div>
<div class="comment-section">
<div class="added-comments"></div>
<div class="comment-input-wrapper">
<input type="text" class="comment-input" placeholder="Add a comment...">
<button class="post-btn">Post</button>
</div>
</div>
`;
// 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);
}
}
// 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);
}
});
});

View File

@@ -647,4 +647,89 @@ p {
.nav-btn.active svg {
stroke: var(--text-main);
stroke-width: 2.5px;
}
/* --- CREATE POST VIEW --- */
#create-post-view {
width: 100%;
max-width: 480px;
min-height: 100vh;
background: var(--bg-dark);
position: relative;
z-index: 200;
/* Above everything */
}
.text-btn {
background: none;
border: none;
color: var(--text-main);
font-size: 16px;
font-weight: 500;
cursor: pointer;
}
.text-btn.accent {
color: #3B82F6;
font-weight: 600;
}
.header-title {
font-weight: 700;
font-size: 16px;
}
.post-creation-area {
display: flex;
flex-direction: column;
}
.image-upload-wrapper {
width: 100%;
aspect-ratio: 1/1;
background: #111;
position: relative;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
overflow: hidden;
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
color: var(--text-muted);
}
#image-preview {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-wrapper {
display: flex;
gap: 12px;
padding: 20px;
border-top: 1px solid var(--glass-border);
}
#post-caption-input {
flex: 1;
background: none;
border: none;
color: var(--text-main);
font-family: inherit;
font-size: 15px;
resize: none;
height: 100px;
outline: none;
padding-top: 8px;
/* Align with avatar */
}