Browse Source

add member join UI

pull/18/head
Sun 4 years ago
parent
commit
c932db2e30
  1. 15
      lib/apps/group_chat/add.dart
  2. 36
      lib/apps/group_chat/models.dart
  3. 32
      lib/apps/group_chat/provider.dart
  4. 2
      pubspec.lock
  5. 23
      src/apps/group_chat/layer.rs
  6. 103
      src/apps/group_chat/models.rs
  7. 45
      src/apps/group_chat/rpc.rs
  8. 2
      src/migrate/group_chat.rs

15
lib/apps/group_chat/add.dart

@ -16,7 +16,6 @@ import 'package:esse/widgets/qr_scan.dart';
import 'package:esse/global.dart'; import 'package:esse/global.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/apps/chat/models.dart' show Request; // TODO delete.
import 'package:esse/apps/group_chat/models.dart'; import 'package:esse/apps/group_chat/models.dart';
import 'package:esse/apps/group_chat/provider.dart'; import 'package:esse/apps/group_chat/provider.dart';
@ -112,7 +111,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
return; return;
} }
if (id.substring(0, 2) == 'EH') { if (id.substring(0, 2) == 'EG') {
id = id.substring(2); id = id.substring(2);
} }
@ -123,8 +122,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
} }
var name = _joinNameController.text; var name = _joinNameController.text;
var remark = _joinRemarkController.text; var remark = _joinRemarkController.text;
context.read<GroupChatProvider>().join(id, addr, name, remark);
//context.read<GroupChatProvider>().requestCreate(Request(id, addr, name, remark));
setState(() { setState(() {
_joinIdController.text = ''; _joinIdController.text = '';
_joinAddrController.text = ''; _joinAddrController.text = '';
@ -135,7 +133,11 @@ class _GroupAddPageState extends State<GroupAddPage> {
_create() { _create() {
final myName = context.read<AccountProvider>().activedAccount.name; final myName = context.read<AccountProvider>().activedAccount.name;
final addr = _createAddrController.text.trim(); var addr = _createAddrController.text.trim();
// if has 0x, need remove
if (addr.substring(0, 2) == '0x') {
addr = addr.substring(2);
}
final name = _createNameController.text.trim(); final name = _createNameController.text.trim();
final bio = _createBioController.text.trim(); final bio = _createBioController.text.trim();
context.read<GroupChatProvider>().create(myName, addr, name, bio, _groupNeedAgree); context.read<GroupChatProvider>().create(myName, addr, name, bio, _groupNeedAgree);
@ -520,7 +522,8 @@ class _RequestItem extends StatelessWidget {
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)), const Divider(height: 1.0, color: Color(0x40ADB0BB)),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
_infoListTooltip(Icons.person, color.primary, 'EH' + request.gid.toUpperCase()), _infoListTooltip(Icons.person, color.primary,
(request.isMe ? 'EG' : 'EH') + request.gid.toUpperCase()),
_infoListTooltip(Icons.location_on, color.primary, "0x" + request.addr), _infoListTooltip(Icons.location_on, color.primary, "0x" + request.addr),
_infoList(Icons.turned_in, color.primary, request.remark), _infoList(Icons.turned_in, color.primary, request.remark),
_infoList(Icons.access_time_rounded, color.primary, request.time.toString()), _infoList(Icons.access_time_rounded, color.primary, request.time.toString()),

36
lib/apps/group_chat/models.dart

@ -135,6 +135,42 @@ class GroupChat {
} }
} }
class Request {
int id;
int fid;
String gid;
String addr;
String name;
String remark;
bool ok;
bool over;
RelativeTime time;
bool get isMe => this.fid == 0;
Avatar showAvatar([double width = 45.0]) {
final avatar = Global.avatarPath + this.gid + '.png';
return Avatar(
width: width,
name: this.name,
avatarPath: avatar,
needOnline: false,
);
}
Request.fromList(List params) {
this.id = params[0];
this.fid = params[1];
this.gid = params[2];
this.addr = params[3];
this.name = params[4];
this.remark = params[5];
this.ok = params[6];
this.over = params[7];
this.time = RelativeTime.fromInt(params[8]);
}
}
class Member { class Member {
int id; int id;
int fid; int fid;

32
lib/apps/group_chat/provider.dart

@ -15,7 +15,7 @@ class GroupChatProvider extends ChangeNotifier {
Map<int, GroupChat> groups = {}; Map<int, GroupChat> groups = {};
List<int> createKeys = []; List<int> createKeys = [];
List<int> orderKeys = []; List<int> orderKeys = [];
SplayTreeMap<int, GroupChat> requests = SplayTreeMap(); SplayTreeMap<int, Request> requests = SplayTreeMap();
int actived; int actived;
SplayTreeMap<int, Message> activedMessages = SplayTreeMap(); SplayTreeMap<int, Message> activedMessages = SplayTreeMap();
@ -42,11 +42,11 @@ class GroupChatProvider extends ChangeNotifier {
rpc.addListener('group-chat-result', _result, false); rpc.addListener('group-chat-result', _result, false);
rpc.addListener('group-chat-detail', _detail, true); rpc.addListener('group-chat-detail', _detail, true);
// rpc.addListener('group-chat-update', _update, false); // rpc.addListener('group-chat-update', _update, false);
// rpc.addListener('group-chat-join', _join, true); rpc.addListener('group-chat-join', _join, true);
// rpc.addListener('group-chat-agree', _agree, true); // rpc.addListener('group-chat-agree', _agree, true);
// rpc.addListener('group-chat-reject', _reject, false); // rpc.addListener('group-chat-reject', _reject, false);
rpc.addListener('group-chat-member-join', _memberJoin, false); rpc.addListener('group-chat-member-join', _memberJoin, false);
// rpc.addListener('group-chat-member-info', _memberInfo, false); rpc.addListener('group-chat-member-info', _memberInfo, false);
// rpc.addListener('group-chat-member-leave', _memberLeave, false); // rpc.addListener('group-chat-member-leave', _memberLeave, false);
rpc.addListener('group-chat-member-online', _memberOnline, false); rpc.addListener('group-chat-member-online', _memberOnline, false);
rpc.addListener('group-chat-member-offline', _memberOffline, false); rpc.addListener('group-chat-member-offline', _memberOffline, false);
@ -86,6 +86,7 @@ class GroupChatProvider extends ChangeNotifier {
} }
create(String myName, String addr, String name, String bio, bool needAgree) { create(String myName, String addr, String name, String bio, bool needAgree) {
print(addr);
rpc.send('group-chat-create', [myName, addr, name, bio, needAgree]); rpc.send('group-chat-create', [myName, addr, name, bio, needAgree]);
} }
@ -93,6 +94,10 @@ class GroupChatProvider extends ChangeNotifier {
// //
} }
join(String gid, String gaddr, String name, String remark) {
rpc.send('group-chat-join', [gid, gaddr, name, remark]);
}
messageCreate(MessageType mtype, String content) { messageCreate(MessageType mtype, String content) {
final gid = this.activedGroup.gid; final gid = this.activedGroup.gid;
rpc.send('group-chat-message-create', [gid, mtype.toInt(), content]); rpc.send('group-chat-message-create', [gid, mtype.toInt(), content]);
@ -173,8 +178,27 @@ class GroupChatProvider extends ChangeNotifier {
} }
} }
_join(List params) {
this.requests[params[0]] = Request.fromList(params);
notifyListeners();
}
_memberJoin(List params) { _memberJoin(List params) {
// final member = Member.fromList(params);
if (this.actived == member.fid) {
this.activedMembers[member.id] = member;
// TODO Better add UI member joined.
notifyListeners();
}
}
_memberInfo(List params) {
final id = params[0];
if (this.activedMembers.containsKey(id)) {
this.activedMembers[id].addr = params[1];
this.activedMembers[id].name = params[2];
notifyListeners();
}
} }
_memberOnline(List params) { _memberOnline(List params) {

2
pubspec.lock

@ -21,7 +21,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.6.0" version: "2.6.1"
audio_session: audio_session:
dependency: transitive dependency: transitive
description: description:

23
src/apps/group_chat/layer.rs

@ -13,7 +13,7 @@ use group_chat_types::{Event, GroupConnect, GroupResult, JoinProof, LayerEvent};
use tdn_did::Proof; use tdn_did::Proof;
use crate::layer::{Layer, Online}; use crate::layer::{Layer, Online};
use crate::storage::group_chat_db; 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};
use super::{add_layer, rpc}; use super::{add_layer, rpc};
@ -146,20 +146,32 @@ async fn handle_event(
results.rpcs.push(rpc::group_online(mgid, gid)); results.rpcs.push(rpc::group_online(mgid, gid));
} }
LayerEvent::Sync(_, height, event) => { LayerEvent::Sync(_, height, event) => {
let base = layer.read().await.base().clone();
let db = group_chat_db(&base, &mgid)?;
match event { match event {
Event::GroupInfo => {} Event::GroupInfo => {}
Event::GroupTransfer => {} Event::GroupTransfer => {}
Event::GroupManagerAdd => {} Event::GroupManagerAdd => {}
Event::GroupManagerDel => {} Event::GroupManagerDel => {}
Event::GroupClose => {} Event::GroupClose => {}
Event::MemberInfo(mid, maddr, mname, mavatar) => {} Event::MemberInfo(mid, maddr, mname, mavatar) => {
let id = Member::get_id(&db, &gid, &mid)?;
Member::update(&db, &id, &maddr, &mname)?;
if mavatar.len() > 0 {
write_avatar_sync(&base, &mgid, &mid, mavatar)?;
}
results.rpcs.push(rpc::member_info(mgid, id, maddr, mname));
}
Event::MemberJoin(mid, maddr, mname, mavatar, mtime) => { Event::MemberJoin(mid, maddr, mname, mavatar, mtime) => {
let db = group_chat_db(layer.read().await.base(), &mgid)?;
let mut member = Member::new(gid, mid, maddr, mname, false, mtime); let mut member = Member::new(gid, mid, maddr, mname, false, mtime);
member.insert(&db)?; member.insert(&db)?;
if mavatar.len() > 0 {
write_avatar_sync(&base, &mgid, &mid, mavatar)?;
}
results.rpcs.push(rpc::member_join(mgid, member)); results.rpcs.push(rpc::member_join(mgid, member));
} }
Event::MemberLeave(mid) => {} Event::MemberLeave(_mid) => {}
Event::MessageCreate(mid, nmsg, mtime) => { Event::MessageCreate(mid, nmsg, mtime) => {
let base = layer.read().await.base.clone(); let base = layer.read().await.base.clone();
let msg = let msg =
@ -169,8 +181,7 @@ async fn handle_event(
} }
// save event. // save event.
GroupChat::add_height(&db, gid, height)?;
// update to UI.
} }
LayerEvent::MemberOnline(_, mid, maddr) => { LayerEvent::MemberOnline(_, mid, maddr) => {
results.rpcs.push(rpc::member_online(mgid, gid, mid, maddr)); results.rpcs.push(rpc::member_online(mgid, gid, mid, maddr));

103
src/apps/group_chat/models.rs

@ -274,6 +274,11 @@ impl GroupChat {
db.update(&sql) db.update(&sql)
} }
pub fn add_height(db: &DStorage, id: i64, height: i64) -> Result<usize> {
let sql = format!("UPDATE groups SET height={} WHERE id = {}", height, id,);
db.update(&sql)
}
pub fn update_last_message(db: &DStorage, id: i64, msg: &Message, read: bool) -> Result<usize> { pub fn update_last_message(db: &DStorage, id: i64, msg: &Message, read: bool) -> Result<usize> {
let sql = format!( let sql = format!(
"UPDATE groups SET last_datetime={}, last_content='{}', last_readed={} WHERE id = {}", "UPDATE groups SET last_datetime={}, last_content='{}', last_readed={} WHERE id = {}",
@ -291,6 +296,94 @@ impl GroupChat {
} }
} }
/// Group Join Request model. include my requests and other requests.
/// When fid is 0, it's my requests.
pub(crate) struct Request {
id: i64,
fid: i64,
pub gid: GroupId,
pub addr: PeerAddr,
pub name: String,
remark: String,
is_ok: bool,
is_over: bool,
datetime: i64,
}
impl Request {
pub fn new_by_remote(
fid: i64,
gid: GroupId,
addr: PeerAddr,
name: String,
remark: String,
datetime: i64,
) -> Self {
Self {
fid,
gid,
addr,
name,
remark,
datetime,
is_ok: false,
is_over: false,
id: 0,
}
}
pub fn new_by_me(gid: GroupId, addr: PeerAddr, name: String, remark: String) -> 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 {
gid,
addr,
name,
remark,
datetime,
is_ok: false,
is_over: false,
fid: 0,
id: 0,
}
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
self.fid,
self.gid.to_hex(),
self.addr.to_hex(),
self.name,
self.remark,
self.is_ok,
self.is_over,
self.datetime,
])
}
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)",
self.fid,
self.gid.to_hex(),
self.addr.to_hex(),
self.name,
self.remark,
if self.is_ok { 1 } else { 0 },
if self.is_over { 1 } else { 0 },
self.datetime,
);
println!("{}", sql);
let id = db.insert(&sql)?;
self.id = id;
Ok(())
}
}
/// Group Member Model. /// Group Member Model.
pub(crate) struct Member { pub(crate) struct Member {
/// db auto-increment id. /// db auto-increment id.
@ -398,6 +491,16 @@ impl Member {
Err(new_io_error("missing member")) Err(new_io_error("missing member"))
} }
} }
pub fn update(db: &DStorage, id: &i64, addr: &PeerAddr, name: &str) -> Result<usize> {
let sql = format!(
"UPDATE members SET addr='{}', name='{}' WHERE id = {}",
addr.to_hex(),
name,
id,
);
db.update(&sql)
}
} }
/// Group Chat Message Model. /// Group Chat Message Model.

