source: Klonkt/src/views/partials/post-tile.ejs@ 6dd26e5

main
Last change on this file since 6dd26e5 was 6dd26e5, checked in by Robin <roboburr@…>, 6 weeks ago

De audio-tegel opgevrolijkt: golfvorm als thumbnail en een echte titel

Een audiopost uit Shaer was op Klonkt een kale gradient met (untitled)
(Robins schermafdruk, 30-7). Twee dingen tegelijk gefixt.

De thumbnail: de upload-leg tekent bij audio nu een golfvorm met de
gebundelde ffmpeg (showwavespic, wit op transparant) naar
<naam>.poster.png, precies zoals video zijn stilstaand beeld krijgt. Die
golfvorm rijdt mee als data-poster op de audio-tag (voor de tegels), in
c2s_attachments en als AS2-icon op het gefedereerde Audio-attachment. De
tegel toont hem OVER de eigen hue-gradient, dus elke audiopost houdt
zijn kleur en krijgt zijn vorm.

De titel: C2S-posts hebben geen titel, dus elke Shaer-post toonde
(untitled). Tegels en kaarten leiden de titel nu af: eerst de eigen
woorden van de post (afgekapt op 64), anders een vriendelijk typelabel
(muzieknoot audio, pijl video), en pas als er echt niets is (untitled).
Een tegel die zijn beeld al toont forceert geen label meer.

