Skip to main content

qualia_client_core/api/
profile.rs

1//! Profile + social connect, personal directory
2
3#![allow(non_snake_case)]
4
5use super::*;
6
7pub fn get_user_profile() -> Result<serde_json::Value, String> {
8    let profile = crate::user_profile::load_profile();
9    serde_json::to_value(profile).map_err(|e| e.to_string())
10}
11
12pub fn save_user_profile(profile_json: String) -> Result<serde_json::Value, String> {
13    // People tab (and other callers) may send a *partial* profile JSON — e.g. only
14    // `display_name` + `sharing.allow_group_chat_invites`. Full-struct deserialize used
15    // to fail with `missing field share_display_name`. Merge onto the loaded profile.
16    let patch: serde_json::Value =
17        serde_json::from_str(&profile_json).map_err(|e| format!("invalid profile json: {e}"))?;
18    let base = crate::user_profile::load_profile();
19    let mut profile = crate::user_profile::apply_profile_patch(&base, &patch)?;
20    profile.public_did = crate::user_profile::resolve_public_did(&profile);
21    crate::user_profile::save_profile(&profile)?;
22    serde_json::to_value(profile).map_err(|e| e.to_string())
23}
24
25pub fn generate_connect_invite(front_door_id: Option<String>) -> Result<serde_json::Value, String> {
26    let invite = crate::social_connect::generate_connect_invite(front_door_id)?;
27    serde_json::to_value(invite).map_err(|e| e.to_string())
28}
29
30pub fn accept_connect_invite(input: String) -> Result<serde_json::Value, String> {
31    let contact = crate::social_connect::accept_connect_invite(&input)?;
32    serde_json::to_value(contact).map_err(|e| e.to_string())
33}
34
35pub fn list_chat_contacts() -> Result<serde_json::Value, String> {
36    let contacts = crate::social_connect::list_chat_contacts();
37    serde_json::to_value(contacts).map_err(|e| e.to_string())
38}
39
40// ── Personal directory (AD-like): categorised addressbook + agreement slots ─────
41
42/// The unified, categorised personal directory — the addressbook (Parties joined across the directory-actor
43/// + chat-contact stores by DID) grouped into categories, with a per-entry slot for the agreements
44/// governing that relationship. See `docs/plans/rights-aware-peer-agreement-addressbook.md`.
45pub fn list_directory() -> Result<serde_json::Value, String> {
46    let actors = get_directory_actors()?;
47    let contacts = crate::social_connect::list_chat_contacts();
48    let view = crate::directory::build_view(&actors, &contacts);
49    serde_json::to_value(view).map_err(|e| e.to_string())
50}
51
52/// The directory categories (built-in + user-created).
53pub fn list_directory_categories() -> Result<serde_json::Value, String> {
54    serde_json::to_value(crate::directory::list_categories()).map_err(|e| e.to_string())
55}
56
57/// Create a custom directory category. Returns the new category.
58pub fn create_directory_category(label: String) -> Result<serde_json::Value, String> {
59    let cat = crate::directory::create_category(&label)?;
60    serde_json::to_value(cat).map_err(|e| e.to_string())
61}
62
63/// Set the categories a directory entry (by DID) belongs to; returns the refreshed directory.
64pub fn set_directory_entry_categories(
65    did: String,
66    categories: Vec<String>,
67) -> Result<serde_json::Value, String> {
68    crate::directory::set_entry_categories(&did, categories)?;
69    list_directory()
70}
71
72/// Faceted + concept-aware search over the directory. `query` is meaning-aware (a token expands across a
73/// concept cluster, so "doctor" finds a "clinician"); `facets_json` is a JSON object of
74/// `{facet_id: [selected values]}` (AND across facets, OR within). Returns ranked entries + drill-down
75/// facet counts. Both empty → the whole directory with all facet counts.
76pub fn search_directory(query: String, facets_json: String) -> Result<serde_json::Value, String> {
77    let selected: std::collections::BTreeMap<String, Vec<String>> = if facets_json.trim().is_empty()
78    {
79        std::collections::BTreeMap::new()
80    } else {
81        serde_json::from_str(&facets_json).map_err(|e| format!("bad facets json: {e}"))?
82    };
83    let actors = get_directory_actors()?;
84    let contacts = crate::social_connect::list_chat_contacts();
85    let result = crate::directory::search(&actors, &contacts, &query, &selected);
86    serde_json::to_value(result).map_err(|e| e.to_string())
87}