Files
indiekit-endpoint-microsub/views/feeds.njk
Ricardo 5037ff3d8f feat: add feed type indicator and cross-channel duplicate detection
- Store feedType (rss/atom/jsonfeed/hfeed) on feed documents during polling
- Display feed type badge in reader feeds view
- Detect duplicate feeds across all channels using URL normalization
  (trailing slashes, http/https variants)
- Show clear error message when subscribing to a feed that already exists
- Handle duplicates in both reader UI and Microsub API (HTTP 409)
- Bump version to 1.0.44

Confab-Link: http://localhost:8080/sessions/f1d9ff88-e037-4d6e-b595-ed6d9e00898e
2026-03-10 14:27:01 +01:00

111 lines
4.1 KiB
Plaintext

{% extends "layouts/reader.njk" %}
{% block reader %}
<div class="feeds">
<header class="feeds__header">
<a href="{{ baseUrl }}/channels/{{ channel.uid }}" class="back-link">
{{ icon("previous") }} {{ channel.name }}
</a>
</header>
<h2>{{ __("microsub.feeds.title") }}</h2>
{% if error %}
<div class="notice notice--error" role="alert">
{{ error }}
</div>
{% endif %}
{% if feeds.length > 0 %}
<div class="feeds__list">
{% for feed in feeds %}
<div class="feeds__item{% if feed.status == 'error' %} feeds__item--error{% endif %}">
<div class="feeds__info">
{% if feed.photo %}
<img src="{{ feed.photo }}"
alt=""
class="feeds__photo"
width="48"
height="48"
loading="lazy"
onerror="this.style.display='none'">
{% endif %}
<div class="feeds__details">
<span class="feeds__name">
{{ feed.title or feed.url }}
{% if feed.feedType %}
<span class="badge badge--offset badge--small" title="Feed format">{{ feed.feedType | upper }}</span>
{% endif %}
{% if feed.status == 'error' %}
<span class="badge badge--red">Error</span>
{% elif feed.status == 'active' %}
<span class="badge badge--green">Active</span>
{% endif %}
</span>
<a href="{{ feed.url }}" class="feeds__url" target="_blank" rel="noopener">
{{ feed.url | replace("https://", "") | replace("http://", "") }}
</a>
{% if feed.lastError %}
<span class="feeds__error">{{ feed.lastError }}</span>
{% endif %}
{% if feed.consecutiveErrors > 0 %}
<span class="feeds__error-count">{{ feed.consecutiveErrors }} consecutive errors</span>
{% endif %}
{% if feed.lastSuccessAt %}
<span class="feeds__meta">Last success: {{ feed.lastSuccessAt }}</span>
{% endif %}
</div>
</div>
<div class="feeds__actions">
<a href="{{ baseUrl }}/channels/{{ channel.uid }}/feeds/{{ feed._id }}/edit"
class="button button--secondary button--small"
title="Edit feed URL">
{{ icon("updatePost") }}
</a>
<form method="post" action="{{ baseUrl }}/channels/{{ channel.uid }}/feeds/{{ feed._id }}/rediscover" style="display:inline;">
<button type="submit" class="button button--secondary button--small" title="Rediscover feed">
{{ icon("syndicate") }}
</button>
</form>
<form method="post" action="{{ baseUrl }}/channels/{{ channel.uid }}/feeds/{{ feed._id }}/refresh" style="display:inline;">
<button type="submit" class="button button--secondary button--small" title="Refresh now">
{{ icon("repost") }}
</button>
</form>
<form method="post" action="{{ baseUrl }}/channels/{{ channel.uid }}/feeds/remove" style="display:inline;">
<input type="hidden" name="url" value="{{ feed.url }}">
<button type="submit" class="button button--warning button--small" title="Unfollow">
{{ icon("delete") }}
</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="reader__empty">
{{ icon("syndicate") }}
<p>{{ __("microsub.feeds.empty") }}</p>
</div>
{% endif %}
<div class="feeds__add">
<h3>{{ __("microsub.feeds.follow") }}</h3>
<form method="post" action="{{ baseUrl }}/channels/{{ channel.uid }}/feeds" class="feeds__form">
{{ input({
id: "url",
name: "url",
label: __("microsub.feeds.url"),
type: "url",
required: true,
placeholder: __("microsub.feeds.urlPlaceholder"),
autocomplete: "off"
}) }}
<div class="button-group">
{{ button({ text: __("microsub.feeds.follow") }) }}
</div>
</form>
</div>
</div>
{% endblock %}