Encrypted peer-to-peer IM for data security. Own data, own privacy. (Rust+Flutter)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

214 lines
6.4 KiB

use esse_primitives::{id_from_str, id_to_str};
use std::time::{SystemTime, UNIX_EPOCH};
use tdn::types::{
primitives::{PeerId, Result},
rpc::{json, RpcParam},
};
use tdn_storage::local::{DStorage, DsValue};
use crate::session::{Session, SessionType};
use super::Message;
pub(crate) struct Friend {
pub id: i64,
pub pid: PeerId,
pub name: String,
pub wallet: String,
pub height: i64,
pub remark: String,
pub is_closed: bool,
pub datetime: i64,
}
impl Friend {
pub fn new(pid: PeerId, name: String, wallet: String, remark: String, height: i64) -> Friend {
let start = SystemTime::now();
let datetime = start
.duration_since(UNIX_EPOCH)
.map(|s| s.as_secs())
.unwrap_or(0) as i64; // safe for all life.
Friend {
id: 0,
pid,
name,
wallet,
height,
remark,
datetime,
is_closed: false,
}
}
/// here is zero-copy and unwrap is safe.
fn from_values(mut v: Vec<DsValue>) -> Friend {
Friend {
datetime: v.pop().unwrap().as_i64(),
is_closed: v.pop().unwrap().as_bool(),
remark: v.pop().unwrap().as_string(),
height: v.pop().unwrap().as_i64(),
wallet: v.pop().unwrap().as_string(),
name: v.pop().unwrap().as_string(),
pid: id_from_str(v.pop().unwrap().as_str()).unwrap_or(PeerId::default()),
id: v.pop().unwrap().as_i64(),
}
}
pub fn from_remote(db: &DStorage, pid: PeerId, name: String, wallet: String) -> Result<Friend> {
if let Ok(mut friend) = Friend::get_id(&db, &pid) {
friend.name = name;
friend.wallet = wallet;
friend.is_closed = false;
friend.remote_update(&db)?;
Ok(friend)
} else {
let mut friend = Friend::new(pid, name, wallet, "".to_owned(), 0);
friend.insert(&db)?;
Ok(friend)
}
}
pub fn to_session(&self) -> Session {
Session::new(
self.id,
id_to_str(&self.pid),
self.pid,
SessionType::Chat,
self.name.clone(),
self.datetime,
)
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
id_to_str(&self.pid),
self.name,
self.wallet,
self.remark,
self.is_closed,
self.datetime
])
}
pub fn to_rpc_online(&self, online: bool) -> RpcParam {
json!([
self.id,
id_to_str(&self.pid),
self.name,
self.wallet,
self.remark,
self.is_closed,
self.datetime,
online
])
}
pub fn get_id(db: &DStorage, pid: &PeerId) -> Result<Friend> {
let sql = format!("SELECT id, pid, name, wallet, height, remark, is_closed, datetime FROM friends WHERE pid = '{}'", id_to_str(pid));
let mut matrix = db.query(&sql)?;
if matrix.len() > 0 {
Ok(Friend::from_values(matrix.pop().unwrap())) // safe unwrap()
} else {
Err(anyhow!("friend is missing."))
}
}
pub fn get(db: &DStorage, id: &i64) -> Result<Friend> {
let sql = format!("SELECT id, pid, name, wallet, height, remark, is_closed, datetime FROM friends WHERE id = {}", id);
let mut matrix = db.query(&sql)?;
if matrix.len() > 0 {
Ok(Friend::from_values(matrix.pop().unwrap())) // safe unwrap()
} else {
Err(anyhow!("friend is missing."))
}
}
/// use in rpc when load account friends.
pub fn list(db: &DStorage) -> Result<Vec<Friend>> {
let matrix = db.query(
"SELECT id, pid, name, wallet, height, remark, is_closed, datetime FROM friends",
)?;
let mut friends = vec![];
for values in matrix {
friends.push(Friend::from_values(values));
}
Ok(friends)
}
pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO friends (pid, name, wallet, height, remark, is_closed, datetime) VALUES ('{}', '{}', '{}', {}, '{}', {}, {})",
id_to_str(&self.pid),
self.name,
self.wallet,
self.height,
self.remark,
self.is_closed,
self.datetime,
);
let id = db.insert(&sql)?;
self.id = id;
Ok(())
}
pub fn update(&self, db: &DStorage) -> Result<usize> {
let sql = format!("UPDATE friends SET name = '{}', wallet = '{}', height={}, remark = '{}', is_closed = {} WHERE id = {}",
self.name,
self.wallet,
self.height,
self.remark,
self.is_closed,
self.id
);
db.update(&sql)
}
pub fn me_update(&mut self, db: &DStorage) -> Result<usize> {
let sql = format!(
"UPDATE friends SET remark='{}' WHERE id = {}",
self.remark, self.id,
);
db.update(&sql)
}
pub fn remote_update(&self, db: &DStorage) -> Result<usize> {
let sql = format!(
"UPDATE friends SET name='{}', wallet='{}', height={}, is_closed = false WHERE id = {}",
self.name, self.wallet, self.height, self.id,
);
db.update(&sql)
}
/// used in rpc, when what to delete a friend.
pub fn close(&self, db: &DStorage) -> Result<usize> {
let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", self.id);
db.update(&sql)
}
/// used in rpc, when what to delete a friend.
pub fn delete(db: &DStorage, id: &i64) -> Result<usize> {
let sql = format!("DELETE FROM friends WHERE id = {}", id);
db.update(&sql)?;
// TODO delete friend avatar.
// delete messages;
Message::delete_by_fid(&db, id)
}
pub fn is_friend(db: &DStorage, pid: &PeerId) -> Result<bool> {
let sql = format!(
"SELECT id FROM friends WHERE is_closed = false and pid = '{}'",
id_to_str(pid)
);
let matrix = db.query(&sql)?;
Ok(matrix.len() > 0)
}
/// used in layers, when receive remote had closed.
pub fn id_close(db: &DStorage, id: i64) -> Result<usize> {
let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", id);
db.update(&sql)
}
}