From 2aae4094e77fa08084b0eafd50ba3693759cd98e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 03:43:03 +0000 Subject: [PATCH] [constants] refactor duplicate numeric constants - Add `UI_FEEDBACK_DELAY_MS`, `DEBOUNCE_DELAY_MS`, and `NETWORK_RETRY_DELAY_MS` to `js/constants.js`. - Replace magic numbers (5000, 2000, 1500) with semantic constants in: - `js/ui/notificationController.js` - `js/ui/profileModal/ProfileDirectMessageActions.js` - `js/ui/urlHealthController.js` - `js/app/uiCoordinator.js` - `js/ui/components/VideoModal.js` - `js/ui/components/UploadModal.js` - `js/services/dmNostrService.js` - `js/services/hashtagPreferencesService.js` - `js/app/feedCoordinator.js` - `js/services/moderationService.js` Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/app/feedCoordinator.js | 4 ++-- js/app/uiCoordinator.js | 6 ++++-- js/constants.js | 4 ++++ js/services/dmNostrService.js | 3 ++- js/services/hashtagPreferencesService.js | 5 +++-- js/services/moderationService.js | 3 ++- js/ui/components/UploadModal.js | 3 ++- js/ui/components/VideoModal.js | 3 ++- js/ui/notificationController.js | 5 +++-- js/ui/profileModal/ProfileDirectMessageActions.js | 13 +++++++------ js/ui/urlHealthController.js | 6 ++++-- 11 files changed, 35 insertions(+), 20 deletions(-) diff --git a/js/app/feedCoordinator.js b/js/app/feedCoordinator.js index 616a2e5d..d3437053 100644 --- a/js/app/feedCoordinator.js +++ b/js/app/feedCoordinator.js @@ -9,7 +9,7 @@ * Methods use `this` which is bound to the Application instance. */ -import { FEED_TYPES, STANDARD_TIMEOUT_MS, MEDIUM_TIMEOUT_MS } from "../constants.js"; +import { FEED_TYPES, STANDARD_TIMEOUT_MS, MEDIUM_TIMEOUT_MS, DEBOUNCE_DELAY_MS } from "../constants.js"; /** * @param {object} deps - Injected dependencies. @@ -1056,7 +1056,7 @@ export function createFeedCoordinator(deps) { } const now = Date.now(); - if (this.lastLoadVideosTime && (now - this.lastLoadVideosTime < 2000) && !forceFetch) { + if (this.lastLoadVideosTime && (now - this.lastLoadVideosTime < DEBOUNCE_DELAY_MS) && !forceFetch) { devLogger.log("Skipping redundant loadVideos request (cooldown)."); return Promise.resolve(); } diff --git a/js/app/uiCoordinator.js b/js/app/uiCoordinator.js index 2679346f..e5a71e35 100644 --- a/js/app/uiCoordinator.js +++ b/js/app/uiCoordinator.js @@ -8,6 +8,8 @@ * * Methods use `this` which is bound to the Application instance. */ +import { SHORT_TIMEOUT_MS, DEBOUNCE_DELAY_MS } from "../constants.js"; + export function createUiCoordinator(deps) { const { devLogger, @@ -286,7 +288,7 @@ export function createUiCoordinator(deps) { if ( this.lastExperimentalWarningKey === normalizedProvider && typeof this.lastExperimentalWarningAt === "number" && - now - this.lastExperimentalWarningAt < 2000 + now - this.lastExperimentalWarningAt < DEBOUNCE_DELAY_MS ) { return; } @@ -301,7 +303,7 @@ export function createUiCoordinator(deps) { this.showStatus( `${providerLabel} logins are still in development and may not work well yet. We recommend using a NIP-07 browser extension for the most reliable experience.`, - { showSpinner: false, autoHideMs: 5000 }, + { showSpinner: false, autoHideMs: SHORT_TIMEOUT_MS }, ); } }; diff --git a/js/constants.js b/js/constants.js index 0deab851..d938a20c 100644 --- a/js/constants.js +++ b/js/constants.js @@ -24,6 +24,10 @@ export const STANDARD_TIMEOUT_MS = 10000; export const MEDIUM_TIMEOUT_MS = 30000; export const LONG_TIMEOUT_MS = 60000; +export const UI_FEEDBACK_DELAY_MS = 2000; +export const DEBOUNCE_DELAY_MS = 2000; +export const NETWORK_RETRY_DELAY_MS = 1500; + export const DEFAULT_CACHE_TTL_MS = FIVE_MINUTES_MS; export const PROFILE_CACHE_TTL_MS = TEN_MINUTES_MS; export const URL_HEALTH_TTL_MS = FORTY_FIVE_MINUTES_MS; diff --git a/js/services/dmNostrService.js b/js/services/dmNostrService.js index b1c07390..6769e707 100644 --- a/js/services/dmNostrService.js +++ b/js/services/dmNostrService.js @@ -7,6 +7,7 @@ import { import { sanitizeRelayList } from "../nostr/nip46Client.js"; import { publishEventToRelay } from "../nostrPublish.js"; import logger from "../utils/logger.js"; +import { SHORT_TIMEOUT_MS } from "../constants.js"; const DEFAULT_BACKOFF = { baseMs: 1000, @@ -15,7 +16,7 @@ const DEFAULT_BACKOFF = { }; const MAX_SEEN_EVENT_IDS = 5000; -const DM_RELAY_LIST_TIMEOUT_MS = 5000; +const DM_RELAY_LIST_TIMEOUT_MS = SHORT_TIMEOUT_MS; export const DM_RELAY_WARNING_FALLBACK = "dm-relays-fallback"; function normalizeRelayList(relays) { diff --git a/js/services/hashtagPreferencesService.js b/js/services/hashtagPreferencesService.js index 3bc58cdf..fda53d55 100644 --- a/js/services/hashtagPreferencesService.js +++ b/js/services/hashtagPreferencesService.js @@ -31,6 +31,7 @@ import { setLastSuccessfulScheme, } from "../nostr/decryptionSchemeCache.js"; import { HEX64_REGEX } from "../utils/hex.js"; +import { SHORT_TIMEOUT_MS, NETWORK_RETRY_DELAY_MS } from "../constants.js"; const LOG_PREFIX = "[HashtagPreferences]"; const HASHTAG_IDENTIFIER = "bitvid:tag-preferences"; @@ -43,7 +44,7 @@ const DEFAULT_VERSION = 1; const DECRYPT_TIMEOUT_MS = 6000; // PERF: Reduced from 3s to 1.5s — extensions that already granted permission // should recover near-instantly. Shorter delay speeds up the login path. -const DECRYPT_RETRY_DELAY_MS = 1500; +const DECRYPT_RETRY_DELAY_MS = NETWORK_RETRY_DELAY_MS; class TinyEventEmitter { constructor() { @@ -1052,7 +1053,7 @@ class HashtagPreferencesService { // gate guarantees the extension is ready before decryption starts, so // decrypt calls should complete within 1-2s. 5s accommodates slow // extensions while still failing fast for scheme fallback. - const nip07DecryptTimeoutMs = allowPermissionPrompt ? 12000 : 5000; + const nip07DecryptTimeoutMs = allowPermissionPrompt ? 12000 : SHORT_TIMEOUT_MS; const signerDecryptOptions = { priority: NIP07_PRIORITY.NORMAL, timeoutMs: nip07DecryptTimeoutMs, diff --git a/js/services/moderationService.js b/js/services/moderationService.js index 27ac31f3..a41c7ebd 100644 --- a/js/services/moderationService.js +++ b/js/services/moderationService.js @@ -8,6 +8,7 @@ import { accessControl } from "../accessControl.js"; import { userBlocks, USER_BLOCK_EVENTS } from "../userBlocks.js"; import { buildReportEvent, KIND_MUTE_LIST } from "../nostrEventSchemas.js"; import logger from "../utils/logger.js"; +import { DEBOUNCE_DELAY_MS } from "../constants.js"; /** * Trust threshold for disabling autoplay on reported videos. @@ -1135,7 +1136,7 @@ export class ModerationService { this.refreshTrustedMuteSubscriptions().catch((error) => { this.log("[moderationService] refreshTrustedMuteSubscriptions failed", error); }); - }, 2000); + }, DEBOUNCE_DELAY_MS); } async refreshTrustedMuteSubscriptions() { diff --git a/js/ui/components/UploadModal.js b/js/ui/components/UploadModal.js index 6274d96f..40f446d5 100644 --- a/js/ui/components/UploadModal.js +++ b/js/ui/components/UploadModal.js @@ -23,6 +23,7 @@ import { getActiveSigner, requestDefaultExtensionPermissions, } from "../../nostrClientFacade.js"; +import { UI_FEEDBACK_DELAY_MS } from "../../constants.js"; const INFO_HASH_PATTERN = /^[a-f0-9]{40}$/; @@ -790,7 +791,7 @@ export class UploadModal { if (this.thumbnailUploadId === currentUploadId) { this.sourceSections.thumbnailProgress.classList.add("hidden"); } - }, 2000); + }, UI_FEEDBACK_DELAY_MS); } catch (err) { if (this.thumbnailUploadId !== currentUploadId) return; diff --git a/js/ui/components/VideoModal.js b/js/ui/components/VideoModal.js index 62ee9f6f..1cd9f598 100644 --- a/js/ui/components/VideoModal.js +++ b/js/ui/components/VideoModal.js @@ -47,6 +47,7 @@ import { subscribeToLinkPreviewSettings, } from "../../utils/linkPreviewSettings.js"; import { HEX64_REGEX } from "../../utils/hex.js"; +import { UI_FEEDBACK_DELAY_MS } from "../../constants.js"; const SIMILAR_CONTENT_LIMIT = 10; @@ -1534,7 +1535,7 @@ export class VideoModal { if (button.dataset.feedbackTimeoutId) { delete button.dataset.feedbackTimeoutId; } - }, 2000); + }, UI_FEEDBACK_DELAY_MS); button.dataset.feedbackTimeoutId = String(nextId); } diff --git a/js/ui/notificationController.js b/js/ui/notificationController.js index 5e86b8e0..a382feb8 100644 --- a/js/ui/notificationController.js +++ b/js/ui/notificationController.js @@ -1,4 +1,5 @@ import { userLogger as _fallbackUserLogger, devLogger as _fallbackDevLogger } from "../utils/logger.js"; +import { SHORT_TIMEOUT_MS } from "../constants.js"; export default class NotificationController { constructor({ @@ -98,7 +99,7 @@ export default class NotificationController { container.textContent = ""; container.classList.add("hidden"); this.updateNotificationPortalVisibility(); - }, 5000); + }, SHORT_TIMEOUT_MS); } } @@ -236,7 +237,7 @@ export default class NotificationController { container.textContent = ""; container.classList.add("hidden"); this.updateNotificationPortalVisibility(); - }, 5000); + }, SHORT_TIMEOUT_MS); } } diff --git a/js/ui/profileModal/ProfileDirectMessageActions.js b/js/ui/profileModal/ProfileDirectMessageActions.js index aa942054..ddda863a 100644 --- a/js/ui/profileModal/ProfileDirectMessageActions.js +++ b/js/ui/profileModal/ProfileDirectMessageActions.js @@ -3,6 +3,7 @@ import { extractAttachmentsFromMessage, formatAttachmentSize, describeAttachment import { downloadAttachment, clearAttachmentCache, getAttachmentCacheStats } from "../../services/attachmentService.js"; import { getLinkPreviewSettings, setLinkPreviewAutoFetch } from "../../utils/linkPreviewSettings.js"; import { sanitizeRelayList } from "../../nostr/nip46Client.js"; +import { SHORT_TIMEOUT_MS } from "../../constants.js"; const TYPING_INDICATOR_TTL_SECONDS = 15; const TYPING_INDICATOR_COOLDOWN_MS = 4000; @@ -118,7 +119,7 @@ export class ProfileDirectMessageActions { if (this.controller.enableNip17RelayWarning && enabled && !relayHints.length) { this.mainController.showStatus( "Privacy warning: this recipient has not shared NIP-17 relays, so we'll use your default relays.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } @@ -207,7 +208,7 @@ export class ProfileDirectMessageActions { if (this.controller.enableNip17RelayWarning && useNip17 && !recipientRelayHints.length) { this.mainController.showStatus( "Privacy warning: this recipient has not shared NIP-17 relays, so we'll use your default relays.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } @@ -263,7 +264,7 @@ export class ProfileDirectMessageActions { if (this.controller.enableNip17RelayWarning && result?.warning === "dm-relays-fallback") { this.mainController.showStatus( "Privacy warning: this message used default relays because no NIP-17 relay list was found.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } void this.populateProfileMessages({ force: true, reason: "send-message" }); @@ -494,7 +495,7 @@ export class ProfileDirectMessageActions { if (useNip17 && !recipientRelayHints.length) { this.mainController.showStatus( "Privacy warning: this recipient has not shared NIP-17 relays, so we'll use your default relays.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } @@ -523,7 +524,7 @@ export class ProfileDirectMessageActions { if (result?.warning === "dm-relays-fallback") { this.mainController.showStatus( "Privacy warning: this message used default relays because no NIP-17 relay list was found.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } if ( @@ -804,7 +805,7 @@ export class ProfileDirectMessageActions { this.mainController.showStatus( "Privacy warning: direct messages are using your default relays because no NIP-17 relay list is available.", - { autoHideMs: 5000 }, + { autoHideMs: SHORT_TIMEOUT_MS }, ); } diff --git a/js/ui/urlHealthController.js b/js/ui/urlHealthController.js index 62a7a921..a4503692 100644 --- a/js/ui/urlHealthController.js +++ b/js/ui/urlHealthController.js @@ -1,3 +1,5 @@ +import { SHORT_TIMEOUT_MS } from "../constants.js"; + export default class UrlHealthController { constructor({ state = {}, @@ -249,7 +251,7 @@ export default class UrlHealthController { } async probeUrlWithVideoElement(url, timeoutMs) { - const defaultTimeout = this.constants.URL_PROBE_TIMEOUT_MS || 5000; + const defaultTimeout = this.constants.URL_PROBE_TIMEOUT_MS || SHORT_TIMEOUT_MS; const effectiveTimeout = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : defaultTimeout; @@ -346,7 +348,7 @@ export default class UrlHealthController { } const confirmPlayable = options?.confirmPlayable === true; - const defaultTimeout = this.constants.URL_PROBE_TIMEOUT_MS || 5000; + const defaultTimeout = this.constants.URL_PROBE_TIMEOUT_MS || SHORT_TIMEOUT_MS; const urlHealthConstants = this.constants.urlHealthConstants || {}; const confirmWithVideoElement = async () => {