Profile-post backfill + prune disposable first-run persona on import
Two bugs the v0.6.2 Discover story was going to expose: 1) Many named personas don't have a profile post yet, so the planned profile-post-driven Discover listing would show them as headless. Existing personas predate the Phase 2d profile-post primitive, and imported personas arrive with a display_name but no matching post. 2) Fresh install always generates a blank disposable persona before the user can pick fresh-vs-import. If the user picks import, that blank persona lingers forever — visible in the Personas list, a potential default, confusing. Fixes: Profile-post backfill (node.rs): - New `Node::backfill_profile_posts_for_named_personas`: scans posting identities, skips unnamed or already-covered ones, and emits a signed profile post at the persona's own `created_at` timestamp. Run once from `Node::open_with_bind` after migrations. Idempotent via the new `Storage::has_profile_post_by_author` check. Chronology is preserved (post timestamp matches persona creation), so a later genuine profile update the user authors always wins the `> old_ts` monotonicity check on receivers. - `Node::create_posting_identity(name)`: if `name` is non-empty, emits the profile post inline so new named personas are Discover-able the moment they're created. Uses the new `publish_profile_post_as` helper, which signs with the persona's own secret (not the default posting secret) and propagates via `update_neighbor_manifests_as`. First-run persona marker + targeted prune (node.rs + storage.rs + import): - `Node::open_with_bind`'s auto-gen block now also writes `first_run_auto_persona_id = <hex>` into the settings kv. This marks the specific disposable persona from the fresh-install flow; later prune logic uses this id, not a "any empty persona" rule, so manual empty personas are never touched. - `Node::try_prune_first_run_auto_persona`: deletes the marked id iff all four gates pass — still exists, no display_name, no authored posts, no authored reactions/comments, and it's no longer the current default. Any one failure → clear the marker and keep the persona. New storage helpers `has_any_post_by_author`, `has_any_engagement_by_author` back the check. - `set_profile` clears the marker when a non-empty name lands on the marked persona (user claimed it). - `storage.delete_setting(key)` — new one-line helper. - `import_as_personas_cmd` in tauri-app calls the prune after import completes; the cmd's return message reports "(cleared blank starter persona)" when it fires. - New `get_first_run_auto_persona_id` Tauri command so the frontend can filter the blank persona out of the Personas list while it still exists. - Frontend `loadPersonas` filters the marker id out of `personasCache` before rendering. Tests: 124 / 124 core tests pass.
This commit is contained in:
parent
d990da5bda
commit
e74bd4e6c6
4 changed files with 308 additions and 6 deletions
|
|
@ -1416,6 +1416,20 @@ async fn get_update_channel(state: State<'_, AppNode>) -> Result<String, String>
|
|||
.unwrap_or_else(|| "stable".to_string()))
|
||||
}
|
||||
|
||||
/// Return the hex id of the disposable fresh-install persona (if it still
|
||||
/// exists and hasn't been claimed). Frontend uses this to filter the blank
|
||||
/// starter out of the Personas list so the user never sees a "ghost"
|
||||
/// persona waiting between install and their first-run choice.
|
||||
/// Returns an empty string when there's no disposable persona to hide.
|
||||
#[tauri::command]
|
||||
async fn get_first_run_auto_persona_id(state: State<'_, AppNode>) -> Result<String, String> {
|
||||
let node = get_node(&state).await;
|
||||
let storage = node.storage.get().await;
|
||||
Ok(storage.get_setting("first_run_auto_persona_id")
|
||||
.map_err(|e| e.to_string())?
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Persist the user's preferred update channel.
|
||||
#[tauri::command]
|
||||
async fn set_update_channel(state: State<'_, AppNode>, channel: String) -> Result<(), String> {
|
||||
|
|
@ -2804,7 +2818,17 @@ async fn import_as_personas_cmd(
|
|||
&node.storage,
|
||||
&node.blob_store,
|
||||
).await.map_err(|e| e.to_string())?;
|
||||
Ok(result.message)
|
||||
// Drop the disposable fresh-install persona if it's still pristine.
|
||||
// Safe-by-construction: the helper bails unless ALL of [no name, no
|
||||
// posts, no engagement, no longer the default] hold.
|
||||
let pruned = node.try_prune_first_run_auto_persona().await
|
||||
.unwrap_or(false);
|
||||
let msg = if pruned {
|
||||
format!("{} (cleared blank starter persona)", result.message)
|
||||
} else {
|
||||
result.message
|
||||
};
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
@ -3023,6 +3047,7 @@ pub fn run() {
|
|||
get_update_channel,
|
||||
set_update_channel,
|
||||
open_url_external,
|
||||
get_first_run_auto_persona_id,
|
||||
list_connections,
|
||||
worm_lookup,
|
||||
list_social_routes,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue