Render cached feeds before recommendation refresh

This commit is contained in:
thePR0M3TH3AN committed 2026-07-26 18:38:03 -04:00
1 parent 687f8c438d
commit bb6614e3d3
2 files changed
+89 -1

No files matched your search

+31 -1
View File
@@ -1016,8 +1016,21 @@ export function createFeedCoordinator(deps) {
const recommendationFeed =
feedType === FEED_TYPES.FOR_YOU || feedType === FEED_TYPES.EXPLORE;
let exploreActivation = null;
if (recommendationFeed && typeof this.ensureExploreDataService === "function") {
await this.ensureExploreDataService();
// Recommendation indexes are an enhancement, not a prerequisite for
// showing cached videos. Activating the worker here used to leave a
// returning For You view stuck on its static loading markup whenever
// worker startup was slow or unavailable.
exploreActivation = Promise.resolve(this.ensureExploreDataService()).catch(
(error) => {
devLogger.warn(
`[Application] Recommendation data activation failed for ${feedType}:`,
error,
);
return null;
},
);
} else if (this.exploreDataService?.setActive) {
this.exploreDataService.setActive(false);
}
@@ -1117,6 +1130,23 @@ export function createFeedCoordinator(deps) {
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
// Re-score once the optional recommendation indexes are ready, but only
// if the user is still looking at this feed. The first render above is
// deliberately cache-first and must not wait for worker startup.
if (exploreActivation) {
void exploreActivation.then(() => {
if (!this.isFeedActive(feedType)) {
return;
}
return this.refreshFeed(feedType, { reason: "recommendation-data-ready" });
}).catch((error) => {
devLogger.warn(
`[Application] Failed to refresh ${feedType} after recommendation data activation:`,
error,
);
});
}
},
/**
+58
View File
@@ -85,3 +85,61 @@ test('createFeedCoordinator - loadForYouVideos', async (t) => {
assert.strictEqual(app.videoSubscription, 'mock-subscription', 'Should update videoSubscription');
});
});
test("loadForYouVideos renders without waiting for recommendation worker activation", async () => {
let resolveActivation;
const activation = new Promise((resolve) => {
resolveActivation = resolve;
});
let loadVideosStarted = false;
const mockDeps = {
devLogger: { log: mock.fn(), warn: mock.fn() },
userLogger: { warn: mock.fn() },
nostrClient: {},
getSidebarLoadingMarkup: mock.fn(() => '<div>Loading...</div>'),
watchHistoryService: {},
subscriptions: {},
createActiveNostrSource: mock.fn(),
createBlacklistFilterStage: mock.fn(),
createDisinterestFilterStage: mock.fn(),
createDedupeByRootStage: mock.fn(),
createExploreDiversitySorter: mock.fn(),
createExploreScorerStage: mock.fn(),
createKidsAudienceFilterStage: mock.fn(),
createKidsScorerStage: mock.fn(),
createKidsScoreSorter: mock.fn(),
createModerationStage: mock.fn(),
createResolvePostedAtStage: mock.fn(),
createTagPreferenceFilterStage: mock.fn(),
createWatchHistorySuppressionStage: mock.fn(),
createChronologicalSorter: mock.fn(),
createSubscriptionAuthorsSource: mock.fn(),
registerWatchHistoryFeed: mock.fn(),
};
const coordinator = createFeedCoordinator(mockDeps);
const app = {
...coordinator,
nostrService: {
getFilteredActiveVideos: mock.fn(() => [{ id: "cached" }]),
loadVideos: mock.fn(async ({ onVideos }) => {
loadVideosStarted = true;
onVideos([{ id: "cached", title: "Cached", created_at: 1 }], { reason: "cache" });
return [{ id: "cached", title: "Cached", created_at: 1 }];
}),
getVideoSubscription: mock.fn(() => null),
getVideosMap: mock.fn(() => new Map()),
},
videoListView: { state: {}, render: mock.fn() },
mountVideoListView: mock.fn(() => ({ innerHTML: "" })),
checkRelayHealthWarning: mock.fn(),
setFeedTelemetryContext: mock.fn(),
isAuthorBlocked: mock.fn(() => false),
refreshFeed: mock.fn(async () => {}),
ensureExploreDataService: mock.fn(() => activation),
blacklistedEventIds: new Set(),
};
await app.loadForYouVideos(false);
assert.equal(loadVideosStarted, true);
resolveActivation();
});