friends system (UI WIP)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"core:event:allow-listen",
|
||||
"core:event:allow-unlisten"
|
||||
"core:event:allow-unlisten",
|
||||
"core:window:allow-close"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::remotes::user::UserProfile;
|
||||
use crate::remotes::{friends::FriendshipResponseDto, user::UserProfile};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[ts(export)]
|
||||
pub struct AppData {
|
||||
pub user: Option<UserProfile>,
|
||||
pub friends: Option<Vec<FriendshipResponseDto>>,
|
||||
}
|
||||
|
||||
140
src-tauri/src/remotes/friends.rs
Normal file
140
src-tauri/src/remotes/friends.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use reqwest::{Client, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::{lock_r, services::auth::with_auth, state::FDOLL};
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct UserBasicDto {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub username: String,
|
||||
pub picture: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct FriendshipResponseDto {
|
||||
pub id: String,
|
||||
pub friend: UserBasicDto,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct SendFriendRequestDto {
|
||||
pub receiver_id: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct FriendRequestResponseDto {
|
||||
pub id: String,
|
||||
pub sender: UserBasicDto,
|
||||
pub receiver: UserBasicDto,
|
||||
pub status: String,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
pub struct FriendRemote {
|
||||
pub base_url: String,
|
||||
pub client: Client,
|
||||
}
|
||||
|
||||
impl FriendRemote {
|
||||
pub fn new() -> Self {
|
||||
let guard = lock_r!(FDOLL);
|
||||
Self {
|
||||
base_url: guard
|
||||
.app_config
|
||||
.api_base_url
|
||||
.as_ref()
|
||||
.expect("App configuration error")
|
||||
.clone(),
|
||||
client: guard
|
||||
.clients
|
||||
.as_ref()
|
||||
.expect("App configuration error")
|
||||
.http_client
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_friends(&self) -> Result<Vec<FriendshipResponseDto>, Error> {
|
||||
let url = format!("{}/friends", self.base_url);
|
||||
let resp = with_auth(self.client.get(url)).await.send().await?;
|
||||
let friends = resp.json().await?;
|
||||
Ok(friends)
|
||||
}
|
||||
|
||||
pub async fn search_users(&self, username: Option<&str>) -> Result<Vec<UserBasicDto>, Error> {
|
||||
let mut url = format!("{}/friends/search", self.base_url);
|
||||
if let Some(u) = username {
|
||||
url.push_str(&format!("?username={}", u));
|
||||
}
|
||||
let resp = with_auth(self.client.get(&url)).await.send().await?;
|
||||
let users = resp.json().await?;
|
||||
Ok(users)
|
||||
}
|
||||
|
||||
pub async fn send_friend_request(
|
||||
&self,
|
||||
request: SendFriendRequestDto,
|
||||
) -> Result<FriendRequestResponseDto, Error> {
|
||||
let url = format!("{}/friends/requests", self.base_url);
|
||||
let resp = with_auth(self.client.post(url))
|
||||
.await
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?;
|
||||
let req_resp = resp.json().await?;
|
||||
Ok(req_resp)
|
||||
}
|
||||
|
||||
pub async fn get_received_requests(&self) -> Result<Vec<FriendRequestResponseDto>, Error> {
|
||||
let url = format!("{}/friends/requests/received", self.base_url);
|
||||
let resp = with_auth(self.client.get(url)).await.send().await?;
|
||||
let requests = resp.json().await?;
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
pub async fn get_sent_requests(&self) -> Result<Vec<FriendRequestResponseDto>, Error> {
|
||||
let url = format!("{}/friends/requests/sent", self.base_url);
|
||||
let resp = with_auth(self.client.get(url)).await.send().await?;
|
||||
let requests = resp.json().await?;
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
pub async fn accept_friend_request(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<FriendRequestResponseDto, Error> {
|
||||
let url = format!("{}/friends/requests/{}/accept", self.base_url, request_id);
|
||||
let resp = with_auth(self.client.post(url)).await.send().await?;
|
||||
let req_resp = resp.json().await?;
|
||||
Ok(req_resp)
|
||||
}
|
||||
|
||||
pub async fn deny_friend_request(
|
||||
&self,
|
||||
request_id: &str,
|
||||
) -> Result<FriendRequestResponseDto, Error> {
|
||||
let url = format!("{}/friends/requests/{}/deny", self.base_url, request_id);
|
||||
let resp = with_auth(self.client.post(url)).await.send().await?;
|
||||
let req_resp = resp.json().await?;
|
||||
Ok(req_resp)
|
||||
}
|
||||
|
||||
pub async fn unfriend(&self, friend_id: &str) -> Result<(), Error> {
|
||||
let url = format!("{}/friends/{}", self.base_url, friend_id);
|
||||
let resp = with_auth(self.client.delete(url)).await.send().await?;
|
||||
resp.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod friends;
|
||||
pub mod user;
|
||||
|
||||
@@ -9,11 +9,22 @@ use crate::{lock_r, services::auth::with_auth, state::FDOLL};
|
||||
#[ts(export)]
|
||||
pub struct UserProfile {
|
||||
pub id: String,
|
||||
pub keycloak_sub: String,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub username: String,
|
||||
pub username: Option<String>,
|
||||
pub picture: Option<String>,
|
||||
pub roles: Vec<String>,
|
||||
pub created_at: String,
|
||||
pub last_login_at: String,
|
||||
pub updated_at: String,
|
||||
pub last_login_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export)]
|
||||
pub struct UpdateUserDto {
|
||||
// Empty as per API schema
|
||||
}
|
||||
|
||||
pub struct UserRemote {
|
||||
@@ -47,5 +58,25 @@ impl UserRemote {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
// TODO: Add other endpoints as methods
|
||||
pub async fn update_user(
|
||||
&self,
|
||||
user_id: Option<&str>,
|
||||
update: UpdateUserDto,
|
||||
) -> Result<UserProfile, Error> {
|
||||
let url = format!("{}/users/{}", self.base_url, user_id.unwrap_or("me"));
|
||||
let resp = with_auth(self.client.put(url))
|
||||
.await
|
||||
.json(&update)
|
||||
.send()
|
||||
.await?;
|
||||
let user = resp.json().await?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn delete_user(&self, user_id: Option<&str>) -> Result<(), Error> {
|
||||
let url = format!("{}/users/{}", self.base_url, user_id.unwrap_or("me"));
|
||||
let resp = with_auth(self.client.delete(url)).await.send().await?;
|
||||
resp.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use rust_socketio::{ClientBuilder, Payload, RawClient};
|
||||
use serde_json::json;
|
||||
use tauri::async_runtime;
|
||||
use tauri::{async_runtime, Emitter};
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::{
|
||||
lock_r, lock_w,
|
||||
services::cursor::CursorPosition,
|
||||
{models::app_config::AppConfig, state::FDOLL},
|
||||
get_app_handle, lock_r, lock_w, models::app_config::AppConfig,
|
||||
services::cursor::CursorPosition, state::FDOLL,
|
||||
};
|
||||
|
||||
#[allow(non_camel_case_types)] // pretend to be a const like in js
|
||||
@@ -14,13 +13,54 @@ pub struct WS_EVENT;
|
||||
|
||||
impl WS_EVENT {
|
||||
pub const CURSOR_REPORT_POSITION: &str = "cursor-report-position";
|
||||
pub const FRIEND_REQUEST_RECEIVED: &str = "friend-request-received";
|
||||
pub const FRIEND_REQUEST_ACCEPTED: &str = "friend-request-accepted";
|
||||
pub const FRIEND_REQUEST_DENIED: &str = "friend-request-denied";
|
||||
pub const UNFRIENDED: &str = "unfriended";
|
||||
}
|
||||
|
||||
// Define a callback for handling incoming messages (e.g., 'pong')
|
||||
fn on_pong(payload: Payload, _socket: RawClient) {
|
||||
fn on_friend_request_received(payload: Payload, _socket: RawClient) {
|
||||
match payload {
|
||||
Payload::Text(str) => println!("Received pong: {:?}", str),
|
||||
Payload::Binary(bin) => println!("Received pong (binary): {:?}", bin),
|
||||
Payload::Text(str) => {
|
||||
println!("Received friend request: {:?}", str);
|
||||
get_app_handle()
|
||||
.emit(WS_EVENT::FRIEND_REQUEST_RECEIVED, str)
|
||||
.unwrap();
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_friend_request_accepted(payload: Payload, _socket: RawClient) {
|
||||
match payload {
|
||||
Payload::Text(str) => {
|
||||
println!("Received friend request accepted: {:?}", str);
|
||||
get_app_handle()
|
||||
.emit(WS_EVENT::FRIEND_REQUEST_ACCEPTED, str)
|
||||
.unwrap();
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_friend_request_denied(payload: Payload, _socket: RawClient) {
|
||||
match payload {
|
||||
Payload::Text(str) => {
|
||||
println!("Received friend request denied: {:?}", str);
|
||||
get_app_handle()
|
||||
.emit(WS_EVENT::FRIEND_REQUEST_DENIED, str)
|
||||
.unwrap();
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_unfriended(payload: Payload, _socket: RawClient) {
|
||||
match payload {
|
||||
Payload::Text(str) => {
|
||||
println!("Received unfriended: {:?}", str);
|
||||
get_app_handle().emit(WS_EVENT::UNFRIENDED, str).unwrap();
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
@@ -82,7 +122,16 @@ pub async fn build_ws_client(app_config: &AppConfig) -> rust_socketio::client::C
|
||||
let client = async_runtime::spawn_blocking(move || {
|
||||
ClientBuilder::new(api_base_url)
|
||||
.namespace("/")
|
||||
.on("pong", on_pong)
|
||||
.on(
|
||||
WS_EVENT::FRIEND_REQUEST_RECEIVED,
|
||||
on_friend_request_received,
|
||||
)
|
||||
.on(
|
||||
WS_EVENT::FRIEND_REQUEST_ACCEPTED,
|
||||
on_friend_request_accepted,
|
||||
)
|
||||
.on(WS_EVENT::FRIEND_REQUEST_DENIED, on_friend_request_denied)
|
||||
.on(WS_EVENT::UNFRIENDED, on_unfriended)
|
||||
.auth(json!({ "token": token }))
|
||||
.connect()
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
app_config::{AppConfig, AuthConfig},
|
||||
app_data::AppData,
|
||||
},
|
||||
remotes::user::UserRemote,
|
||||
remotes::{friends::FriendRemote, user::UserRemote},
|
||||
services::auth::{load_auth_pass, AuthPass},
|
||||
};
|
||||
use serde_json::json;
|
||||
@@ -99,13 +99,20 @@ pub fn init_fdoll_state() {
|
||||
/// Populate user data in app state from the server.
|
||||
pub async fn init_app_data() {
|
||||
let user_remote = UserRemote::new();
|
||||
let friend_remote = FriendRemote::new();
|
||||
let user = user_remote
|
||||
.get_user(None)
|
||||
.await
|
||||
.expect("TODO: handle user profile fetch failure");
|
||||
let friends = friend_remote
|
||||
.get_friends()
|
||||
.await
|
||||
.expect("TODO: handle friends fetch failure");
|
||||
|
||||
{
|
||||
let mut guard = lock_w!(FDOLL);
|
||||
guard.app_data.user = Some(user);
|
||||
guard.app_data.friends = Some(friends);
|
||||
get_app_handle()
|
||||
.emit("app-data-refreshed", json!(guard.app_data))
|
||||
.expect("TODO: handle event emit fail");
|
||||
|
||||
Reference in New Issue
Block a user