Skip to content

Commit

Permalink
Merge pull request #4 from idkncc/feat/user-profiles
Browse files Browse the repository at this point in the history
feat: user profile
  • Loading branch information
Mon4ik authored Sep 4, 2024
2 parents 66ce4e5 + bdee39e commit 34f3533
Show file tree
Hide file tree
Showing 16 changed files with 848 additions and 106 deletions.
99 changes: 98 additions & 1 deletion src-tauri/src/client/friends/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::default;

use reqwest::header;
use serde::{Deserialize, Serialize};

Expand All @@ -6,10 +8,16 @@ use super::{presence::PresenceType, RobloxApi, RobloxError};
mod request_types;

const FRIENDS_LIST_API: &str = "https://friends.roblox.com/v1/users/{user_id}/friends";
const FRIENDS_STATUS_API: &str =
"https://friends.roblox.com/v1/users/{user_id}/friends/statuses?userIds[]={user_ids}";

const FRIENDS_COUNT_API: &str = "https://friends.roblox.com/v1/users/{user_id}/friends/count";
const FOLLOWERS_COUNT_API: &str = "https://friends.roblox.com/v1/users/{user_id}/followers/count";
const FOLLOWINGS_COUNT_API: &str = "https://friends.roblox.com/v1/users/{user_id}/followings/count";

const FRIEND_REQUESTS_API: &str = "https://friends.roblox.com/v1/my/friends/requests";
const PENDING_FRIEND_REQUESTS_API: &str =
"https://friends.roblox.com/v1/user/friend-requests/count";

const ACCEPT_FRIEND_REQUEST_API: &str =
"https://friends.roblox.com/v1/users/{requester_id}/accept-friend-request";
const DECLINE_FRIEND_REQUEST_API: &str =
Expand Down Expand Up @@ -84,6 +92,31 @@ pub struct FriendRequest {
pub sent_at: String,
}

#[derive(
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
)]
pub enum FriendStatus {
#[default]
NotFriends,
Friends,
RequestSent,
RequestReceived,
}

impl TryFrom<String> for FriendStatus {
type Error = RobloxError;

fn try_from(value: String) -> Result<Self, Self::Error> {
match value.as_str() {
"NotFriends" => Ok(Self::NotFriends),
"Friends" => Ok(Self::Friends),
"RequestSent" => Ok(Self::RequestSent),
"RequestReceived" => Ok(Self::RequestReceived),
_ => Err(RobloxError::MalformedResponse),
}
}
}

