import app, {
showToast
} from "./app.js"
import store from "./store.js"
import {
formatPostDate
} from './utils.js'
import {
fetchComments,
maybeLikePost,
maybeLikeComment,
addComment,
deletePost,
deleteComment
} from './api/posts.js'
import {
getSessionUser
} from "./api/auth.js"
import {
renderPost
} from "./post-view.js"
var $ = Dom7
var currentPostsPage = 1
var currentFollowingPostsPage = 1
var postsStore = store.getters.posts
var followingPostsStore = store.getters.followingPosts
var totalPostPages = 0
var totalFPostPages = 0
// Infinite Scroll Event
var isFetchingPosts = false
var activeTab = 'latest'
var refreshed = false
//screen width
var containerWidth = window.innerWidth
postsStore.onUpdated((data) => {
totalPostPages = data.total_pages
//console.log('total: '+totalPostPages)
//console.log('data-length: '+data.data.length)
//console.log('homapege: data.page: '+data.page)
//console.log('homapege: data.total_pages: '+data.total_pages)
//console.log(new_data)
if ((data.page == data.total_pages) || (data.new_data.length == 0)) {
$('.infinite-scroll-preloader.home-posts').hide()
if (data.data.length == 0) {
$('#tab-latest .data').html('<p class="text-center">No posts</p>')
return;
}
}
displayPosts(data.new_data)
})
followingPostsStore.onUpdated((data) => {
totalFPostPages = data.total_pages
if ((data.page == data.total_pages) || (data.new_data.length == 0)) {
$('.infinite-scroll-preloader.home-following-posts').hide()
if (data.data.length == 0) {
$('#tab-following .data').html('<p class="text-center">No posts</p>')
return;
}
}
displayPosts(data.new_data, true)
})
/*
ori:
$(document).on('infinite', '.infinite-scroll-content.home-page', async function () {
if (isFetchingPosts) return
const totalPages = activeTab === 'following' ? totalFPostPages : totalPostPages
const storeName = activeTab === 'following' ? 'getFollowingPosts' : 'getPosts'
console.log('---- start event -------------')
console.log('infinite activeTab: '+activeTab)
console.log('infinite totalPages: '+totalPages)
console.log('infinite storeName: '+storeName)
console.log('infinite totalPostPages: '+totalPostPages)
if (activeTab === 'following') {
currentFollowingPostsPage++
} else {
currentPostsPage++
}
const currentPage = activeTab === 'following' ? currentFollowingPostsPage : currentPostsPage
if (currentPage >= totalPages) {
return
}
isFetchingPosts = true
console.log('infinite currentPostsPage: '+currentPostsPage)
console.log('infinite currentPage: '+currentPage)
await store.dispatch(storeName, currentPage)
isFetchingPosts = false
})
*/
$(document).on('infinite', '.infinite-scroll-content.home-page', async function () {
if (isFetchingPosts) return
const totalPages = activeTab === 'following' ? totalFPostPages : totalPostPages
const storeName = activeTab === 'following' ? 'getFollowingPosts' : 'getPosts'
//console.log('---- start event -------------')
//console.log('infinite activeTab: '+activeTab)
//console.log('infinite totalPages: '+totalPages)
//console.log('infinite storeName: '+storeName)
//console.log('infinite totalPostPages: '+totalPostPages)
if (activeTab === 'latest') {
currentFollowingPostsPage++
} else {
currentPostsPage++
}
//console.log('infinite currentPostsPage 0: '+currentPostsPage)
const currentPage = activeTab === 'latest' ? currentFollowingPostsPage : currentPostsPage
//console.log('infinite currentPage 1: '+currentPage)
if(currentPage<=totalPages) {
//console.log('infinite currentPostsPage 2: '+currentPostsPage)
//console.log('infinite currentPage 2: '+currentPage)
isFetchingPosts = true
await store.dispatch(storeName, currentPage)
isFetchingPosts = false
}
})
$(document).on('page:beforein', '.page[data-name="social"]', function (e) {
const ptrContent = app.ptr.get('.ptr-content.home-page')
ptrContent.on('refresh', async function () {
refreshed = true
const storeName = activeTab === 'following' ? 'getFollowingPosts' : 'getPosts'
if (isFetchingPosts) return
isFetchingPosts = true
if (activeTab === 'following') {
currentFollowingPostsPage = 1
} else {
currentPostsPage = 1
}
await store.dispatch(storeName, 1)
isFetchingPosts = false
app.ptr.done()
})
app.toolbar.show('.toolbar.toolbar-bottom', true)
})
/* Based on this http://jsfiddle.net/brettwp/J4djY/*/
export function detectDoubleTapClosure(callback) {
let lastTap = 0
let timeout
return function detectDoubleTap(event) {
const curTime = new Date().getTime()
const tapLen = curTime - lastTap
if (tapLen < 500 && tapLen > 0) {
event.preventDefault()
// pass the event target to the callback
callback(event.target)
} else {
timeout = setTimeout(() => {
clearTimeout(timeout)
}, 500)
}
lastTap = curTime
}
}
// event listener for tab change
$(document).on('click', '.social-tabs .tab-link', async function (e) {
const type = this.getAttribute('data-type')
activeTab = type
})
async function displayPosts(posts, following = false) {
const postsContainer = $(following ? '#tab-following .data' : '#tab-latest .data');
if (refreshed) {
postsContainer.html('')
refreshed = false
}
const user = await getSessionUser()
posts.forEach(post => {
let post_actions = `
<div class="media-post-actions">
<div class="media-post-like" data-post-id="${post.id}">
<i class="icon f7-icons ${post.is_liked ? 'text-red' : ''}" data-post-id="${post.id}">${post.is_liked ? 'heart_fill' : 'heart'}</i>
</div>
<div class="media-post-comment popup-open" data-popup=".comments-popup" data-post-id="${post.id}">
<i class="icon f7-icons">chat_bubble</i>
</div>
<div class="media-post-share popup-open" data-popup=".share-popup">
<i class="icon f7-icons">paperplane</i>
</div>
`;
if (post.user_id == user.id) {
post_actions += `
<div class="media-post-edit popup-open" data-popup=".edit-post-popup" data-post-id="${post.id}">
<i class="icon f7-icons">gear_alt</i>
</div>
`;
}
post_actions += `</div>`;
const date = formatPostDate(post.post_date);
const maxDescriptionLength = 200; // Set your character limit here
const isLongDescription = post.caption.length > maxDescriptionLength;
const shortDescription = isLongDescription ? post.caption.slice(0, maxDescriptionLength) : post.caption;
let imageHeight = 400;
if (post.media.length > 0) {
const intrinsicWidth = post.media[0].media_width;
const intrinsicHeight = post.media[0].media_height;
// Calculate intrinsic aspect ratio
const intrinsicRatio = intrinsicWidth / intrinsicHeight;
// Calculate the rendered height based on the container width
const renderedHeight = containerWidth / intrinsicRatio;
// Use either the rendered height or the fallback height
if (renderedHeight > 0) {
if (renderedHeight > 500) {
imageHeight = 500
} else {
imageHeight = renderedHeight
}
}
}
let profile_link;
if (post.user_id == user.id) {
profile_link = `
<a href="#" class="view-profile media-post-header">
<div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
<div class="media-post-user">${post.username}</div>
<div class="media-post-date">${date}</div>
</a>`
} else {
profile_link = `
<a href="/profile-view/${post.user_id}" class="media-post-header">
<div class="media-post-avatar" style="background-image: url('${post.user_profile_image || 'assets/img/profile-placeholder.jpg'}');"></div>
<div class="media-post-user">${post.username}</div>
<div class="media-post-date">${date}</div>
</a>`
}
const postItem = `
<div class="media-post" data-post-id="${post.id}" data-is-liked="${post.is_liked}">
<div class="media-post-content">
${profile_link}
<div class="media-post-content">
<div class="swiper-container">
<div class="swiper-wrapper">
${post.media.map(mediaItem => `
<div class="swiper-slide post-media" style="height: ${imageHeight}px; ">
${mediaItem.media_type === 'video' ?
// `
// <video autoplay loop muted playsinline class="video-background media-post-video" id="${mediaItem.id}">
// <source src="${mediaItem.media_url}" type="${mediaItem.media_mime_type}" />
// </video>
// `
'Disabled for testing'
: `
<img
src="${mediaItem.media_url}"
alt="${mediaItem.caption || post.username + 's post'}"
loading="lazy"
style="text-align: center;"
onerror = "this.style.display='none';"
/>
`}
</div>
`).join('')}
</div>
<div class="swiper-pagination"></div>
</div>
</div>
${post_actions}
<div class="media-post-likecount" data-like-count="${post.likes_count}">${post.likes_count} likes</div>
<div class="media-post-description">
<strong>${post.username}</strong> <br/> <span class="post-caption">${shortDescription}</span>
<span class="full-description hidden">${post.caption}</span>
${isLongDescription ? `<span class="media-post-readmore">... more</span>` : ''}
</div>
${post.comments_count > 0 ? `<div class="media-post-commentcount popup-open" data-popup=".comments-popup" data-post-id="${post.id}">View ${post.comments_count} comments</div>` : ''}
</div>
</div>
`;
postsContainer.append(postItem);
});
}
export function togglePostLikeV1(postId, single = false) {
// Find the post element and its like icon
let container = single ? `.media-post.single[data-post-id="${postId}"]` : `.media-post[data-post-id="${postId}"]`
const postElement = document.querySelector(container)
const likeIcon = postElement.querySelector('.media-post-like i')
const isLiked = postElement.getAttribute('data-is-liked') === 'true'
const likeCountElem = postElement.querySelector('.media-post-likecount')
let likeCount = parseInt(likeCountElem.getAttribute('data-like-count'))
// Toggle the like state
if (isLiked) {
likeIcon.classList.remove('text-red')
likeIcon.innerText = 'heart'
likeCount--
postElement.setAttribute('data-is-liked', 'false')
} else {
likeIcon.classList.add('text-red')
likeIcon.innerText = 'heart_fill'
likeCount++
postElement.setAttribute('data-is-liked', 'true')
}
// Update like count
likeCountElem.innerText = `${likeCount} likes`
likeCountElem.setAttribute('data-like-count', likeCount)
// Optionally, make an API call to update the like status on the server
// fetch(`/api/posts/${postId}/like`, { method: 'POST' });
maybeLikePost(postId)
}
export function togglePostLike(postId, single = false) {
// Find all post elements with the specified postId
let container = single ? `.media-post.single[data-post-id="${postId}"]` : `.media-post[data-post-id="${postId}"]`
const postElements = document.querySelectorAll(container)
// Iterate through all matching post elements and update them
postElements.forEach(postElement => {
const likeIcon = postElement.querySelector('.media-post-like i')
const isLiked = postElement.getAttribute('data-is-liked') === 'true'
const likeCountElem = postElement.querySelector('.media-post-likecount')
let likeCount = parseInt(likeCountElem.getAttribute('data-like-count'))
// Toggle the like state
if (isLiked) {
likeIcon.classList.remove('text-red')
likeIcon.innerText = 'heart'
likeCount--
postElement.setAttribute('data-is-liked', 'false')
} else {
likeIcon.classList.add('text-red')
likeIcon.innerText = 'heart_fill'
likeCount++
postElement.setAttribute('data-is-liked', 'true')
}
// Update like count
likeCountElem.innerText = `${likeCount} likes`
likeCountElem.setAttribute('data-like-count', likeCount)
})
// Optionally, make an API call to update the like status on the server
maybeLikePost(postId)
}
function displayComments(comments, postId) {
const user = store.getters.user.value
const commentsContainer = document.getElementById('comments-list')
// reset the comments container
commentsContainer.innerHTML = ''
const commentForm = document.getElementById('comment-form')
commentForm.setAttribute('data-post-id', postId)
if (!comments.length) {
commentsContainer.innerHTML = '<div class="no-comments">No comments found</div>'
return
}
comments.forEach(comment => {
const replyItems = comment.replies.length > 0 ? `
<div class="comment-replies">
<span class="comment-replies-toggle" data-replies-count="${comment.replies.length}">
Show ${comment.replies.length} ${comment.replies.length > 1 ? 'replies' : 'reply'}
</span>
<div class="comment-replies-container">
${comment.replies.map(reply => {
// Determine the delete button visibility
const deleteButton = reply.user_id == user.id ?
`<div class="comment-delete" data-comment-id="${reply.id}">
<i class="icon f7-icons text-red">trash</i>
</div>` :
'';
return `
<div class="comment" data-comment-id="${reply.id}" data-is-liked="${reply.liked}" data-owner-id="${reply.user_id}"
data-owner-name="${reply.user_login}">
<a href="#" data-url="${reply.user_id == user.id ? '#' : `/profile-view/${reply.user_id}`}" class="${reply.user_id == user.id ? 'view-profile' : ''} comment-profile-img" style="background-image:url('${reply.profile_image || 'assets/img/profile-placeholder.jpg'}');">
</a>
<div class="comment-content-container">
<div class="comment-username">
<a href="#" data-url="${reply.user_id == user.id ? '#' : `/profile-view/${reply.user_id}`}" class="${reply.user_id == user.id ? 'view-profile' : 'a'}">
${reply.user_login}
</a>
<span class="date">${formatPostDate(reply.comment_date)}</span>
</div>
<div class="comment-content">${reply.comment}</div>
<div class="comment-actions">
<div class="comment-like">
<i class="icon f7-icons ${reply.liked ? 'text-red' : ''}">
${reply.liked ? 'heart_fill' : 'heart'}
</i>
<span class="comment-likes-count" data-likes-count="${reply.likes_count}">
${reply.likes_count}
</span>
</div>
<div class="comment-reply">
<i class="icon f7-icons">chat_bubble</i> <span>Reply</span>
</div>
${deleteButton}
</div>
</div>
<div class="clearfix"></div>
</div>`;
}).join('')}
</div>
</div>` : '';
let commenter_link = `/profile-view/${comment.user_id}`;
if (comment.user_id == user.id) {
commenter_link = '/profile/';
}
const deleteButton = comment.user_id == user.id ?
`<div class="comment-delete" data-comment-id="${comment.id}"><i class="icon f7-icons text-red">trash</i></div>` : '';
const commentItem = `
<div class="comment"
data-comment-id="${comment.id}"
data-is-liked="${comment.liked}"
data-owner-id="${comment.user_id}"
data-owner-name="${comment.user_login}">
<a href="#" data-url="${comment.user_id == user.id ? '#' : `/profile-view/${comment.user_id}`}" class="${comment.user_id == user.id ? 'view-profile' : ''} comment-profile-img"
style="background-image:url('${comment.profile_image || 'assets/img/profile-placeholder.jpg'}');">
</a>
<div class="comment-content-container">
<div class="comment-username">
<a href="#" data-url="${comment.user_id == user.id ? '#' : `/profile-view/${comment.user_id}`}" class="${comment.user_id == user.id ? 'view-profile' : 'a'}">
${comment.user_login}
</a>
<span class="date">${formatPostDate(comment.comment_date)}</span>
</div>
<div class="comment-content">${comment.comment}</div>
<div class="comment-actions">
<div class="comment-like">
<i class="icon f7-icons ${comment.liked && 'text-red'}">${comment.liked ? 'heart_fill' : 'heart'}</i>
<span class="comment-likes-count" data-likes-count="${comment.likes_count}">
${comment.likes_count}
</span>
</div>
<div class="comment-reply">
<i class="icon f7-icons">chat_bubble</i> <span>Reply</span>
</div>
${deleteButton}
</div>
${replyItems}
</div>
<div class="clearfix"></div>
</div>
`
commentsContainer.insertAdjacentHTML('beforeend', commentItem)
})
// Add click event listener for liking a comment
const likeButtons = document.querySelectorAll('.comment-like')
likeButtons.forEach(button => {
button.addEventListener('click', (event) => {
const commentId = event.currentTarget.closest('.comment').getAttribute('data-comment-id')
const ownerId = event.currentTarget.closest('.comment').getAttribute('data-owner-id')
toggleCommentLike(commentId, ownerId)
})
})
}
function toggleCommentLike(commentId, ownerId) {
// Find the comment element and its like icon
const commentElement = document.querySelector(`.comment[data-comment-id="${commentId}"]`)
const likeIcon = commentElement.querySelector('.comment-like i')
const isLiked = commentElement.getAttribute('data-is-liked') === 'true'
const likeCountElem = commentElement.querySelector('.comment-likes-count')
let likeCount = parseInt(likeCountElem.getAttribute('data-likes-count'))
// Toggle the like state
if (isLiked) {
likeIcon.classList.remove('text-red')
likeIcon.innerText = 'heart'
likeCount--
commentElement.setAttribute('data-is-liked', 'false')
} else {
likeIcon.classList.add('text-red')
likeIcon.innerText = 'heart_fill'
likeCount++
commentElement.setAttribute('data-is-liked', 'true')
}
// Update like count
likeCountElem.innerText = likeCount
likeCountElem.setAttribute('data-likes-count', likeCount)
maybeLikeComment(commentId, ownerId)
}
$(document).on('click', '.media-post-readmore', function () {
const postDescription = this.previousElementSibling.previousElementSibling; // The short description
const fullDescription = this.previousElementSibling; // The full description
if (fullDescription.classList.contains('hidden')) {
postDescription.classList.add('hidden');
fullDescription.classList.remove('hidden');
this.textContent = '... less';
} else {
postDescription.classList.remove('hidden');
fullDescription.classList.add('hidden');
this.textContent = '... more';
}
});
$(document).on('click', '.media-post-like i', (e) => {
const postId = e.target.getAttribute('data-post-id')
const parent = e.target.closest('.media-post')
const isSingle = parent.classList.contains('single') ? true : false
togglePostLike(postId, isSingle)
})
// set the post id as a data attribute from the edit post popup
$(document).on('click', '.media-post-edit', function () {
const postId = $(this).closest('.media-post').attr('data-post-id')
const isSingleView = $(this).closest('.media-post').hasClass('single')
$('.edit-post-popup').attr('data-post-id', postId)
$('.edit-post-popup').attr('data-is-single', isSingleView)
})
$(document).on('click', '#delete-post', function () {
var view = app.views.current
// set the post id as a data attribute from the edit post popup
const postId = $('.edit-post-popup').attr('data-post-id')
const isSingleView = $('.edit-post-popup').attr('data-is-single')
app.dialog.confirm('Are you sure you want to delete this post?', 'Delete Post', async () => {
const response = await deletePost(postId)
if (response) {
store.dispatch('getMyPosts', {
page: 1,
clear: true
})
store.dispatch('getMyTags', {
page: 1,
clear: true
})
if (isSingleView) {
view.router.navigate('/profile/')
}
showToast('Post deleted successfully')
// remove the post from the DOM
$(`.media-post[data-post-id="${postId}"]`).remove()
app.popup.close('.edit-post-popup')
} else {
showToast('Failed to delete post')
}
})
})
$(document).on('click', '#edit-post', function () {
var view = app.views.current
// set the post id as a data attribute from the edit post popup
const postId = $('.edit-post-popup').attr('data-post-id')
view.router.navigate(`/post-edit/${postId}`, {
force: true
})
app.popup.close('.edit-post-popup')
})
$(document).on('touchstart', '.media-post-content .swiper-wrapper', detectDoubleTapClosure((e) => {
return // Disable double tap for now
const parent = e.closest('.media-post')
const postId = parent.getAttribute('data-post-id')
const isLiked = parent.getAttribute('data-is-liked') === 'true'
if (isLiked) {
return
}
togglePostLike(postId)
}), {
passive: false
})
// media-post-video click
$(document).on('click', '.media-post-video', function () {
if (this.paused) {
this.play()
} else {
this.pause()
}
})
// on .popup-open click
$(document).on('click', '.media-post-comment, .media-post-commentcount', async function () {
const postId = this.getAttribute('data-post-id')
if (!postId) {
return
}
document.getElementById('comments-list').innerHTML = '<div class="preloader"></div>'
document.getElementById('comment-form').reset()
// update the post id in the comment form
document.getElementById('comment-form').setAttribute('data-post-id', '')
document.getElementById('comment-form').removeAttribute('data-comment-id')
document.getElementById('comment-form').querySelector('.replying-to').innerHTML = ''
document.getElementById('comment-form').querySelector('.replying-to').classList.add('hidden')
try {
const comments = await fetchComments(postId)
displayComments(comments, postId)
} catch (error) {
app.notification.create({
titleRightText: 'now',
subtitle: 'Oops, something went wrong',
text: error.message || 'Failed to fetch comments',
}).open()
}
})
$(document).on('click', '.media-post-share', function () {
// set the post id as a data attribute
const postId = $(this).closest('.media-post').attr('data-post-id')
$('.share-popup').attr('data-post-id', postId)
$('#copy-link').attr('data-clipboard-text', `${window.location.origin}/post-view/${postId}`)
})
$(document).on('click', '#share-post-email', function () {
const postId = $(this).closest('.popup').attr('data-post-id')
const postLink = `${window.location.origin}/post-view/${postId}`
// open the email composer
window.open(`mailto:?subject=Check out this post&body=${postLink}`)
})
// data-clipboard-text click
$(document).on('click', '#copy-link', function () {
const copyText = $(this).attr('data-clipboard-text')
navigator.clipboard.writeText(copyText)
app.toast.create({
text: 'Link copied to clipboard',
closeTimeout: 2000
}).open()
})
// on .comment-replies-toggle click
$(document).on('click', '.comment-replies-toggle', function () {
const commentRepliesContainer = this.nextElementSibling
commentRepliesContainer.classList.toggle('show')
const repliesCount = this.getAttribute('data-replies-count')
this.innerText = this.innerText === `Show ${repliesCount} replies` ? `Hide ${repliesCount} replies` : `Show ${repliesCount} replies`
})
// on comment form submit
$('#comment-form').on('submit', async function (e) {
e.preventDefault()
const postId = this.getAttribute('data-post-id')
const commentId = this.getAttribute('data-comment-id')
const comment = this.comment.value
if (!comment) {
// app.dialog.alert('Please enter a comment')
return
}
app.preloader.show()
try {
const response = await addComment(postId, comment, commentId)
app.preloader.hide()
if (response) {
this.reset()
this.removeAttribute('data-comment-id')
this.querySelector('.replying-to').innerHTML = ''
this.querySelector('.replying-to').classList.add('hidden')
const comments = await fetchComments(postId)
displayComments(comments, postId)
} else {
app.notification.create({
text: 'Failed to add comment',
titleRightText: 'now',
subtitle: 'Oops, something went wrong',
}).open()
}
} catch (error) {
app.notification.create({
titleRightText: 'now',
subtitle: 'Oops, something went wrong',
text: error.message || 'Failed to add comment',
}).open()
app.preloader.hide()
}
})
//.comment-reply click
$(document).on('click', '.comment-reply', function () {
// get the comment id, and comment owner id
const commentId = this.closest('.comment').getAttribute('data-comment-id')
const ownerId = this.closest('.comment').getAttribute('data-owner-id')
const ownerName = this.closest('.comment').getAttribute('data-owner-name')
// add something above the comment form to show the user they are replying to a comment
// add the comment id to the form
document.getElementById('comment-form').setAttribute('data-comment-id', commentId)
document.getElementById('comment-form').comment.focus()
// add the owner name to the form
// <span class="replying-to">Replying to <strong>m88xrk</strong></span>
const replyingTo = document.getElementById('comment-form').querySelector('.replying-to')
replyingTo.innerHTML = `Replying to <strong>${ownerName}</strong>`
replyingTo.classList.remove('hidden')
document.getElementById('comment-form').prepend(replyingTo)
})
$(document).on('click', '.comment-delete', async function () {
app.dialog.confirm('Are you sure you want to delete this comment? This will remove all replies to this comment', 'Delete Comment', async () => {
try {
const commentId = this.getAttribute('data-comment-id')
const response = await deleteComment(commentId)
if (response && response.success) {
// remove the comment from the DOM
$(`.comment[data-comment-id="${commentId}"]`).remove()
showToast('Comment deleted successfully')
}
} catch (error) {
app.dialog.alert('Failed to delete comment')
}
})
})
$(document).on('click', '.comment a', function (e) {
var view = app.views.current
// hide the comments popup
app.popup.close()
// get the href attribute
const href = this.getAttribute('data-url')
if (!href || href === '#') {
return
}
// prevent the default action
e.preventDefault()
view.router.navigate(href, {
force: true
})
})