Changeset 28f797d in Klonkt


Ignore:
Timestamp:
06/30/2026 04:31:56 PM (2 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
ecbfa41
Parents:
28bf7f0
Message:

feat(post): share button at the bottom of posts

A "Share/Deel" button under the post (visible to everyone). Uses the Web Share API (native share
sheet) where available, otherwise copies the post URL to the clipboard with a "Link copied"
confirmation. Wired through the existing delegated click handler (CSP-safe, no inline on*).

  • src/views/pages/post.ejs — share button + feedback span
  • src/views/shell.ejs — data-share delegated handler (navigator.share / clipboard)
  • src/services/i18n.js — post.share, post.share_copied (nl/en/de)

Co-Authored-By: Claude <noreply@…>

Location:
src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • src/services/i18n.js

    r28bf7f0 r28f797d  
    695695    'pedit.pin_top': 'bovenaan',
    696696    'pedit.pin_nth_suffix': 'e van boven',
    697     'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen',
     697    'pedit.noindex_label': 'noindex (verberg voor zoekmachines)', 'pedit.nsfw_label': 'NSFW / gevoelige inhoud', 'pedit.fedi_audio_label': 'Audio openbaar delen op de fediverse (speelt inline in apps; bestand downloadbaar)', 'pedit.nsfw_cw_ph': 'Waarschuwingstekst (optioneel, standaard: Gevoelige inhoud)', 'post.nsfw_warning': 'Gevoelige inhoud', 'post.nsfw_show': 'Tonen', 'post.share': 'Deel', 'post.share_copied': 'Link gekopieerd ✓',
    698698    'pedit.fan_only_label': 'Alleen voor ingelogde fans',
    699699    'pedit.schedule_label': 'Publicatie inplannen',
     
    16111611    'pedit.pin_top': 'at the top',
    16121612    'pedit.pin_nth_suffix': 'th from top',
    1613     'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show',
     1613    'pedit.noindex_label': 'noindex (hide from search engines)', 'pedit.nsfw_label': 'NSFW / sensitive content', 'pedit.fedi_audio_label': 'Share audio openly on the fediverse (plays inline in apps; file downloadable)', 'pedit.nsfw_cw_ph': 'Warning text (optional, default: Sensitive content)', 'post.nsfw_warning': 'Sensitive content', 'post.nsfw_show': 'Show', 'post.share': 'Share', 'post.share_copied': 'Link copied ✓',
    16141614    'pedit.fan_only_label': 'Logged-in fans only',
    16151615    'pedit.schedule_label': 'Schedule publication',
     
    25272527    'pedit.pin_top': 'ganz oben',
    25282528    'pedit.pin_nth_suffix': '. von oben',
    2529     'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen',
     2529    'pedit.noindex_label': 'noindex (vor Suchmaschinen verbergen)', 'pedit.nsfw_label': 'NSFW / sensibler Inhalt', 'pedit.fedi_audio_label': 'Audio offen im Fediverse teilen (spielt inline in Apps; Datei herunterladbar)', 'pedit.nsfw_cw_ph': 'Warntext (optional, Standard: Sensibler Inhalt)', 'post.nsfw_warning': 'Sensibler Inhalt', 'post.nsfw_show': 'Anzeigen', 'post.share': 'Teilen', 'post.share_copied': 'Link kopiert ✓',
    25302530    'pedit.fan_only_label': 'Nur für angemeldete Fans',
    25312531    'pedit.schedule_label': 'Veröffentlichung planen',
  • src/views/pages/post.ejs

    r28bf7f0 r28f797d  
    5656    </div>
    5757  <% } %>
     58
     59  <div class="post-share" style="margin:1.25rem 0;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap">
     60    <button type="button" class="btn post-share-btn" data-share data-share-title="<%= post.title %>">🔗 <%= t('post.share') %></button>
     61    <span class="post-share-feedback" id="post-share-feedback" style="color:var(--accent);font-size:.85rem" hidden><%= t('post.share_copied') %></span>
     62  </div>
    5863
    5964  <!-- Inline admin actions: only visible if user has permission -->
  • src/views/shell.ejs

    r28bf7f0 r28f797d  
    807807  // Misc click helpers (select-all in a field, history-back button).
    808808  document.addEventListener('click', function (e) {
    809     var el = e.target.closest && e.target.closest('[data-selectall],[data-back]');
     809    var el = e.target.closest && e.target.closest('[data-selectall],[data-back],[data-share]');
    810810    if (!el) return;
    811811    if (el.dataset.selectall !== undefined && el.select) el.select();
    812812    if (el.dataset.back !== undefined) { e.preventDefault(); history.back(); }
     813    if (el.dataset.share !== undefined) {
     814      e.preventDefault();
     815      var url = location.href, title = el.dataset.shareTitle || document.title;
     816      if (navigator.share) { navigator.share({ title: title, url: url }).catch(function () {}); }
     817      else if (navigator.clipboard && navigator.clipboard.writeText) {
     818        navigator.clipboard.writeText(url).then(function () {
     819          var fb = document.getElementById('post-share-feedback');
     820          if (fb) { fb.hidden = false; setTimeout(function () { fb.hidden = true; }, 2000); }
     821        }).catch(function () { window.prompt('Copy link:', url); });
     822      } else { window.prompt('Copy link:', url); }
     823    }
    813824  });
    814825  // Image fallback (the error event doesn't bubble → capture phase).
Note: See TracChangeset for help on using the changeset viewer.