community: the drawer asks a peer for its card and the server for interests, and discover gets the similar-users shelf

This commit is contained in:
KeelrLeek committed 2026-09-02 14:03:26 -05:00
1 parent 64a7561750
commit 5160d97a5f
10 files changed
+412 -2

No files matched your search

+88
View File
@@ -1084,6 +1084,94 @@ pub struct BrowseDirDto {
/// Walk a user's shares. Long-running: the client lock is held only long
/// enough to start the request.
#[derive(Serialize)]
pub struct UserInfoDto {
pub username: String,
pub description: String,
/// The picture ONLY after the avatar pipeline's bounded decode + resize
/// (profile.rs): a peer's bytes are hostile input, never rendered raw.
pub picture_png_base64: Option<String>,
pub picture_note: Option<String>,
pub total_uploads: u32,
pub queue_size: u32,
pub has_free_slot: bool,
}
/// A peer's native user-info card: description, picture, upload state.
#[tauri::command]
pub async fn slsk_user_info(
state: State<'_, AppState>,
username: String,
) -> Result<UserInfoDto, String> {
let fut = {
let guard = state.client.lock().await;
let client = guard.as_ref().ok_or("not connected to Soulseek")?;
client.user_info(&username)
};
let info = fut.await.map_err(|e| e.to_string())?;
let (picture_png_base64, picture_note) = match info.picture.as_deref() {
None => (None, None),
Some(bytes) => match crate::profile::normalize_avatar(bytes) {
Ok(png) => {
use base64::Engine;
(Some(base64::engine::general_purpose::STANDARD.encode(png)), None)
}
Err(e) => (None, Some(format!("picture refused: {e}"))),
},
};
Ok(UserInfoDto {
username,
description: info.description,
picture_png_base64,
picture_note,
total_uploads: info.total_uploads,
queue_size: info.queue_size,
has_free_slot: info.has_free_slot,
})
}
#[derive(Serialize)]
pub struct InterestsDto {
pub likes: Vec<String>,
pub hates: Vec<String>,
}
/// A user's declared Soulseek interests — raw likes and hates, not follows.
#[tauri::command]
pub async fn slsk_user_interests(
state: State<'_, AppState>,
username: String,
) -> Result<InterestsDto, String> {
let fut = {
let guard = state.client.lock().await;
let client = guard.as_ref().ok_or("not connected to Soulseek")?;
client.user_interests(&username)
};
let (likes, hates) = fut.await.map_err(|e| e.to_string())?;
Ok(InterestsDto { likes, hates })
}
#[derive(Serialize)]
pub struct SimilarUserDto {
pub username: String,
pub rating: u32,
}
/// Users the server ranks as similar to us by shared interests.
#[tauri::command]
pub async fn slsk_similar_users(state: State<'_, AppState>) -> Result<Vec<SimilarUserDto>, String> {
let fut = {
let guard = state.client.lock().await;
let client = guard.as_ref().ok_or("not connected to Soulseek")?;
client.similar_users()
};
let users = fut.await.map_err(|e| e.to_string())?;
Ok(users
.into_iter()
.map(|(username, rating)| SimilarUserDto { username, rating })
.collect())
}
#[tauri::command]
pub async fn slsk_browse_user(
state: State<'_, AppState>,
+3
View File
@@ -120,6 +120,9 @@ pub fn run() {
commands::slsk_message_user,
commands::slsk_room_list,
commands::slsk_browse_user,
commands::slsk_user_info,
commands::slsk_user_interests,
commands::slsk_similar_users,
commands::buddy_list,
commands::buddy_add,
commands::buddy_remove,