mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 21:02:28 +00:00
Improve subscriptions empty state rendering
This commit is contained in:
1 parent
c1d8a8b9dc
commit
2322b19a3f
2 files changed
+136
-8
No files matched your search
+78
-8
@@ -339,7 +339,14 @@ class SubscriptionsManager {
|
||||
}
|
||||
|
||||
if (!this.loaded) {
|
||||
await this.loadSubscriptions(userPubkey);
|
||||
try {
|
||||
await this.loadSubscriptions(userPubkey);
|
||||
} catch (error) {
|
||||
userLogger.error(
|
||||
"[SubscriptionsManager] Failed to load subscriptions while rendering feed:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const channelHexes = this.getSubscribedAuthors();
|
||||
@@ -422,6 +429,22 @@ class SubscriptionsManager {
|
||||
? result.items.map((item) => item?.video).filter(Boolean)
|
||||
: [];
|
||||
|
||||
const metadata = result && typeof result.metadata === "object"
|
||||
? { ...result.metadata }
|
||||
: {};
|
||||
|
||||
if (!metadata.feed) {
|
||||
metadata.feed = "subscriptions";
|
||||
}
|
||||
if (limit) {
|
||||
metadata.limit = limit;
|
||||
}
|
||||
if (reason) {
|
||||
metadata.reason = reason;
|
||||
}
|
||||
|
||||
const enrichedResult = { ...result, metadata };
|
||||
|
||||
if (app?.videosMap instanceof Map) {
|
||||
videos.forEach((video) => {
|
||||
if (video && typeof video.id === "string" && video.id) {
|
||||
@@ -430,10 +453,15 @@ class SubscriptionsManager {
|
||||
});
|
||||
}
|
||||
|
||||
this.lastResult = result;
|
||||
this.renderSameGridStyle(result, containerId, { limit, reason });
|
||||
this.lastResult = enrichedResult;
|
||||
this.renderSameGridStyle(enrichedResult, containerId, {
|
||||
limit,
|
||||
reason,
|
||||
emptyMessage:
|
||||
"No playable subscription videos found yet. We'll keep watching for new posts.",
|
||||
});
|
||||
this.hasRenderedOnce = true;
|
||||
return result;
|
||||
return enrichedResult;
|
||||
} catch (error) {
|
||||
userLogger.error(
|
||||
"[SubscriptionsManager] Failed to run subscriptions feed:",
|
||||
@@ -587,6 +615,10 @@ class SubscriptionsManager {
|
||||
? { ...result.metadata }
|
||||
: {};
|
||||
|
||||
if (!metadata.feed) {
|
||||
metadata.feed = "subscriptions";
|
||||
}
|
||||
|
||||
const limitCandidate = Number(options?.limit);
|
||||
const limit =
|
||||
Number.isFinite(limitCandidate) && limitCandidate > 0
|
||||
@@ -599,10 +631,15 @@ class SubscriptionsManager {
|
||||
.filter((video) => video && typeof video === "object");
|
||||
|
||||
if (!videos.length) {
|
||||
container.innerHTML = `
|
||||
<p class="flex justify-center items-center h-full w-full text-center text-muted-strong">
|
||||
No videos available yet.
|
||||
</p>`;
|
||||
const reasonDetail =
|
||||
typeof metadata.reason === "string" && metadata.reason
|
||||
? metadata.reason
|
||||
: "empty";
|
||||
this.renderEmptyState(container, {
|
||||
message: options?.emptyMessage,
|
||||
reason: reasonDetail,
|
||||
metadata,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -636,6 +673,39 @@ class SubscriptionsManager {
|
||||
listView.render(videos, enrichedMetadata);
|
||||
}
|
||||
|
||||
renderEmptyState(container, { message, reason, metadata } = {}) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const copy =
|
||||
typeof message === "string" && message.trim()
|
||||
? message.trim()
|
||||
: "No playable subscription videos found yet. We'll keep watching for new posts.";
|
||||
|
||||
container.innerHTML = getSidebarLoadingMarkup(copy, { showSpinner: false });
|
||||
|
||||
if (this.subscriptionListView && this.subscriptionListView.state) {
|
||||
const currentMetadata =
|
||||
this.subscriptionListView.state.feedMetadata &&
|
||||
typeof this.subscriptionListView.state.feedMetadata === "object"
|
||||
? { ...this.subscriptionListView.state.feedMetadata }
|
||||
: {};
|
||||
|
||||
if (metadata && typeof metadata === "object") {
|
||||
Object.assign(currentMetadata, metadata);
|
||||
}
|
||||
|
||||
if (reason && typeof reason === "string") {
|
||||
currentMetadata.reason = reason;
|
||||
} else if (!currentMetadata.reason) {
|
||||
currentMetadata.reason = "empty";
|
||||
}
|
||||
|
||||
this.subscriptionListView.state.feedMetadata = currentMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
getListView(container, app) {
|
||||
if (this.subscriptionListView) {
|
||||
return this.subscriptionListView;
|
||||
|
||||
@@ -203,6 +203,11 @@ test(
|
||||
|
||||
assert.equal(runCalls.length, 1, "engine.run should execute after warm-up");
|
||||
assert.equal(renderCalls.length, 1, "initial render should occur once");
|
||||
assert.equal(
|
||||
renderCalls[0].options.emptyMessage.includes("No playable subscription videos"),
|
||||
true,
|
||||
"empty state should provide a descriptive message",
|
||||
);
|
||||
|
||||
nostrService.emit("videos:updated", {
|
||||
reason: "subscription",
|
||||
@@ -226,6 +231,11 @@ test(
|
||||
"video-1",
|
||||
"updated render should receive fresh videos",
|
||||
);
|
||||
assert.equal(
|
||||
renderCalls[1].options.emptyMessage.includes("No playable subscription videos"),
|
||||
true,
|
||||
"subsequent renders should retain the empty state message for metadata",
|
||||
);
|
||||
|
||||
if (typeof manager.unsubscribeFromNostrUpdates === "function") {
|
||||
manager.unsubscribeFromNostrUpdates();
|
||||
@@ -257,3 +267,51 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test("renderSameGridStyle shows empty state message", async () => {
|
||||
const SubscriptionsManager = subscriptions.constructor;
|
||||
|
||||
const originalWindow = globalThis.window;
|
||||
const originalDocument = globalThis.document;
|
||||
|
||||
const dom = new JSDOM(
|
||||
"<!doctype html><div id=\"subscriptionsVideoList\"></div>",
|
||||
{ url: "https://example.test/" },
|
||||
);
|
||||
|
||||
globalThis.window = dom.window;
|
||||
globalThis.document = dom.window.document;
|
||||
|
||||
const manager = new SubscriptionsManager();
|
||||
|
||||
manager.renderSameGridStyle(
|
||||
{ items: [] },
|
||||
"subscriptionsVideoList",
|
||||
{ emptyMessage: "Custom empty state copy." },
|
||||
);
|
||||
|
||||
const container = dom.window.document.getElementById("subscriptionsVideoList");
|
||||
assert.match(
|
||||
container.textContent,
|
||||
/Custom empty state copy\./,
|
||||
"empty state should render the provided copy",
|
||||
);
|
||||
assert.ok(
|
||||
!container.textContent.includes("Fetching subscriptions"),
|
||||
"loading spinner should be removed when rendering the empty state",
|
||||
);
|
||||
|
||||
dom.window.close();
|
||||
|
||||
if (typeof originalWindow === "undefined") {
|
||||
delete globalThis.window;
|
||||
} else {
|
||||
globalThis.window = originalWindow;
|
||||
}
|
||||
|
||||
if (typeof originalDocument === "undefined") {
|
||||
delete globalThis.document;
|
||||
} else {
|
||||
globalThis.document = originalDocument;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user