1use async_trait::async_trait;
4use ed25519_dalek::SigningKey;
5use eyre::{bail, Result};
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use tonic::Response;
9
10use crate::profile_builder::ProfileUpdate;
11use crate::proto::on_chain_event::Body;
12use crate::proto::{
13 FidRequest, Message, SignerEventType, SignerRequest, StorageLimitsResponse, UserDataRequest,
14 UserDataType,
15};
16use crate::to_messages::ToMessages;
17use crate::utils::{optional_get_user_data, optional_get_user_data_value};
18use crate::{HubService, PUBLIC_KEY_LENGTH};
19
20#[derive(Clone, Debug, Serialize, Deserialize, Default)]
22pub struct Profile {
23 pub fid: u64,
24 pub username: Option<String>,
25 pub display_name: Option<String>,
26 pub profile_picture: Option<String>,
27 pub bio: Option<String>,
28 pub url: Option<String>,
29}
30
31#[derive(Error, Debug)]
32pub enum UserError {
33 #[error("no storage available")]
34 NoStorage,
35}
36
37#[async_trait]
39pub trait UserService {
40 async fn get_user_profile(&mut self, fid: u64) -> Result<Profile>;
42 async fn key_valid_for_fid(&mut self, key: &[u8; PUBLIC_KEY_LENGTH], fid: u64) -> Result<bool>;
44 async fn get_user_followers(&mut self, fid: u64) -> Result<Vec<Profile>>;
46 async fn get_user_following(&mut self, fid: u64) -> Result<Vec<Profile>>;
48 async fn get_all_follow_events(&mut self, fid: u64) -> Result<Vec<u64>>;
50 async fn update_user(
53 &mut self,
54 signing_key: &SigningKey,
55 fid: u64,
56 update: ProfileUpdate,
57 ) -> Result<Profile>;
58}
59
60fn get_user_data_request(fid: u64, data_type: UserDataType) -> UserDataRequest {
61 UserDataRequest {
62 fid,
63 user_data_type: data_type as i32,
64 }
65}
66
67fn get_user_data(response: Response<Message>) -> Option<String> {
68 optional_get_user_data(response).and_then(optional_get_user_data_value)
69}
70
71fn get_reaction_data(response: Response<Message>) -> Option<u64> {
72 todo!()
73 }
75
76fn contains_any_storage_units(response: Response<StorageLimitsResponse>) -> bool {
77 response.into_inner().units > 0
78}
79
80#[async_trait]
81impl UserService for HubService {
82 async fn get_user_profile(&mut self, fid: u64) -> Result<Profile> {
83 let fid_check = self
85 .get_current_storage_limits_by_fid(FidRequest {
86 fid,
87 page_size: Some(1),
88 page_token: None,
89 reverse: None,
90 })
91 .await
92 .is_ok_and(contains_any_storage_units);
93
94 if !fid_check {
95 bail!(UserError::NoStorage)
96 }
97
98 let username = self
100 .get_user_data(get_user_data_request(fid, UserDataType::Username))
101 .await
102 .map_or(None, get_user_data);
103
104 let display_name = self
106 .get_user_data(get_user_data_request(fid, UserDataType::Display))
107 .await
108 .map_or(None, get_user_data);
109
110 let profile_picture = self
112 .get_user_data(get_user_data_request(fid, UserDataType::Pfp))
113 .await
114 .map_or(None, get_user_data);
115
116 let bio = self
118 .get_user_data(get_user_data_request(fid, UserDataType::Bio))
119 .await
120 .map_or(None, get_user_data);
121
122 let url = self
124 .get_user_data(get_user_data_request(fid, UserDataType::Url))
125 .await
126 .map_or(None, get_user_data);
127
128 Ok(Profile {
130 fid,
131 username,
132 display_name,
133 profile_picture,
134 bio,
135 url,
136 })
137 }
138
139 async fn key_valid_for_fid(&mut self, key: &[u8; PUBLIC_KEY_LENGTH], fid: u64) -> Result<bool> {
140 let onchain_event = self
141 .get_on_chain_signer(SignerRequest {
142 fid: fid,
143 signer: key.to_vec(),
144 })
145 .await?
146 .into_inner();
147
148 match onchain_event.body {
149 Some(Body::SignerEventBody(body)) => {
150 Ok(body.event_type == SignerEventType::Add as i32)
153 }
154 _ => Ok(false),
155 }
156 }
157
158 async fn get_user_followers(&mut self, fid: u64) -> Result<Vec<Profile>> {
159 todo!()
160 }
162
163 async fn get_user_following(&mut self, fid: u64) -> Result<Vec<Profile>> {
164 let all_followed = self.get_all_follow_events(fid).await?;
165 todo!()
166 }
167
168 async fn get_all_follow_events(&mut self, fid: u64) -> Result<Vec<u64>> {
169 todo!()
186 }
187
188 async fn update_user(
189 &mut self,
190 signing_key: &SigningKey,
191 fid: u64,
192 update: ProfileUpdate,
193 ) -> Result<Profile> {
194 let messages = update.to_messages(fid, &signing_key);
195 for message in messages {
196 self.submit_message(message).await?;
197 }
198 self.get_user_profile(fid).await
199 }
200
201 }