Enable local watch history fallback when sync disabled

This commit is contained in:
thePR0M3TH3AN committed 2025-10-02 17:07:21 -04:00
1 parent 84b70475d9
commit b1a493f113
3 files changed
+118 -51

No files matched your search

+25 -6
View File
@@ -739,6 +739,12 @@ export function createWatchHistoryRenderer(config = {}) {
},
} = config;
const syncEnabled = watchHistoryService.isEnabled?.() === true;
const localSupported =
typeof watchHistoryService.supportsLocalHistory === "function"
? watchHistoryService.supportsLocalHistory() !== false
: true;
const state = {
initialized: false,
actor: null,
@@ -763,7 +769,9 @@ export function createWatchHistoryRenderer(config = {}) {
? watchHistoryService.shouldStoreMetadata() !== false
: true,
sessionFallbackActive: false,
featureEnabled: watchHistoryService.isEnabled?.() === true,
syncEnabled,
localSupported,
featureEnabled: syncEnabled || localSupported,
};
let elements = {
@@ -895,18 +903,29 @@ export function createWatchHistoryRenderer(config = {}) {
}
function updateFeatureBanner() {
const enabled = watchHistoryService.isEnabled?.() === true;
state.featureEnabled = enabled;
const syncEnabled = watchHistoryService.isEnabled?.() === true;
const localSupported =
typeof watchHistoryService.supportsLocalHistory === "function"
? watchHistoryService.supportsLocalHistory() !== false
: true;
state.syncEnabled = syncEnabled;
state.localSupported = localSupported;
state.featureEnabled = syncEnabled || localSupported;
if (!(elements.featureBanner instanceof HTMLElement)) {
return;
}
if (enabled) {
if (syncEnabled) {
elements.featureBanner.textContent = "";
setHidden(elements.featureBanner, true);
return;
}
elements.featureBanner.textContent =
"Watch history sync is disabled on this server. Local history only.";
if (localSupported) {
elements.featureBanner.textContent =
"Watch history sync is disabled on this server. Local history only.";
} else {
elements.featureBanner.textContent =
"Watch history is disabled on this server.";
}
setHidden(elements.featureBanner, false);
}
+27 -45
View File
@@ -454,9 +454,6 @@ function getOrCreateQueue(actorKey) {
}
function restoreQueueState() {
if (!isFeatureEnabled()) {
return;
}
if (state.restored) {
return;
}
@@ -567,9 +564,6 @@ function restoreQueueState() {
}
function ensureQueue(actorKey) {
if (!isFeatureEnabled()) {
return null;
}
if (!actorKey) {
return null;
}
@@ -580,9 +574,6 @@ function ensureQueue(actorKey) {
}
function persistQueueState() {
if (!isFeatureEnabled()) {
return;
}
const storage = getSessionStorage();
if (!storage) {
return;
@@ -647,9 +638,6 @@ function resolveActorKey(actorInput) {
}
function collectQueueItems(actorKey) {
if (!isFeatureEnabled()) {
return [];
}
const queue = state.queues.get(actorKey);
if (!queue) {
return [];
@@ -675,9 +663,6 @@ function collectQueueItems(actorKey) {
}
function notifyQueueChange(actorKey) {
if (!isFeatureEnabled()) {
return;
}
emit("queue-changed", {
actor: actorKey,
items: collectQueueItems(actorKey),
@@ -685,9 +670,6 @@ function notifyQueueChange(actorKey) {
}
function clearQueue(actorKey) {
if (!isFeatureEnabled()) {
return;
}
const queue = state.queues.get(actorKey);
if (!queue) {
return;
@@ -834,17 +816,6 @@ async function publishView(pointerInput, createdAt, metadata = {}) {
}
);
if (!isFeatureEnabled()) {
console.info(
"[watchHistoryService] Watch history feature disabled; skipping queue update.",
{
pointer: pointerInput,
createdAt,
}
);
return viewResult;
}
const pointer = normalizePointerInput(pointerInput);
if (!pointer) {
console.warn(
@@ -981,6 +952,17 @@ async function publishView(pointerInput, createdAt, metadata = {}) {
}
);
if (!isFeatureEnabled()) {
console.info(
"[watchHistoryService] Watch history sync disabled; retaining pointer in local session queue only.",
{
actor: actorKey,
pointerKey: key,
watchedAt: normalizedPointer.watchedAt,
}
);
}
return viewResult;
}
@@ -1104,18 +1086,26 @@ async function snapshot(items, options = {}) {
}
async function loadLatest(actorInput) {
if (!isFeatureEnabled()) {
console.info(
"[watchHistoryService] loadLatest skipped because watch history feature is disabled.",
{ actor: resolveActorKey(actorInput) || null }
);
return [];
}
const actorKey = resolveActorKey(actorInput);
if (!actorKey) {
return [];
}
if (!isFeatureEnabled()) {
if (!state.restored) {
restoreQueueState();
}
const items = collectQueueItems(actorKey);
console.info(
"[watchHistoryService] loadLatest returning session watch history queue.",
{
actor: actorKey,
itemCount: items.length,
}
);
return items;
}
const now = Date.now();
const cacheEntry = state.fingerprintCache.get(actorKey);
console.info(
@@ -1217,9 +1207,6 @@ async function getFingerprint(actorInput) {
}
function resetProgress(actorInput) {
if (!isFeatureEnabled()) {
return;
}
const actorKey = normalizeActorKey(actorInput);
if (actorKey) {
state.queues.delete(actorKey);
@@ -1237,9 +1224,6 @@ function resetProgress(actorInput) {
}
function getQueuedPointers(actorInput) {
if (!isFeatureEnabled()) {
return [];
}
const actorKey = normalizeActorKey(actorInput);
if (!actorKey) {
return [];
@@ -1251,9 +1235,6 @@ function getQueuedPointers(actorInput) {
}
function getAllQueues() {
if (!isFeatureEnabled()) {
return {};
}
if (!state.restored) {
restoreQueueState();
}
@@ -1280,6 +1261,7 @@ function getSettings() {
const watchHistoryService = {
isEnabled: isFeatureEnabled,
supportsLocalHistory: () => true,
publishView,
snapshot,
loadLatest,
+66
View File
@@ -980,12 +980,78 @@ async function testWatchHistoryServiceIntegration() {
}
}
async function testWatchHistoryLocalFallbackWhenDisabled() {
console.log("Running watch history local fallback test...");
const actor = "local-fallback-actor";
const originalRecordView = nostrClient.recordVideoView;
const originalPub = nostrClient.pubkey;
try {
setWatchHistoryV2Enabled(false);
localStorage.clear();
watchHistoryService.resetProgress();
nostrClient.pubkey = "";
nostrClient.recordVideoView = async (_pointer, options = {}) => ({
ok: true,
event: {
id: "local-view",
pubkey: actor,
created_at: options.created_at || 1_700_500_000,
},
});
const supported =
typeof watchHistoryService.supportsLocalHistory === "function"
? watchHistoryService.supportsLocalHistory()
: true;
assert.equal(
supported,
true,
"service should report local history support while sync disabled",
);
const createdAt = 1_700_500_000;
await watchHistoryService.publishView(
{ type: "e", value: "local-pointer" },
createdAt,
{ actor },
);
const queued = watchHistoryService.getQueuedPointers(actor);
assert.equal(
queued.length,
1,
"local queue should retain pointer when sync is disabled",
);
const latest = await watchHistoryService.loadLatest(actor);
assert.equal(
latest.length,
1,
"loadLatest should surface session queue entries when sync disabled",
);
assert.equal(latest[0]?.value, "local-pointer");
assert.equal(latest[0]?.type, "e");
assert(
Number.isFinite(latest[0]?.watchedAt) && latest[0].watchedAt > 0,
"local fallback entries should carry watchedAt timestamps",
);
} finally {
setWatchHistoryV2Enabled(true);
watchHistoryService.resetProgress();
nostrClient.recordVideoView = originalRecordView;
nostrClient.pubkey = originalPub;
}
}
await testPublishSnapshotCanonicalizationAndChunking();
await testPublishSnapshotUsesExtensionCrypto();
await testPublishSnapshotFailureRetry();
await testWatchHistoryPartialRelayRetry();
await testResolveWatchHistoryBatchingWindow();
await testWatchHistoryServiceIntegration();
await testWatchHistoryLocalFallbackWhenDisabled();
console.log("watch-history.test.mjs completed successfully");