impl RobloxApi {
/// Get list of all friends for the specified user using <https://friends.roblox.com/v1/users/{userId}/friends>.
pub async fn friends_list(
Expand Down Expand Up @@ -122,6 +155,70 @@ impl RobloxApi {
Ok(friends)
}

/// Gets friends count of specific user using <https://friends.roblox.com/v1/users/{user_id}/friends/count>
pub async fn friends_count(&self, user_id: u64) -> Result<usize, RobloxError> {
let formatted_url = FRIENDS_COUNT_API.replace("{user_id}", &user_id.to_string());

let request_result = self.reqwest_client.get(formatted_url).send().await;

let response = Self::validate_request_result(request_result).await?;

Self::parse_to_raw::<request_types::CountBasedResponse>(response)
.await
.map(|res| res.count)
}

/// Gets followers count of specific user using <https://friends.roblox.com/v1/users/{user_id}/followers/count>
pub async fn followers_count(&self, user_id: u64) -> Result<usize, RobloxError> {
let formatted_url = FOLLOWERS_COUNT_API.replace("{user_id}", &user_id.to_string());

let request_result = self.reqwest_client.get(formatted_url).send().await;

let response = Self::validate_request_result(request_result).await?;

Self::parse_to_raw::<request_types::CountBasedResponse>(response)
.await
.map(|res| res.count)
}

/// Gets followings count of specific user using <https://friends.roblox.com/v1/users/{user_id}/followings/count>
pub async fn followings_count(&self, user_id: u64) -> Result<usize, RobloxError> {
let formatted_url = FOLLOWINGS_COUNT_API.replace("{user_id}", &user_id.to_string());

let request_result = self.reqwest_client.get(formatted_url).send().await;

let response = Self::validate_request_result(request_result).await?;

Self::parse_to_raw::<request_types::CountBasedResponse>(response)
.await
.map(|res| res.count)
}

/// Gets friend status with specific user using <https://friends.roblox.com/v1/users/{user_id}/friends/statuses?userIds[]={user_ids}>
pub async fn friend_status(&self, user_id: u64) -> Result<FriendStatus, RobloxError> {
let formatted_url = FRIENDS_STATUS_API
.replace(
"{user_id}",
&self.user_id().await.map(|user_id| user_id.to_string())?,
)
.replace("{user_ids}", &user_id.to_string());

let cookie = self.cookie_string().await?;

let request_result = self
.reqwest_client
.get(formatted_url)
.header(header::COOKIE, cookie)
.send()
.await;

let response = Self::validate_request_result(request_result).await?;

Self::parse_to_raw::<request_types::FriendsStatusResponse>(response)
.await
.map(|res| res.data[0].status)
}

/// Get list of friend requests with cursor using <https://friends.roblox.com/v1/my/friends/requests>.
pub async fn friend_requests(
&self,
Expand Down
25 changes: 25 additions & 0 deletions src-tauri/src/client/friends/request_types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};

use super::FriendStatus;

/// Model, representing user information that also contains select presence information
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
Expand All @@ -26,6 +28,23 @@ pub struct FriendUserInformationRaw {
pub has_verified_badge: bool,
}

/// Model, representing FriendsStatus endpoint response
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FriendsStatusResponse {
pub data: Vec<FriendStatusRaw>,
}

/// Model, representing friend status
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FriendStatusRaw {
pub id: u64,
pub status: FriendStatus,
}

/// Model, representing a friend request.
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
Expand Down Expand Up @@ -84,6 +103,12 @@ pub(super) struct FriendsListResponse {
pub data: Vec<FriendUserInformationRaw>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct CountBasedResponse {
pub count: usize,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct FriendRequestsResponse {
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(dead_code)]

mod client;
mod validation;

Expand Down
1 change: 0 additions & 1 deletion src-tauri/src/client/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ impl RobloxApi {
response: Response,
) -> Result<T, RobloxError> {
let response_text = response.text().await.unwrap();
// println!("{}", response_text);
let response_struct = match serde_json::from_str::<T>(&response_text) {
Ok(x) => x,
Err(_) => {
Expand Down
112 changes: 111 additions & 1 deletion src-tauri/src/commands/users.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use tauri::State;

use crate::client::friends::FriendUserInformation;
use crate::client::friends::{FriendStatus, FriendUserInformation};
use crate::client::users::UserDetails;
use crate::types::UserProfileStats;
use crate::{
client::RobloxError,
types::{ClientInfo, RobloxApiState},
Expand All @@ -22,6 +24,55 @@ pub async fn get_me(state: State<'_, RobloxApiState>) -> Result<ClientInfo, Stri
})
}

#[tauri::command(async)]
pub async fn get_user(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<UserDetails, String> {
let client = state.0.read().await;

client
.user_details(user_id)
.await
.map_err(|e| e.to_string())
}

#[tauri::command(async)]
pub async fn get_user_stats(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<UserProfileStats, String> {
let client = state.0.read().await;

Ok(UserProfileStats {
friends: client
.friends_count(user_id)
.await
.map_err(|e| e.to_string())?,
followers: client
.followers_count(user_id)
.await
.map_err(|e| e.to_string())?,
followings: client
.followings_count(user_id)
.await
.map_err(|e| e.to_string())?,
})
}

#[tauri::command(async)]
pub async fn friend_status(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<FriendStatus, String> {
let client = state.0.read().await;

client
.friend_status(user_id)
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn friends_list(
state: State<'_, RobloxApiState>,
Expand All @@ -33,3 +84,62 @@ pub async fn friends_list(
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn users_friends_list(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<Vec<FriendUserInformation>, String> {
let client = state.0.read().await;

client
.friends_list(user_id)
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn friend(state: State<'_, RobloxApiState>, user_id: u64) -> Result<(), String> {
let client = state.0.read().await;

client
.send_friend_request(user_id)
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn unfriend(state: State<'_, RobloxApiState>, user_id: u64) -> Result<(), String> {
let client = state.0.read().await;

client
.unfriend(user_id)
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn accept_friend_request(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<(), String> {
let client = state.0.read().await;

client
.accept_friend_request(user_id)
.await
.map_err(|err| err.to_string())
}

#[tauri::command(async)]
pub async fn decline_friend_request(
state: State<'_, RobloxApiState>,
user_id: u64,
) -> Result<(), String> {
let client = state.0.read().await;

client
.decline_friend_request(user_id)
.await
.map_err(|err| err.to_string())
}
8 changes: 8 additions & 0 deletions src-tauri/src/roblox_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,15 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
get_presences,
// users.rs
commands::get_me,
commands::get_user,
commands::get_user_stats,
commands::friend_status,
commands::friends_list,
commands::users_friends_list,
commands::friend,
commands::unfriend,
commands::accept_friend_request,
commands::decline_friend_request,
// games.rs
commands::game_media,
commands::game_details,
Expand Down
7 changes: 7 additions & 0 deletions src-tauri/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ pub struct ClientInfo {
pub display_name: String,
pub robux: u64,
}

#[derive(Serialize, Deserialize)]
pub struct UserProfileStats {
pub friends: usize,
pub followers: usize,
pub followings: usize,
}
Loading

0 comments on commit 34f3533

Please sign in to comment.