mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-01 02:12:26 +00:00
Merge pull request #1987 from PR0M3TH3AN/load-relays-before-fetching-profile-7456935442893055659
Load user relays before fetching profile and feed
This commit is contained in:
3 files changed
+123
-240
No files matched your search
@@ -4080,10 +4080,6 @@ class Application {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const hasVideoParam = urlParams.has("v");
|
||||
|
||||
if (!normalizedView || normalizedView === "most-recent-videos") {
|
||||
setHashView("for-you", { preserveVideoParam: hasVideoParam });
|
||||
}
|
||||
|
||||
const rawProviderId =
|
||||
typeof detail?.providerId === "string" ? detail.providerId.trim() : "";
|
||||
const rawAuthType =
|
||||
@@ -4100,75 +4096,6 @@ class Application {
|
||||
};
|
||||
const activePubkey = detail?.pubkey || this.pubkey;
|
||||
|
||||
Promise.resolve()
|
||||
.then(() =>
|
||||
this.handleModerationSettingsChange({
|
||||
settings: getModerationSettings(),
|
||||
skipRefresh: true,
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"Failed to sync moderation settings after login:",
|
||||
error,
|
||||
);
|
||||
});
|
||||
|
||||
if (loginContext.identityChanged) {
|
||||
this.resetHashtagPreferencesState();
|
||||
try {
|
||||
this.watchHistoryTelemetry?.resetPlaybackLoggingState?.();
|
||||
this.watchHistoryTelemetry?.refreshPreferenceSettings?.();
|
||||
} catch (error) {
|
||||
devLogger.warn(
|
||||
"Failed to refresh watch history telemetry after identity change:",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const hashtagPreferencesPromise = Promise.resolve(
|
||||
this.loadHashtagPreferencesForPubkey(loginContext.pubkey, {
|
||||
allowPermissionPrompt: false,
|
||||
}),
|
||||
).catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Background hashtag preferences load failed:",
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
if (this.zapController) {
|
||||
try {
|
||||
this.zapController.setVisibility(
|
||||
Boolean(this.currentVideo?.lightningAddress),
|
||||
);
|
||||
} catch (error) {
|
||||
devLogger.warn("[Application] Failed to refresh zap visibility after login:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const shouldReopenZap = this.pendingModalZapOpen;
|
||||
this.pendingModalZapOpen = false;
|
||||
if (shouldReopenZap) {
|
||||
const hasLightning = Boolean(this.currentVideo?.lightningAddress);
|
||||
if (hasLightning && this.videoModal?.openZapDialog) {
|
||||
Promise.resolve()
|
||||
.then(() => this.videoModal.openZapDialog())
|
||||
.then((opened) => {
|
||||
if (opened) {
|
||||
this.zapController?.open();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to reopen zap dialog after login:",
|
||||
error,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const initialLoadingState = {
|
||||
profile: activePubkey ? "loading" : "idle",
|
||||
lists: activePubkey ? "loading" : "idle",
|
||||
@@ -4184,28 +4111,6 @@ class Application {
|
||||
authLoadingState: this.authLoadingState,
|
||||
});
|
||||
|
||||
const accessControlReadyPromise =
|
||||
accessControl && typeof accessControl.ensureReady === "function"
|
||||
? Promise.resolve()
|
||||
.then(() => accessControl.ensureReady())
|
||||
.catch((error) => {
|
||||
userLogger.error(
|
||||
"[Application] Failed to refresh admin lists after login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
})
|
||||
: null;
|
||||
|
||||
const scheduleAfterFirstRender = () =>
|
||||
new Promise((resolve) => {
|
||||
if (typeof requestAnimationFrame === "function") {
|
||||
requestAnimationFrame(() => requestAnimationFrame(resolve));
|
||||
} else {
|
||||
setTimeout(resolve, 0);
|
||||
}
|
||||
});
|
||||
|
||||
const cachedProfile =
|
||||
detail?.postLogin && typeof detail.postLogin === "object"
|
||||
? detail.postLogin.profile || null
|
||||
@@ -4221,78 +4126,6 @@ class Application {
|
||||
}
|
||||
}
|
||||
|
||||
const startDmHydration = () => {
|
||||
const dmTasks = [];
|
||||
if (activePubkey) {
|
||||
dmTasks.push(
|
||||
Promise.resolve()
|
||||
.then(() =>
|
||||
this.nostrService.loadDirectMessages({
|
||||
actorPubkey: activePubkey,
|
||||
limit: 50,
|
||||
decryptLimit: 50,
|
||||
initialLoad: true,
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to sync direct messages during login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
typeof this.nostrService.ensureDirectMessageSubscription === "function"
|
||||
) {
|
||||
dmTasks.push(
|
||||
Promise.resolve()
|
||||
.then(() =>
|
||||
this.nostrService.ensureDirectMessageSubscription({
|
||||
actorPubkey: activePubkey,
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
devLogger.warn(
|
||||
"[Application] Failed to subscribe to direct messages during login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return dmTasks.length
|
||||
? Promise.allSettled(dmTasks).then((results) => {
|
||||
const success = results.every(
|
||||
(result) => result.status === "fulfilled",
|
||||
);
|
||||
this.updateAuthLoadingState({
|
||||
dms: success ? "ready" : "error",
|
||||
});
|
||||
return success;
|
||||
})
|
||||
: Promise.resolve(this.updateAuthLoadingState({ dms: "idle" }));
|
||||
};
|
||||
|
||||
const dmStatePromise = Promise.resolve()
|
||||
.then(() => scheduleAfterFirstRender())
|
||||
.then(() => startDmHydration())
|
||||
.catch((error) => {
|
||||
devLogger.warn("[Application] Delayed DM hydration failed:", error);
|
||||
this.updateAuthLoadingState({ dms: "error" });
|
||||
return false;
|
||||
});
|
||||
|
||||
const nwcPromise = Promise.resolve()
|
||||
.then(() => this.nwcSettingsService.onLogin(loginContext))
|
||||
.catch((error) => {
|
||||
devLogger.error("Failed to process NWC settings during login:", error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (this.profileController) {
|
||||
try {
|
||||
const maybePromise = this.profileController.handleAuthLogin(detail);
|
||||
@@ -4314,8 +4147,22 @@ class Application {
|
||||
this.renderSavedProfiles();
|
||||
}
|
||||
|
||||
const accessControlReadyPromise =
|
||||
accessControl && typeof accessControl.ensureReady === "function"
|
||||
? Promise.resolve()
|
||||
.then(() => accessControl.ensureReady())
|
||||
.catch((error) => {
|
||||
userLogger.error(
|
||||
"[Application] Failed to refresh admin lists after login:",
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
})
|
||||
: null;
|
||||
|
||||
const profileStatePromise = Promise.resolve(postLoginPromise)
|
||||
.then((postLogin) => {
|
||||
.then(async (postLogin) => {
|
||||
// 1. Relays and Profile are now loaded (sequentially or efficiently by authService)
|
||||
const nextProfile = postLogin?.profile || cachedProfile;
|
||||
if (activePubkey && nextProfile) {
|
||||
this.updateActiveProfileUI(activePubkey, nextProfile);
|
||||
|
||||
+68
-64
@@ -697,79 +697,83 @@ export default class AuthService {
|
||||
return { detail, completionPromise: Promise.resolve(detail) };
|
||||
}
|
||||
|
||||
const runOperation = async (operation) => {
|
||||
try {
|
||||
const value = await operation.promise;
|
||||
const resolved = operation.onFulfilled(value);
|
||||
detail[operation.name] = resolved;
|
||||
if (typeof operation.emit === "function") {
|
||||
operation.emit(resolved, null);
|
||||
}
|
||||
return resolved;
|
||||
} catch (error) {
|
||||
const resolved = operation.onRejected(error);
|
||||
detail[operation.name] = resolved;
|
||||
if (typeof operation.emit === "function") {
|
||||
operation.emit(resolved, error);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
};
|
||||
|
||||
const schedule = (callback) => Promise.resolve().then(() => callback());
|
||||
|
||||
const operations = [];
|
||||
|
||||
if (!deferBlocks) {
|
||||
const blocksOperation = this.createBlocksLoadOperation(activePubkey, schedule);
|
||||
if (blocksOperation) {
|
||||
operations.push(blocksOperation);
|
||||
const completionPromise = (async () => {
|
||||
if (this.relayManager && typeof this.relayManager.loadRelayList === "function") {
|
||||
const relayOp = {
|
||||
name: "relaysLoaded",
|
||||
promise: schedule(() => this.relayManager.loadRelayList(activePubkey)),
|
||||
onFulfilled: () => true,
|
||||
onRejected: (error) => {
|
||||
this.log("[AuthService] Failed to load relay list", error);
|
||||
return false;
|
||||
},
|
||||
emit: (value, error) => {
|
||||
this.emit("relaysLoaded", {
|
||||
pubkey: activePubkey,
|
||||
relaysLoaded: value,
|
||||
error: error ?? null,
|
||||
});
|
||||
},
|
||||
};
|
||||
await runOperation(relayOp);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.relayManager && typeof this.relayManager.loadRelayList === "function") {
|
||||
operations.push({
|
||||
name: "relaysLoaded",
|
||||
promise: schedule(() => this.relayManager.loadRelayList(activePubkey)),
|
||||
onFulfilled: () => true,
|
||||
onRejected: (error) => {
|
||||
this.log("[AuthService] Failed to load relay list", error);
|
||||
return false;
|
||||
},
|
||||
emit: (value, error) => {
|
||||
this.emit("relaysLoaded", {
|
||||
pubkey: activePubkey,
|
||||
relaysLoaded: value,
|
||||
error: error ?? null,
|
||||
const concurrentOps = [];
|
||||
|
||||
if (!deferBlocks) {
|
||||
const blocksOperation = this.createBlocksLoadOperation(activePubkey, schedule);
|
||||
if (blocksOperation) {
|
||||
concurrentOps.push(blocksOperation);
|
||||
}
|
||||
}
|
||||
|
||||
concurrentOps.push({
|
||||
name: "profile",
|
||||
promise: schedule(() => this.loadOwnProfile(activePubkey)),
|
||||
onFulfilled: (value) => {
|
||||
const profile = value || FALLBACK_PROFILE;
|
||||
this.setProfileCacheEntry(activePubkey, profile, {
|
||||
persist: true,
|
||||
reason: "post-login",
|
||||
});
|
||||
return profile;
|
||||
},
|
||||
onRejected: (error) => {
|
||||
this.log("[AuthService] Failed to load own profile", error);
|
||||
const fallbackProfile = cachedProfile || FALLBACK_PROFILE;
|
||||
this.setProfileCacheEntry(activePubkey, fallbackProfile, {
|
||||
persist: true,
|
||||
reason: "post-login-fallback",
|
||||
});
|
||||
return fallbackProfile;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
operations.push({
|
||||
name: "profile",
|
||||
promise: schedule(() => this.loadOwnProfile(activePubkey)),
|
||||
onFulfilled: (value) => {
|
||||
const profile = value || FALLBACK_PROFILE;
|
||||
this.setProfileCacheEntry(activePubkey, profile, {
|
||||
persist: true,
|
||||
reason: "post-login",
|
||||
});
|
||||
return profile;
|
||||
},
|
||||
onRejected: (error) => {
|
||||
this.log("[AuthService] Failed to load own profile", error);
|
||||
const fallbackProfile = cachedProfile || FALLBACK_PROFILE;
|
||||
this.setProfileCacheEntry(activePubkey, fallbackProfile, {
|
||||
persist: true,
|
||||
reason: "post-login-fallback",
|
||||
});
|
||||
return fallbackProfile;
|
||||
},
|
||||
});
|
||||
await Promise.allSettled(concurrentOps.map(runOperation));
|
||||
|
||||
const wrappedPromises = operations.map((operation) =>
|
||||
operation.promise
|
||||
.then((value) => {
|
||||
const resolved = operation.onFulfilled(value);
|
||||
detail[operation.name] = resolved;
|
||||
if (typeof operation.emit === "function") {
|
||||
operation.emit(resolved, null);
|
||||
}
|
||||
return resolved;
|
||||
})
|
||||
.catch((error) => {
|
||||
const resolved = operation.onRejected(error);
|
||||
detail[operation.name] = resolved;
|
||||
if (typeof operation.emit === "function") {
|
||||
operation.emit(resolved, error);
|
||||
}
|
||||
return resolved;
|
||||
}),
|
||||
);
|
||||
|
||||
const completionPromise = Promise.allSettled(wrappedPromises).then(() => detail);
|
||||
return detail;
|
||||
})();
|
||||
|
||||
return { detail, completionPromise };
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
setActiveProfilePubkey,
|
||||
getProfileCacheMap,
|
||||
} from "../js/state/cache.js";
|
||||
import { profileCache } from "../js/state/profileCache.js";
|
||||
|
||||
if (typeof globalThis.window === "undefined") {
|
||||
globalThis.window = {};
|
||||
@@ -61,6 +62,12 @@ function resetState() {
|
||||
if (cache && typeof cache.clear === "function") {
|
||||
cache.clear();
|
||||
}
|
||||
if (profileCache && typeof profileCache.reset === "function") {
|
||||
profileCache.reset();
|
||||
}
|
||||
if (typeof localStorage !== "undefined") {
|
||||
localStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
await (async () => {
|
||||
@@ -94,13 +101,15 @@ await (async () => {
|
||||
|
||||
const promise = service.applyPostLoginState();
|
||||
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
assert.deepEqual(calls, ["blocks:start", "relays:start", "profile:start"]);
|
||||
assert.deepEqual(calls, ["relays:start", "blocks:start", "profile:start"]);
|
||||
|
||||
resolveBlocks();
|
||||
|
||||
const detail = await promise;
|
||||
const result = await promise;
|
||||
const detail = result.detail;
|
||||
await result.completionPromise;
|
||||
|
||||
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
||||
assert.equal(detail.blocksLoaded, true);
|
||||
@@ -130,12 +139,22 @@ await (async () => {
|
||||
|
||||
service.loadOwnProfile = () => Promise.reject(new Error("profile failed"));
|
||||
|
||||
const detail = await service.applyPostLoginState();
|
||||
const result = service.applyPostLoginState();
|
||||
const detail = result.detail;
|
||||
await result.completionPromise;
|
||||
|
||||
assert.equal(detail.pubkey, SAMPLE_PUBKEY);
|
||||
assert.equal(detail.blocksLoaded, false);
|
||||
assert.equal(detail.relaysLoaded, false);
|
||||
assert.equal(detail.profile, null);
|
||||
assert.deepEqual(detail.profile, {
|
||||
name: "Unknown",
|
||||
picture: "assets/svg/default-profile.svg",
|
||||
about: "",
|
||||
website: "",
|
||||
banner: "",
|
||||
lud16: "",
|
||||
lud06: "",
|
||||
});
|
||||
|
||||
assert.equal(logs.length, 3);
|
||||
assert(logs.some(({ message }) => message.includes("block list")));
|
||||
@@ -147,13 +166,23 @@ await (async () => {
|
||||
resetState();
|
||||
|
||||
const service = new AuthService();
|
||||
const detail = await service.applyPostLoginState();
|
||||
const result = service.applyPostLoginState();
|
||||
const detail = result.detail;
|
||||
await result.completionPromise;
|
||||
|
||||
assert.deepEqual(detail, {
|
||||
pubkey: null,
|
||||
blocksLoaded: false,
|
||||
relaysLoaded: false,
|
||||
profile: null,
|
||||
profile: {
|
||||
name: "Unknown",
|
||||
picture: "assets/svg/default-profile.svg",
|
||||
about: "",
|
||||
website: "",
|
||||
banner: "",
|
||||
lud16: "",
|
||||
lud06: "",
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -168,7 +197,10 @@ await (async () => {
|
||||
profile: { name: "Stub" },
|
||||
};
|
||||
|
||||
service.applyPostLoginState = async () => postLogin;
|
||||
service.applyPostLoginState = () => ({
|
||||
detail: postLogin,
|
||||
completionPromise: Promise.resolve(postLogin),
|
||||
});
|
||||
|
||||
const detail = await service.login(SAMPLE_PUBKEY);
|
||||
|
||||
|
||||
Reference in new issue
Block a user