PostV1
This commit is contained in:
29
index.html
29
index.html
@@ -226,6 +226,35 @@
|
|||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</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>
|
<script src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
189
script.js
189
script.js
@@ -327,4 +327,193 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
commentEl.innerHTML = `<span class="comment-user">you</span> ${text}`;
|
commentEl.innerHTML = `<span class="comment-user">you</span> ${text}`;
|
||||||
container.appendChild(commentEl);
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -648,3 +648,88 @@ p {
|
|||||||
stroke: var(--text-main);
|
stroke: var(--text-main);
|
||||||
stroke-width: 2.5px;
|
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 */
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user