Changeset c7ecaf9 in Klonkt


Ignore:
Timestamp:
06/26/2026 11:45:53 PM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
9d34855
Parents:
2f42b60
Message:

feat(fedi): like/boost toggle in place on the interact page (no reload)

The Like/Boost forms now POST via fetch and flip the button + label client-side, so you
stay on the page instead of a full redirect+re-resolve. Route returns JSON {on} for a
fetch request (X-Requested-With), and still redirects for the no-JS fallback. Body sent
as urlencoded so the existing body parser reads it.

Location:
src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r2f42b60 rc7ecaf9  
    511511  const site = res.locals.site;
    512512  const uri = (req.body.uri || '').toString();
     513  let on = false;
    513514  if (site && uri) {
    514     const on = !ActivityPubService.getMyReactions(site.slug, uri).liked;
     515    on = !ActivityPubService.getMyReactions(site.slug, uri).liked;
    515516    ActivityPubService.resolveRemoteNote(uri)
    516517      .then((note) => note && ActivityPubService.sendInteraction(site, on ? 'like' : 'unlike', note.object_uri || uri, note.actor_uri))
     
    518519    ActivityPubService.setMyReaction(site.slug, uri, 'like', on);
    519520  }
     521  if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on });
    520522  res.redirect('/authorize_interaction?uri=' + encodeURIComponent(uri));
    521523});
     
    526528  const site = res.locals.site;
    527529  const uri = (req.body.uri || '').toString();
     530  let on = false;
    528531  if (site && uri) {
    529     const on = !ActivityPubService.getMyReactions(site.slug, uri).boosted;
     532    on = !ActivityPubService.getMyReactions(site.slug, uri).boosted;
    530533    ActivityPubService.resolveRemoteNote(uri)
    531534      .then((note) => {
     
    538541    ActivityPubService.setMyReaction(site.slug, uri, 'boost', on);
    539542  }
     543  if (req.get('X-Requested-With') === 'fetch') return res.json({ ok: true, on });
    540544  res.redirect('/authorize_interaction?uri=' + encodeURIComponent(uri));
    541545});
  • src/views/pages/authorize-interaction.ejs

    r2f42b60 rc7ecaf9  
    9696      </a>
    9797    <% } %>
     98    <% var _liked = (typeof reacted !== 'undefined' && reacted.liked); var _boosted = (typeof reacted !== 'undefined' && reacted.boosted); %>
    9899    <div class="auth-interact-react">
    99       <form method="post" action="/authorize_interaction/like">
     100      <form method="post" action="/authorize_interaction/like" class="fedi-react-form">
    100101        <input type="hidden" name="uri" value="<%= uri %>">
    101         <button type="submit" class="fedi-bigact fedi-bigact-like<%= (typeof reacted !== 'undefined' && reacted.liked) ? ' is-on' : '' %>">
     102        <button type="submit" class="fedi-bigact fedi-bigact-like<%= _liked ? ' is-on' : '' %>" data-on="<%= t('fedi.unlike_short') %>" data-off="<%= t('fedi.like_short') %>">
    102103          <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.9 5.88 6.49.95-4.7 4.58 1.11 6.46L12 17.96l-5.8 3.06 1.1-6.46-4.69-4.58 6.49-.95z"/></svg>
    103           <span><%= (typeof reacted !== 'undefined' && reacted.liked) ? t('fedi.unlike_short') : t('fedi.like_short') %></span>
     104          <span class="fedi-bigact-label"><%= _liked ? t('fedi.unlike_short') : t('fedi.like_short') %></span>
    104105        </button>
    105106      </form>
    106       <form method="post" action="/authorize_interaction/boost">
     107      <form method="post" action="/authorize_interaction/boost" class="fedi-react-form">
    107108        <input type="hidden" name="uri" value="<%= uri %>">
    108         <button type="submit" class="fedi-bigact fedi-bigact-boost<%= (typeof reacted !== 'undefined' && reacted.boosted) ? ' is-on' : '' %>">
     109        <button type="submit" class="fedi-bigact fedi-bigact-boost<%= _boosted ? ' is-on' : '' %>" data-on="<%= t('tl.unboost') %>" data-off="<%= t('fedi.boost_short') %>">
    109110          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg>
    110           <span><%= (typeof reacted !== 'undefined' && reacted.boosted) ? t('tl.unboost') : t('fedi.boost_short') %></span>
     111          <span class="fedi-bigact-label"><%= _boosted ? t('tl.unboost') : t('fedi.boost_short') %></span>
    111112        </button>
    112113      </form>
     
    185186  .fedi-del-btn:hover { background: color-mix(in srgb, #d9534f 24%, transparent); }
    186187</style>
     188
     189<script>
     190/* Like/Boost toggle in place — POST via fetch, flip the button, stay on the page. */
     191(function(){
     192  if (window.__fediReactWired) return; window.__fediReactWired = true;
     193  document.addEventListener('submit', function(e){
     194    var f = e.target.closest && e.target.closest('.fedi-react-form');
     195    if (!f) return;
     196    e.preventDefault();
     197    var btn = f.querySelector('button'); if (!btn || btn.disabled) return;
     198    btn.disabled = true;
     199    var body = new URLSearchParams();
     200    new FormData(f).forEach(function(v, k){ body.append(k, v); });
     201    fetch(f.action, { method: 'POST', body: body, headers: { 'X-Requested-With': 'fetch' }, credentials: 'same-origin' })
     202      .then(function(r){ return r.ok ? r.json() : null; })
     203      .then(function(j){
     204        if (j) {
     205          var on = !!j.on;
     206          btn.classList.toggle('is-on', on);
     207          var lbl = btn.querySelector('.fedi-bigact-label');
     208          if (lbl) lbl.textContent = on ? (btn.getAttribute('data-on') || lbl.textContent) : (btn.getAttribute('data-off') || lbl.textContent);
     209        }
     210      })
     211      .catch(function(){})
     212      .then(function(){ btn.disabled = false; });
     213  });
     214})();
     215</script>
Note: See TracChangeset for help on using the changeset viewer.