commit fcb0415c4bc3ebace1339d1df6bfbd6c2795928a
parent e1fc4c3086586e1adc69774df277a4acec71aa22
Author: triesap <tyson@radroots.org>
Date: Sun, 2 Aug 2026 19:26:57 +0000
core(profile): expose manual active refresh
- add the supported asynchronous active-profile command
- keep signed-out invocation idempotent and network free
- allow repeated refreshes to advance public snapshot revisions
- complete the relay and profile runtime checkpoint ledger
Diffstat:
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/crates/studio_application/src/profile_refresh.rs b/crates/studio_application/src/profile_refresh.rs
@@ -7,6 +7,25 @@ use crate::{
};
impl AppCore {
+ /// Manually refreshes the active account's Nostr kind-0 profile.
+ ///
+ /// Cached public metadata remains visible while the asynchronous request is
+ /// running. Calling this command while signed out is an idempotent no-op.
+ ///
+ /// # Errors
+ ///
+ /// Returns a safe storage or application-state error. Relay and invalid-data
+ /// failures are represented as nonfatal snapshot state.
+ pub async fn refresh_active_profile(
+ &self,
+ profiles: &(impl ProfileRepository + ?Sized),
+ client: &(impl NostrClient + ?Sized),
+ clock: &(impl Clock + ?Sized),
+ ) -> Result<AppSnapshot, SafeError> {
+ self.refresh_profile_for_active_account(profiles, client, clock)
+ .await
+ }
+
/// Refreshes the current active account while retaining any cached profile.
///
/// Stale results are discarded when the account is replaced or signed out.
@@ -15,7 +34,7 @@ impl AppCore {
///
/// Returns a safe storage or application-state error. Relay and invalid-data
/// failures are represented as nonfatal snapshot state.
- pub async fn refresh_profile_for_active_account(
+ async fn refresh_profile_for_active_account(
&self,
profiles: &(impl ProfileRepository + ?Sized),
client: &(impl NostrClient + ?Sized),
@@ -365,4 +384,41 @@ mod tests {
Some("Cached")
);
}
+
+ #[tokio::test]
+ async fn manual_refresh_is_repeatable_and_signed_out_safe() {
+ let profiles = MemoryProfiles::default();
+ let (core, public_key) = active_core(&profiles, None);
+ let first = core
+ .refresh_active_profile(
+ &profiles,
+ &FixedClient(Ok(Some(profile(public_key, "First", 10)))),
+ &FixedClock,
+ )
+ .await
+ .expect("first refresh");
+ let second = core
+ .refresh_active_profile(
+ &profiles,
+ &FixedClient(Ok(Some(profile(public_key, "Second", 20)))),
+ &FixedClock,
+ )
+ .await
+ .expect("second refresh");
+
+ assert!(second.revision() > first.revision());
+ assert_eq!(
+ second
+ .active_account()
+ .and_then(|active| active.profile())
+ .and_then(ProfileMetadata::name),
+ Some("Second")
+ );
+ let signed_out = core.sign_out().expect("sign out");
+ let no_op = core
+ .refresh_active_profile(&profiles, &FixedClient(Ok(None)), &FixedClock)
+ .await
+ .expect("signed-out no-op");
+ assert_eq!(no_op, signed_out);
+ }
}