fix: mark-as-read for items from orphan channels

Items from channels with userId: null (created during earlier setup)
appeared in the unified timeline but had no _channelUid, causing the
mark-read JS handler to silently abort. Fall back to channelId (MongoDB
ObjectId) when channelUid is unavailable, and resolve it server-side
via getChannelById.

Confab-Link: http://localhost:8080/sessions/4d40ef89-a713-48c1-b4ed-0ffafca25677
This commit is contained in:
Ricardo
2026-03-05 20:25:30 +01:00
parent 90b47627e2
commit d1f0fffe35
3 changed files with 15 additions and 5 deletions
+11 -3
View File
@@ -6,7 +6,7 @@
import { IndiekitError } from "@indiekit/error"; import { IndiekitError } from "@indiekit/error";
import { proxyItemImages } from "../media/proxy.js"; import { proxyItemImages } from "../media/proxy.js";
import { getChannel } from "../storage/channels.js"; import { getChannel, getChannelById } from "../storage/channels.js";
import { import {
getTimelineItems, getTimelineItems,
markItemsRead, markItemsRead,
@@ -72,8 +72,16 @@ export async function action(request, response) {
validateChannel(channel); validateChannel(channel);
// Verify channel exists // Verify channel exists — try by UID first, fall back to ObjectId
const channelDocument = await getChannel(application, channel, userId); // (timeline view may send ObjectId string for items from orphan channels)
let channelDocument = await getChannel(application, channel, userId);
if (!channelDocument) {
try {
channelDocument = await getChannelById(application, channel);
} catch {
// Invalid ObjectId format — channel string is not a valid ObjectId
}
}
if (!channelDocument) { if (!channelDocument) {
throw new IndiekitError("Channel not found", { throw new IndiekitError("Channel not found", {
status: 404, status: 404,
+1
View File
@@ -203,6 +203,7 @@
data-action="mark-read" data-action="mark-read"
data-item-id="{{ item._id }}" data-item-id="{{ item._id }}"
{% if item._channelUid %}data-channel-uid="{{ item._channelUid }}"{% endif %} {% if item._channelUid %}data-channel-uid="{{ item._channelUid }}"{% endif %}
{% if item._channelId %}data-channel-id="{{ item._channelId }}"{% endif %}
title="Mark as read"> title="Mark as read">
{{ icon("checkboxChecked") }} {{ icon("checkboxChecked") }}
<span class="visually-hidden">Mark read</span> <span class="visually-hidden">Mark read</span>
+3 -2
View File
@@ -108,7 +108,8 @@
const itemId = button.dataset.itemId; const itemId = button.dataset.itemId;
const channelUid = button.dataset.channelUid; const channelUid = button.dataset.channelUid;
if (!itemId || !channelUid) return; const channelId = button.dataset.channelId;
if (!itemId || (!channelUid && !channelId)) return;
button.disabled = true; button.disabled = true;
@@ -116,7 +117,7 @@
const formData = new URLSearchParams(); const formData = new URLSearchParams();
formData.append('action', 'timeline'); formData.append('action', 'timeline');
formData.append('method', 'mark_read'); formData.append('method', 'mark_read');
formData.append('channel', channelUid); formData.append('channel', channelUid || channelId);
formData.append('entry', itemId); formData.append('entry', itemId);
const response = await fetch(microsubApiUrl, { const response = await fetch(microsubApiUrl, {