Split subscriptions/hashtag readiness and expose unavailable preference states

This commit is contained in:
thePR0M3TH3AN committed 2026-02-08 14:10:39 -05:00
1 parent c785894943
commit fd3f9e723a
6 files changed
+246 -36

No files matched your search

+64 -17
View File
@@ -249,7 +249,12 @@ class HashtagPreferencesService {
this.activePubkey = null;
this.eventId = null;
this.eventCreatedAt = null;
this.uiReady = false;
this.dataReady = false;
this.loaded = false;
this.loadedFromCache = false;
this.lastSuccessfulSyncAt = null;
this.lastAttemptAt = null;
this.backgroundLoading = false;
this.preferencesVersion = DEFAULT_VERSION;
this.decryptRetryTimeoutId = null;
@@ -283,7 +288,12 @@ class HashtagPreferencesService {
this.disinterests = new Set();
this.eventId = null;
this.eventCreatedAt = null;
this.uiReady = false;
this.dataReady = false;
this.loaded = false;
this.loadedFromCache = false;
this.lastSuccessfulSyncAt = null;
this.lastAttemptAt = null;
this.backgroundLoading = false;
this.preferencesVersion = DEFAULT_VERSION;
this.lastLoadError = null;
@@ -390,7 +400,10 @@ class HashtagPreferencesService {
this.eventId = cached.eventId || null;
this.eventCreatedAt = cached.createdAt || null;
this.preferencesVersion = cached.version || DEFAULT_VERSION;
this.uiReady = true;
this.dataReady = true;
this.loaded = true;
this.loadedFromCache = true;
this.emitChange("cache-load");
return true;
}
@@ -548,7 +561,12 @@ class HashtagPreferencesService {
this.loadingPubkey = normalized;
try {
return await loadPromise;
await loadPromise;
return {
ok: this.dataReady === true,
error: this.lastLoadError || null,
state: this.getLoadState(),
};
} finally {
if (this.loadPromise === loadPromise) {
this.loadPromise = null;
@@ -560,6 +578,7 @@ class HashtagPreferencesService {
async loadInternal(pubkey, options = {}) {
const normalized = normalizeHexPubkey(pubkey);
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
this.lastAttemptAt = Date.now();
this.lastLoadError = null;
let wasLoadedForUser =
this.activePubkey &&
@@ -573,6 +592,8 @@ class HashtagPreferencesService {
if (!normalized) {
this.reset();
this.uiReady = true;
this.dataReady = true;
this.loaded = true;
return;
}
@@ -606,8 +627,7 @@ class HashtagPreferencesService {
}
return;
}
this.reset();
this.loaded = true;
this.uiReady = true;
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitChange("background-loaded", { background: false });
@@ -628,8 +648,7 @@ class HashtagPreferencesService {
}
return;
}
this.reset();
this.loaded = true;
this.uiReady = true;
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitChange("background-loaded", { background: false });
@@ -720,8 +739,16 @@ class HashtagPreferencesService {
return;
}
this.reset();
this.interests = new Set();
this.disinterests = new Set();
this.eventId = null;
this.eventCreatedAt = null;
this.preferencesVersion = DEFAULT_VERSION;
this.uiReady = true;
this.dataReady = true;
this.loaded = true;
this.loadedFromCache = false;
this.lastSuccessfulSyncAt = Date.now();
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitChange("background-loaded", { background: false });
@@ -764,8 +791,16 @@ class HashtagPreferencesService {
}
return;
}
this.reset();
this.interests = new Set();
this.disinterests = new Set();
this.eventId = null;
this.eventCreatedAt = null;
this.preferencesVersion = DEFAULT_VERSION;
this.uiReady = true;
this.dataReady = true;
this.loaded = true;
this.loadedFromCache = false;
this.lastSuccessfulSyncAt = Date.now();
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitChange("background-loaded", { background: false });
@@ -815,15 +850,13 @@ class HashtagPreferencesService {
if (!decryptResult.ok) {
this.lastLoadError = decryptResult.error || null;
this.uiReady = true;
userLogger.warn(
`${LOG_PREFIX} Failed to decrypt hashtag preferences`,
decryptResult.error,
);
if (decryptResult.error?.code === "hashtag-preferences-decrypt-timeout") {
if (!wasLoadedForUser && !this.loaded) {
this.loaded = true;
}
this.scheduleDecryptRetry(normalized, decryptResult.error, {
allowPermissionPrompt,
});
@@ -833,9 +866,6 @@ class HashtagPreferencesService {
!allowPermissionPrompt &&
decryptResult.error?.code === "hashtag-preferences-permission-required"
) {
if (!wasLoadedForUser && !this.loaded) {
this.loaded = true;
}
this.scheduleDecryptRetry(normalized, decryptResult.error, {
allowPermissionPrompt: true,
});
@@ -859,8 +889,7 @@ class HashtagPreferencesService {
return;
}
this.reset();
this.loaded = true;
this.uiReady = true;
if (wasBackgroundLoading) {
this.backgroundLoading = false;
this.emitChange("background-loaded", { background: false });
@@ -881,7 +910,11 @@ class HashtagPreferencesService {
this.eventCreatedAt = Number.isFinite(latest?.created_at)
? latest.created_at
: null;
this.uiReady = true;
this.dataReady = true;
this.loaded = true;
this.loadedFromCache = false;
this.lastSuccessfulSyncAt = Date.now();
if (this.backgroundLoading) {
this.backgroundLoading = false;
}
@@ -892,11 +925,25 @@ class HashtagPreferencesService {
`${LOG_PREFIX} Failed to parse decrypted hashtag preferences`,
error,
);
this.reset();
this.loaded = true;
this.uiReady = true;
}
}
getLoadState() {
return {
uiReady: this.uiReady === true,
dataReady: this.dataReady === true,
lastLoadError: this.lastLoadError || null,
loadedFromCache: this.loadedFromCache === true,
lastSuccessfulSyncAt: Number.isFinite(this.lastSuccessfulSyncAt)
? Number(this.lastSuccessfulSyncAt)
: null,
lastAttemptAt: Number.isFinite(this.lastAttemptAt)
? Number(this.lastAttemptAt)
: null,
};
}
async decryptEvent(event, userPubkey, options = {}) {
const ciphertext = typeof event?.content === "string" ? event.content : "";
if (!ciphertext) {