Changeset 5410d4d in Klonkt
- Timestamp:
- 06/24/2026 06:05:39 PM (3 months ago)
- Branches:
- main
- Children:
- 7008b28
- Parents:
- e6272d1
- Location:
- src
- Files:
-
- 1 deleted
- 3 edited
-
routes/posts.js (modified) (4 diffs)
-
views/pages/favorites.ejs (deleted)
-
views/partials/profile-sheet.ejs (modified) (1 diff)
-
views/partials/topnav.ejs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/posts.js
re6272d1 r5410d4d 10 10 import { renderPage } from '../middleware/render.js'; 11 11 import { recordPageview, recordPostView } from '../services/StatsService.js'; 12 import { notify } from '../services/NotificationService.js';13 12 import PermissionsService from '../services/PermissionsService.js'; 14 13 import MarkdownService from '../services/MarkdownService.js'; … … 430 429 }); 431 430 432 // Path to the like button partial (for the htmx toggle re-render). 433 const LIKE_PARTIAL = path.join(__dirname, '..', 'views', 'partials', 'like-button.ejs'); 434 435 // ==================== LIKE / FAVOURITE ==================== 436 // A logged-in user (not a viewer — the global guard blocks non-GET for viewers) 437 // toggles a like on a published post. Returns the re-rendered button (htmx outerHTML swap). 438 router.post('/posts/:id/like', requireAuth, (req, res) => { 439 const userId = req.session.user.id; 440 const post = db.prepare('SELECT id, status, slug, title, author_id FROM posts WHERE id = ?').get(req.params.id); 441 if (!post) return res.status(404).send('Post niet gevonden'); 442 if (post.status !== 'published') return res.status(403).send('Niet beschikbaar'); 443 444 const exists = db.prepare('SELECT 1 FROM post_likes WHERE post_id = ? AND user_id = ?').get(post.id, userId); 445 if (exists) { 446 db.prepare('DELETE FROM post_likes WHERE post_id = ? AND user_id = ?').run(post.id, userId); 447 } else { 448 db.prepare('INSERT OR IGNORE INTO post_likes (post_id, user_id) VALUES (?, ?)').run(post.id, userId); 449 // Notification for the post author (notify skips self-likes). 450 notify({ 451 userId: post.author_id, actorId: userId, actorName: req.session.user.username, type: 'like', 452 postSlug: post.slug, postTitle: post.title, url: (res.locals.siteUrlBase || '') + '/' + post.slug, 453 }); 454 } 455 const likeCount = db.prepare('SELECT COUNT(*) AS c FROM post_likes WHERE post_id = ?').get(post.id).c; 456 457 const html = ejs.render(fs.readFileSync(LIKE_PARTIAL, 'utf8'), { 458 post: { id: post.id }, likedByMe: !exists, likeCount, loggedIn: true, loginNext: '/', 459 }); 460 res.send(html); 461 }); 462 463 // Favourites = posts the logged-in user has liked. Solo: within the current 464 // site. Hub: across all sites (with correct /user/<slug> links). 465 router.get('/favorieten', requireAuth, (req, res) => { 466 const userId = req.session.user.id; 467 const isHub = res.locals.tenancy === 'hub'; 468 const site = res.locals.site; 469 const rows = isHub 470 ? db.prepare(` 471 SELECT p.id, p.slug, p.title, p.excerpt, p.cover_image_url, p.published_at, 472 p.tags, p.type, p.pinned, p.status, s.slug AS site_slug 473 FROM post_likes pl JOIN posts p ON p.id = pl.post_id JOIN sites s ON s.id = p.site_id 474 WHERE pl.user_id = ? AND p.status = 'published' 475 ORDER BY pl.created_at DESC 476 `).all(userId) 477 : db.prepare(` 478 SELECT p.id, p.slug, p.title, p.excerpt, p.cover_image_url, p.published_at, 479 p.tags, p.type, p.pinned, p.status 480 FROM post_likes pl JOIN posts p ON p.id = pl.post_id 481 WHERE pl.user_id = ? AND p.site_id = ? AND p.status = 'published' 482 ORDER BY pl.created_at DESC 483 `).all(userId, site ? site.id : ''); 484 const posts = rows.map((p) => ({ ...p, _urlBase: (isHub && p.site_slug) ? `/user/${p.site_slug}` : '' })); 485 renderPage(req, res, 'pages/favorites', { posts, pageTitle: 'Favorieten', bodyClass: 'on-favorites' }); 486 }); 431 // Local likes/favourites are removed — engagement is fediverse-only now 432 // (the ⭐ on a post likes via the fediverse). No post_likes, no /favorieten. 487 433 488 434 // Newer/Older neighbours across ALL posts in feed order. Shared by the full … … 869 815 relatedPosts = relatedPosts.map(({ _overlap, tags, ...rest }) => ({ ...rest, _urlBase: urlBaseFor(rest) })); 870 816 871 // Likes / favourites: count + whether the logged-in user liked this post.872 const likeCount = db.prepare('SELECT COUNT(*) AS c FROM post_likes WHERE post_id = ?').get(post.id).c;873 const likedByMe = !!(req.session?.user &&874 db.prepare('SELECT 1 FROM post_likes WHERE post_id = ? AND user_id = ?').get(post.id, req.session.user.id));875 876 817 // Inbound fediverse activity (threaded) for this post. 877 818 let fediverse = { thread: [], likeCount: 0, announceCount: 0, total: 0 }; … … 893 834 canManageSite, 894 835 siteAvatar, 895 likeCount,896 likedByMe,897 836 pageTitle: post.title + ' - ' + site.title, 898 837 socialDescr: post.excerpt || '', -
src/views/partials/profile-sheet.ejs
re6272d1 r5410d4d 60 60 <svg class="psi-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg> 61 61 <span class="psi-label"><%= t('nav.notifications') %><% if (typeof notifUnread !== 'undefined' && notifUnread > 0) { %> (<%= notifUnread %>)<% } %></span> 62 <svg class="psi-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg>63 </a>64 </li>65 <li role="none">66 <a href="/favorieten" class="profile-sheet-item" role="menuitem" data-close-sheet67 hx-get="/favorieten?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"68 hx-push-url="/favorieten" hx-indicator="#pcms-loading">69 <svg class="psi-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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"/></svg>70 <span class="psi-label">Favorieten</span>71 62 <svg class="psi-chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg> 72 63 </a> -
src/views/partials/topnav.ejs
re6272d1 r5410d4d 137 137 <svg class="udi-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg> 138 138 <span><%= t('nav.account') %></span> 139 </a>140 <a href="/favorieten" role="menuitem" class="udi"141 hx-get="/favorieten?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"142 hx-push-url="/favorieten" hx-indicator="#pcms-loading">143 <svg class="udi-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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"/></svg>144 <span><%= t('nav.favorites') %></span>145 139 </a> 146 140 <% if (typeof canSeeBeheer !== 'undefined' && canSeeBeheer) { %>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)