use async_trait::async_trait;
use ed25519_dalek::SigningKey;
use eyre::{bail, Result};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tonic::Response;
use crate::profile_builder::ProfileUpdate;
use crate::proto::on_chain_event::Body;
use crate::proto::{
FidRequest, Message, SignerEventType, SignerRequest, StorageLimitsResponse, UserDataRequest,
UserDataType,
};
use crate::to_messages::ToMessages;
use crate::utils::{optional_get_user_data, optional_get_user_data_value};
use crate::{HubService, PUBLIC_KEY_LENGTH};
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct Profile {
pub fid: u64,
pub username: Option<String>,
pub display_name: Option<String>,
pub profile_picture: Option<String>,
pub bio: Option<String>,
pub url: Option<String>,
}
#[derive(Error, Debug)]
pub enum UserError {
#[error("no storage available")]
NoStorage,
}
#[async_trait]
pub trait UserService {
async fn get_user_profile(&mut self, fid: u64) -> Result<Profile>;
async fn key_valid_for_fid(&mut self, key: &[u8; PUBLIC_KEY_LENGTH], fid: u64) -> Result<bool>;
async fn get_user_followers(&mut self, fid: u64) -> Result<Vec<Profile>>;
async fn get_user_following(&mut self, fid: u64) -> Result<Vec<Profile>>;
async fn get_all_follow_events(&mut self, fid: u64) -> Result<Vec<u64>>;
async fn update_user(
&mut self,
signing_key: &SigningKey,
fid: u64,
update: ProfileUpdate,
) -> Result<Profile>;
}
fn get_user_data_request(fid: u64, data_type: UserDataType) -> UserDataRequest {
UserDataRequest {
fid,
user_data_type: data_type as i32,
}
}
fn get_user_data(response: Response<Message>) -> Option<String> {
optional_get_user_data(response).and_then(optional_get_user_data_value)
}
fn get_reaction_data(response: Response<Message>) -> Option<u64> {
todo!()
}
fn contains_any_storage_units(response: Response<StorageLimitsResponse>) -> bool {
response.into_inner().units > 0
}
#[async_trait]
impl UserService for HubService {
async fn get_user_profile(&mut self, fid: u64) -> Result<Profile> {
let fid_check = self
.get_current_storage_limits_by_fid(FidRequest {
fid,
page_size: Some(1),
page_token: None,
reverse: None,
})
.await
.is_ok_and(contains_any_storage_units);
if !fid_check {
bail!(UserError::NoStorage)
}
let username = self
.get_user_data(get_user_data_request(fid, UserDataType::Username))
.await
.map_or(None, get_user_data);
let display_name = self
.get_user_data(get_user_data_request(fid, UserDataType::Display))
.await
.map_or(None, get_user_data);
let profile_picture = self
.get_user_data(get_user_data_request(fid, UserDataType::Pfp))
.await
.map_or(None, get_user_data);
let bio = self
.get_user_data(get_user_data_request(fid, UserDataType::Bio))
.await
.map_or(None, get_user_data);
let url = self
.get_user_data(get_user_data_request(fid, UserDataType::Url))
.await
.map_or(None, get_user_data);
Ok(Profile {
fid,
username,
display_name,
profile_picture,
bio,
url,
})
}
async fn key_valid_for_fid(&mut self, key: &[u8; PUBLIC_KEY_LENGTH], fid: u64) -> Result<bool> {
let onchain_event = self
.get_on_chain_signer(SignerRequest {
fid: fid,
signer: key.to_vec(),
})
.await?
.into_inner();
match onchain_event.body {
Some(Body::SignerEventBody(body)) => {
Ok(body.event_type == SignerEventType::Add as i32)
}
_ => Ok(false),
}
}
async fn get_user_followers(&mut self, fid: u64) -> Result<Vec<Profile>> {
todo!()
}
async fn get_user_following(&mut self, fid: u64) -> Result<Vec<Profile>> {
let all_followed = self.get_all_follow_events(fid).await?;
todo!()
}
async fn get_all_follow_events(&mut self, fid: u64) -> Result<Vec<u64>> {
todo!()
}
async fn update_user(
&mut self,
signing_key: &SigningKey,
fid: u64,
update: ProfileUpdate,
) -> Result<Profile> {
let messages = update.to_messages(fid, &signing_key);
for message in messages {
self.submit_message(message).await?;
}
self.get_user_profile(fid).await
}
}