From: Simon Tatham Date: Sat, 13 Jan 2024 10:28:49 +0000 (+0000) Subject: Retrieve extra details about your own account. X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=efaa3c50742117f7ae65ab9c28053075f6407f13;p=mastodonochrome.git Retrieve extra details about your own account. Turns out that the API data model has two related types: 'Account', which you can retrieve for anyone, and 'CredentialAccount' which contains an extra sub-object full of settings you can only set (or see) for your own account. In another language I could make those distinct actual data types, one a subclass of the other adding the extra field. Here, it's easier to just fudge it a bit, pretending that all Account objects _optionally_ have the 'source' subobject, and when called on to retrieve our own record, always doing it via the API call that provides the extra. This also introduces a bit of a wrinkle about caching the result - we _do_ also receive copies of our own Account record in other contexts (e.g. as the author of a status we posted that reappears in a feed), and so we have to ensure we don't use those to overwrite our cached record to delete the source. This is ugly, but I prefer it to the other option, which is to maintain a _separate_ cache of our own account record with the extra data, and have to worry about which version of the rest of it is up to date. --- diff --git a/src/client.rs b/src/client.rs index a488827..bd771f2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -468,7 +468,17 @@ impl Client { } fn cache_account(&mut self, ac: &Account) { - self.accounts.insert(ac.id.to_string(), ac.clone()); + let mut ac = ac.clone(); + // Don't overwrite a cached account with a 'source' to one + // without. + if !ac.source.is_some() { + if let Some(source) = self.accounts.get(&ac.id) + .and_then(|ac| ac.source.as_ref()) + { + ac.source = Some(source.clone()); + } + } + self.accounts.insert(ac.id.to_string(), ac); } fn cache_status(&mut self, st: &Status) { @@ -495,12 +505,25 @@ impl Client { } pub fn account_by_id(&mut self, id: &str) -> Result { - if let Some(st) = self.accounts.get(id) { - return Ok(st.clone()); + // If we're fetching our own account, we do it via the + // verify_credentials request, which gets the extra + // information. This also means we must repeat the request if + // we've already received a copy of our own account details + // via some other API and it doesn't contain the extra + // information. + + if let Some(ac) = self.accounts.get(id) { + if ac.id != self.auth.account_id || ac.source.is_some() { + return Ok(ac.clone()); + } } - let (url, rsp) = self.api_request(Req::get( - &("v1/accounts/".to_owned() + id)))?; + let req = if id == self.auth.account_id { + Req::get(&format!("v1/accounts/verify_credentials")) + } else { + Req::get(&format!("v1/accounts/{id}")) + }; + let (url, rsp) = self.api_request(req)?; let rspstatus = rsp.status(); let ac: Account = if !rspstatus.is_success() { Err(ClientError::UrlError(url.clone(), rspstatus.to_string())) diff --git a/src/types.rs b/src/types.rs index 2903992..05dcce7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -111,6 +111,28 @@ pub struct Account { pub statuses_count: u64, pub followers_count: u64, pub following_count: u64, + + // In the wire protocol, 'CredentialAccount' is a subclass of + // 'Account' containing this extra field, only available from some + // requests, and only for your own account. We regard it as an + // optional field of Account in general. + pub source: Option, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct AccountSourceDetails { + pub privacy: Visibility, + pub sensitive: bool, + pub language: Option, + pub follow_requests_count: usize, + pub hide_collections: Option, + pub discoverable: Option, + pub indexable: Option, + // 'fields' and 'note' here differ from the ones in the main + // Account in that the bio, and the 'value' of each field, are in + // the source text form rather than renderable HTML. + pub note: String, + pub fields: Vec, } #[derive(Deserialize, Debug, Clone)]