fix: resolve coordinator refactoring regressions breaking DM, lists, and feed pipeline

- Remove undefined hashtagPreferencesPromise reference in handleAuthLogin
  that caused the feed sync Promise.allSettled to silently fail, preventing
  proper list/profile synchronization before grid refresh
- Add missing getModerationSettings and getActiveProfilePubkey dependency
  injection to authSessionCoordinator, fixing crashes during logout
  moderation reset and profile switch pubkey resolution
- Parallelize NIP-07 permission request with relay readiness instead of
  gating list loading behind it; parallelize subscriptions and hashtag
  preference loading (independent of each other, only blocks must sequence)
- Add native run() alias to feedEngine so callers don't rely solely on
  the applicationBootstrap compatibility shim
- Remove redundant refreshVisibleModerationUi call from renderVideoList
  that re-decorated every video immediately after decoration
- Avoid re-normalizing moderation settings on every getActiveModerationThresholds
  call when settings are already an object

https://claude.ai/code/session_01FCosieUXjEn95KtUeyGemY
This commit is contained in:
Claude committed 2026-02-06 21:42:49 +00:00
1 parent 31c99f06e6
commit 3a55e1bf03
5 files changed
+71 -63

No files matched your search

+2
View File
@@ -603,6 +603,8 @@ class Application {
assertAnyRelayAccepted,
queueSignEvent,
bootstrapTrustedSeeds,
getModerationSettings,
getActiveProfilePubkey,
})),
writable: true,
configurable: true,
+64 -46
View File
@@ -190,27 +190,34 @@ export function createAuthSessionCoordinator(deps) {
return null;
});
// PRE-AUTH: Request full permissions once so subsequent services don't
// spam individual prompts. This runs immediately (not gated behind
// relaysReadyPromise) so the user can approve while relays connect.
const permissionPromise = activePubkey
? Promise.resolve()
.then(async () => {
const signer = await nostrClient.ensureActiveSignerForPubkey(
activePubkey,
);
if (signer?.type === "extension" || signer?.type === "nip07") {
await nostrClient.ensureExtensionPermissions(
DEFAULT_NIP07_PERMISSION_METHODS,
);
}
})
.catch((error) => {
devLogger.warn("[Application] Pre-authorization failed:", error);
})
: Promise.resolve();
// Start list loading as soon as relays are ready — lists don't depend on
// profile data, only on relay URLs. This runs in parallel with profile
// fetching instead of sequentially after it.
const listStatePromise = relaysReadyPromise.then(async () => {
// PRE-AUTH: Request full permissions once so subsequent services don't spam prompts.
if (activePubkey) {
try {
const signer = await nostrClient.ensureActiveSignerForPubkey(
activePubkey,
);
if (signer?.type === "extension" || signer?.type === "nip07") {
await nostrClient.ensureExtensionPermissions(
DEFAULT_NIP07_PERMISSION_METHODS,
);
}
} catch (error) {
devLogger.warn("[Application] Pre-authorization failed:", error);
}
}
// SEQUENTIAL LOAD: Ensure critical lists (blocks) load before content filters.
const listStatePromise = Promise.all([
relaysReadyPromise,
permissionPromise,
]).then(async () => {
// SEQUENTIAL LOAD: Blocks must load first (foundation for all filtering).
if (
activePubkey &&
typeof this.authService.loadBlocksForPubkey === "function"
@@ -227,23 +234,31 @@ export function createAuthSessionCoordinator(deps) {
}
}
// Subscriptions and hashtag preferences are independent of each other,
// so load them in parallel after blocks are ready.
const parallelListTasks = [];
if (
activePubkey &&
subscriptions &&
typeof subscriptions.ensureLoaded === "function"
) {
try {
await subscriptions.ensureLoaded(activePubkey, {
allowPermissionPrompt: false,
});
this.capturePermissionPromptFromError(subscriptions?.lastLoadError);
} catch (error) {
devLogger.warn(
"[Application] Failed to load subscriptions during login:",
error,
);
this.capturePermissionPromptFromError(error);
}
parallelListTasks.push(
subscriptions
.ensureLoaded(activePubkey, { allowPermissionPrompt: false })
.then(() => {
this.capturePermissionPromptFromError(
subscriptions?.lastLoadError,
);
})
.catch((error) => {
devLogger.warn(
"[Application] Failed to load subscriptions during login:",
error,
);
this.capturePermissionPromptFromError(error);
}),
);
}
if (
@@ -251,23 +266,27 @@ export function createAuthSessionCoordinator(deps) {
this.hashtagPreferences &&
typeof this.hashtagPreferences.load === "function"
) {
try {
await this.hashtagPreferences.load(activePubkey, {
allowPermissionPrompt: false,
});
this.capturePermissionPromptFromError(
this.hashtagPreferences?.lastLoadError,
);
this.updateCachedHashtagPreferences();
} catch (error) {
devLogger.warn(
"[Application] Failed to load hashtag preferences during login:",
error,
);
this.capturePermissionPromptFromError(error);
}
parallelListTasks.push(
this.hashtagPreferences
.load(activePubkey, { allowPermissionPrompt: false })
.then(() => {
this.capturePermissionPromptFromError(
this.hashtagPreferences?.lastLoadError,
);
this.updateCachedHashtagPreferences();
})
.catch((error) => {
devLogger.warn(
"[Application] Failed to load hashtag preferences during login:",
error,
);
this.capturePermissionPromptFromError(error);
}),
);
}
await Promise.allSettled(parallelListTasks);
this.updateAuthLoadingState({ lists: "ready" });
return true;
});
@@ -370,7 +389,6 @@ export function createAuthSessionCoordinator(deps) {
const feedSyncPromise = Promise.race([
Promise.allSettled([
listStatePromise,
hashtagPreferencesPromise,
profileStatePromise,
]),
new Promise((resolve) => setTimeout(resolve, FEED_SYNC_TIMEOUT_MS)),
-15
View File
@@ -1962,21 +1962,6 @@ export function createFeedCoordinator(deps) {
this.videoListView.render(decoratedVideos, metadata);
this.emitFeedImpressions(decoratedVideos);
if (typeof this.refreshVisibleModerationUi === "function") {
const renderReason =
metadata && typeof metadata.reason === "string" && metadata.reason
? `render-${metadata.reason}`
: "render-video-list";
try {
this.refreshVisibleModerationUi({ reason: renderReason });
} catch (error) {
devLogger.warn(
"[Application] Failed to refresh moderation UI after rendering video list:",
error,
);
}
}
this.updateModalSimilarContent();
},
+4 -2
View File
@@ -179,8 +179,10 @@ export function createModerationCoordinator(deps) {
},
getActiveModerationThresholds() {
this.moderationSettings = this.moderationDecorator.normalizeModerationSettings(this.moderationSettings);
return { ...this.moderationSettings };
if (!this.moderationSettings || typeof this.moderationSettings !== "object") {
this.moderationSettings = this.moderationDecorator.normalizeModerationSettings(this.moderationSettings);
}
return this.moderationSettings;
},
decorateVideoModeration(video, feedContext = {}) {
+1
View File
@@ -246,6 +246,7 @@ export function createFeedEngine({ logger } = {}) {
return {
registerFeed,
runFeed,
run: runFeed,
listFeeds,
getFeedDefinition,
};