Merge pull request #1118 from PR0M3TH3AN/fix-watch-history-dynamic-refresh-8086273073608034739

Fix Watch History Dynamic Pulling and Add Refresh Button
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-07 09:34:18 -05:00
commit 425f772fcc
5 files changed
+87 -18

No files matched your search

+7 -1
View File
@@ -174,13 +174,19 @@ function createWatchHistorySource({ service = watchHistoryService } = {}) {
context?.runtime?.actor,
);
const forceRefresh =
context?.runtime?.watchHistory?.forceRefresh === true ||
context?.runtime?.forceRefresh === true;
if (!service || typeof service.loadLatest !== "function") {
return [];
}
let items = [];
try {
items = await Promise.resolve(service.loadLatest(actor || undefined));
items = await Promise.resolve(
service.loadLatest(actor || undefined, { forceRefresh })
);
} catch (error) {
context?.log?.("[watch-history-feed] Failed to load history", error);
items = [];
+67 -14
View File
@@ -85,7 +85,11 @@ function getAppInstance() {
return getApplication();
}
function buildWatchHistoryFeedRuntime({ actor, cursor = 0 } = {}) {
function buildWatchHistoryFeedRuntime({
actor,
cursor = 0,
forceRefresh = false,
} = {}) {
const app = getAppInstance();
const normalizedActor =
typeof actor === "string" && actor.trim() ? actor.trim() : "";
@@ -103,8 +107,9 @@ function buildWatchHistoryFeedRuntime({ actor, cursor = 0 } = {}) {
false,
watchHistory: {
actor: normalizedActor,
cursor: Number.isFinite(cursor) ? cursor : 0
}
cursor: Number.isFinite(cursor) ? cursor : 0,
forceRefresh: forceRefresh === true,
},
};
const preferenceSource =
@@ -1367,18 +1372,23 @@ export function buildHistoryCard({ item, video, profile }) {
export function createWatchHistoryRenderer(config = {}) {
const {
fetchHistory = async (actorInput, { cursor = 0 } = {}) => {
fetchHistory = async (
actorInput,
{ cursor = 0, forceRefresh = false } = {}
) => {
const app = getAppInstance();
const engine = app?.feedEngine;
if (engine && typeof engine.run === "function") {
const runtime = buildWatchHistoryFeedRuntime({
actor: actorInput,
cursor
cursor,
forceRefresh,
});
return engine.run("watch-history", { runtime });
}
const items = await watchHistoryService.loadLatest(actorInput, {
allowStale: true
allowStale: !forceRefresh,
forceRefresh,
});
const normalized = normalizeHistoryItems(items);
return { items: normalized, metadata: { engine: "service-fallback" } };
@@ -1394,6 +1404,7 @@ export function createWatchHistoryRenderer(config = {}) {
loadMoreSelector = "#watchHistoryLoadMore",
clearButtonSelector = '[data-history-action="clear-cache"]',
republishButtonSelector = '[data-history-action="republish"]',
refreshButtonSelector = '[data-history-action="refresh"]',
privacyBannerSelector = "#watchHistoryPrivacyBanner",
privacyMessageSelector = "#watchHistoryPrivacyMessage",
privacyToggleSelector = "#watchHistoryPrivacyToggle",
@@ -1466,6 +1477,7 @@ export function createWatchHistoryRenderer(config = {}) {
loadMore: null,
clearButton: null,
republishButton: null,
refreshButton: null,
privacyBanner: null,
privacyMessage: null,
privacyToggle: null,
@@ -1481,6 +1493,7 @@ export function createWatchHistoryRenderer(config = {}) {
let boundLoadMoreHandler = null;
let boundClearHandler = null;
let boundRepublishHandler = null;
let boundRefreshHandler = null;
let boundPrivacyDismissHandler = null;
const subscriptions = new Set();
@@ -1496,6 +1509,7 @@ export function createWatchHistoryRenderer(config = {}) {
loadMore: resolveElement(loadMoreSelector),
clearButton: resolveElement(clearButtonSelector),
republishButton: resolveElement(republishButtonSelector),
refreshButton: resolveElement(refreshButtonSelector),
privacyBanner: resolveElement(privacyBannerSelector),
privacyMessage: resolveElement(privacyMessageSelector),
privacyToggle: resolveElement(privacyToggleSelector),
@@ -2123,6 +2137,7 @@ export function createWatchHistoryRenderer(config = {}) {
}
boundClearHandler = async (event) => {
event.preventDefault();
userLogger.info("[historyView] 'Clear local history' clicked.");
const actor = await resolveActorKey();
try {
await watchHistoryService.resetProgress(actor);
@@ -2131,14 +2146,22 @@ export function createWatchHistoryRenderer(config = {}) {
state.hasMore = false;
showEmptyState();
const app = getAppInstance();
app?.showSuccess?.("Local watch history reset.");
if (typeof app?.showSuccess === "function") {
app.showSuccess("Local watch history reset.");
} else {
console.log("Local watch history reset.");
}
} catch (error) {
const message =
error && typeof error.message === "string"
? error.message
: "Failed to reset local watch history.";
const app = getAppInstance();
app?.showError?.(message);
if (typeof app?.showError === "function") {
app.showError(message);
} else {
console.error(message);
}
}
};
elements.clearButton.addEventListener("click", boundClearHandler);
@@ -2153,25 +2176,48 @@ export function createWatchHistoryRenderer(config = {}) {
}
boundRepublishHandler = async (event) => {
event.preventDefault();
userLogger.info("[historyView] 'Republish now' clicked.");
const actor = await resolveActorKey();
try {
const payload = state.items.map((entry) => ({
...entry.pointer,
watchedAt: entry.watchedAt
watchedAt: entry.watchedAt,
}));
await snapshot(payload, { actor, reason: "manual-republish" });
const app = getAppInstance();
app?.showSuccess?.("Watch history snapshot queued for publish.");
if (typeof app?.showSuccess === "function") {
app.showSuccess("Watch history snapshot queued for publish.");
} else {
console.log("Watch history snapshot queued for publish.");
}
} catch (error) {
const app = getAppInstance();
app?.showError?.("Failed to publish watch history. Try again later.");
if (isDevEnv) {
userLogger.warn("[historyView] Republish failed:", error);
if (typeof app?.showError === "function") {
app.showError("Failed to publish watch history. Try again later.");
} else {
console.error("Failed to publish watch history.");
}
userLogger.warn("[historyView] Republish failed:", error);
}
};
elements.republishButton.addEventListener("click", boundRepublishHandler);
}
if (elements.refreshButton) {
if (boundRefreshHandler) {
elements.refreshButton.removeEventListener("click", boundRefreshHandler);
}
boundRefreshHandler = async (event) => {
event.preventDefault();
userLogger.info("[historyView] 'Refresh' clicked.");
await renderer.refresh({ force: true });
const app = getAppInstance();
if (typeof app?.showSuccess === "function") {
app.showSuccess("Watch history refreshed from relays.");
}
};
elements.refreshButton.addEventListener("click", boundRefreshHandler);
}
}
function bindPrivacyControls() {
@@ -2223,7 +2269,10 @@ export function createWatchHistoryRenderer(config = {}) {
updateSessionFallbackWarning();
let result = null;
try {
result = await fetchHistory(actor, { cursor: 0 });
result = await fetchHistory(actor, {
cursor: 0,
forceRefresh: force === true,
});
state.lastError = null;
hideErrorBanner();
} catch (error) {
@@ -2432,6 +2481,10 @@ export function createWatchHistoryRenderer(config = {}) {
);
boundRepublishHandler = null;
}
if (elements.refreshButton && boundRefreshHandler) {
elements.refreshButton.removeEventListener("click", boundRefreshHandler);
boundRefreshHandler = null;
}
if (elements.privacyDismiss && boundPrivacyDismissHandler) {
elements.privacyDismiss.removeEventListener(
"click",
+3 -1
View File
@@ -1032,6 +1032,7 @@ async function loadLatest(actorInput, options = {}) {
// stale cache reads; everyone else waits for the fresh list so they do not
// miss entries.
const allowStale = normalizedOptions.allowStale === true;
const forceRefresh = normalizedOptions.forceRefresh === true;
if (!isFeatureEnabled(actorKey)) {
const items = collectQueueItems(actorKey);
@@ -1053,6 +1054,7 @@ async function loadLatest(actorInput, options = {}) {
actor: actorKey,
cacheHasItems: Array.isArray(cacheEntry?.items),
cacheExpiresAt: cacheEntry?.expiresAt || null,
forceRefresh,
}
);
const hasCachedItems = Array.isArray(cacheEntry?.items);
@@ -1061,7 +1063,7 @@ async function loadLatest(actorInput, options = {}) {
cacheEntry?.expiresAt &&
cacheEntry.expiresAt > now;
if (cacheIsFresh) {
if (cacheIsFresh && !forceRefresh) {
devLogger.debug(
"[watchHistoryService] Returning cached watch list items.",
{
+2 -2
View File
@@ -2569,9 +2569,9 @@ async function testWatchHistoryFeedHydration() {
// Simulate renderer fetching via feed engine
const renderer = createWatchHistoryRenderer({
fetchHistory: async (actorInput, { cursor = 0 } = {}) => {
fetchHistory: async (actorInput, { cursor = 0, forceRefresh = false } = {}) => {
const runtime = {
watchHistory: { actor: actorInput, cursor },
watchHistory: { actor: actorInput, cursor, forceRefresh },
blacklistedEventIds: new Set(),
isAuthorBlocked: () => false
};
+8
View File
@@ -25,6 +25,14 @@
>
Republish now
</button>
<button
type="button"
data-history-action="refresh"
class="btn-ghost"
aria-label="Refresh watch history from relays"
>
Refresh
</button>
</div>
</div>