feat(dvm-favorites): rename user-facing to "Favorite Feed Algorithms" + fix cold-start race
- User-facing rename. All user-visible strings switch from "favourite" to "favorite" (American spelling) and from "DVM(s)" to "Feed Algorithm(s)": settings entry, spinner group label, empty state, banner messages, menu accessibility labels. Code identifiers still reference NIP-90's "DVM" protocol name since that's the wire spec. - Cold-start race. When the app relaunches with a persisted TopFilter.FavoriteDvm, the AppDefinitionEvent may not be in cache yet. mergeInterests was filtering out any favorite whose AppDefinitionEvent didn't yet advertise kind 5300, so the spinner didn't include a chip matching the persisted selection — it showed "Select an option" while the orchestrator quietly fired the RPC with no UI to ground it. Always include every favorite; the 5300 check at add time (in FavoriteDvmToggle) is enough.
This commit is contained in:
+2
-2
@@ -45,7 +45,7 @@ import kotlinx.coroutines.sync.withLock
|
|||||||
private const val RESPONSE_TIMEOUT_MS = 20_000L
|
private const val RESPONSE_TIMEOUT_MS = 20_000L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable snapshot of a favourite DVM's current request/response state.
|
* Immutable snapshot of a favorite DVM's current request/response state.
|
||||||
*
|
*
|
||||||
* - [requestId] is the id of the most recently published kind-5300 request.
|
* - [requestId] is the id of the most recently published kind-5300 request.
|
||||||
* - [responseRelays] is the relay set the kind-5300 was sent to — the same set on
|
* - [responseRelays] is the relay set the kind-5300 was sent to — the same set on
|
||||||
@@ -65,7 +65,7 @@ data class FavoriteDvmSnapshot(
|
|||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the NIP-90 content-discovery RPC cycle for each favourite DVM the user
|
* Manages the NIP-90 content-discovery RPC cycle for each favorite DVM the user
|
||||||
* pins to the top-nav.
|
* pins to the top-nav.
|
||||||
*
|
*
|
||||||
* The orchestrator is lazy: it starts a request/response cycle the first time any
|
* The orchestrator is lazy: it starts a request/response cycle the first time any
|
||||||
|
|||||||
+2
-2
@@ -34,9 +34,9 @@ import kotlinx.coroutines.flow.combine
|
|||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feed flow that merges snapshots from every currently-favourited DVM into a
|
* Feed flow that merges snapshots from every currently-favorited DVM into a
|
||||||
* single [AllFavoriteDvmsTopNavFilter]. Re-wires subscriptions whenever the
|
* single [AllFavoriteDvmsTopNavFilter]. Re-wires subscriptions whenever the
|
||||||
* favourite set changes.
|
* favorite set changes.
|
||||||
*/
|
*/
|
||||||
class AllFavoriteDvmsFeedFlow(
|
class AllFavoriteDvmsFeedFlow(
|
||||||
val favoriteDvmAddresses: StateFlow<Set<Address>>,
|
val favoriteDvmAddresses: StateFlow<Set<Address>>,
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-nav filter that unions the latest kind-6300 responses from every currently
|
* Top-nav filter that unions the latest kind-6300 responses from every currently
|
||||||
* favourited DVM. Behaves like [FavoriteDvmTopNavFilter] (pure membership check
|
* favorited DVM. Behaves like [FavoriteDvmTopNavFilter] (pure membership check
|
||||||
* against a snapshot), but the accepted set is the union across N DVMs and the
|
* against a snapshot), but the accepted set is the union across N DVMs and the
|
||||||
* request-id list carries one entry per DVM for the relay-listen subscription.
|
* request-id list carries one entry per DVM for the relay-listen subscription.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-nav filter backed by the latest kind-6300 response from a favourite DVM.
|
* Top-nav filter backed by the latest kind-6300 response from a favorite DVM.
|
||||||
*
|
*
|
||||||
* The filter is a pure immutable membership check: [match] accepts a note only if the
|
* The filter is a pure immutable membership check: [match] accepts a note only if the
|
||||||
* DVM's latest response included it. When a new response arrives, a new instance is
|
* DVM's latest response included it. When a new response arrives, a new instance is
|
||||||
|
|||||||
+2
-2
@@ -32,8 +32,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
|||||||
* - [listenRelays] — the union of DVM publish relays across all active DVMs
|
* - [listenRelays] — the union of DVM publish relays across all active DVMs
|
||||||
* (where they will deliver future kind 6300 / 7000 events for their requests).
|
* (where they will deliver future kind 6300 / 7000 events for their requests).
|
||||||
* - [requestIds] — the set of currently-active kind-5300 request ids to listen
|
* - [requestIds] — the set of currently-active kind-5300 request ids to listen
|
||||||
* for. A single-DVM filter carries one; the merged "All favourite DVMs"
|
* for. A single-DVM filter carries one; the merged "All favorite DVMs"
|
||||||
* filter carries one per favourite DVM.
|
* filter carries one per favorite DVM.
|
||||||
*/
|
*/
|
||||||
class FavoriteDvmTopNavPerRelayFilterSet(
|
class FavoriteDvmTopNavPerRelayFilterSet(
|
||||||
val contentFetches: Map<NormalizedRelayUrl, FavoriteDvmTopNavPerRelayFilter>,
|
val contentFetches: Map<NormalizedRelayUrl, FavoriteDvmTopNavPerRelayFilter>,
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
|
|||||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
||||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent
|
|
||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
@@ -182,24 +181,22 @@ class TopNavFilterState(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only DVMs that advertise NIP-90 content discovery (kind 5300) can produce a
|
// Favorites can only be added through FavoriteDvmToggle, which itself checks
|
||||||
// home feed. Hide entries whose AppDefinitionEvent isn't loaded yet OR doesn't
|
// that the AppDefinitionEvent advertises kind 5300. Don't re-check here: on
|
||||||
// include kind 5300 so the chip doesn't appear for image-generation, translation,
|
// cold start the AppDefinitionEvent may not be in cache yet, and dropping
|
||||||
// search, or other DVM kinds that would never reply.
|
// the entry means the persisted TopFilter.FavoriteDvm can't find its chip
|
||||||
|
// in the spinner (user sees "Select an option" while the banner fires the
|
||||||
|
// RPC — the bug we had before this change).
|
||||||
val favoriteDvms =
|
val favoriteDvms =
|
||||||
favoriteDvmList.mapNotNull { dvmNote ->
|
favoriteDvmList.map { dvmNote ->
|
||||||
val supports5300 =
|
|
||||||
(dvmNote.event as? AppDefinitionEvent)
|
|
||||||
?.includeKind(NIP90ContentDiscoveryRequestEvent.KIND) == true
|
|
||||||
if (!supports5300) return@mapNotNull null
|
|
||||||
FeedDefinition(
|
FeedDefinition(
|
||||||
TopFilter.FavoriteDvm(dvmNote.address),
|
TopFilter.FavoriteDvm(dvmNote.address),
|
||||||
FavoriteDvmName(dvmNote),
|
FavoriteDvmName(dvmNote),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only show the "All favourite DVMs" meta-chip when there is at least one
|
// Only show the "All favorite DVMs" meta-chip when there is at least one
|
||||||
// real favourite to merge; otherwise the chip opens to an empty feed.
|
// real favorite to merge; otherwise the chip opens to an empty feed.
|
||||||
val allFavorites =
|
val allFavorites =
|
||||||
if (favoriteDvms.isNotEmpty()) listOf(allFavoriteDvmsFollow) else emptyList()
|
if (favoriteDvms.isNotEmpty()) listOf(allFavoriteDvmsFollow) else emptyList()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -31,7 +31,7 @@ import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentD
|
|||||||
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
|
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds relay REQ filters for a favourite-DVM home feed.
|
* Builds relay REQ filters for a favorite-DVM home feed.
|
||||||
*
|
*
|
||||||
* Two distinct subscription kinds with two distinct relay sets:
|
* Two distinct subscription kinds with two distinct relay sets:
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1728,20 +1728,20 @@
|
|||||||
<string name="feed_group_locations">Locations</string>
|
<string name="feed_group_locations">Locations</string>
|
||||||
<string name="feed_group_communities">Communities</string>
|
<string name="feed_group_communities">Communities</string>
|
||||||
<string name="feed_group_lists">Lists</string>
|
<string name="feed_group_lists">Lists</string>
|
||||||
<string name="feed_group_dvms">DVMs</string>
|
<string name="feed_group_dvms">Feed Algorithms</string>
|
||||||
<string name="follow_list_all_favorite_dvms">All favourite DVMs</string>
|
<string name="follow_list_all_favorite_dvms">All favorite feed algorithms</string>
|
||||||
<string name="feed_group_relays">Relays</string>
|
<string name="feed_group_relays">Relays</string>
|
||||||
|
|
||||||
<string name="add_dvm_to_favorites">Add DVM to favorites</string>
|
<string name="add_dvm_to_favorites">Add feed algorithm to favorites</string>
|
||||||
<string name="remove_dvm_from_favorites">Remove from favorites</string>
|
<string name="remove_dvm_from_favorites">Remove from favorites</string>
|
||||||
<string name="favorite_dvms_title">Favourite DVMs</string>
|
<string name="favorite_dvms_title">Favorite Feed Algorithms</string>
|
||||||
<string name="favorite_dvms_explainer">Content-discovery DVMs you starred here appear as filter chips on the Home feed. Open Discover to add more.</string>
|
<string name="favorite_dvms_explainer">Feed algorithms you starred here appear as filter chips on the Home feed. Open Discover to add more.</string>
|
||||||
<string name="favorite_dvms_empty">No favourite DVMs yet. Open Discover, tap a content-discovery DVM, and star it to add it here.</string>
|
<string name="favorite_dvms_empty">No favorite feed algorithms yet. Open Discover, tap one, and star it to add it here.</string>
|
||||||
<string name="dvm_home_status_requesting">Asking %1$s for a feed…</string>
|
<string name="dvm_home_status_requesting">Asking %1$s for a feed…</string>
|
||||||
<string name="dvm_home_status_requesting_all">Asking your favourite DVMs for feeds…</string>
|
<string name="dvm_home_status_requesting_all">Asking your favorite feed algorithms for feeds…</string>
|
||||||
<string name="dvm_home_status_processing">Processing your feed…</string>
|
<string name="dvm_home_status_processing">Processing your feed…</string>
|
||||||
<string name="dvm_home_status_payment_required">This DVM requires payment</string>
|
<string name="dvm_home_status_payment_required">This feed algorithm requires payment</string>
|
||||||
<string name="dvm_home_status_error">DVM returned an error</string>
|
<string name="dvm_home_status_error">The feed algorithm returned an error</string>
|
||||||
<string name="dvm_home_retry">Retry</string>
|
<string name="dvm_home_retry">Retry</string>
|
||||||
|
|
||||||
<string name="temporary_account">Log off on device lock</string>
|
<string name="temporary_account">Log off on device lock</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user