forked from Keelrleek/SoulStream
holdings: a member of a Space publishes what they hold as room-salted album keys, sealed to the room, so "who has this" is answered while they are offline
This commit is contained in:
1 parent
08ca09586a
commit
a98c750908
12 files changed
+2955
-8
No files matched your search
+64
-5
@@ -66,6 +66,11 @@ struct SpaceRecord {
|
||||
/// room is not consent to be watched in it.
|
||||
#[serde(default)]
|
||||
share_listens: bool,
|
||||
/// Whether this device publishes what it HOLDS into this Space — album
|
||||
/// keys sealed to the room, never files (`holdings.rs`). Per Space and
|
||||
/// off until asked, for the same reason as listens.
|
||||
#[serde(default)]
|
||||
share_holdings: bool,
|
||||
/// The last listen posted here — what, and when. A reload or a restart
|
||||
/// re-reports whatever is already past the player's threshold, and this
|
||||
/// is the only side that knows what a room was last told.
|
||||
@@ -153,6 +158,8 @@ pub struct SpaceView {
|
||||
pub can_post: bool,
|
||||
/// Whether THIS device posts what it plays here.
|
||||
pub share_listens: bool,
|
||||
/// Whether THIS device publishes its holdings here.
|
||||
pub share_holdings: bool,
|
||||
pub has_key: bool,
|
||||
/// The contract exists on the network (a Space someone invited us to
|
||||
/// may not have propagated to this node yet).
|
||||
@@ -226,13 +233,13 @@ fn rand32() -> [u8; 32] {
|
||||
rand::thread_rng().fill_bytes(&mut n);
|
||||
n
|
||||
}
|
||||
fn b58(bytes: &[u8]) -> String {
|
||||
pub(crate) fn b58(bytes: &[u8]) -> String {
|
||||
bs58::encode(bytes).into_string()
|
||||
}
|
||||
fn key_of(s: &str) -> Result<[u8; 32], String> {
|
||||
pub(crate) fn key_of(s: &str) -> Result<[u8; 32], String> {
|
||||
inbox::key_of(s)
|
||||
}
|
||||
fn id_of(s: &str) -> Result<[u8; 16], String> {
|
||||
pub(crate) fn id_of(s: &str) -> Result<[u8; 16], String> {
|
||||
bs58::decode(s)
|
||||
.into_vec()
|
||||
.map_err(|e| format!("bad id: {e}"))?
|
||||
@@ -250,7 +257,7 @@ fn cap(s: &str, max: usize) -> String {
|
||||
}
|
||||
s[..end].to_string()
|
||||
}
|
||||
fn record_id(owner: &str, space_id: &str) -> String {
|
||||
pub(crate) fn record_id(owner: &str, space_id: &str) -> String {
|
||||
format!("{owner}:{space_id}")
|
||||
}
|
||||
|
||||
@@ -281,7 +288,7 @@ fn save(app: &tauri::AppHandle, l: &Ledger) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn my_key(app: &tauri::AppHandle) -> Result<[u8; 32], String> {
|
||||
pub(crate) async fn my_key(app: &tauri::AppHandle) -> Result<[u8; 32], String> {
|
||||
let adapter = app.state::<AppState>().freenet.clone();
|
||||
let info = persona::current(&adapter)
|
||||
.await?
|
||||
@@ -404,6 +411,7 @@ fn view_of(app: &tauri::AppHandle, rec: &SpaceRecord, state: &sp::SpaceState, me
|
||||
listens: vec![],
|
||||
can_post: false,
|
||||
share_listens: rec.share_listens,
|
||||
share_holdings: rec.share_holdings,
|
||||
has_key: false,
|
||||
reachable,
|
||||
};
|
||||
@@ -490,6 +498,7 @@ fn view_of(app: &tauri::AppHandle, rec: &SpaceRecord, state: &sp::SpaceState, me
|
||||
.collect(),
|
||||
can_post: is_member && has_key,
|
||||
share_listens: rec.share_listens,
|
||||
share_holdings: rec.share_holdings,
|
||||
has_key,
|
||||
reachable,
|
||||
}
|
||||
@@ -563,6 +572,7 @@ pub async fn create(app: &tauri::AppHandle, name: &str, description: &str, priva
|
||||
roster: BTreeMap::new(),
|
||||
invited: BTreeMap::new(),
|
||||
share_listens: false,
|
||||
share_holdings: false,
|
||||
last_listen: None,
|
||||
join_sent_at: 0,
|
||||
};
|
||||
@@ -826,6 +836,53 @@ pub async fn set_pinned(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str
|
||||
|
||||
/// Turn posting-what-I-play on or off for one Space. Nothing is published
|
||||
/// by flipping it; it only decides what the next track does.
|
||||
/// What `holdings.rs` needs to know about a room, without the record.
|
||||
pub(crate) struct RoomFacts {
|
||||
pub name: String,
|
||||
pub private: bool,
|
||||
pub my_role: String,
|
||||
pub share_holdings: bool,
|
||||
}
|
||||
|
||||
pub(crate) fn room_facts(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str) -> Option<RoomFacts> {
|
||||
let l = load(app);
|
||||
let rec = l.spaces.get(&record_id(owner_b58, space_b58))?;
|
||||
Some(RoomFacts { name: rec.name.clone(), private: rec.private, my_role: rec.my_role.clone(), share_holdings: rec.share_holdings })
|
||||
}
|
||||
|
||||
/// The room's key for one epoch, if this device was handed it.
|
||||
pub(crate) fn epoch_key_for(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str, epoch: u64) -> Option<[u8; 32]> {
|
||||
let l = load(app);
|
||||
let rec = l.spaces.get(&record_id(owner_b58, space_b58))?;
|
||||
rec.keys.get(&epoch.to_string()).and_then(|k| key_of(k).ok())
|
||||
}
|
||||
|
||||
/// Every room this device is in: (owner, space id, name).
|
||||
pub(crate) fn rooms(app: &tauri::AppHandle) -> Vec<(String, String, String)> {
|
||||
load(app).spaces.values().map(|r| (r.owner.clone(), r.space_id.clone(), r.name.clone())).collect()
|
||||
}
|
||||
|
||||
/// The rooms this device publishes its holdings into.
|
||||
pub(crate) fn rooms_sharing_holdings(app: &tauri::AppHandle) -> Vec<(String, String)> {
|
||||
load(app).spaces.values().filter(|r| r.share_holdings).map(|r| (r.owner.clone(), r.space_id.clone())).collect()
|
||||
}
|
||||
|
||||
/// The Soulseek name behind a room key as this device knows it: through the
|
||||
/// owner's private roster, or the key itself when it is a persona (an owner's).
|
||||
pub(crate) fn username_behind(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str, member_b58: &str) -> Option<String> {
|
||||
let l = load(app);
|
||||
let rec = l.spaces.get(&record_id(owner_b58, space_b58))?;
|
||||
slsk_link::username_for_key(app, rec.roster.get(member_b58).map(String::as_str).unwrap_or(member_b58))
|
||||
}
|
||||
|
||||
pub(crate) async fn set_share_holdings_flag(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str, on: bool) -> Result<(), String> {
|
||||
let _ledger = ledger::hold(LEDGER_FILE).await;
|
||||
let mut l = load(app);
|
||||
let rec = l.spaces.get_mut(&record_id(owner_b58, space_b58)).ok_or("not a Space this device knows")?;
|
||||
rec.share_holdings = on;
|
||||
save(app, &l)
|
||||
}
|
||||
|
||||
pub async fn set_share_listens(app: &tauri::AppHandle, owner_b58: &str, space_b58: &str, on: bool) -> Result<SpaceView, String> {
|
||||
{
|
||||
let _ledger = ledger::hold(LEDGER_FILE).await;
|
||||
@@ -946,6 +1003,7 @@ pub(crate) async fn on_inbox_invite(app: &tauri::AppHandle, sender: [u8; 32], in
|
||||
roster: BTreeMap::new(),
|
||||
invited: BTreeMap::new(),
|
||||
share_listens: false,
|
||||
share_holdings: false,
|
||||
last_listen: None,
|
||||
join_sent_at: 0,
|
||||
});
|
||||
@@ -1197,6 +1255,7 @@ mod tests {
|
||||
roster: BTreeMap::new(),
|
||||
invited: BTreeMap::new(),
|
||||
share_listens: false,
|
||||
share_holdings: false,
|
||||
last_listen: None,
|
||||
join_sent_at: 0,
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user