201 lines
13 KiB
JavaScript
201 lines
13 KiB
JavaScript
// Reaction and share functionality
|
|
(function() {
|
|
// Generate or retrieve visitor ID
|
|
function getVisitorId() {
|
|
let visitorId = localStorage.getItem('visitorId');
|
|
if (!visitorId) {
|
|
visitorId = 'visitor_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
localStorage.setItem('visitorId', visitorId);
|
|
}
|
|
return visitorId;
|
|
}
|
|
|
|
// Load reactions for a post
|
|
async function loadReactions(postId) {
|
|
try {
|
|
const response = await fetch(`/api/reaction.php?postId=${postId}`);
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
updateReactionButtons(postId, data.counts);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading reactions:', error);
|
|
}
|
|
}
|
|
|
|
// Send reaction
|
|
async function sendReaction(postId, reaction) {
|
|
const visitorId = getVisitorId();
|
|
try {
|
|
const response = await fetch('/api/reaction.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
postId: postId,
|
|
reaction: reaction,
|
|
visitorId: visitorId
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error('API error:', response.status, response.statusText);
|
|
return;
|
|
}
|
|
|
|
const data = await response.json();
|
|
console.log('Reaction response:', data); // Debug log
|
|
|
|
if (data.success && data.counts) {
|
|
updateReactionButtons(postId, data.counts);
|
|
} else {
|
|
console.error('Invalid response:', data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending reaction:', error);
|
|
}
|
|
}
|
|
|
|
// Update reaction button displays
|
|
function updateReactionButtons(postId, counts) {
|
|
const container = document.querySelector(`#post-${postId} .reactions-buttons`);
|
|
if (!container) return;
|
|
|
|
['like', 'love', 'helpful'].forEach(reaction => {
|
|
const button = container.querySelector(`.reaction-btn[data-reaction="${reaction}"]`);
|
|
if (button) {
|
|
const count = counts[reaction] || 0;
|
|
const countSpan = button.querySelector('.reaction-count');
|
|
if (countSpan) {
|
|
countSpan.textContent = count;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Track share event
|
|
async function trackShare(postId, platform) {
|
|
const visitorId = getVisitorId();
|
|
try {
|
|
await fetch('/api/track.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
type: 'share',
|
|
page: `/blog/post.html?id=${postId}`,
|
|
visitorId: visitorId,
|
|
platform: platform
|
|
})
|
|
});
|
|
} catch (error) {
|
|
console.error('Error tracking share:', error);
|
|
}
|
|
}
|
|
|
|
// Inline SVG icons - using currentColor for theme support
|
|
const iconSVGs = {
|
|
like: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 -0.5 21 21" class="reaction-icon-img"><g stroke="none" stroke-width="1" fill-rule="evenodd"><g transform="translate(-219.000000, -760.000000)" fill="currentColor"><g transform="translate(56.000000, 160.000000)"><path fill="currentColor" d="M163,610.021159 L163,618.021159 C163,619.126159 163.93975,620.000159 165.1,620.000159 L167.199999,620.000159 L167.199999,608.000159 L165.1,608.000159 C163.93975,608.000159 163,608.916159 163,610.021159 M183.925446,611.355159 L182.100546,617.890159 C181.800246,619.131159 180.639996,620.000159 179.302297,620.000159 L169.299999,620.000159 L169.299999,608.021159 L171.104948,601.826159 C171.318098,600.509159 172.754498,599.625159 174.209798,600.157159 C175.080247,600.476159 175.599997,601.339159 175.599997,602.228159 L175.599997,607.021159 C175.599997,607.573159 176.070397,608.000159 176.649997,608.000159 L181.127196,608.000159 C182.974146,608.000159 184.340196,609.642159 183.925446,611.355159"/></g></g></g></svg>',
|
|
love: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 122.88 107.41" class="reaction-icon-img"><path fill="currentColor" d="M60.83,17.19C68.84,8.84,74.45,1.62,86.79,0.21c23.17-2.66,44.48,21.06,32.78,44.41 c-3.33,6.65-10.11,14.56-17.61,22.32c-8.23,8.52-17.34,16.87-23.72,23.2l-17.4,17.26L46.46,93.56C29.16,76.9,0.95,55.93,0.02,29.95 C-0.63,11.75,13.73,0.09,30.25,0.3C45.01,0.5,51.22,7.84,60.83,17.19L60.83,17.19L60.83,17.19z"/></svg>',
|
|
helpful: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 103.19 122.88" class="reaction-icon-img"><path fill="currentColor" d="M17.16 0h82.72a3.32 3.32 0 013.31 3.31v92.32c-.15 2.58-3.48 2.64-7.08 2.48H15.94c-4.98 0-9.05 4.07-9.05 9.05s4.07 9.05 9.05 9.05h80.17v-9.63h7.08v12.24c0 2.23-1.82 4.05-4.05 4.05H16.29C7.33 122.88 0 115.55 0 106.59V17.16C0 7.72 7.72 0 17.16 0zm3.19 13.4h2.86c1.46 0 2.66.97 2.66 2.15v67.47c0 1.18-1.2 2.15-2.66 2.15h-2.86c-1.46 0-2.66-.97-2.66-2.15V15.55c.01-1.19 1.2-2.15 2.66-2.15z" fill-rule="evenodd" clip-rule="evenodd"/></svg>',
|
|
mastodon: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="share-icon-img"><path fill="currentColor" d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>',
|
|
bluesky: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 512 452.265" class="share-icon-img"><path fill="currentColor" d="M110.985 30.441c58.695 44.217 121.837 133.856 145.013 181.961 23.177-48.105 86.323-137.744 145.017-181.961C443.376-1.455 512-26.142 512 52.402c0 15.68-8.962 131.775-14.223 150.628-18.273 65.515-84.873 82.228-144.112 72.116 103.55 17.679 129.889 76.237 73 134.799-108.041 111.223-155.288-27.905-167.386-63.554-3.488-10.262-2.991-10.498-6.56 0-12.098 35.649-59.342 174.777-167.383 63.554-56.889-58.562-30.551-117.12 72.999-134.799-59.239 10.112-125.84-6.601-144.112-72.116C8.962 184.177 0 68.082 0 52.402 0-26.142 68.633-1.455 110.985 30.441z"/></svg>',
|
|
copy: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="share-icon-img"><path fill="currentColor" d="M15.24 2H11.3458C9.58159 1.99999 8.18418 1.99997 7.09054 2.1476C5.96501 2.29953 5.05402 2.61964 4.33559 3.34096C3.61717 4.06227 3.29833 4.97692 3.14701 6.10697C2.99997 7.205 2.99999 8.60802 3 10.3793V16.2169C3 17.725 3.91995 19.0174 5.22717 19.5592C5.15989 18.6498 5.15994 17.3737 5.16 16.312L5.16 11.3976L5.16 11.3024C5.15993 10.0207 5.15986 8.91644 5.27828 8.03211C5.40519 7.08438 5.69139 6.17592 6.4253 5.43906C7.15921 4.70219 8.06404 4.41485 9.00798 4.28743C9.88877 4.16854 10.9887 4.1686 12.2652 4.16867L12.36 4.16868H15.24L15.3348 4.16867C16.6113 4.1686 17.7088 4.16854 18.5896 4.28743C18.0627 2.94779 16.7616 2 15.24 2Z"/><path fill="currentColor" d="M6.6001 11.3974C6.6001 8.67119 6.6001 7.3081 7.44363 6.46118C8.28716 5.61426 9.64481 5.61426 12.3601 5.61426H15.2401C17.9554 5.61426 19.313 5.61426 20.1566 6.46118C21.0001 7.3081 21.0001 8.6712 21.0001 11.3974V16.2167C21.0001 18.9429 21.0001 20.306 20.1566 21.1529C19.313 21.9998 17.9554 21.9998 15.2401 21.9998H12.3601C9.64481 21.9998 8.28716 21.9998 7.44363 21.1529C6.6001 20.306 6.6001 18.9429 6.6001 16.2167V11.3974Z"/></svg>'
|
|
};
|
|
|
|
// Create reaction buttons HTML
|
|
function createReactionButtons(postId) {
|
|
return `
|
|
<div class="reactions-container">
|
|
<div class="reactions-label">React</div>
|
|
<div class="reactions-buttons">
|
|
<button class="reaction-btn" data-post="${postId}" data-reaction="like" title="Like">
|
|
${iconSVGs.like}<span class="reaction-count">0</span>
|
|
</button>
|
|
<button class="reaction-btn" data-post="${postId}" data-reaction="love" title="Love">
|
|
${iconSVGs.love}<span class="reaction-count">0</span>
|
|
</button>
|
|
<button class="reaction-btn" data-post="${postId}" data-reaction="helpful" title="Helpful">
|
|
${iconSVGs.helpful}<span class="reaction-count">0</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Create share buttons HTML
|
|
function createShareButtons(postId) {
|
|
return `
|
|
<div class="share-container">
|
|
<div class="share-label">Share</div>
|
|
<div class="share-buttons">
|
|
<button class="share-btn" data-platform="mastodon" data-post="${postId}" title="Share on Mastodon">${iconSVGs.mastodon}</button>
|
|
<button class="share-btn" data-platform="bluesky" data-post="${postId}" title="Share on Bluesky">${iconSVGs.bluesky}</button>
|
|
<button class="share-btn" data-platform="copy" data-post="${postId}" title="Copy link">${iconSVGs.copy}</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Handle reaction clicks
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('.reaction-btn')) {
|
|
const button = e.target.closest('.reaction-btn');
|
|
const postId = button.dataset.post;
|
|
const reaction = button.dataset.reaction;
|
|
sendReaction(postId, reaction);
|
|
}
|
|
});
|
|
|
|
// Handle share clicks
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('.share-btn')) {
|
|
const button = e.target.closest('.share-btn');
|
|
const platform = button.dataset.platform;
|
|
const postId = button.dataset.post;
|
|
const postTitle = document.querySelector(`#post-${postId} .blog-post-title`)?.textContent || '';
|
|
// Construct URL without port (for .onion compatibility)
|
|
const origin = window.location.protocol + '//' + window.location.hostname;
|
|
const url = origin + '/blog/post.html?id=' + postId;
|
|
|
|
if (platform === 'mastodon') {
|
|
trackShare(postId, 'mastodon');
|
|
// Open Mastodon share - user can paste the URL
|
|
const mastodonUrl = `https://defcon.social/share?text=${encodeURIComponent(postTitle + ' ' + url)}`;
|
|
window.open(mastodonUrl, '_blank', 'width=550,height=420');
|
|
} else if (platform === 'bluesky') {
|
|
trackShare(postId, 'bluesky');
|
|
// Open Bluesky share
|
|
const blueskyUrl = `https://bsky.app/intent/compose?text=${encodeURIComponent(postTitle + ' ' + url)}`;
|
|
window.open(blueskyUrl, '_blank', 'width=550,height=420');
|
|
} else if (platform === 'copy') {
|
|
trackShare(postId, 'copy');
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
const icon = button.querySelector('.share-icon-img');
|
|
if (icon) {
|
|
const originalHTML = icon.outerHTML;
|
|
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
|
|
const checkColor = isDarkMode ? '#59C99C' : 'currentColor';
|
|
icon.outerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" class="share-icon-img"><path fill="${checkColor}" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
|
|
setTimeout(() => {
|
|
const checkIcon = button.querySelector('.share-icon-img');
|
|
if (checkIcon) {
|
|
checkIcon.outerHTML = originalHTML;
|
|
}
|
|
}, 2000);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// Export functions for use in blog.js
|
|
window.blogReactions = {
|
|
createReactionButtons: createReactionButtons,
|
|
createShareButtons: createShareButtons,
|
|
loadReactions: loadReactions
|
|
};
|
|
})();
|