Changeset f1ce47a in Klonkt for src/views/partials


Ignore:
Timestamp:
06/27/2026 10:45:01 AM (2 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
ce16d8a
Parents:
7eddb3b
Message:

fix(profile): replace native prompt() with a styled follow modal

  • views/partials/profile-header.ejs — clicking "follow via the fediverse" opened the ugly browser prompt(); now opens an in-app .pf-follow modal (server input + suggestions datalist + remembered server via localStorage; Enter/Esc/backdrop) that bounces to authorize_interaction. + CSS.
  • services/i18n.js — fedi.cancel (NL/EN/DE).
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/partials/profile-header.ejs

    r7eddb3b rf1ce47a  
    144144</div>
    145145<% } %>
     146<% if (_showFollow) { %>
     147<%# Follow-via-fediverse modal — replaces the native prompt() (which looked ugly). %>
     148<div class="pf-follow">
     149  <div class="pf-follow-card" role="dialog" aria-label="<%= t('fedi.follow_heading') %>">
     150    <button type="button" class="pf-follow-cancel pf-follow-x" aria-label="Sluiten">&times;</button>
     151    <h3 class="pf-follow-title"><%= t('fedi.follow_heading') %></h3>
     152    <p class="pf-follow-sub"><%= t('fedi.remote_prompt') %></p>
     153    <input type="text" class="pf-follow-input" inputmode="url" autocomplete="off" autocapitalize="none" spellcheck="false"
     154           placeholder="<%= t('fedi.remote_ph') %>" list="pf-follow-servers">
     155    <datalist id="pf-follow-servers"><option value="mastodon.social"></option><option value="mastodon.online"></option><option value="mas.to"></option><option value="fosstodon.org"></option><option value="hachyderm.io"></option></datalist>
     156    <div class="pf-follow-actions">
     157      <button type="button" class="pf-follow-cancel"><%= t('fedi.cancel') %></button>
     158      <button type="button" class="pf-follow-go"><%= t('fedi.follow_btn') %></button>
     159    </div>
     160  </div>
     161</div>
     162<% } %>
    146163<script>
    147164(function () {
    148165  if (window.__pcmsFediFollowWired) return;
    149166  window.__pcmsFediFollowWired = true;
     167  function modal() { return document.querySelector('.pf-follow'); }
     168  function closeM() { var m = modal(); if (m) m.classList.remove('is-open'); }
     169  function go() {
     170    var m = modal(); if (!m) return;
     171    var inp = m.querySelector('.pf-follow-input'), raw = inp ? inp.value : '';
     172    // Strip a full @user@host handle (and any scheme/path) down to the server host.
     173    var server = String(raw || '').trim().replace(/^@?[^@\s]*@/, '').replace(/^https?:\/\//i, '').replace(/\/.*$/, '').trim();
     174    if (!server) { if (inp) inp.focus(); return; }
     175    try { localStorage.setItem('pcmsFediServer', server); } catch (e) {}
     176    var actor = location.origin + '/ap/users/' + encodeURIComponent(m.getAttribute('data-actor-slug') || '');
     177    location.href = 'https://' + server + '/authorize_interaction?uri=' + encodeURIComponent(actor);
     178  }
    150179  document.addEventListener('click', function (e) {
    151180    var b = e.target.closest && e.target.closest('.profile-fedi-link');
    152     if (!b) return;
    153     var raw = window.prompt(<%- JSON.stringify(t('fedi.remote_prompt')) %>, '');
    154     if (!raw) return;
    155     // Strip a full @user@host handle (and any scheme/path) down to the server host.
    156     var server = raw.trim().replace(/^@?[^@\s]*@/, '').replace(/^https?:\/\//i, '').replace(/\/.*$/, '').trim();
    157     if (!server) return;
    158     var actor = location.origin + '/ap/users/' + encodeURIComponent(b.getAttribute('data-fedi-actor-slug') || '');
    159     location.href = 'https://' + server + '/authorize_interaction?uri=' + encodeURIComponent(actor);
     181    if (b) { e.preventDefault();
     182      var m = modal(); if (!m) return;
     183      m.setAttribute('data-actor-slug', b.getAttribute('data-fedi-actor-slug') || '');
     184      var inp = m.querySelector('.pf-follow-input');
     185      try { if (inp && !inp.value) inp.value = localStorage.getItem('pcmsFediServer') || ''; } catch (e2) {}
     186      m.classList.add('is-open');
     187      setTimeout(function () { if (inp) inp.focus(); }, 30);
     188      return;
     189    }
     190    if (e.target.closest && e.target.closest('.pf-follow-go')) { e.preventDefault(); go(); return; }
     191    if (e.target.closest && e.target.closest('.pf-follow-cancel')) { e.preventDefault(); closeM(); return; }
     192    var open = document.querySelector('.pf-follow.is-open');
     193    if (open && e.target === open) closeM(); /* click on backdrop */
     194  });
     195  document.addEventListener('keydown', function (e) {
     196    var m = document.querySelector('.pf-follow.is-open'); if (!m) return;
     197    if (e.key === 'Escape') { closeM(); }
     198    else if (e.key === 'Enter' && e.target.closest && e.target.closest('.pf-follow')) { e.preventDefault(); go(); }
    160199  });
    161200})();
     
    384423.pf-summary-follow:hover { background: var(--accent); color: var(--paper); }
    385424.pf-summary-follow-ico { width: 18px; height: 18px; flex: 0 0 auto; }
     425.pf-follow { position: fixed; inset: 0; z-index: 2147483601; display: none; align-items: center; justify-content: center; padding: 1rem; background: rgba(0,0,0,.55); }
     426.pf-follow.is-open { display: flex; }
     427.pf-follow-card { position: relative; width: 100%; max-width: 380px; text-align: left;
     428  background: var(--paper); color: var(--ink); border: 1px solid var(--rule); border-radius: 16px;
     429  padding: 1.5rem 1.4rem 1.3rem; box-shadow: 0 18px 50px rgba(0,0,0,.4); }
     430.pf-follow-x { position: absolute; top: .55rem; right: .65rem; width: 2rem; height: 2rem; border: 0; border-radius: 999px;
     431  background: transparent; color: var(--ink-soft); font-size: 1.4rem; line-height: 1; cursor: pointer; }
     432.pf-follow-title { margin: 0 0 .35rem; font-size: 1.15rem; }
     433.pf-follow-sub { margin: 0 0 .8rem; color: var(--ink-soft); font-size: .86rem; line-height: 1.45; }
     434.pf-follow-input { width: 100%; box-sizing: border-box; padding: .6rem .8rem; border: 1.5px solid var(--rule);
     435  border-radius: 10px; background: var(--paper-2); color: var(--ink); font: inherit; font-size: .95rem; }
     436.pf-follow-input:focus { outline: none; border-color: var(--accent); }
     437.pf-follow-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1rem; }
     438.pf-follow-actions button { padding: .5rem 1.1rem; border-radius: 999px; font: inherit; font-weight: 600; font-size: .88rem; cursor: pointer; border: 0; }
     439.pf-follow-actions .pf-follow-cancel { background: transparent; border: 1.5px solid var(--rule); color: var(--ink); }
     440.pf-follow-actions .pf-follow-go { background: var(--accent); color: var(--paper); }
     441.pf-follow-actions .pf-follow-go:hover { filter: brightness(1.06); }
    386442</style>
Note: See TracChangeset for help on using the changeset viewer.