diff --git a/src/api/friends.rs b/src/api/friends.rs new file mode 100644 index 0000000..d6d7f32 --- /dev/null +++ b/src/api/friends.rs @@ -0,0 +1,227 @@ +use leptos::*; +use cfg_if::cfg_if; +use crate::frienddata::FriendData; + +cfg_if! { + if #[cfg(feature = "ssr")] { + use crate::auth::get_user; + use server_fn::error::NoCustomError; + + use crate::database::get_db_conn; + use diesel::prelude::*; + use diesel::dsl::exists; + use crate::models::*; + use crate::schema::*; + + use chrono::prelude::*; + } +} + +/// Get a user's list of friends from the database +#[server(endpoint = "/profile/friends")] +pub async fn friends(for_user_id: i32) + -> Result, ServerFnError> +{ + let mut db_con = get_db_conn(); + + let friends = friendships::table + .filter(friendships::friend_1_id.eq(for_user_id)) + .filter(friendships::friend_1_id.ne(friendships::friend_2_id)) + .inner_join(users::table.on(users::id.eq(friendships::friend_2_id))) + .select((users::all_columns, friendships::created_at)) + .order(friendships::created_at.desc()) + .order(users::username.asc()) + .union( + friendships::table + .filter(friendships::friend_2_id.eq(for_user_id)) + .filter(friendships::friend_1_id.ne(friendships::friend_2_id)) + .inner_join(users::table.on(users::id.eq(friendships::friend_1_id))) + .select((users::all_columns, friendships::created_at)) + .order(friendships::created_at.desc()) + .order(users::username.asc()) + ) + .load(&mut db_con)?; + + let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { + FriendData { + username: user.username, + created_at: created_at.into(), + user_id: user.id.unwrap() + } + }).collect(); + + Ok(friend_list) +} + +/// Get a user's list of friend requests (outgoing) from the database +#[server(endpoint = "/profile/friend-requests-outgoing")] +pub async fn friend_requests_outgoing(for_user_id: i32) + -> Result, ServerFnError> +{ + let mut db_con = get_db_conn(); + + let friends = friend_requests::table + .filter(friend_requests::from_id.eq(for_user_id)) + .filter(friend_requests::from_id.ne(friend_requests::to_id)) + .inner_join(users::table.on(users::id.eq(friend_requests::to_id))) + .select((users::all_columns, friend_requests::created_at)) + .order(friend_requests::created_at.desc()) + .order(users::username.asc()) + .load(&mut db_con)?; + + let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { + FriendData { + username: user.username, + created_at: created_at.into(), + user_id: user.id.unwrap() + } + }).collect(); + + Ok(friend_list) +} + +/// Get a user's list of friend requests (incoming) from the database +#[server(endpoint = "/profile/friend-requests-incoming")] +pub async fn friend_requests_incoming(for_user_id: i32) + -> Result, ServerFnError> +{ + let mut db_con = get_db_conn(); + + let friends = friend_requests::table + .filter(friend_requests::to_id.eq(for_user_id)) + .filter(friend_requests::from_id.ne(friend_requests::to_id)) + .inner_join(users::table.on(users::id.eq(friend_requests::from_id))) + .select((users::all_columns, friend_requests::created_at)) + .order(friend_requests::created_at.desc()) + .order(users::username.asc()) + .load(&mut db_con)?; + + let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { + FriendData { + username: user.username, + created_at: created_at.into(), + user_id: user.id.unwrap() + } + }).collect(); + + Ok(friend_list) +} + +/// Send a friend request +#[server(endpoint = "/profile/send-friend-request")] +pub async fn send_friend_request(to_user_id: i32) + -> Result<(), ServerFnError> +{ + let mut db_con = get_db_conn(); + + // Get user id from session + let user = get_user().await + .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; + + // Get current time for request + let timestamp: NaiveDateTime = Utc::now().naive_utc(); + + // Insert into database (if already exists, won't succeed due to primary key) + diesel::insert_into(crate::schema::friend_requests::table) + .values((friend_requests::created_at.eq(timestamp),friend_requests::from_id.eq(user.id.unwrap()),friend_requests::to_id.eq(to_user_id))) + .execute(&mut db_con) + .map_err(|e| { + let msg = format!("Error saving friend request to database: {}", e); + ServerFnError::::ServerError(msg) + })?; + + Ok(()) +} + +/// Remove an outgoing friend request +#[server(endpoint = "/profile/friend-requests-incoming")] +pub async fn delete_friend_request(to_user_id: i32) + -> Result<(), ServerFnError> +{ + let mut db_con = get_db_conn(); + + // Get user id from session + let user = get_user().await + .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; + + // Delete the friend request + diesel::delete(friend_requests::table + .filter(friend_requests::from_id.eq(user.id.unwrap())) + .filter(friend_requests::to_id.eq(to_user_id)) + ).execute(&mut db_con)?; + + Ok(()) +} + +/// Remove an existing friendship +#[server(endpoint = "/profile/delete-friend")] +pub async fn delete_friend(for_user_id: i32) + -> Result<(), ServerFnError> +{ + let mut db_con = get_db_conn(); + + // Get user id from session + let user = get_user().await + .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; + + // Delete the friend request + diesel::delete(friendships::table + .filter(friendships::friend_1_id.eq(user.id.unwrap())) + .filter(friendships::friend_2_id.eq(for_user_id)) + ).execute(&mut db_con)?; + + diesel::delete(friendships::table + .filter(friendships::friend_2_id.eq(user.id.unwrap())) + .filter(friendships::friend_1_id.eq(for_user_id)) + ).execute(&mut db_con)?; + + Ok(()) +} + +/// Accept a friend request +#[server(endpoint = "/profile/accept-friend-request")] +pub async fn accept_friend_request(to_user_id: i32) + -> Result<(), ServerFnError> +{ + let mut db_con = get_db_conn(); + + // Get user id from session + let user = get_user().await + .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; + + // Get current time for request + let timestamp: NaiveDateTime = Utc::now().naive_utc(); + + // Make sure the person has received a friend request from the other person + let req = diesel::select(exists( + friend_requests::table + .filter(friend_requests::from_id.eq(user.id.unwrap())) + .filter(friend_requests::to_id.eq(to_user_id)) + )).get_result::(&mut db_con)?; + + if req == false { + Err(ServerFnError::::ServerError(format!("Error, the friend request does not exist!")))?; + } + + // Delete the friend requests + diesel::delete(friend_requests::table + .filter(friend_requests::from_id.eq(user.id.unwrap())) + .filter(friend_requests::to_id.eq(to_user_id)) + ).execute(&mut db_con)?; + + diesel::delete(friend_requests::table + .filter(friend_requests::to_id.eq(user.id.unwrap())) + .filter(friend_requests::from_id.eq(to_user_id)) + ).execute(&mut db_con)?; + + // Add the new friend request either direction + diesel::insert_into(crate::schema::friendships::table) + .values((friendships::created_at.eq(timestamp),friendships::friend_1_id.eq(user.id.unwrap()),friendships::friend_2_id.eq(to_user_id))) + .execute(&mut db_con) + .map_err(|e| { + let msg = format!("Error saving friendship to database: {}", e); + ServerFnError::::ServerError(msg) + })?; + + Ok(()) +} \ No newline at end of file diff --git a/src/api/mod.rs b/src/api/mod.rs index e9ee6c9..9a2518a 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -2,3 +2,4 @@ pub mod history; pub mod profile; pub mod songs; pub mod album; +pub mod friends; \ No newline at end of file diff --git a/src/api/profile.rs b/src/api/profile.rs index 8bc8878..f994b49 100644 --- a/src/api/profile.rs +++ b/src/api/profile.rs @@ -5,7 +5,6 @@ use cfg_if::cfg_if; use crate::songdata::SongData; use crate::artistdata::ArtistData; -use crate::frienddata::FriendData; use chrono::NaiveDateTime; @@ -17,11 +16,9 @@ cfg_if! { use crate::database::get_db_conn; use diesel::prelude::*; use diesel::dsl::count; - use diesel::dsl::exists; use crate::models::*; use crate::schema::*; - use chrono::prelude::*; use std::collections::HashMap; } } @@ -301,212 +298,3 @@ pub async fn top_artists(for_user_id: i32, start_date: NaiveDateTime, end_date: Ok(artist_data) } - -/// Get a user's list of friends from the database -#[server(endpoint = "/profile/friends")] -pub async fn friends(for_user_id: i32) - -> Result, ServerFnError> -{ - let mut db_con = get_db_conn(); - - let friends = friendships::table - .filter(friendships::friend_1_id.eq(for_user_id)) - .filter(friendships::friend_1_id.ne(friendships::friend_2_id)) - .inner_join(users::table.on(users::id.eq(friendships::friend_2_id))) - .select((users::all_columns, friendships::created_at)) - .order(friendships::created_at.desc()) - .order(users::username.asc()) - .union( - friendships::table - .filter(friendships::friend_2_id.eq(for_user_id)) - .filter(friendships::friend_1_id.ne(friendships::friend_2_id)) - .inner_join(users::table.on(users::id.eq(friendships::friend_1_id))) - .select((users::all_columns, friendships::created_at)) - .order(friendships::created_at.desc()) - .order(users::username.asc()) - ) - .load(&mut db_con)?; - - let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { - FriendData { - username: user.username, - created_at: created_at.into(), - user_id: user.id.unwrap() - } - }).collect(); - - Ok(friend_list) -} - -/// Get a user's list of friend requests (outgoing) from the database -#[server(endpoint = "/profile/friend-requests-outgoing")] -pub async fn friend_requests_outgoing(for_user_id: i32) - -> Result, ServerFnError> -{ - let mut db_con = get_db_conn(); - - let friends = friend_requests::table - .filter(friend_requests::from_id.eq(for_user_id)) - .filter(friend_requests::from_id.ne(friend_requests::to_id)) - .inner_join(users::table.on(users::id.eq(friend_requests::to_id))) - .select((users::all_columns, friend_requests::created_at)) - .order(friend_requests::created_at.desc()) - .order(users::username.asc()) - .load(&mut db_con)?; - - let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { - FriendData { - username: user.username, - created_at: created_at.into(), - user_id: user.id.unwrap() - } - }).collect(); - - Ok(friend_list) -} - -/// Get a user's list of friend requests (incoming) from the database -#[server(endpoint = "/profile/friend-requests-incoming")] -pub async fn friend_requests_incoming(for_user_id: i32) - -> Result, ServerFnError> -{ - let mut db_con = get_db_conn(); - - let friends = friend_requests::table - .filter(friend_requests::to_id.eq(for_user_id)) - .filter(friend_requests::from_id.ne(friend_requests::to_id)) - .inner_join(users::table.on(users::id.eq(friend_requests::from_id))) - .select((users::all_columns, friend_requests::created_at)) - .order(friend_requests::created_at.desc()) - .order(users::username.asc()) - .load(&mut db_con)?; - - let friend_list: Vec = friends.into_iter().map(|(user, created_at): (User, NaiveDateTime)| { - FriendData { - username: user.username, - created_at: created_at.into(), - user_id: user.id.unwrap() - } - }).collect(); - - Ok(friend_list) -} - -/// Send a friend request -#[server(endpoint = "/profile/send-friend-request")] -pub async fn send_friend_request(to_user_id: i32) - -> Result<(), ServerFnError> -{ - let mut db_con = get_db_conn(); - - // Get user id from session - let user = get_user().await - .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; - - // Get current time for request - let timestamp: NaiveDateTime = Utc::now().naive_utc(); - - // Insert into database (if already exists, won't succeed due to primary key) - diesel::insert_into(crate::schema::friend_requests::table) - .values((friend_requests::created_at.eq(timestamp),friend_requests::from_id.eq(user.id.unwrap()),friend_requests::to_id.eq(to_user_id))) - .execute(&mut db_con) - .map_err(|e| { - let msg = format!("Error saving friend request to database: {}", e); - ServerFnError::::ServerError(msg) - })?; - - Ok(()) -} - -/// Remove an outgoing friend request -#[server(endpoint = "/profile/friend-requests-incoming")] -pub async fn delete_friend_request(to_user_id: i32) - -> Result<(), ServerFnError> -{ - let mut db_con = get_db_conn(); - - // Get user id from session - let user = get_user().await - .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; - - // Delete the friend request - diesel::delete(friend_requests::table - .filter(friend_requests::from_id.eq(user.id.unwrap())) - .filter(friend_requests::to_id.eq(to_user_id)) - ).execute(&mut db_con)?; - - Ok(()) -} - -/// Remove an existing friendship -#[server(endpoint = "/profile/delete-friend")] -pub async fn delete_friend(for_user_id: i32) - -> Result<(), ServerFnError> -{ - let mut db_con = get_db_conn(); - - // Get user id from session - let user = get_user().await - .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; - - // Delete the friend request - diesel::delete(friendships::table - .filter(friendships::friend_1_id.eq(user.id.unwrap())) - .filter(friendships::friend_2_id.eq(for_user_id)) - ).execute(&mut db_con)?; - - diesel::delete(friendships::table - .filter(friendships::friend_2_id.eq(user.id.unwrap())) - .filter(friendships::friend_1_id.eq(for_user_id)) - ).execute(&mut db_con)?; - - Ok(()) -} - -/// Accept a friend request -#[server(endpoint = "/profile/accept-friend-request")] -pub async fn accept_friend_request(to_user_id: i32) - -> Result<(), ServerFnError> -{ - let mut db_con = get_db_conn(); - - // Get user id from session - let user = get_user().await - .map_err(|e| ServerFnError::::ServerError(format!("Error getting user: {}", e)))?; - - // Get current time for request - let timestamp: NaiveDateTime = Utc::now().naive_utc(); - - // Make sure the person has received a friend request from the other person - let req = diesel::select(exists( - friend_requests::table - .filter(friend_requests::from_id.eq(user.id.unwrap())) - .filter(friend_requests::to_id.eq(to_user_id)) - )).get_result::(&mut db_con)?; - - if req == false { - Err(ServerFnError::::ServerError(format!("Error, the friend request does not exist!")))?; - } - - // Delete the friend requests - diesel::delete(friend_requests::table - .filter(friend_requests::from_id.eq(user.id.unwrap())) - .filter(friend_requests::to_id.eq(to_user_id)) - ).execute(&mut db_con)?; - - diesel::delete(friend_requests::table - .filter(friend_requests::to_id.eq(user.id.unwrap())) - .filter(friend_requests::from_id.eq(to_user_id)) - ).execute(&mut db_con)?; - - // Add the new friend request either direction - diesel::insert_into(crate::schema::friendships::table) - .values((friendships::created_at.eq(timestamp),friendships::friend_1_id.eq(user.id.unwrap()),friendships::friend_2_id.eq(to_user_id))) - .execute(&mut db_con) - .map_err(|e| { - let msg = format!("Error saving friendship to database: {}", e); - ServerFnError::::ServerError(msg) - })?; - - Ok(()) -} \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 4259cea..5e0b93e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -47,6 +47,7 @@ pub fn App() -> impl IntoView { + diff --git a/src/frienddata.rs b/src/frienddata.rs index cadeb07..d8c3375 100644 --- a/src/frienddata.rs +++ b/src/frienddata.rs @@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize}; use chrono::NaiveDate; -/// Holds information about an album +/// Holds information about a user (friend) /// /// Intended to be used in the front-end @@ -10,7 +10,7 @@ use chrono::NaiveDate; pub struct FriendData { /// Username pub username: String, - /// Date which the friend was added + /// Date which the user/friend was added pub created_at: NaiveDate, /// User's id to be used to locate their profile image pub user_id: i32 diff --git a/src/pages/friends.rs b/src/pages/friends.rs index 2a8d297..12dbc50 100644 --- a/src/pages/friends.rs +++ b/src/pages/friends.rs @@ -1,7 +1,7 @@ use leptos::leptos_dom::*; use leptos::*; use leptos_router::*; -use crate::api::profile::*; +use crate::api::friends::*; use crate::components::friend_list::*; use crate::components::loading::Loading; @@ -62,3 +62,73 @@ pub fn Friends() -> impl IntoView { } } +#[component] +pub fn FriendRequests() -> impl IntoView { + let params = use_params::(); + + let id = move || {params.with(|params| { + params.as_ref() + .map(|params| params.id) + .map_err(|e| e.clone()) + }) + }; + + let friend_list_incoming = create_resource( + id, + |value| async move { + match value { + Ok(v) => {friend_requests_incoming(v).await}, + Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e).into()))}, + } + }, + ); + + let friend_list_outgoing = create_resource( + id, + |value| async move { + send_friend_request(1).await; + match value { + Ok(v) => {friend_requests_outgoing(v).await}, + Err(e) => {Err(ServerFnError::Request(format!("Error getting song data: {}", e).into()))}, + } + }, + ); + + view! { +
+

"Friend Requests:"

+ + } + > + {e.to_string()}

}) + .collect_view() + } + } + > +

Sent:

+ { + friend_list_outgoing.get().map(|friend_list| { + friend_list.map(|friend_list| { + view! {} + }) + }) + } +

Received:

+ { + friend_list_incoming.get().map(|friend_list| { + friend_list.map(|friend_list| { + view! {} + }) + }) + } +
+
+
+ } +}