@rmdes/indiekit-endpoint-microsub 1.0.9 → 1.0.10
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.
- package/lib/controllers/reader.js +15 -1
- package/lib/storage/items.js +24 -0
- package/locales/en.json +3 -0
- package/package.json +1 -1
- package/views/channel.njk +19 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getTimelineItems,
|
|
22
22
|
getItemById,
|
|
23
23
|
markItemsRead,
|
|
24
|
+
countReadItems,
|
|
24
25
|
} from "../storage/items.js";
|
|
25
26
|
import { getUserId } from "../utils/auth.js";
|
|
26
27
|
import {
|
|
@@ -95,24 +96,37 @@ export async function channel(request, response) {
|
|
|
95
96
|
const { application } = request.app.locals;
|
|
96
97
|
const userId = getUserId(request);
|
|
97
98
|
const { uid } = request.params;
|
|
98
|
-
const { before, after } = request.query;
|
|
99
|
+
const { before, after, showRead } = request.query;
|
|
99
100
|
|
|
100
101
|
const channelDocument = await getChannel(application, uid, userId);
|
|
101
102
|
if (!channelDocument) {
|
|
102
103
|
return response.status(404).render("404");
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
// Check if showing read items
|
|
107
|
+
const showReadItems = showRead === "true";
|
|
108
|
+
|
|
105
109
|
const timeline = await getTimelineItems(application, channelDocument._id, {
|
|
106
110
|
before,
|
|
107
111
|
after,
|
|
108
112
|
userId,
|
|
113
|
+
showRead: showReadItems,
|
|
109
114
|
});
|
|
110
115
|
|
|
116
|
+
// Count read items to show "View read items" button
|
|
117
|
+
const readCount = await countReadItems(
|
|
118
|
+
application,
|
|
119
|
+
channelDocument._id,
|
|
120
|
+
userId,
|
|
121
|
+
);
|
|
122
|
+
|
|
111
123
|
response.render("channel", {
|
|
112
124
|
title: channelDocument.name,
|
|
113
125
|
channel: channelDocument,
|
|
114
126
|
items: timeline.items,
|
|
115
127
|
paging: timeline.paging,
|
|
128
|
+
readCount,
|
|
129
|
+
showRead: showReadItems,
|
|
116
130
|
baseUrl: request.baseUrl,
|
|
117
131
|
});
|
|
118
132
|
}
|
package/lib/storage/items.js
CHANGED
|
@@ -78,6 +78,7 @@ export async function addItem(application, { channelId, feedId, uid, item }) {
|
|
|
78
78
|
* @param {string} [options.after] - After cursor
|
|
79
79
|
* @param {number} [options.limit] - Items per page
|
|
80
80
|
* @param {string} [options.userId] - User ID for read state
|
|
81
|
+
* @param {boolean} [options.showRead] - Whether to show read items (default: false)
|
|
81
82
|
* @returns {Promise<object>} Timeline with items and paging
|
|
82
83
|
*/
|
|
83
84
|
export async function getTimelineItems(application, channelId, options = {}) {
|
|
@@ -86,7 +87,12 @@ export async function getTimelineItems(application, channelId, options = {}) {
|
|
|
86
87
|
typeof channelId === "string" ? new ObjectId(channelId) : channelId;
|
|
87
88
|
const limit = parseLimit(options.limit);
|
|
88
89
|
|
|
90
|
+
// Base query - filter out read items unless showRead is true
|
|
89
91
|
const baseQuery = { channelId: objectId };
|
|
92
|
+
if (options.userId && !options.showRead) {
|
|
93
|
+
baseQuery.readBy = { $ne: options.userId };
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
const query = buildPaginationQuery({
|
|
91
97
|
before: options.before,
|
|
92
98
|
after: options.after,
|
|
@@ -256,6 +262,24 @@ export async function getItemsByUids(application, uids, userId) {
|
|
|
256
262
|
return items.map((item) => transformToJf2(item, userId));
|
|
257
263
|
}
|
|
258
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Count read items in a channel for a user
|
|
267
|
+
* @param {object} application - Indiekit application
|
|
268
|
+
* @param {ObjectId|string} channelId - Channel ObjectId
|
|
269
|
+
* @param {string} userId - User ID
|
|
270
|
+
* @returns {Promise<number>} Count of read items
|
|
271
|
+
*/
|
|
272
|
+
export async function countReadItems(application, channelId, userId) {
|
|
273
|
+
const collection = getCollection(application);
|
|
274
|
+
const objectId =
|
|
275
|
+
typeof channelId === "string" ? new ObjectId(channelId) : channelId;
|
|
276
|
+
|
|
277
|
+
return collection.countDocuments({
|
|
278
|
+
channelId: objectId,
|
|
279
|
+
readBy: userId,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
259
283
|
/**
|
|
260
284
|
* Mark items as read
|
|
261
285
|
* @param {object} application - Indiekit application
|
package/locales/en.json
CHANGED
package/package.json
CHANGED
package/views/channel.njk
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
{{ icon("previous") }} {{ __("microsub.channels.title") }}
|
|
8
8
|
</a>
|
|
9
9
|
<div class="channel__actions">
|
|
10
|
+
{% if not showRead and items.length > 0 %}
|
|
10
11
|
<form action="{{ baseUrl }}/api/mark-read" method="POST" style="display: inline;">
|
|
11
12
|
<input type="hidden" name="channel" value="{{ channel.uid }}">
|
|
12
13
|
<input type="hidden" name="entry" value="last-read-entry">
|
|
@@ -14,6 +15,16 @@
|
|
|
14
15
|
{{ icon("checkboxChecked") }} {{ __("microsub.reader.markAllRead") }}
|
|
15
16
|
</button>
|
|
16
17
|
</form>
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if showRead %}
|
|
20
|
+
<a href="{{ baseUrl }}/channels/{{ channel.uid }}" class="button button--secondary button--small">
|
|
21
|
+
{{ icon("hide") }} {{ __("microsub.reader.hideRead") }}
|
|
22
|
+
</a>
|
|
23
|
+
{% elif readCount > 0 %}
|
|
24
|
+
<a href="{{ baseUrl }}/channels/{{ channel.uid }}?showRead=true" class="button button--secondary button--small">
|
|
25
|
+
{{ icon("show") }} {{ __("microsub.reader.showRead", { count: readCount }) }}
|
|
26
|
+
</a>
|
|
27
|
+
{% endif %}
|
|
17
28
|
<a href="{{ baseUrl }}/channels/{{ channel.uid }}/feeds" class="button button--secondary button--small">
|
|
18
29
|
{{ icon("syndicate") }} {{ __("microsub.feeds.title") }}
|
|
19
30
|
</a>
|
|
@@ -48,11 +59,19 @@
|
|
|
48
59
|
{% endif %}
|
|
49
60
|
{% else %}
|
|
50
61
|
<div class="reader__empty">
|
|
62
|
+
{% if readCount > 0 and not showRead %}
|
|
63
|
+
{{ icon("checkboxChecked") }}
|
|
64
|
+
<p>{{ __("microsub.reader.allRead") }}</p>
|
|
65
|
+
<a href="{{ baseUrl }}/channels/{{ channel.uid }}?showRead=true" class="button button--secondary">
|
|
66
|
+
{{ icon("show") }} {{ __("microsub.reader.showRead", { count: readCount }) }}
|
|
67
|
+
</a>
|
|
68
|
+
{% else %}
|
|
51
69
|
{{ icon("syndicate") }}
|
|
52
70
|
<p>{{ __("microsub.timeline.empty") }}</p>
|
|
53
71
|
<a href="{{ baseUrl }}/channels/{{ channel.uid }}/feeds" class="button button--primary">
|
|
54
72
|
{{ __("microsub.feeds.subscribe") }}
|
|
55
73
|
</a>
|
|
74
|
+
{% endif %}
|
|
56
75
|
</div>
|
|
57
76
|
{% endif %}
|
|
58
77
|
</div>
|