forked from Keelrleek/SoulStream
community: one area for the people on both networks, and the hub grows search tabs, a folder column and a queue column
This commit is contained in:
1 parent
6dce4e2ac7
commit
8c25f93afe
14 files changed
+2368
-68
No files matched your search
@@ -0,0 +1,89 @@
|
||||
//! The Community area's own small backend: the local contact metadata
|
||||
//! (petnames, groups, notes, the two local marks) and the file dialogs the
|
||||
//! You section needs. Everything Soulseek lives in commands.rs, everything
|
||||
//! Freenet in freenet.rs / persona.rs / profile.rs — this is the glue that
|
||||
//! is neither.
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::config::{self, ContactMeta};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn contacts_meta_list(app: tauri::AppHandle) -> Vec<ContactMeta> {
|
||||
config::load(&app).contacts
|
||||
}
|
||||
|
||||
/// Upsert one contact's local metadata. An entry with nothing set is
|
||||
/// dropped rather than stored empty, so the list only ever holds people the
|
||||
/// user actually said something about.
|
||||
#[tauri::command]
|
||||
pub fn contact_meta_set(app: tauri::AppHandle, meta: ContactMeta) -> Vec<ContactMeta> {
|
||||
let blank = |s: &Option<String>| s.as_deref().map_or(true, |v| v.trim().is_empty());
|
||||
let mut cfg = config::load(&app);
|
||||
cfg.contacts.retain(|c| c.username != meta.username);
|
||||
let empty = blank(&meta.petname)
|
||||
&& blank(&meta.group)
|
||||
&& blank(&meta.note)
|
||||
&& !meta.trusted
|
||||
&& !meta.favorite;
|
||||
if !empty {
|
||||
let trim = |s: Option<String>| s.map(|v| v.trim().to_string()).filter(|v| !v.is_empty());
|
||||
cfg.contacts.push(ContactMeta {
|
||||
username: meta.username,
|
||||
petname: trim(meta.petname),
|
||||
group: trim(meta.group),
|
||||
note: trim(meta.note),
|
||||
trusted: meta.trusted,
|
||||
favorite: meta.favorite,
|
||||
});
|
||||
}
|
||||
config::save(&app, &cfg);
|
||||
cfg.contacts
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum PickKind {
|
||||
/// A source image for the avatar pipeline.
|
||||
Image,
|
||||
/// An existing recovery artifact to import.
|
||||
RecoveryOpen,
|
||||
/// Where to write a new recovery artifact.
|
||||
RecoverySave,
|
||||
}
|
||||
|
||||
const RECOVERY_EXT: &str = "ssrecovery";
|
||||
|
||||
/// Native file dialogs for the You section. Paths only — the bytes behind
|
||||
/// them are read and written by Rust (persona.rs, profile.rs), never by the
|
||||
/// webview.
|
||||
#[tauri::command]
|
||||
pub async fn community_pick_file(
|
||||
app: tauri::AppHandle,
|
||||
kind: PickKind,
|
||||
) -> Result<Option<String>, String> {
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
let done = move |p: Option<tauri_plugin_dialog::FilePath>| {
|
||||
let _ = tx.send(p.map(|p| p.to_string()));
|
||||
};
|
||||
match kind {
|
||||
PickKind::Image => app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("Image", &["jpg", "jpeg", "png", "webp"])
|
||||
.pick_file(done),
|
||||
PickKind::RecoveryOpen => app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("SoulStream recovery file", &[RECOVERY_EXT])
|
||||
.pick_file(done),
|
||||
PickKind::RecoverySave => app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("SoulStream recovery file", &[RECOVERY_EXT])
|
||||
.set_file_name(&format!("soulstream-identity.{RECOVERY_EXT}"))
|
||||
.save_file(done),
|
||||
}
|
||||
Ok(rx.await.unwrap_or(None))
|
||||
}
|
||||
Reference in new issue
Block a user