Changed files:
src/routes/activitypub.js

  • uploadMedia: audio/* -> showwavespic-golfvorm naar .poster.png (best-effort, zelfde patroon als de videoposter)

src/services/ActivityPubService.js

  • c2sCreatePost: posterExt per type (video .jpg, audio .png); de audio-tag krijgt data-poster

src/views/partials/post-tile.ejs

  • audio-detectie + golfvorm over de gradient; titelcascade (titel -> tekst -> typelabel)

src/views/partials/post-card.ejs

  • zelfde spiegel: golfvorm als cover met eigen accent-gradient, zelfde titelcascade

src/assets/css/style.css

  • .grid-tile-wave: contain, padding, transparant op de gradient

test/c2s-compose.test.js

  • audio-golfvormketen: data-poster op de tag, poster in de store, icon op het gefedereerde attachment

remarks: bestaande audioposts kregen hun upload voor deze code en hebben
dus nog geen golfvorm-bestand; die tonen nu wel het muzieknoot-label. Een
verse audiopost laat het hele effect zien. ffmpeg-commando en beide
templates hier lokaal gesmoke-test (RGBA-png, render ok), 345 tests
groen.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

  • Property mode set to 100644
File size: 5.1 KB
RevLine 
[7bc636b]1<%
2// Square Instagram-style tile for the .feed-grid view (v9 pattern).
3// - With cover → background-image, dark gradient overlay reveals title on hover/touch
4// - No cover → .grid-tile-gradient with the title centered (random hue per tile)
5const _tileHue = ((post.id || post.slug || '0').charCodeAt(0) * 7) % 360;
6const _isPinned = !!post.pinned;
[c7c7f02]7const _isBoost = !!post.isBoost;
[5f3f9ec]8const _hasCover = !!post.cover_image_url || !!post.cover_video_url;
[2a10445]9// A C2S post carries its media IN the content (a cover next to it showed the
10// video twice on the post page). The tile derives its picture from the
11// content instead, like the Cirkel card: first <video> (with its poster) or
12// first <img>.
[6dd26e5]13let _cVideo = null, _cPoster = null, _cImg = null, _cAudio = false, _cWave = null;
[2a10445]14if (!_hasCover && post.content) {
15 const vm = String(post.content).match(/<video[^>]*\ssrc="([^"]+)"[^>]*>/i);
16 if (vm) { _cVideo = vm[1]; const pm = vm[0].match(/poster="([^"]+)"/i); if (pm) _cPoster = pm[1]; }
[6dd26e5]17 else {
18 const im = String(post.content).match(/<img[^>]*\ssrc="([^"]+)"/i);
19 if (im) _cImg = im[1];
20 else {
21 // An audio post (Robins vraag, 30-7): the waveform the upload leg drew
22 // (data-poster) goes OVER the gradient, so the tile keeps its own hue.
23 const am = String(post.content).match(/<audio[^>]*>/i);
24 if (am) { _cAudio = true; const wm = am[0].match(/data-poster="([^"]+)"/i); if (wm) _cWave = wm[1]; }
25 }
26 }
[2a10445]27}
[6dd26e5]28const _showCover = _hasCover || !!_cVideo || !!_cImg; // the wave keeps the gradient
29// A C2S post has no title: fall back to the post's own words, and for a
30// media-only post to a friendly type label instead of "(untitled)". A tile
31// that already shows its picture needs no forced label at all.
32let _title = post.title || '';
33if (!_title && post.content) {
34 const _txt = String(post.content).replace(/<[^>]*>/g, ' ').replace(/&[a-zA-Z#0-9]+;/g, ' ').replace(/\s+/g, ' ').trim();
35 if (_txt) _title = _txt.length > 64 ? _txt.slice(0, 63).replace(/\s+\S*$/, '') + '…' : _txt;
36}
37if (!_title) _title = _cAudio ? '♪ audio' : (_cVideo || _cImg) ? '' : '(untitled)';
[7bc636b]38const _typeLabel = (post.type && post.type !== 'post') ? post.type : '';
[1f0c85b]39const _src = post.source_name || ''; // bron-site (cirkel-feed: van welke site komt deze post)
[7963a6e]40const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
[43dbf9c]41const _external = post.external_url || null;
42const _href = _external || (_base + '/' + post.slug);
43const _ext = !!_external;
[7bc636b]44%>
[2a10445]45<a class="grid-tile<%= _showCover ? '' : ' grid-tile-gradient' %><%= (_isPinned || _isBoost) ? ' is-pinned' : '' %><%= post.nsfw ? ' nsfw-media' : '' %>"
[7963a6e]46 href="<%= _href %>"
[2eca083]47 style="--tile-hue: <%= _tileHue %>;"
[43dbf9c]48 <% if (_ext) { %>target="_blank" rel="noopener"<% } else { %>hx-get="<%= _href %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" hx-push-url="<%= _href %>" hx-indicator="#pcms-loading"<% } %>>
[7bc636b]49
[6dd26e5]50 <% if (post.cover_image_url) { %><img class="grid-tile-img" src="<%= thumb(post.cover_image_url, 480) %>" alt="" loading="lazy" decoding="async"<% if (post.cover_video_url) { %> data-ios-mp4="<%= post.cover_video_url %>"<% } %>><% } else if (post.cover_video_url) { %><video class="grid-tile-img" src="<%= post.cover_video_url %>" poster="<%= thumb(post.cover_video_url, 480) %>" autoplay loop muted playsinline></video><% } else if (_cVideo) { %><video class="grid-tile-img" src="<%= _cVideo %>"<% if (_cPoster) { %> poster="<%= _cPoster %>"<% } %> autoplay loop muted playsinline preload="metadata"></video><% } else if (_cImg) { %><img class="grid-tile-img" src="<%= _cImg %>" alt="" loading="lazy" decoding="async"><% } else if (_cWave) { %><img class="grid-tile-img grid-tile-wave" src="<%= _cWave %>" alt="" loading="lazy" decoding="async"><% } %>
[2eca083]51
[7bc636b]52 <% if (_typeLabel) { %>
53 <span class="grid-tile-type"><%= _typeLabel %></span>
54 <% } %>
[c7c7f02]55 <% if (_isBoost) { %>
56 <span class="grid-tile-pin grid-tile-boost"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m17 2 4 4-4 4"/><path d="M3 11v-1a4 4 0 0 1 4-4h14"/><path d="m7 22-4-4 4-4"/><path d="M21 13v1a4 4 0 0 1-4 4H3"/></svg>Boost</span>
57 <% } else if (_isPinned) { %>
[6bee3ec]58 <span class="grid-tile-pin"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></svg>Pinned</span>
[7bc636b]59 <% } %>
60
61 <% if (_hasCover) { %>
62 <div class="grid-tile-overlay">
[6dd26e5]63 <strong><%= _title %></strong>
[1f0c85b]64 <% if (_src) { %><span class="grid-tile-overlay-src">van <%= _src %></span><% } %>
[7bc636b]65 </div>
66 <% } else { %>
[6dd26e5]67 <span class="grid-tile-title"><%= _title %></span>
[1f0c85b]68 <% if (_src) { %><span class="grid-tile-gradient-src">van <%= _src %></span><% } %>
[7bc636b]69 <% } %>
[476d6e7]70 <% if (post.nsfw) { %><div class="nsfw-veil"><%- include('nsfw-veil', { cw: post.content_warning }) %></div><% } %>
[7bc636b]71</a>
Note: See TracBrowser for help on using the repository browser.