mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Split subscriptions/hashtag readiness and expose unavailable preference states
This commit is contained in:
1 parent
c785894943
commit
fd3f9e723a
6 files changed
+246
-36
No files matched your search
@@ -2223,6 +2223,22 @@ class Application {
|
||||
(detail?.loaded === false
|
||||
? false
|
||||
: Boolean(service && service.loaded));
|
||||
const uiReady =
|
||||
detail?.uiReady === true ||
|
||||
(detail?.uiReady === false
|
||||
? false
|
||||
: Boolean(service && service.uiReady));
|
||||
const dataReady =
|
||||
detail?.dataReady === true ||
|
||||
(detail?.dataReady === false
|
||||
? false
|
||||
: Boolean(service && service.dataReady));
|
||||
const loadedFromCache =
|
||||
detail?.loadedFromCache === true ||
|
||||
(detail?.loadedFromCache === false
|
||||
? false
|
||||
: Boolean(service && service.loadedFromCache));
|
||||
const lastLoadError = detail?.lastLoadError || service?.lastLoadError || null;
|
||||
|
||||
return {
|
||||
interests: this.normalizeHashtagPreferenceList(sourceInterests),
|
||||
@@ -2230,6 +2246,10 @@ class Application {
|
||||
eventId: rawEventId || null,
|
||||
createdAt,
|
||||
loaded,
|
||||
uiReady,
|
||||
dataReady,
|
||||
loadedFromCache,
|
||||
lastLoadError,
|
||||
action,
|
||||
};
|
||||
}
|
||||
@@ -2249,8 +2269,22 @@ class Application {
|
||||
? Number(snapshot.createdAt)
|
||||
: "";
|
||||
const loaded = snapshot.loaded === true ? "1" : "0";
|
||||
const uiReady = snapshot.uiReady === true ? "1" : "0";
|
||||
const dataReady = snapshot.dataReady === true ? "1" : "0";
|
||||
const loadedFromCache = snapshot.loadedFromCache === true ? "1" : "0";
|
||||
const lastLoadError = snapshot?.lastLoadError?.code || "";
|
||||
|
||||
return [interests, disinterests, eventId, createdAt, loaded].join("|");
|
||||
return [
|
||||
interests,
|
||||
disinterests,
|
||||
eventId,
|
||||
createdAt,
|
||||
loaded,
|
||||
uiReady,
|
||||
dataReady,
|
||||
loadedFromCache,
|
||||
lastLoadError,
|
||||
].join("|");
|
||||
}
|
||||
|
||||
updateCachedHashtagPreferences(detail = {}) {
|
||||
@@ -2264,6 +2298,10 @@ class Application {
|
||||
eventId: snapshot.eventId,
|
||||
createdAt: snapshot.createdAt,
|
||||
loaded: snapshot.loaded,
|
||||
uiReady: snapshot.uiReady,
|
||||
dataReady: snapshot.dataReady,
|
||||
loadedFromCache: snapshot.loadedFromCache,
|
||||
lastLoadError: snapshot.lastLoadError,
|
||||
action: snapshot.action,
|
||||
};
|
||||
this.hashtagPreferencesSnapshotSignature = signature;
|
||||
@@ -2334,6 +2372,10 @@ class Application {
|
||||
? Number(snapshot.createdAt)
|
||||
: null,
|
||||
loaded: snapshot.loaded === true,
|
||||
uiReady: snapshot.uiReady === true,
|
||||
dataReady: snapshot.dataReady === true,
|
||||
loadedFromCache: snapshot.loadedFromCache === true,
|
||||
lastLoadError: snapshot.lastLoadError || null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -523,6 +523,7 @@ export function createFeedCoordinator(deps) {
|
||||
? this.getHashtagPreferences()
|
||||
: {};
|
||||
const { interests = [], disinterests = [] } = preferenceSource || {};
|
||||
const preferencesAvailable = preferenceSource?.dataReady === true;
|
||||
const moderationThresholds = this.getActiveModerationThresholds();
|
||||
|
||||
const watchedKeys = new Set();
|
||||
@@ -544,6 +545,7 @@ export function createFeedCoordinator(deps) {
|
||||
tagPreferences: {
|
||||
interests: Array.isArray(interests) ? [...interests] : [],
|
||||
disinterests: Array.isArray(disinterests) ? [...disinterests] : [],
|
||||
available: preferencesAvailable,
|
||||
},
|
||||
moderationThresholds: moderationThresholds
|
||||
? { ...moderationThresholds }
|
||||
@@ -606,6 +608,7 @@ export function createFeedCoordinator(deps) {
|
||||
? this.getHashtagPreferences()
|
||||
: {};
|
||||
const { interests = [], disinterests = [] } = preferenceSource || {};
|
||||
const preferencesAvailable = preferenceSource?.dataReady === true;
|
||||
const moderationThresholds = this.getActiveModerationThresholds();
|
||||
|
||||
const watchHistorySource =
|
||||
@@ -636,6 +639,7 @@ export function createFeedCoordinator(deps) {
|
||||
tagPreferences: {
|
||||
interests: Array.isArray(interests) ? [...interests] : [],
|
||||
disinterests: Array.isArray(disinterests) ? [...disinterests] : [],
|
||||
available: preferencesAvailable,
|
||||
},
|
||||
watchHistoryTagCounts,
|
||||
exploreTagIdf,
|
||||
|
||||
@@ -341,6 +341,14 @@ function filterByTagPreferences({
|
||||
enforceInterests,
|
||||
}) {
|
||||
const tagPreferences = context?.runtime?.tagPreferences;
|
||||
if (tagPreferences?.available === false) {
|
||||
context?.addWhy?.({
|
||||
stage: stageName,
|
||||
type: "status",
|
||||
reason: "preferences-unavailable",
|
||||
});
|
||||
return items;
|
||||
}
|
||||
const interests = normalizeTagSet(tagPreferences?.interests);
|
||||
const disinterests = normalizeTagSet(tagPreferences?.disinterests);
|
||||
if (!interests.size && !disinterests.size) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+56
-16
@@ -349,7 +349,12 @@ class SubscriptionsManager {
|
||||
this.subsEventId = null;
|
||||
this.subsEventCreatedAt = null;
|
||||
this.currentUserPubkey = null;
|
||||
this.uiReady = false;
|
||||
this.dataReady = false;
|
||||
this.loaded = false;
|
||||
this.loadedFromCache = false;
|
||||
this.lastSuccessfulSyncAt = null;
|
||||
this.lastAttemptAt = null;
|
||||
this.loadingPromise = null;
|
||||
this.backgroundLoading = false;
|
||||
this.subscriptionListView = null;
|
||||
@@ -394,6 +399,7 @@ class SubscriptionsManager {
|
||||
|
||||
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
|
||||
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
|
||||
this.lastAttemptAt = Date.now();
|
||||
|
||||
// 1. Attempt to load from cache first
|
||||
const cached = profileCache.getProfileData(normalizedUserPubkey, "subscriptions");
|
||||
@@ -404,7 +410,10 @@ class SubscriptionsManager {
|
||||
this.subsEventId = cachedSnapshot.eventId;
|
||||
this.subsEventCreatedAt = cachedSnapshot.createdAt;
|
||||
this.currentUserPubkey = normalizedUserPubkey;
|
||||
this.uiReady = true;
|
||||
this.dataReady = true;
|
||||
this.loaded = true;
|
||||
this.loadedFromCache = true;
|
||||
|
||||
// Trigger background update
|
||||
if (!allowPermissionPrompt && !this.backgroundLoading) {
|
||||
@@ -468,6 +477,7 @@ class SubscriptionsManager {
|
||||
if (!userPubkey) return;
|
||||
|
||||
try {
|
||||
this.lastAttemptAt = Date.now();
|
||||
const allowPermissionPrompt = options?.allowPermissionPrompt !== false;
|
||||
const wasBackgroundLoading = this.backgroundLoading;
|
||||
const normalizedUserPubkey = normalizeHexPubkey(userPubkey) || userPubkey;
|
||||
@@ -481,6 +491,7 @@ class SubscriptionsManager {
|
||||
devLogger.warn(
|
||||
"[SubscriptionsManager] No relay URLs available while loading subscriptions.",
|
||||
);
|
||||
this.uiReady = true;
|
||||
if (wasBackgroundLoading) {
|
||||
this.backgroundLoading = false;
|
||||
this.emitter.emit("change", {
|
||||
@@ -583,11 +594,20 @@ class SubscriptionsManager {
|
||||
this.subscribedPubkeys.clear();
|
||||
this.subsEventId = null;
|
||||
this.subsEventCreatedAt = null;
|
||||
this.uiReady = true;
|
||||
this.dataReady = true;
|
||||
this.loaded = true;
|
||||
this.loadedFromCache = false;
|
||||
this.lastSuccessfulSyncAt = Date.now();
|
||||
} else {
|
||||
// We have data loaded, and relays returned nothing.
|
||||
// This means no updates. We keep what we have.
|
||||
devLogger.log("[SubscriptionsManager] No updates from relays.");
|
||||
this.uiReady = true;
|
||||
this.dataReady = true;
|
||||
this.loaded = true;
|
||||
this.loadedFromCache = false;
|
||||
this.lastSuccessfulSyncAt = Date.now();
|
||||
}
|
||||
if (wasBackgroundLoading) {
|
||||
this.backgroundLoading = false;
|
||||
@@ -636,10 +656,8 @@ class SubscriptionsManager {
|
||||
|
||||
if (!decryptResult.ok) {
|
||||
this.lastLoadError = decryptResult.error || null;
|
||||
this.uiReady = true;
|
||||
if (decryptResult.error?.code === "subscriptions-decrypt-timeout") {
|
||||
if (!this.loaded && !cachedSnapshot.hasSnapshot) {
|
||||
this.loaded = true;
|
||||
}
|
||||
this.scheduleDecryptRetry(normalizedUserPubkey, decryptResult.error, {
|
||||
allowPermissionPrompt,
|
||||
});
|
||||
@@ -649,9 +667,6 @@ class SubscriptionsManager {
|
||||
!allowPermissionPrompt &&
|
||||
decryptResult.error?.code === "subscriptions-permission-required"
|
||||
) {
|
||||
if (!this.loaded && !cachedSnapshot.hasSnapshot) {
|
||||
this.loaded = true;
|
||||
}
|
||||
// Retry with permission prompt enabled so the signer / window.nostr
|
||||
// path is available once the extension has finished initializing.
|
||||
this.scheduleDecryptRetry(normalizedUserPubkey, decryptResult.error, {
|
||||
@@ -690,6 +705,7 @@ class SubscriptionsManager {
|
||||
this.subscribedPubkeys.clear();
|
||||
this.subsEventId = null;
|
||||
this.subsEventCreatedAt = null;
|
||||
this.dataReady = false;
|
||||
this.loaded = true;
|
||||
}
|
||||
this.scheduleDecryptRetry(normalizedUserPubkey, decryptResult.error, {
|
||||
@@ -707,7 +723,11 @@ class SubscriptionsManager {
|
||||
// Update state
|
||||
this.subscribedPubkeys = newSet;
|
||||
this.currentUserPubkey = normalizedUserPubkey;
|
||||
this.uiReady = true;
|
||||
this.dataReady = true;
|
||||
this.loaded = true;
|
||||
this.loadedFromCache = false;
|
||||
this.lastSuccessfulSyncAt = Date.now();
|
||||
if (wasBackgroundLoading) {
|
||||
this.backgroundLoading = false;
|
||||
}
|
||||
@@ -747,6 +767,7 @@ class SubscriptionsManager {
|
||||
|
||||
} catch (err) {
|
||||
this.lastLoadError = err || null;
|
||||
this.uiReady = true;
|
||||
userLogger.error("[SubscriptionsManager] Failed to update subs from relays:", err);
|
||||
}
|
||||
}
|
||||
@@ -757,7 +778,12 @@ class SubscriptionsManager {
|
||||
this.subsEventId = null;
|
||||
this.subsEventCreatedAt = null;
|
||||
this.currentUserPubkey = null;
|
||||
this.uiReady = false;
|
||||
this.dataReady = false;
|
||||
this.loaded = false;
|
||||
this.loadedFromCache = false;
|
||||
this.lastSuccessfulSyncAt = null;
|
||||
this.lastAttemptAt = null;
|
||||
this.backgroundLoading = false;
|
||||
this.lastRunOptions = null;
|
||||
this.lastResult = null;
|
||||
@@ -823,12 +849,12 @@ class SubscriptionsManager {
|
||||
devLogger.log("[SubscriptionsManager] ensureLoaded start", actorHex);
|
||||
const normalizedActor = normalizeHexPubkey(actorHex) || actorHex;
|
||||
if (!normalizedActor) {
|
||||
return;
|
||||
return { ok: false, error: new Error("Missing actor pubkey."), state: this.getLoadState() };
|
||||
}
|
||||
|
||||
if (this.loaded && this.currentUserPubkey === normalizedActor) {
|
||||
if (this.dataReady && this.currentUserPubkey === normalizedActor) {
|
||||
devLogger.log("[SubscriptionsManager] ensureLoaded already loaded");
|
||||
return;
|
||||
return { ok: true, error: null, state: this.getLoadState() };
|
||||
}
|
||||
|
||||
if (this.loadingPromise) {
|
||||
@@ -836,9 +862,9 @@ class SubscriptionsManager {
|
||||
devLogger.log("[SubscriptionsManager] ensureLoaded awaiting existing promise");
|
||||
await this.loadingPromise;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
return { ok: false, error, state: this.getLoadState() };
|
||||
}
|
||||
return;
|
||||
return { ok: this.dataReady === true, error: this.lastLoadError || null, state: this.getLoadState() };
|
||||
}
|
||||
|
||||
const loader = this.loadSubscriptions(normalizedActor, options);
|
||||
@@ -869,16 +895,30 @@ class SubscriptionsManager {
|
||||
),
|
||||
]);
|
||||
devLogger.log("[SubscriptionsManager] ensureLoaded success");
|
||||
return { ok: this.dataReady === true, error: this.lastLoadError || null, state: this.getLoadState() };
|
||||
} catch (error) {
|
||||
userLogger.warn("[SubscriptionsManager] ensureLoaded timed out or failed:", error);
|
||||
// Mark as loaded so the UI can proceed. The background loader will
|
||||
// continue and emit a "change" event if it eventually succeeds.
|
||||
if (!this.loaded) {
|
||||
this.loaded = true;
|
||||
}
|
||||
this.lastLoadError = this.lastLoadError || error;
|
||||
this.uiReady = true;
|
||||
return { ok: false, error: this.lastLoadError, state: this.getLoadState() };
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
isSubscribed(channelHex) {
|
||||
const normalized = normalizeHexPubkey(channelHex);
|
||||
if (!normalized) {
|
||||
|
||||
@@ -8066,6 +8066,8 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
const service = this.hashtagPreferencesService || {};
|
||||
const loadState =
|
||||
typeof service.getLoadState === "function" ? service.getLoadState() : null;
|
||||
|
||||
const interestsSource = Array.isArray(snapshot?.interests)
|
||||
? snapshot.interests
|
||||
@@ -8081,6 +8083,17 @@ export class ProfileModalController {
|
||||
return {
|
||||
interests: this.sanitizeHashtagList(interestsSource),
|
||||
disinterests: this.sanitizeHashtagList(disinterestsSource),
|
||||
uiReady:
|
||||
loadState?.uiReady === true ||
|
||||
service.uiReady === true ||
|
||||
(!loadState && service.uiReady !== false),
|
||||
dataReady:
|
||||
loadState?.dataReady === true ||
|
||||
service.dataReady === true ||
|
||||
(!loadState && service.dataReady !== false),
|
||||
lastLoadError: loadState?.lastLoadError || service.lastLoadError || null,
|
||||
loadedFromCache:
|
||||
loadState?.loadedFromCache === true || service.loadedFromCache === true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8343,11 +8356,37 @@ export class ProfileModalController {
|
||||
populateHashtagPreferences(preferences = null) {
|
||||
const snapshot = this.getResolvedHashtagPreferences(preferences);
|
||||
|
||||
if (!snapshot.uiReady) {
|
||||
this.setHashtagStatus("Loading hashtag preferences…", "info");
|
||||
this.renderHashtagList("interest", []);
|
||||
this.renderHashtagList("disinterest", []);
|
||||
this.refreshHashtagBackgroundStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!snapshot.dataReady) {
|
||||
const message = snapshot.lastLoadError
|
||||
? "Couldn’t sync hashtag preferences. Retry to load your lists."
|
||||
: "Hashtag preferences are unavailable right now. Retry to sync.";
|
||||
this.setHashtagStatus(message, "warning");
|
||||
this.renderHashtagList("interest", []);
|
||||
this.renderHashtagList("disinterest", []);
|
||||
this.refreshHashtagBackgroundStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
this.renderHashtagList("interest", snapshot.interests);
|
||||
this.renderHashtagList("disinterest", snapshot.disinterests);
|
||||
|
||||
if (!snapshot.interests.length && !snapshot.disinterests.length) {
|
||||
this.setHashtagStatus("", "muted");
|
||||
if (snapshot.loadedFromCache) {
|
||||
this.setHashtagStatus(
|
||||
"No hashtag preferences yet (showing cached state).",
|
||||
"info",
|
||||
);
|
||||
} else {
|
||||
this.setHashtagStatus("", "muted");
|
||||
}
|
||||
}
|
||||
this.refreshHashtagBackgroundStatus();
|
||||
}
|
||||
@@ -8911,6 +8950,31 @@ export class ProfileModalController {
|
||||
}
|
||||
|
||||
try {
|
||||
const loadState =
|
||||
typeof service.getLoadState === "function" ? service.getLoadState() : {};
|
||||
const uiReady = loadState?.uiReady === true || service.uiReady === true;
|
||||
const dataReady = loadState?.dataReady === true || service.dataReady === true;
|
||||
const lastLoadError = loadState?.lastLoadError || service.lastLoadError || null;
|
||||
|
||||
if (!uiReady) {
|
||||
this.clearSubscriptionsList("Loading subscriptions…");
|
||||
this.setSubscriptionsStatus("Loading subscriptions…", "info");
|
||||
this.refreshSubscriptionsBackgroundStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dataReady) {
|
||||
this.clearSubscriptionsList("Subscriptions unavailable. Retry to sync your list.");
|
||||
this.setSubscriptionsStatus(
|
||||
lastLoadError
|
||||
? "Couldn’t sync subscriptions. Retry to load your list."
|
||||
: "Subscriptions unavailable right now. Retry to sync your list.",
|
||||
"warning",
|
||||
);
|
||||
this.refreshSubscriptionsBackgroundStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
let sourceEntries = [];
|
||||
|
||||
if (Array.isArray(subscriptions) && subscriptions.length) {
|
||||
@@ -9003,6 +9067,7 @@ export class ProfileModalController {
|
||||
this.subscriptionList.innerHTML = "";
|
||||
|
||||
if (!deduped.length) {
|
||||
this.setSubscriptionsStatus("No subscriptions yet.", "muted");
|
||||
this.subscriptionListEmpty.classList.remove("hidden");
|
||||
this.subscriptionList.classList.add("hidden");
|
||||
this.refreshSubscriptionsBackgroundStatus();
|
||||
@@ -9108,13 +9173,17 @@ export class ProfileModalController {
|
||||
}
|
||||
}
|
||||
|
||||
clearSubscriptionsList() {
|
||||
clearSubscriptionsList(emptyMessage = "No subscriptions yet.") {
|
||||
if (this.subscriptionList instanceof HTMLElement) {
|
||||
this.subscriptionList.innerHTML = "";
|
||||
this.subscriptionList.classList.add("hidden");
|
||||
}
|
||||
|
||||
if (this.subscriptionListEmpty instanceof HTMLElement) {
|
||||
this.subscriptionListEmpty.textContent =
|
||||
typeof emptyMessage === "string" && emptyMessage.trim()
|
||||
? emptyMessage.trim()
|
||||
: "No subscriptions yet.";
|
||||
this.subscriptionListEmpty.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user