-
Richard Githuba authoredRichard Githuba authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
feed.js 8.39 KiB
const API_BASE_URL = '/api/feed';
const postFeed = document.getElementById('postFeed');
const postTemplate = document.getElementById('post-template');
const addNewPost = document.getElementById('add-post');
const closeModalBtn = document.getElementById('closeModalBtn');
const modal = document.getElementById('create-new-modal');
const postForm = document.getElementById('post-form');
let isEditing = false;
let editPostId = null;
// maintaining state
let posts = [];
// had issues with form so func to reset it
function resetForm() {
postForm.reset();
isEditing = false;
editPostId = null;
}
addNewPost.addEventListener('click', () => {
resetForm();
modal.style.display = 'flex';
});
closeModalBtn.addEventListener('click', () => {
modal.style.display = 'none';
resetForm();
});
// getting all posts using api
async function fetchPosts() {
try {
const response = await fetch(API_BASE_URL);
if (!response.ok) throw new Error('Failed to fetch posts');
posts = await response.json();
renderPosts();
} catch (error) {
console.error('Error fetching posts:', error);
}
}
// render all posts after getting them
function renderPosts() {
postFeed.innerHTML = ''; // clear any posts
posts.forEach(post => renderPost(post)); // go over each item in post array and call the function renderPost on it
}
// a single post
async function renderPost(post) {
const postElement = postTemplate.content.cloneNode(true);
const deleteButton = postElement.querySelector('.delete-post');
const editButton = postElement.querySelector('.edit-post');
deleteButton.style.display = post.isDeletable ? 'block' : 'none';
editButton.style.display = post.isEditable ? 'block' : 'none';
postElement.querySelector('.author').textContent = post.authorName;
postElement.querySelector('.author-title').textContent = post.authorOrganization;
postElement.querySelector('.post-title').textContent = post.postTitle;
postElement.querySelector('.post-description').textContent = post.postDescription;
const postImage = postElement.querySelector('.post-image img');
if (post.postImageUrl) {
postImage.src = post.postImageUrl;
} else {
postImage.style.display = 'none';
}
// render the tags
const tagsContainer = postElement.querySelector('.post-tags');
if (post.tags && post.tags.length > 0) {
post.tags.forEach(tag => {
const tagSpan = document.createElement('span');
tagSpan.innerHTML = `<i class="bi bi-tag"></i> ${tag}`;
tagsContainer.appendChild(tagSpan);
});
}
// set the count of the likes
const likeButton = postElement.querySelector('.like-button');
const likeCount = postElement.querySelector('.like-count');
likeCount.textContent = post.likesCount || 0;
likeButton.addEventListener('click', () => handleLike(post.postId, likeCount, likeButton));
// timestamp
const timestamp = postElement.querySelector('.timestamp');
timestamp.textContent = new Date(post.postTime).toLocaleDateString();
// Set data attributes for reference
const postDiv = postElement.querySelector('.post');
postDiv.dataset.postId = post.postId;
//deleting post
if (post.isDeletable) {
deleteButton.addEventListener('click', async () => {
if (confirm('Are you sure you want to delete this post?')) {
try {
const response = await fetch(`${API_BASE_URL}/${post.postId}`, {
method: 'DELETE',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Failed to delete post');
}
const postDiv = deleteButton.closest('.post');
postDiv.remove();
posts = posts.filter(p => p.postId !== post.postId);
} catch (error) {
console.error('Error deleting post:', error);
alert('Error deleting post. Please try again.');
}
}
});
}
postFeed.appendChild(postElement);
}
// add a like if user had not already liked and remove a like if already liked
async function handleLike(postId, likeCountElement, likeButton) {
try {
const response = await fetch(`${API_BASE_URL}/${postId}/hasLiked`, {
method: 'GET',
credentials: 'include'
});
if (!response.ok) throw new Error('Failed to check like status');
const hasLiked = await response.json();
const method = hasLiked ? 'DELETE' : 'POST';
const likeResponse = await fetch(`${API_BASE_URL}/${postId}/like`, {
method: method,
credentials: 'include'
});
if (!likeResponse.ok) throw new Error('Failed to update like');
const currentCount = parseInt(likeCountElement.textContent);
likeCountElement.textContent = hasLiked ? currentCount - 1 : currentCount + 1;
likeButton.classList.toggle('liked', !hasLiked);
} catch (error) {
console.error('Error updating like:', error);
alert('Error updating like. Please try again.');
}
}
// handling form submission whether update or post
postForm.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = new FormData();
// getting post data
const postData = {
postTitle: document.getElementById('postTitle').value,
postDescription: document.getElementById('postDescription').value,
tags: document.getElementById('postTags').value
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0)
};
formData.append('post', new Blob([JSON.stringify(postData)], {
type: 'application/json'
}));
// handle the case of an image selected
const imageFile = document.getElementById('postImage').files[0];
if (imageFile) {
formData.append('image', imageFile);
}
try {
let url = `${API_BASE_URL}/add`;
let method = 'POST';
if (isEditing && editPostId) {
url = `${API_BASE_URL}/${editPostId}`;
method = 'PATCH';
}
const response = await fetch(url, {
method: method,
credentials: 'include',
body: formData
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
alert(isEditing ? 'Post updated successfully' : 'Post added successfully');
modal.style.display = 'none';
resetForm();
fetchPosts();
} catch (error) {
console.error('Error:', error);
alert(isEditing ? 'Error updating post' : 'Error adding post');
}
});
//previewing the image selected
document.getElementById('postImage').addEventListener('change', function(e) {
const file = e.target.files[0];
const preview = document.getElementById('imagePreview');
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
preview.innerHTML = `<img src="${e.target.result}" style="max-width: 200px; max-height: 200px;">`;
}
reader.readAsDataURL(file);
} else {
preview.innerHTML = '';
}
});
// onclick handling for edit
document.addEventListener('click', async (event) => {
const editButton = event.target.closest('.edit-post');
if (!editButton) return;
const postDiv = editButton.closest('.post');
const postId = postDiv.dataset.postId;
try {
const response = await fetch(`${API_BASE_URL}/${postId}`);
if (!response.ok) throw new Error('Failed to fetch post data');
const postData = await response.json();
// set edit state ti true
isEditing = true;
editPostId = postId;
document.getElementById('postTitle').value = postData.postTitle;
document.getElementById('postDescription').value = postData.postDescription;
document.getElementById('postTags').value = postData.tags.join(', ');
modal.style.display = 'flex';
} catch (error) {
console.error('Error fetching post data:', error);
alert('Error fetching post data. Please try again.');
}
});
// initialize on page load posts on page load
document.addEventListener('DOMContentLoaded', () => {
fetchPosts();
});
// refresh posts periodically
// setInterval(fetchPosts, 60000);