@rmdes/indiekit-endpoint-activitypub 2.0.26 → 2.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,23 @@
4
4
  {% from "prose/macro.njk" import prose with context %}
5
5
 
6
6
  {% block readercontent %}
7
+ {# Explore link #}
8
+ <div class="ap-reader-tools">
9
+ <a href="{{ mountPath }}/admin/reader/explore" class="ap-reader-tools__explore">
10
+ 🔭 {{ __("activitypub.reader.explore.title") }}
11
+ </a>
12
+ </div>
13
+
14
+ {# Followed tags #}
15
+ {% if followedTags and followedTags.length > 0 %}
16
+ <div class="ap-followed-tags">
17
+ <span class="ap-followed-tags__label">{{ __("activitypub.reader.tagTimeline.following") }}:</span>
18
+ {% for tag in followedTags %}
19
+ <a href="{{ mountPath }}/admin/reader/tag?tag={{ tag | urlencode }}" class="ap-card__tag">#{{ tag }}</a>
20
+ {% endfor %}
21
+ </div>
22
+ {% endif %}
23
+
7
24
  {# Fediverse lookup #}
8
25
  <form action="{{ mountPath }}/admin/reader/resolve" method="get" class="ap-lookup">
9
26
  <input type="text" name="q" class="ap-lookup__input"
@@ -36,15 +53,18 @@
36
53
 
37
54
  {# Timeline items #}
38
55
  {% if items.length > 0 %}
39
- <div class="ap-timeline" data-mount-path="{{ mountPath }}">
56
+ <div class="ap-timeline"
57
+ id="ap-timeline"
58
+ data-mount-path="{{ mountPath }}"
59
+ data-before="{{ before if before else '' }}">
40
60
  {% for item in items %}
41
61
  {% include "partials/ap-item-card.njk" %}
42
62
  {% endfor %}
43
63
  </div>
44
64
 
45
- {# Pagination #}
65
+ {# Pagination (progressive enhancement — visible without JS, hidden when Alpine active) #}
46
66
  {% if before or after %}
47
- <nav class="ap-pagination">
67
+ <nav class="ap-pagination ap-pagination--js-hidden" id="ap-reader-pagination">
48
68
  {% if after %}
49
69
  <a href="?tab={{ tab }}&after={{ after }}" class="ap-pagination__prev">
50
70
  {{ __("activitypub.reader.pagination.newer") }}
@@ -57,6 +77,25 @@
57
77
  {% endif %}
58
78
  </nav>
59
79
  {% endif %}
80
+
81
+ {# Infinite scroll load-more sentinel #}
82
+ {% if before %}
83
+ <div class="ap-load-more"
84
+ id="ap-load-more"
85
+ data-before="{{ before }}"
86
+ data-tab="{{ tab }}"
87
+ data-tag=""
88
+ x-data="apInfiniteScroll()"
89
+ x-init="init()"
90
+ @ap-append-items.window="appendItems($event.detail)">
91
+ <div class="ap-load-more__sentinel" x-ref="sentinel"></div>
92
+ <button class="ap-load-more__btn" @click="loadMore()" :disabled="loading" x-show="!done">
93
+ <span x-show="!loading">{{ __("activitypub.reader.pagination.loadMore") }}</span>
94
+ <span x-show="loading">{{ __("activitypub.reader.pagination.loading") }}</span>
95
+ </button>
96
+ <p class="ap-load-more__done" x-show="done" x-cloak>{{ __("activitypub.reader.pagination.noMore") }}</p>
97
+ </div>
98
+ {% endif %}
60
99
  {% else %}
61
100
  {{ prose({ text: __("activitypub.reader.empty") }) }}
62
101
  {% endif %}
@@ -0,0 +1,86 @@
1
+ {% extends "layouts/ap-reader.njk" %}
2
+
3
+ {% from "prose/macro.njk" import prose with context %}
4
+
5
+ {% block readercontent %}
6
+ {# Tag header #}
7
+ <header class="ap-tag-header">
8
+ <div class="ap-tag-header__info">
9
+ <h2 class="ap-tag-header__title">#{{ tag }}</h2>
10
+ <p class="ap-tag-header__count">
11
+ {{ __("activitypub.reader.tagTimeline.postsTagged", items.length) }}
12
+ </p>
13
+ </div>
14
+ <div class="ap-tag-header__actions">
15
+ {% if isFollowed %}
16
+ <form action="{{ mountPath }}/admin/reader/unfollow-tag" method="post" class="ap-tag-header__follow-form">
17
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
18
+ <input type="hidden" name="tag" value="{{ tag }}">
19
+ <button type="submit" class="ap-tag-header__unfollow-btn">
20
+ {{ __("activitypub.reader.tagTimeline.unfollowTag") }}
21
+ </button>
22
+ </form>
23
+ {% else %}
24
+ <form action="{{ mountPath }}/admin/reader/follow-tag" method="post" class="ap-tag-header__follow-form">
25
+ <input type="hidden" name="_csrf" value="{{ csrfToken }}">
26
+ <input type="hidden" name="tag" value="{{ tag }}">
27
+ <button type="submit" class="ap-tag-header__follow-btn">
28
+ {{ __("activitypub.reader.tagTimeline.followTag") }}
29
+ </button>
30
+ </form>
31
+ {% endif %}
32
+ <a href="{{ mountPath }}/admin/reader" class="ap-tag-header__back">
33
+ ← {{ __("activitypub.reader.title") }}
34
+ </a>
35
+ </div>
36
+ </header>
37
+
38
+ {# Timeline items #}
39
+ {% if items.length > 0 %}
40
+ <div class="ap-timeline"
41
+ data-mount-path="{{ mountPath }}"
42
+ data-tag="{{ tag }}"
43
+ data-before="{{ before if before else '' }}">
44
+ {% for item in items %}
45
+ {% include "partials/ap-item-card.njk" %}
46
+ {% endfor %}
47
+ </div>
48
+
49
+ {# Pagination (progressive enhancement fallback — hidden when infinite scroll JS active) #}
50
+ {% if before or after %}
51
+ <nav class="ap-pagination ap-pagination--js-hidden" id="ap-tag-pagination">
52
+ {% if after %}
53
+ <a href="?tag={{ tag }}&after={{ after }}" class="ap-pagination__prev">
54
+ {{ __("activitypub.reader.pagination.newer") }}
55
+ </a>
56
+ {% endif %}
57
+ {% if before %}
58
+ <a href="?tag={{ tag }}&before={{ before }}" class="ap-pagination__next">
59
+ {{ __("activitypub.reader.pagination.older") }}
60
+ </a>
61
+ {% endif %}
62
+ </nav>
63
+ {% endif %}
64
+
65
+ {# Infinite scroll sentinel (Task 5) #}
66
+ {% if before %}
67
+ <div class="ap-load-more"
68
+ id="ap-load-more"
69
+ data-before="{{ before }}"
70
+ data-tab=""
71
+ data-tag="{{ tag }}"
72
+ x-data="apInfiniteScroll()"
73
+ x-init="init()"
74
+ @ap-append-items.window="appendItems($event.detail)">
75
+ <div class="ap-load-more__sentinel" x-ref="sentinel"></div>
76
+ <button class="ap-load-more__btn" @click="loadMore()" :disabled="loading" x-show="!done">
77
+ <span x-show="!loading">{{ __("activitypub.reader.pagination.loadMore") }}</span>
78
+ <span x-show="loading">{{ __("activitypub.reader.pagination.loading") }}</span>
79
+ </button>
80
+ <p class="ap-load-more__done" x-show="done">{{ __("activitypub.reader.pagination.noMore") }}</p>
81
+ </div>
82
+ {% endif %}
83
+ {% else %}
84
+ {{ prose({ text: __("activitypub.reader.tagTimeline.noPosts", tag) }) }}
85
+ {% endif %}
86
+ {% endblock %}
@@ -1,7 +1,10 @@
1
1
  {% extends "document.njk" %}
2
2
 
3
3
  {% block content %}
4
- {# Alpine.js for client-side reactivity (CW toggles, interaction buttons) #}
4
+ {# Infinite scroll component must load before Alpine to register via alpine:init #}
5
+ <script defer src="/assets/@rmdes-indiekit-endpoint-activitypub/reader-infinite-scroll.js"></script>
6
+
7
+ {# Alpine.js for client-side reactivity (CW toggles, interaction buttons, infinite scroll) #}
5
8
  <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js"></script>
6
9
 
7
10
  {# Reader stylesheet — loaded in body is fine for modern browsers #}
@@ -113,12 +113,27 @@
113
113
  {% include "partials/ap-item-media.njk" %}
114
114
  {% endif %}
115
115
 
116
- {# Tags/categories #}
117
- {% if item.category and item.category.length > 0 %}
116
+ {# Mentions and hashtags #}
117
+ {% set hasMentions = item.mentions and item.mentions.length > 0 %}
118
+ {% set hasHashtags = item.category and item.category.length > 0 %}
119
+ {% if hasMentions or hasHashtags %}
118
120
  <div class="ap-card__tags">
119
- {% for tag in item.category %}
120
- <a href="?tag={{ tag }}" class="ap-card__tag">#{{ tag }}</a>
121
- {% endfor %}
121
+ {# Mentions render with @ prefix, link to profile view when URL available #}
122
+ {% if hasMentions %}
123
+ {% for mention in item.mentions %}
124
+ {% if mention.url %}
125
+ <a href="{{ mountPath }}/admin/reader/profile?url={{ mention.url | urlencode }}" class="ap-card__mention">@{{ mention.name }}</a>
126
+ {% else %}
127
+ <span class="ap-card__mention ap-card__mention--legacy">@{{ mention.name }}</span>
128
+ {% endif %}
129
+ {% endfor %}
130
+ {% endif %}
131
+ {# Hashtags — render with # prefix, link to tag timeline #}
132
+ {% if hasHashtags %}
133
+ {% for tag in item.category %}
134
+ <a href="{{ mountPath }}/admin/reader/tag?tag={{ tag | urlencode }}" class="ap-card__tag">#{{ tag }}</a>
135
+ {% endfor %}
136
+ {% endif %}
122
137
  </div>
123
138
  {% endif %}
124
139