diff --git a/lib/apps/group_chat/provider.dart b/lib/apps/group_chat/provider.dart index bd66a87..73834e7 100644 --- a/lib/apps/group_chat/provider.dart +++ b/lib/apps/group_chat/provider.dart @@ -94,8 +94,8 @@ class GroupChatProvider extends ChangeNotifier { // } - join(String gid, String gaddr, String name, String remark) { - rpc.send('group-chat-join', [gid, gaddr, name, remark]); + join(String gid, String gaddr, String name, String remark, [String key = '']) { + rpc.send('group-chat-join', [gid, gaddr, name, remark, key]); } messageCreate(MessageType mtype, String content) { diff --git a/src/apps/group_chat/layer.rs b/src/apps/group_chat/layer.rs index a2eb3be..bb62af6 100644 --- a/src/apps/group_chat/layer.rs +++ b/src/apps/group_chat/layer.rs @@ -15,7 +15,7 @@ use tdn_did::Proof; use crate::layer::{Layer, Online}; use crate::storage::{group_chat_db, write_avatar_sync}; -use super::models::{from_network_message, GroupChat, Member}; +use super::models::{from_network_message, GroupChat, Member, Request}; use super::{add_layer, rpc}; pub(crate) async fn handle( @@ -73,9 +73,11 @@ pub(crate) async fn handle( // 3. online to UI. results.rpcs.push(rpc::group_online(mgid, group.id)); - // 5. sync group height. + // 4. TODO online ping. + + // 5. TODO sync group height. if group.height < height { - // TOOD + // } } else { let msg = SendType::Result(0, addr, false, false, vec![]); @@ -87,11 +89,26 @@ pub(crate) async fn handle( GroupResult::Waiting(_gcd) => { // TODO waiting } - GroupResult::Agree(_gcd, _group_info, _height) => { - // TOOD + GroupResult::Agree(gcd, info, height) => { + let base = layer.read().await.base.clone(); + let db = group_chat_db(&base, &mgid)?; + let (rid, key) = Request::over(&db, &gcd, true)?; + + // 1. add group chat. + let mut group = GroupChat::from_info(key, info, height, addr, base, &mgid)?; + group.insert(&db)?; + + // 2. update UI. + results.rpcs.push(rpc::group_agree(mgid, rid, group)); + + // 3. online ping. + + // 4. sync group height. } - GroupResult::Reject(_gcd) => { - // TOOD + GroupResult::Reject(gcd) => { + let db = group_chat_db(layer.read().await.base(), &mgid)?; + let (rid, _key) = Request::over(&db, &gcd, true)?; + results.rpcs.push(rpc::group_reject(mgid, rid)); } } } diff --git a/src/apps/group_chat/models.rs b/src/apps/group_chat/models.rs index e64fd78..9ddefe8 100644 --- a/src/apps/group_chat/models.rs +++ b/src/apps/group_chat/models.rs @@ -15,7 +15,7 @@ use crate::storage::{ group_chat_db, write_avatar_sync, write_file_sync, write_image_sync, write_record_sync, }; -pub(super) struct GroupChatKey(Vec); +pub(crate) struct GroupChatKey(Vec); impl GroupChatKey { pub fn new(value: Vec) -> Self { @@ -137,6 +137,77 @@ impl GroupChat { } } + fn new_from( + g_id: GroupId, + height: i64, + owner: GroupId, + g_type: GroupType, + g_addr: PeerAddr, + g_name: String, + g_bio: String, + is_need_agree: bool, + key: GroupChatKey, + ) -> Self { + 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. + + Self { + owner, + g_id, + g_type, + g_addr, + g_name, + g_bio, + is_need_agree, + key, + datetime, + id: 0, + height, + is_top: true, + is_ok: true, + is_closed: false, + last_datetime: datetime, + last_content: Default::default(), + last_readed: true, + online: false, + is_deleted: false, + } + } + + pub fn from_info( + key: GroupChatKey, + info: GroupInfo, + height: i64, + addr: PeerAddr, + base: PathBuf, + mgid: &GroupId, + ) -> Result { + match info { + GroupInfo::Common(owner, _, g_id, g_type, agree, name, g_bio, avatar) => { + write_avatar_sync(&base, &mgid, &g_id, avatar)?; + Ok(Self::new_from( + g_id, height, owner, g_type, addr, name, g_bio, agree, key, + )) + } + GroupInfo::Encrypted(owner, _, g_id, agree, _hash, _name, _bio, avatar) => { + // TODO decrypted. + + let g_type = GroupType::Encrypted; + let name = "".to_owned(); + let bio = "".to_owned(); + + write_avatar_sync(&base, &mgid, &g_id, avatar)?; + + Ok(Self::new_from( + g_id, height, owner, g_type, addr, name, bio, agree, key, + )) + } + } + } + pub fn to_group_info(self, name: String, avatar: Vec) -> GroupInfo { match self.g_type { GroupType::Common | GroupType::Open => GroupInfo::Common( @@ -304,6 +375,7 @@ pub(crate) struct Request { pub gid: GroupId, pub addr: PeerAddr, pub name: String, + key: GroupChatKey, remark: String, is_ok: bool, is_over: bool, @@ -326,13 +398,20 @@ impl Request { name, remark, datetime, + key: GroupChatKey(vec![]), is_ok: false, is_over: false, id: 0, } } - pub fn new_by_me(gid: GroupId, addr: PeerAddr, name: String, remark: String) -> Self { + pub fn new_by_me( + gid: GroupId, + addr: PeerAddr, + name: String, + remark: String, + key: GroupChatKey, + ) -> Self { let start = SystemTime::now(); let datetime = start .duration_since(UNIX_EPOCH) @@ -345,6 +424,7 @@ impl Request { name, remark, datetime, + key, is_ok: false, is_over: false, fid: 0, @@ -367,12 +447,13 @@ impl Request { } pub fn insert(&mut self, db: &DStorage) -> Result<()> { - let sql = format!("INSERT INTO requests (fid, gid, addr, name, remark, is_ok, is_over, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', '{}', {}, {}, {}, false)", + let sql = format!("INSERT INTO requests (fid, gid, addr, name, remark, key, is_ok, is_over, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', '{}', '{}', {}, {}, {}, false)", self.fid, self.gid.to_hex(), self.addr.to_hex(), self.name, self.remark, + self.key.to_hex(), if self.is_ok { 1 } else { 0 }, if self.is_over { 1 } else { 0 }, self.datetime, @@ -382,6 +463,33 @@ impl Request { self.id = id; Ok(()) } + + pub fn over(db: &DStorage, gcd: &GroupId, is_ok: bool) -> Result<(i64, GroupChatKey)> { + let matrix = db.query(&format!( + "SELECT id, key from requests WHERE gid = '{}' AND is_over = 0 ORDER BY id", + gcd.to_hex() + ))?; + let mut requests = vec![]; + for mut values in matrix { + let id = values.pop().unwrap().as_i64(); + let key = GroupChatKey::from_hex(values.pop().unwrap().as_string()) + .unwrap_or(GroupChatKey::new(vec![])); + requests.push((id, key)); + } + + let sql = format!( + "UPDATE requests SET is_ok={}, is_over=1 WHERE gid = '{}' AND is_over = 0", + if is_ok { 1 } else { 0 }, + gcd.to_hex(), + ); + db.update(&sql)?; + + if requests.len() > 0 { + Ok(requests.pop().unwrap()) // safe. + } else { + Err(new_io_error("no requests")) + } + } } /// Group Member Model. @@ -629,7 +737,7 @@ impl Message { } pub(super) fn to_network_message( - mtype: MessageType, + _mtype: MessageType, content: &str, ) -> Result<(NetworkMessage, i64)> { let start = SystemTime::now(); diff --git a/src/apps/group_chat/rpc.rs b/src/apps/group_chat/rpc.rs index acd2d64..29f6e56 100644 --- a/src/apps/group_chat/rpc.rs +++ b/src/apps/group_chat/rpc.rs @@ -14,7 +14,7 @@ use crate::rpc::RpcState; use crate::storage::group_chat_db; use super::add_layer; -use super::models::{to_network_message, GroupChat, Member, Message, Request}; +use super::models::{to_network_message, GroupChat, GroupChatKey, Member, Message, Request}; #[inline] pub(crate) fn create_check(mgid: GroupId, ct: CheckType, supported: Vec) -> RpcParam { @@ -37,6 +37,16 @@ pub(crate) fn group_offline(mgid: GroupId, fid: i64, gid: &GroupId) -> RpcParam rpc_response(0, "group-chat-offline", json!([fid, gid.to_hex()]), mgid) } +#[inline] +pub(crate) fn group_agree(mgid: GroupId, rid: i64, group: GroupChat) -> RpcParam { + rpc_response(0, "group-chat-agree", json!([rid, group.to_rpc()]), mgid) +} + +#[inline] +pub(crate) fn group_reject(mgid: GroupId, rid: i64) -> RpcParam { + rpc_response(0, "group-chat-reject", json!([rid]), mgid) +} + #[inline] pub(crate) fn member_join(mgid: GroupId, member: Member) -> RpcParam { rpc_response(0, "group-chat-member-join", json!(member.to_rpc()), mgid) @@ -192,8 +202,10 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let gaddr = PeerAddr::from_hex(params[1].as_str()?)?; let gname = params[2].as_str()?.to_owned(); let gremark = params[3].as_str()?.to_owned(); + let gkey = params[4].as_str()?; + let key = GroupChatKey::from_hex(gkey).unwrap_or(GroupChatKey::new(vec![])); - let mut request = Request::new_by_me(gcd, gaddr, gname, gremark); + let mut request = Request::new_by_me(gcd, gaddr, gname, gremark, key); let db = group_chat_db(state.layer.read().await.base(), &gid)?; request.insert(&db)?; drop(db); diff --git a/src/migrate/group_chat.rs b/src/migrate/group_chat.rs index f2b54c4..e8a5d09 100644 --- a/src/migrate/group_chat.rs +++ b/src/migrate/group_chat.rs @@ -25,7 +25,8 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [ gid TEXT NOT NULL, addr TEXT NOT NULL, name TEXT NOT NULL, - remark TEXT, + remark TEXT NOT NULL, + key TEXT NOT NULL, is_ok INTEGER NOT NULL, is_over INTEGER NOT NULL, datetime INTEGER NOT NULL,