Changeset 37a297d in Klonkt for src/views/pages/news.ejs


Ignore:
Timestamp:
06/28/2026 10:12:34 AM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c6cdce6
Parents:
2e62848
Message:

feat(news): quick 'interact with a fediverse post' button (paste a URL)

  • views/pages/news.ejs — a small [+] button in the News header opens a modal to paste a fediverse post URL (auto-filled from the clipboard when available) and goes to /authorize_interaction?uri=… so you can reply/like/boost any fediverse post via your own site.
  • services/i18n.js — tl.paste_ph + tl.paste_go (nl/en/de).
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/news.ejs

    r2e62848 r37a297d  
    11<div class="tl-wrap">
    22  <%- include('../partials/fedi-tabs', { active: 'feed' }) %>
    3   <h1 class="tl-title"><%= t('tl.title') %></h1>
     3  <div class="tl-head">
     4    <h1 class="tl-title"><%= t('tl.title') %></h1>
     5    <button type="button" id="tl-paste-btn" class="tl-paste-btn" title="<%= t('fedi.remote_interact') %>" aria-label="<%= t('fedi.remote_interact') %>">
     6      <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
     7    </button>
     8  </div>
    49  <p class="tl-lead"><%= t('tl.lead') %></p>
     10
     11  <div class="tl-paste" id="tl-paste">
     12    <div class="tl-paste-card" role="dialog" aria-label="<%= t('fedi.remote_interact') %>">
     13      <button type="button" class="tl-paste-x" aria-label="<%= t('fedi.cancel') %>">&times;</button>
     14      <h3 class="tl-paste-title"><%= t('fedi.remote_interact') %></h3>
     15      <input type="url" class="tl-paste-input" inputmode="url" autocomplete="off" autocapitalize="none" spellcheck="false" placeholder="<%= t('tl.paste_ph') %>">
     16      <div class="tl-paste-actions">
     17        <button type="button" class="tl-paste-cancel"><%= t('fedi.cancel') %></button>
     18        <button type="button" class="tl-paste-go"><%= t('tl.paste_go') %></button>
     19      </div>
     20    </div>
     21  </div>
     22  <style>
     23    .tl-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
     24    .tl-paste-btn { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
     25      border: 1px solid var(--rule); border-radius: 999px; background: var(--paper-2); color: var(--ink-soft); cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
     26    .tl-paste-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
     27    .tl-paste { position: fixed; inset: 0; z-index: 2147483601; display: none; align-items: center; justify-content: center; padding: 1rem; background: rgba(0,0,0,.55); }
     28    .tl-paste.is-open { display: flex; }
     29    .tl-paste-card { position: relative; width: 100%; max-width: 460px; background: var(--paper); color: var(--ink); border: 1px solid var(--rule);
     30      border-radius: 16px; padding: 1.4rem 1.4rem 1.2rem; box-shadow: 0 18px 50px rgba(0,0,0,.4); }
     31    .tl-paste-x { position: absolute; top: .5rem; right: .6rem; width: 2rem; height: 2rem; border: 0; border-radius: 999px; background: transparent; color: var(--ink-soft); font-size: 1.4rem; line-height: 1; cursor: pointer; }
     32    .tl-paste-title { margin: 0 0 .8rem; font-size: 1.1rem; }
     33    .tl-paste-input { width: 100%; box-sizing: border-box; padding: .6rem .8rem; border: 1.5px solid var(--rule); border-radius: 10px; background: var(--paper-2); color: var(--ink); font: inherit; font-size: .95rem; }
     34    .tl-paste-input:focus { outline: none; border-color: var(--accent); }
     35    .tl-paste-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1rem; }
     36    .tl-paste-actions button { padding: .5rem 1.1rem; border-radius: 999px; font: inherit; font-weight: 600; font-size: .88rem; cursor: pointer; border: 0; }
     37    .tl-paste-cancel { background: transparent; border: 1.5px solid var(--rule); color: var(--ink); }
     38    .tl-paste-go { background: var(--accent); color: var(--paper); }
     39  </style>
     40  <script>
     41  (function () {
     42    if (window.__tlPasteWired) return; window.__tlPasteWired = true;
     43    function modal() { return document.getElementById('tl-paste'); }
     44    function openM() {
     45      var m = modal(); if (!m) return;
     46      var inp = m.querySelector('.tl-paste-input');
     47      m.classList.add('is-open');
     48      if (inp) {
     49        inp.value = ''; inp.focus();
     50        if (navigator.clipboard && navigator.clipboard.readText) {
     51          navigator.clipboard.readText().then(function (t) {
     52            t = (t || '').trim();
     53            if (/^https?:\/\//i.test(t) && !inp.value) { inp.value = t; inp.select(); }
     54          }).catch(function () {});
     55        }
     56      }
     57    }
     58    function closeM() { var m = modal(); if (m) m.classList.remove('is-open'); }
     59    function go() {
     60      var m = modal(); if (!m) return;
     61      var inp = m.querySelector('.tl-paste-input'), v = (inp ? inp.value : '').trim();
     62      if (!/^https?:\/\//i.test(v)) { if (inp) inp.focus(); return; }
     63      location.href = '/authorize_interaction?uri=' + encodeURIComponent(v);
     64    }
     65    document.addEventListener('click', function (e) {
     66      if (e.target.closest && e.target.closest('#tl-paste-btn')) { e.preventDefault(); openM(); return; }
     67      if (e.target.closest && e.target.closest('.tl-paste-go')) { e.preventDefault(); go(); return; }
     68      if (e.target.closest && (e.target.closest('.tl-paste-cancel') || e.target.closest('.tl-paste-x'))) { e.preventDefault(); closeM(); return; }
     69      var op = document.querySelector('.tl-paste.is-open'); if (op && e.target === op) closeM();
     70    });
     71    document.addEventListener('keydown', function (e) {
     72      var m = document.querySelector('.tl-paste.is-open'); if (!m) return;
     73      if (e.key === 'Escape') closeM();
     74      else if (e.key === 'Enter' && e.target.closest && e.target.closest('.tl-paste')) { e.preventDefault(); go(); }
     75    });
     76  })();
     77  </script>
    578  <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %>
    679  <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error %></div><% } %>
Note: See TracChangeset for help on using the changeset viewer.