Refactor duplicate feed refresh logic into unified refreshFeed method

- Unify `refreshForYouFeed`, `refreshKidsFeed`, `refreshExploreFeed`, and `refreshRecentFeed` into `refreshFeed(feedType, options)`.
- Update `js/app/feedCoordinator.js` to implement `refreshFeed` and use it in `loadFeedVideos`.
- Update `js/app.js` to delegate specific methods to `refreshFeed`.
- Add `FEED_TYPES` import to `js/app.js`.
- Update `tests/app/feedCoordinator.test.mjs` to match the refactoring.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-08 00:35:47 +00:00
1 parent 566c2dbf89
commit b094d2a035
3 files changed
+99 -234

No files matched your search

+5 -4
View File
@@ -22,6 +22,7 @@ import {
DEFAULT_BLUR_THRESHOLD,
DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD,
DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD,
FEED_TYPES,
} from "./constants.js";
import { attachHealthBadges } from "./gridHealth.js";
import { attachUrlHealthBadges } from "./urlHealthObserver.js";
@@ -3946,22 +3947,22 @@ class Application {
async refreshForYouFeed(...args) {
this._initCoordinators();
return this._feed.refreshForYouFeed(...args);
return this._feed.refreshFeed(FEED_TYPES.FOR_YOU, ...args);
}
refreshKidsFeed(...args) {
this._initCoordinators();
return this._feed.refreshKidsFeed(...args);
return this._feed.refreshFeed(FEED_TYPES.KIDS, ...args);
}
refreshExploreFeed(...args) {
this._initCoordinators();
return this._feed.refreshExploreFeed(...args);
return this._feed.refreshFeed(FEED_TYPES.EXPLORE, ...args);
}
refreshRecentFeed(...args) {
this._initCoordinators();
return this._feed.refreshRecentFeed(...args);
return this._feed.refreshFeed("recent", ...args);
}
checkRelayHealthWarning(...args) {
+90 -224
View File
@@ -785,48 +785,92 @@ export function createFeedCoordinator(deps) {
};
},
async refreshForYouFeed({ reason, fallbackVideos } = {}) {
let watchHistoryItems = [];
async refreshFeed(feedType, { reason, fallbackVideos } = {}) {
const normalizedReason = typeof reason === "string" ? reason : undefined;
const fallback = Array.isArray(fallbackVideos) ? fallbackVideos : [];
let runtime = {};
let metadataModifier = (m) => m;
// 1. Prepare Runtime
try {
watchHistoryItems = await watchHistoryService.loadLatest(undefined, {
allowStale: true,
});
} catch (error) {
devLogger.warn(
"[Application] Failed to preload watch history for For You feed:",
error,
switch (feedType) {
case FEED_TYPES.FOR_YOU: {
let watchHistoryItems = [];
try {
watchHistoryItems = await watchHistoryService.loadLatest(
undefined,
{ allowStale: true },
);
} catch (err) {
devLogger.warn(
"[Application] Failed to preload watch history for For You feed:",
err,
);
}
runtime = this.buildForYouFeedRuntime({ watchHistoryItems });
break;
}
case FEED_TYPES.KIDS:
runtime = this.buildKidsFeedRuntime();
metadataModifier = (m) => {
if (runtime?.ageGroup && !m.ageGroup)
m.ageGroup = runtime.ageGroup;
return m;
};
break;
case FEED_TYPES.EXPLORE:
runtime = this.buildExploreFeedRuntime();
metadataModifier = (m) => {
const next = { ...m };
if (!next.sortOrder) next.sortOrder = FEED_TYPES.EXPLORE;
next.preserveOrder = true;
return next;
};
break;
case "recent":
runtime = this.buildRecentFeedRuntime();
break;
default:
devLogger.warn(`[Application] Unknown feed type: ${feedType}`);
return Promise.resolve({
videos: fallback,
metadata: { reason: normalizedReason, engine: "unknown" },
});
}
} catch (err) {
devLogger.error(
`[Application] Failed to build runtime for ${feedType}:`,
err,
);
}
const runtime = this.buildForYouFeedRuntime({ watchHistoryItems });
const normalizedReason = typeof reason === "string" ? reason : undefined;
const fallback = Array.isArray(fallbackVideos) ? fallbackVideos : [];
// 2. Check Engine
if (!this.feedEngine || typeof this.feedEngine.run !== "function") {
const metadata = {
const metadata = metadataModifier({
reason: normalizedReason,
engine: "unavailable",
};
});
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.updateForYouTelemetryMetadata([], metadata);
// Telemetry update for unavailable engine?
if (feedType === FEED_TYPES.FOR_YOU || feedType === FEED_TYPES.KIDS) {
this.updateFeedTelemetryMetadata(feedType, [], metadata);
}
this.renderVideoList({ videos: fallback, metadata });
return Promise.resolve({ videos: fallback, metadata });
}
// 3. Run Engine
return this.feedEngine
.run(FEED_TYPES.FOR_YOU, { runtime })
.run(feedType, { runtime })
.then((result) => {
const videos = Array.isArray(result?.videos) ? result.videos : [];
const metadata = {
...(result?.metadata || {}),
};
if (normalizedReason) {
metadata.reason = normalizedReason;
}
let metadata = { ...(result?.metadata || {}) };
if (normalizedReason) metadata.reason = normalizedReason;
metadata = metadataModifier(metadata);
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
@@ -835,141 +879,8 @@ export function createFeedCoordinator(deps) {
}
const items = Array.isArray(result?.items) ? result.items : [];
this.updateForYouTelemetryMetadata(items, metadata);
const payload = { videos, metadata };
this.renderVideoList(payload);
return payload;
})
.catch((error) => {
devLogger.error("[Application] Failed to run for-you feed:", error);
const metadata = {
reason: normalizedReason || "error:for-you-feed",
error: true,
};
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.updateForYouTelemetryMetadata([], metadata);
const payload = { videos: fallback, metadata };
this.renderVideoList(payload);
return payload;
});
},
refreshKidsFeed({ reason, fallbackVideos } = {}) {
const runtime = this.buildKidsFeedRuntime();
const normalizedReason = typeof reason === "string" ? reason : undefined;
const fallback = Array.isArray(fallbackVideos) ? fallbackVideos : [];
if (!this.feedEngine || typeof this.feedEngine.run !== "function") {
const metadata = {
reason: normalizedReason,
engine: "unavailable",
};
if (runtime?.ageGroup && !metadata.ageGroup) {
metadata.ageGroup = runtime.ageGroup;
}
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.updateFeedTelemetryMetadata(FEED_TYPES.KIDS, [], metadata);
this.renderVideoList({ videos: fallback, metadata });
return Promise.resolve({ videos: fallback, metadata });
}
return this.feedEngine
.run(FEED_TYPES.KIDS, { runtime })
.then((result) => {
const videos = Array.isArray(result?.videos) ? result.videos : [];
const metadata = {
...(result?.metadata || {}),
};
if (normalizedReason) {
metadata.reason = normalizedReason;
}
if (runtime?.ageGroup && !metadata.ageGroup) {
metadata.ageGroup = runtime.ageGroup;
}
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
const items = Array.isArray(result?.items) ? result.items : [];
this.updateFeedTelemetryMetadata(FEED_TYPES.KIDS, items, metadata);
const payload = { videos, metadata };
this.renderVideoList(payload);
return payload;
})
.catch((error) => {
devLogger.error("[Application] Failed to run kids feed:", error);
const metadata = {
reason: normalizedReason || "error:kids-feed",
error: true,
};
if (runtime?.ageGroup && !metadata.ageGroup) {
metadata.ageGroup = runtime.ageGroup;
}
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.updateFeedTelemetryMetadata(FEED_TYPES.KIDS, [], metadata);
const payload = { videos: fallback, metadata };
this.renderVideoList(payload);
return payload;
});
},
refreshExploreFeed({ reason, fallbackVideos } = {}) {
const runtime = this.buildExploreFeedRuntime();
const normalizedReason = typeof reason === "string" ? reason : undefined;
const fallback = Array.isArray(fallbackVideos) ? fallbackVideos : [];
const applyExploreOrderingMetadata = (source) => {
const next = source && typeof source === "object" ? { ...source } : {};
if (!next.sortOrder) {
next.sortOrder = FEED_TYPES.EXPLORE;
}
next.preserveOrder = true;
return next;
};
if (!this.feedEngine || typeof this.feedEngine.run !== "function") {
const metadata = applyExploreOrderingMetadata({
reason: normalizedReason,
engine: "unavailable",
});
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.renderVideoList({ videos: fallback, metadata });
return Promise.resolve({ videos: fallback, metadata });
}
return this.feedEngine
.run(FEED_TYPES.EXPLORE, { runtime })
.then((result) => {
const videos = Array.isArray(result?.videos) ? result.videos : [];
const metadata = applyExploreOrderingMetadata({
...(result?.metadata || {}),
...(normalizedReason ? { reason: normalizedReason } : {}),
});
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
if (feedType === FEED_TYPES.FOR_YOU || feedType === FEED_TYPES.KIDS) {
this.updateFeedTelemetryMetadata(feedType, items, metadata);
}
const payload = { videos, metadata };
@@ -977,73 +888,26 @@ export function createFeedCoordinator(deps) {
return payload;
})
.catch((error) => {
devLogger.error("[Application] Failed to run explore feed:", error);
const metadata = applyExploreOrderingMetadata({
reason: normalizedReason || "error:explore-feed",
error: true,
});
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
const payload = { videos: fallback, metadata };
this.renderVideoList(payload);
return payload;
});
},
refreshRecentFeed({ reason, fallbackVideos } = {}) {
const runtime = this.buildRecentFeedRuntime();
const normalizedReason = typeof reason === "string" ? reason : undefined;
const fallback = Array.isArray(fallbackVideos) ? fallbackVideos : [];
if (!this.feedEngine || typeof this.feedEngine.run !== "function") {
const metadata = {
reason: normalizedReason,
engine: "unavailable",
};
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
this.renderVideoList({ videos: fallback, metadata });
return Promise.resolve({ videos: fallback, metadata });
}
return this.feedEngine
.run("recent", { runtime })
.then((result) => {
const videos = Array.isArray(result?.videos) ? result.videos : [];
const metadata = {
...(result?.metadata || {}),
};
if (normalizedReason) {
metadata.reason = normalizedReason;
}
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
const payload = { videos, metadata };
this.renderVideoList(payload);
return payload;
})
.catch((error) => {
devLogger.error("[Application] Failed to run recent feed:", error);
const metadata = {
reason: normalizedReason || "error:recent-feed",
devLogger.error(
`[Application] Failed to run ${feedType} feed:`,
error,
);
let metadata = {
reason: normalizedReason || `error:${feedType}-feed`,
error: true,
};
metadata = metadataModifier(metadata);
this.latestFeedMetadata = metadata;
this.videosMap = this.nostrService.getVideosMap();
if (this.videoListView) {
this.videoListView.state.videosMap = this.videosMap;
}
if (feedType === FEED_TYPES.FOR_YOU || feedType === FEED_TYPES.KIDS) {
this.updateFeedTelemetryMetadata(feedType, [], metadata);
}
const payload = { videos: fallback, metadata };
this.renderVideoList(payload);
return payload;
@@ -1101,29 +965,31 @@ export function createFeedCoordinator(deps) {
case "recent":
includeTags = true;
loadingMessage = "Fetching recent videos\u2026";
refreshMethod = (opts) => this.refreshRecentFeed(opts);
refreshMethod = (opts) => this.refreshFeed("recent", opts);
shouldCheckRelayHealth = true;
break;
case FEED_TYPES.FOR_YOU:
includeTags = false;
loadingMessage = "Fetching for-you videos\u2026";
refreshMethod = (opts) => this.refreshForYouFeed(opts);
refreshMethod = (opts) => this.refreshFeed(FEED_TYPES.FOR_YOU, opts);
shouldCheckRelayHealth = true;
break;
case FEED_TYPES.KIDS:
includeTags = true;
loadingMessage = "Fetching kids videos\u2026";
refreshMethod = (opts) => this.refreshKidsFeed(opts);
refreshMethod = (opts) => this.refreshFeed(FEED_TYPES.KIDS, opts);
shouldCheckRelayHealth = false;
break;
case FEED_TYPES.EXPLORE:
includeTags = false;
loadingMessage = "Fetching explore videos\u2026";
refreshMethod = (opts) => this.refreshExploreFeed(opts);
refreshMethod = (opts) => this.refreshFeed(FEED_TYPES.EXPLORE, opts);
shouldCheckRelayHealth = false;
break;
default:
devLogger.warn(`[Application] Unknown feed type for loadFeedVideos: ${feedType}`);
devLogger.warn(
`[Application] Unknown feed type for loadFeedVideos: ${feedType}`,
);
return;
}
@@ -1225,7 +1091,7 @@ export function createFeedCoordinator(deps) {
return;
}
await this.refreshRecentFeed({ reason: "older-fetch" });
await this.refreshFeed("recent", { reason: "older-fetch" });
},
/**
+4 -6
View File
@@ -52,10 +52,7 @@ test('createFeedCoordinator - loadForYouVideos', async (t) => {
checkRelayHealthWarning: mock.fn(),
setFeedTelemetryContext: mock.fn(),
isAuthorBlocked: mock.fn(() => false),
refreshForYouFeed: mock.fn(async () => {}),
refreshRecentFeed: mock.fn(async () => {}),
refreshKidsFeed: mock.fn(async () => {}),
refreshExploreFeed: mock.fn(async () => {}),
refreshFeed: mock.fn(async () => {}),
blacklistedEventIds: new Set(),
};
@@ -80,8 +77,9 @@ test('createFeedCoordinator - loadForYouVideos', async (t) => {
// Verify nostrService.loadVideos called
assert.strictEqual(app.nostrService.loadVideos.mock.callCount(), 1, 'Should call nostrService.loadVideos');
// Verify refreshForYouFeed called via callback
assert.strictEqual(app.refreshForYouFeed.mock.callCount(), 1, 'Should call refreshForYouFeed via callback');
// Verify refreshFeed called via callback
assert.strictEqual(app.refreshFeed.mock.callCount(), 1, 'Should call refreshFeed via callback');
assert.strictEqual(app.refreshFeed.mock.calls[0].arguments[0], FEED_TYPES.FOR_YOU, 'Should call refreshFeed with FOR_YOU');
// Verify subscription updated
assert.strictEqual(app.videoSubscription, 'mock-subscription', 'Should update videoSubscription');