Я уже писал про вебменшены, а сегодня хочу поделиться небольшой штукой, которую написал на Svelte: простая рендерилка веб-меншенов, которые она достаёт с Webmention.io, куда я делегирую свои веб-меншены.
Webmention.io даёт довольно простой API, где можно получить в виде JSON
веб-меншены для переданного URL, и внутри этого JSON есть информация про
автора, иногда картинка, и тип события. Я пока обрабатываю только like-of
,
repost-of
и in-reply-to
.
Но если вглядеться в скриншот, то можно заметить, что это лайки из Твиттера, а не с инди-сайтов. Продолжая тему прагматизма, я не отказываюсь от общения с людьми, которые пока не завели себе свои сайты, но мне хочется делать это с моего сайта. Bridgy делает за меня запросы в API Твиттера и отправляет веб-меншены.
Мне кажется, что проще всего будет объяснить свой компонент в стиле литературного программирования.
В целом, Svelte-компоненты состоят из скриптов, стилей и разметки. Скрипт заворачивается
в <script>
, стили — в <style>
, ну а разметку и заворачивать не надо.
<script>let likes = []
let replies = []
let reposts = []
let other = []
let loc = document.location.href;
let mentionsRequest = fetch('https://webmention.io/api/mentions.jf2?target=' + loc, {
headers: { accept: 'appplication/json' }
})
.then(r => r.json())
.then(mention_feed => {
if (mention_feed && mention_feed.children) {
mention_feed.children.forEach(mention => {
switch (mention['wm-property']) {
case 'like-of': likes.push(mention); break;
case 'repost-of': reposts.push(mention); break;
case 'in-reply-to': replies.push(mention); break;
default: other.push(mention); break;
}
})
}
likes = likes
reposts = reposts
replies = replies
other = other
})
</script>
Я запрашиваю все вебменшены и раскладываю их по четырёх категориям, в зависимости от wm-source
: in-reply-to
, repost-of
, like-of
, прочее.
Стили сами по себе не очень интересные, поэтому я их скипну (если любопытно, то в полном исходнике их легко увидеть).
В Svelte добавляет в HTML немного деклараций, например {\#await promise}контент{/await}
будет отрисовывать контент
,
пока промис не зарезолвится. Выше в скрипте я сохранил запрос к API в mentionsReq
, поэтому могу отрисовать
плейсхолдер, пока ждём ответа от API.
{#await mentionsRequest}
<p>Подгружаю вебменшены...</p>
{/await}
Чтобы отрисовать ответы, я прохожусь по массиву replies
и рисую каждый ответ. {@html reply.content.html}
отрисует «сырой» HTML, который пришёл из API.
Так как ответ написал не я, то я завернул это всё в <blockquote>
, и в футере ставлю ссылку на оригинал.
{#if replies.length}
<section>
{#each replies as reply, i}
<article class="reply">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-message-square"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
{#if reply.content && reply.content.html}
<blockquote class="reply">
{@html reply.content.html}
<footer class="source">
<a class="author" href="{reply.author.url}">
<img class="avatar" src="{reply.author.photo}" alt=""> {reply.author.name}</a>
<a class="link" href="{reply['wm-source']}">{reply['published']}</a>
</footer>
</blockquote>
{:else}
<a href="{reply['wm-source']}">{reply['wm-source']}</a>
{/if}
</article>
{/each}
</section>
{:else}
<p>Пока ответов нет. Если напишешь ответ — пришли <a rel="nofollow" href="https://indieweb.org/Webmention">вебменшен</a>!</p>
{/if}
С лайками и репостами ещё проще — я решил рисовать только аватарку и ссылку на того, кто лайкнул.
{#if likes.length}
<section>
<svg aria-label="лайки" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-heart"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>
<ul class="likes">
{#each likes as like}
<li class="like"><a title="{like.author.name}" href="{like.author.url}"><img alt="{like.author.name}" class="avatar" src="{like.author.photo}"></a></li>
{/each}
</ul>
</section>
{/if}
{#if reposts.length}
<section>
<svg aria-label="репосты" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-repeat"><polyline points="17 1 21 5 17 9"></polyline><path d="M3 11V9a4 4 0 0 1 4-4h14"></path><polyline points="7 23 3 19 7 15"></polyline><path d="M21 13v2a4 4 0 0 1-4 4H3"></path></svg>
<ul class="reposts">
{#each reposts as repost}
<li class="repost"><a title="{repost.author.name}" href="{repost.author.url}"><img alt="{repost.author.name}" class="avatar" src="{repost.author.photo}"></a></li>
{/each}
</ul>
</section>
{/if}
И, наконец, корзина «остальное» — просто рисую ссылками.
{#if other.length}
<section>
<p>Вебменшены, которые не лайки-репосты и не ответы:</p>
<ul>
{#each other as mention}
<li><a href="{mention['wm-source']}">{mention['wm-source']}</a></li>
{/each}
</ul>
</section>
{/if}
Svelte добавляет немного себя в HTML, но мне кажется, что это всё ещё читается почти как HTML. SVG-иконки взяты из проекта Feather Icons.
Компонент я скомпилировал в ES6-модуль, а использование выглядит примерно так:
import Webmentions from '/webmentions.mjs';
new Webmentions({
target: document.querySelector('#webmentions')
})
В принципе, из этого можно сделать рендеринг на стороне сервера, но в таком, не-server-side-виде люди увидят самые свежие упоминания на момент загрузки страницы, а не упоминания-на-момент-сборки-сайта: такое решение подойдёт даже статическим сайтам. Но получается, что вебменшены видны только тем людям, у которых включён джаваскрипт (и в случае, если я ничего не сломал).
В целом, я довольно много за эти дни написал о том, как публиковать, но есть и обратная сторона — как читать. У сообщества есть несколько наработок на эту тему, про которые расскажу в ближайшие дни (спойлеры: RSS/Atom, JSONFeed, WebSub/PuSH, h-feeds, Microsub).
Момент, который стоит учитывать, чтобы получилось отправить свой вебменшен: у записи-ответа
должен быть свой URL. Вообще у всех записей должен быть свой URL.
Это необязательно должна быть отдельная страница, это может быть ссылка-фрагмент (вида example.com/#some-identifier
), главное,
чтобы по этой ссылке можно было достать обратно h-entry
. Проверить, что пост размечен понятным для машин способом можно на IndieWebify.me.
И теперь, когда мой сайт показывает веб-меншены, можно отправить свой — и, если всё ок, через минуту-две увидеть его здесь.
← День 8: логин · День 10: ридер →