45
src/apps/group_chat/rpc.rs

@ -7,14 +7,14 @@ use tdn::types::{
}; };
use tdn_did::Proof; use tdn_did::Proof;
use group_chat_types::{CheckType, Event, GroupConnect, GroupType, LayerEvent, NetworkMessage}; use group_chat_types::{CheckType, Event, GroupConnect, GroupType, JoinProof, LayerEvent};
use crate::apps::chat::MessageType; use crate::apps::chat::MessageType;
use crate::rpc::RpcState; use crate::rpc::RpcState;
use crate::storage::group_chat_db; use crate::storage::group_chat_db;
use super::add_layer; use super::add_layer;
use super::models::{to_network_message, GroupChat, Member, Message}; use super::models::{to_network_message, GroupChat, Member, Message, Request};
#[inline] #[inline]
pub(crate) fn create_check(mgid: GroupId, ct: CheckType, supported: Vec<GroupType>) -> RpcParam { pub(crate) fn create_check(mgid: GroupId, ct: CheckType, supported: Vec<GroupType>) -> RpcParam {
@ -42,6 +42,16 @@ pub(crate) fn member_join(mgid: GroupId, member: Member) -> RpcParam {
rpc_response(0, "group-chat-member-join", json!(member.to_rpc()), mgid) rpc_response(0, "group-chat-member-join", json!(member.to_rpc()), mgid)
} }
#[inline]
pub(crate) fn member_info(mgid: GroupId, id: i64, addr: PeerAddr, name: String) -> RpcParam {
rpc_response(
0,
"group-chat-member-info",
json!([id, addr.to_hex(), name]),
mgid,
)
}
#[inline] #[inline]
pub(crate) fn member_online(mgid: GroupId, gid: i64, mid: GroupId, maddr: PeerAddr) -> RpcParam { pub(crate) fn member_online(mgid: GroupId, gid: i64, mid: GroupId, maddr: PeerAddr) -> RpcParam {
rpc_response( rpc_response(
@ -129,9 +139,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method( handler.add_method(
"group-chat-check", "group-chat-check",
|gid: GroupId, params: Vec<RpcParam>, _state: Arc<RpcState>| async move { |gid: GroupId, params: Vec<RpcParam>, _state: Arc<RpcState>| async move {
let addr = PeerAddr::from_hex(params[0].as_str()?) let addr = PeerAddr::from_hex(params[0].as_str()?)?;
.map_err(|_e| new_io_error("PeerAddr invalid!"))?;
println!("addr: {}", addr.to_hex());
let mut results = HandleResult::new(); let mut results = HandleResult::new();
let data = postcard::to_allocvec(&GroupConnect::Check).unwrap_or(vec![]); let data = postcard::to_allocvec(&GroupConnect::Check).unwrap_or(vec![]);
@ -145,8 +153,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
"group-chat-create", "group-chat-create",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let my_name = params[0].as_str()?.to_owned(); let my_name = params[0].as_str()?.to_owned();
let addr = PeerAddr::from_hex(params[1].as_str()?) let addr = PeerAddr::from_hex(params[1].as_str()?)?;
.map_err(|_e| new_io_error("PeerAddr invalid!"))?;
let name = params[2].as_str()?.to_owned(); let name = params[2].as_str()?.to_owned();
let bio = params[3].as_str()?.to_owned(); let bio = params[3].as_str()?.to_owned();
let need_agree = params[4].as_bool()?; let need_agree = params[4].as_bool()?;
@ -178,6 +185,30 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
}, },
); );
handler.add_method(
"group-chat-join",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let gcd = GroupId::from_hex(params[0].as_str()?)?;
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 mut request = Request::new_by_me(gcd, gaddr, gname, gremark);
let db = group_chat_db(state.layer.read().await.base(), &gid)?;
request.insert(&db)?;
drop(db);
let mut results = HandleResult::rpc(request.to_rpc());
let me = state.group.read().await.clone_user(&gid)?;
let join_proof = JoinProof::Open(me.name, me.avatar);
let data = postcard::to_allocvec(&GroupConnect::Join(request.gid, join_proof))
.unwrap_or(vec![]);
let s = SendType::Connect(0, request.addr, None, None, data);
add_layer(&mut results, gid, s);
Ok(results)
},
);
handler.add_method( handler.add_method(
"group-chat-message-create", "group-chat-message-create",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {

2
src/migrate/group_chat.rs

@ -22,7 +22,7 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [
"CREATE TABLE IF NOT EXISTS requests( "CREATE TABLE IF NOT EXISTS requests(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
fid INTEGER NOT NULL, fid INTEGER NOT NULL,
mid TEXT NOT NULL, gid TEXT NOT NULL,
addr TEXT NOT NULL, addr TEXT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
remark TEXT, remark TEXT,

Loading…
Cancel
Save