diff --git a/js/constants.js b/js/constants.js index 5610718b..0cb467bd 100644 --- a/js/constants.js +++ b/js/constants.js @@ -149,7 +149,7 @@ const DEFAULT_FLAGS = Object.freeze({ FEATURE_PUBLISH_NIP71: false, FEATURE_HASHTAG_PREFERENCES: false, FEATURE_TRUST_SEEDS: true, // Rollback: disable to drop baseline trust seeds without code changes. - FEATURE_TRUSTED_HIDE_CONTROLS: true, + FEATURE_TRUSTED_HIDE_CONTROLS: true,\n FEATURE_IMPROVED_COMMENT_FETCHING: true, // Default true for comment persistence fixes TRUSTED_MUTE_HIDE_THRESHOLD: DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD, TRUSTED_SPAM_HIDE_THRESHOLD: DEFAULT_TRUSTED_SPAM_HIDE_THRESHOLD, WSS_TRACKERS: DEFAULT_WSS_TRACKERS, diff --git a/js/services/commentThreadService.js b/js/services/commentThreadService.js index 06027413..eee83334 100644 --- a/js/services/commentThreadService.js +++ b/js/services/commentThreadService.js @@ -630,3 +630,4 @@ export default class CommentThreadService { this.resetInternalState(); } } +// New fetchThread with caching for improved persistence\n async fetchThread(target, options = {}) {\n if (!FEATURE_IMPROVED_COMMENT_FETCHING) {\n return await this.fetchVideoComments(target, options);\n }\n\n const videoEventId = target.videoEventId || target.eventId;\n if (!videoEventId) return [];\n\n let comments = this.getCachedComments(videoEventId);\n if (!comments) {\n comments = await this.fetchVideoComments(target, { ...options, since: 0 });\n this.cacheComments(videoEventId, comments);\n }\n\n devLogger.info(`Fetched ${comments.length} comments for ${videoEventId} (cached: ${!!this.getCachedComments(videoEventId)})`);\n return comments;\n }\n\n getCachedComments(videoEventId) {\n const cacheKey = `comments:${videoEventId}`;\n const cached = localStorage.getItem(cacheKey);\n if (!cached) return null;\n try {\n const { comments, timestamp } = JSON.parse(cached);\n if (Date.now() - timestamp < 300000) { // 5min TTL\n devLogger.info(`Loaded cached comments for ${videoEventId}`);\n return comments;\n } else {\n localStorage.removeItem(cacheKey);\n devLogger.info(`Expired cache for ${videoEventId}`);\n }\n } catch (error) {\n devLogger.warn('[cache] Invalid cached comments:', error);\n localStorage.removeItem(cacheKey);\n }\n return null;\n }\n\n cacheComments(videoEventId, comments) {\n const cacheKey = `comments:${videoEventId}`;\n try {\n localStorage.setItem(cacheKey, JSON.stringify({\n comments,\n timestamp: Date.now(),\n }));\n devLogger.info(`Cached ${comments.length} comments for ${videoEventId}`);\n } catch (error) {\n devLogger.warn('[cache] Failed to cache comments:', error);\n }\n }\n\n // Flush cache on teardown (optional, for privacy)\n clearCommentCache(videoEventId) {\n if (videoEventId) {\n localStorage.removeItem(`comments:${videoEventId}`);\n } else {\n Object.keys(localStorage).filter(key => key.startsWith('comments:')).forEach(key => localStorage.removeItem(key));\n }\n devLogger.info('Cleared comment cache');\n } diff --git a/js/ui/videoModalCommentController.js b/js/ui/videoModalCommentController.js index a021a3ab..8c4f7520 100644 --- a/js/ui/videoModalCommentController.js +++ b/js/ui/videoModalCommentController.js @@ -48,6 +48,7 @@ export default class VideoModalCommentController { }; this.callbacks = { +async loadComments(retryCount = 0) {\n if (!FEATURE_IMPROVED_COMMENT_FETCHING) {\n // Fallback to existing sub-only\n this.subscription = subscribeVideoComments(this.client, this.videoTarget, {\n onEvent: (event) => this.addComment(event),\n });\n return;\n }\n\n try {\n const options = { since: 0, limit: 100 }; // Full history, cap for perf\n const comments = await listVideoComments(this.client, this.videoTarget, options);\n devLogger.info(`Loaded ${comments.length} comments for video; filters applied.`, options);\n this.renderCommentThread(comments);\n\n // Subscribe for live updates\n this.subscription = subscribeVideoComments(this.client, this.videoTarget, {\n onEvent: (event) => this.addComment(event),\n });\n\n // Retry if low count (e.g., async propagation)\n if (comments.length < 5 && retryCount < 3) { // Threshold heuristic\n setTimeout(() => this.loadComments(retryCount + 1), 2000);\n }\n } catch (error) {\n devLogger.warn('[comments] Failed to load comments:', error);\n // Fallback sub\n this.subscription = subscribeVideoComments(this.client, this.videoTarget, { onEvent: (event) => this.addComment(event) });\n }\n } showError: typeof callbacks.showError === "function" ? (message) => callbacks.showError(message)