Browse Source

update group chat ui

pull/18/head
Sun 4 years ago
parent
commit
6b1ea04c97
  1. 180
      lib/apps/group_chat/detail.dart
  2. 48
      lib/apps/group_chat/models.dart
  3. 34
      lib/apps/group_chat/provider.dart
  4. 2
      lib/l10n/localizations.dart
  5. 4
      lib/l10n/localizations_en.dart
  6. 4
      lib/l10n/localizations_zh.dart
  7. 2
      lib/pages/account_generate.dart
  8. 2
      lib/pages/account_restore.dart
  9. 3
      lib/security.dart
  10. 9
      lib/widgets/avatar.dart
  11. 1
      src/apps/group_chat/mod.rs
  12. 155
      src/apps/group_chat/models.rs
  13. 50
      src/apps/group_chat/rpc.rs
  14. 27
      src/layer.rs
  15. 5
      src/migrate/group_chat.rs

180
lib/apps/group_chat/detail.dart

@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:esse/utils/adaptive.dart';
import 'package:esse/utils/better_print.dart';
import 'package:esse/utils/toast.dart';
import 'package:esse/utils/pick_image.dart';
import 'package:esse/utils/pick_file.dart';
@ -205,6 +206,9 @@ class _GroupChatDetailState extends State<GroupChatDetail> { @@ -205,6 +206,9 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
final meName = context.read<AccountProvider>().activedAccount.name;
this.group = provider.activedGroup;
final isGroupOwner = true;
final isGroupManager = true;
if (this.group == null) {
return Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0),
@ -279,14 +283,31 @@ class _GroupChatDetailState extends State<GroupChatDetail> { @@ -279,14 +283,31 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
showShadowDialog(
context,
Icons.info,
lang.friendInfo,
lang.groupChat,
UserInfo(
id: 'EH' + this.group.gid.toUpperCase(),
id: 'EG' + this.group.gid.toUpperCase(),
name: this.group.name,
addr: '0x' + this.group.addr)
);
} else if (value == 3) {
print('TODO remark');
final memberWidgets = provider.activedMembers.values.map((m) {
return MemberAvatar(
member: m, title: lang.members,
isGroupManager: isGroupManager, isGroupOwner: isGroupOwner,
);
}).toList();
showShadowDialog(
context,
Icons.group_rounded,
lang.members,
Wrap(
spacing: 10.0,
runSpacing: 10.0,
children: memberWidgets,
),
10.0, //height
);
} else if (value == 4) {
showDialog(
context: context,
@ -322,7 +343,7 @@ class _GroupChatDetailState extends State<GroupChatDetail> { @@ -322,7 +343,7 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(lang.delete),
title: Text(lang.delete + ' ' + lang.groupChat),
content: Text(this.group.name,
style: TextStyle(color: Colors.red)),
actions: [
@ -349,12 +370,14 @@ class _GroupChatDetailState extends State<GroupChatDetail> { @@ -349,12 +370,14 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
itemBuilder: (context) {
return <PopupMenuEntry<int>>[
_menuItem(Color(0xFF6174FF), 1, Icons.vertical_align_top_rounded, this.group.isTop ? lang.cancelTop : lang.setTop),
// _menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo),
_menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.info),
_menuItem(Color(0xFF6174FF), 3, Icons.group_rounded, lang.members),
// _menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark),
// this.group.isClosed
// ? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addGroup)
// : _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend),
// _menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete),
_menuItem(Colors.orange, 6, Icons.block_rounded, lang.exit),
_menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete),
];
},
)
@ -584,3 +607,148 @@ Widget _menuItem(Color color, int value, IconData icon, String text) { @@ -584,3 +607,148 @@ Widget _menuItem(Color color, int value, IconData icon, String text) {
),
);
}
class MemberAvatar extends StatelessWidget {
final String title;
final Member member;
final bool isGroupManager;
final bool isGroupOwner;
const MemberAvatar({Key key, this.title, this.member, this.isGroupManager, this.isGroupOwner}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => showShadowDialog(
context,
Icons.group_rounded,
title,
MemberDetail(member: member, isGroupOwner: isGroupOwner, isGroupManager: isGroupManager),
10.0,
),
hoverColor: Colors.transparent,
child: Column(
children: [
member.showAvatar(),
SizedBox(height: 4.0),
Container(
alignment: Alignment.center,
width: 60.0,
child: Text(
member.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14.0)),
)
]
)
);
}
}
class MemberDetail extends StatefulWidget {
Member member;
bool isGroupManager;
bool isGroupOwner;
MemberDetail({Key key, this.member, this.isGroupManager, this.isGroupOwner}) : super(key: key);
@override
_MemberDetailState createState() => _MemberDetailState();
}
class _MemberDetailState extends State<MemberDetail> {
Widget _infoListTooltip(icon, color, text) {
return Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
children: [
Icon(icon, size: 20.0, color: color),
const SizedBox(width: 20.0),
Expanded(
child: Tooltip(
message: text,
child: Text(betterPrint(text)),
)
)
]
),
);
}
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final bool notFriend = false; // TODO
return Column(
mainAxisSize: MainAxisSize.max,
children: [
widget.member.showAvatar(width: 100.0),
const SizedBox(height: 10.0),
Text(widget.member.name),
const SizedBox(height: 10.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
const SizedBox(height: 10.0),
_infoListTooltip(Icons.person, color.primary, widget.member.mid),
_infoListTooltip(Icons.location_on, color.primary, widget.member.addr),
const SizedBox(height: 10.0),
if (widget.isGroupOwner)
InkWell(
onTap: () {
Navigator.pop(context);
// TODO delete.
},
hoverColor: Colors.transparent,
child: Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(widget.member.isManager ? 'Cancel Manager' : 'Set Manager',
style: TextStyle(fontSize: 14.0))),
)
),
const SizedBox(height: 10.0),
if (notFriend)
InkWell(
onTap: () {
Navigator.pop(context);
// TODO delete.
},
hoverColor: Colors.transparent,
child: Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Color(0xFF6174FF)),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(lang.addFriend,
style: TextStyle(fontSize: 14.0, color: Color(0xFF6174FF)))),
)
),
const SizedBox(height: 10.0),
if (widget.isGroupManager || widget.isGroupOwner)
InkWell(
onTap: () {
Navigator.pop(context);
// TODO delete.
},
hoverColor: Colors.transparent,
child: Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(lang.delete,
style: TextStyle(fontSize: 14.0, color: Colors.red))),
)
),
]
);
}
}

48
lib/apps/group_chat/models.dart

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/utils/relative_time.dart';
import 'package:esse/widgets/avatar.dart';
@ -122,6 +124,46 @@ class GroupChat { @@ -122,6 +124,46 @@ class GroupChat {
}
}
class Member {
int id;
int fid;
String mid;
String addr;
String name;
bool isManager;
// MOCK need deleted.
Member(String name, bool isManager) {
this.id = 0;
this.fid = 0;
this.mid = 'EH0000000000000000000000000000000000000000000000000000000000000000';
this.addr = '0x0000000000000000000000000000000000000000000000000000000000000000';
this.name = name;
this.isManager = isManager;
}
Member.fromList(List params) {
this.id = params[0];
this.fid = params[1];
this.mid = params[2];
this.addr = params[3];
this.name = params[4];
this.isManager = params[5];
}
Avatar showAvatar({double width = 45.0}) {
final avatar = Global.avatarPath + this.mid + '.png';
return Avatar(
width: width,
name: this.name,
avatarPath: avatar,
needOnline: false,
hasNew: this.isManager,
hasNewColor: Color(0xFF6174FF),
);
}
}
class Message extends BaseMessage {
int height;
int fid;
@ -136,9 +178,9 @@ class Message extends BaseMessage { @@ -136,9 +178,9 @@ class Message extends BaseMessage {
Message.fromList(List params) {
this.id = params[0];
this.height = params[1];
this.isMe = params[2];
this.fid = params[3];
this.mid = params[4];
this.fid = params[2];
this.mid = params[3];
this.isMe = params[4];
this.type = MessageTypeExtension.fromInt(params[5]);
this.content = params[6];
this.isDelivery = params[7];

34
lib/apps/group_chat/provider.dart

@ -18,6 +18,7 @@ class GroupChatProvider extends ChangeNotifier { @@ -18,6 +18,7 @@ class GroupChatProvider extends ChangeNotifier {
int actived;
SplayTreeMap<int, Message> activedMessages = SplayTreeMap();
SplayTreeMap<int, Member> activedMembers = SplayTreeMap();
GroupChat get activedGroup => this.groups[this.actived];
@ -29,6 +30,7 @@ class GroupChatProvider extends ChangeNotifier { @@ -29,6 +30,7 @@ class GroupChatProvider extends ChangeNotifier {
rpc.addListener('group-chat-check', _check, false);
rpc.addListener('group-chat-create', _create, false);
rpc.addListener('group-chat-result', _result, false);
rpc.addListener('group-chat-detail', _detail, true);
// rpc.addListener('group-chat-update', _update, false);
// rpc.addListener('group-chat-join', _join, true);
// rpc.addListener('group-chat-agree', _agree, true);
@ -44,6 +46,12 @@ class GroupChatProvider extends ChangeNotifier { @@ -44,6 +46,12 @@ class GroupChatProvider extends ChangeNotifier {
}
clear() {
this.groups.clear();
this.createKeys.clear();
this.orderKeys.clear();
this.requests.clear();
this.activedMessages.clear();
this.activedMembers.clear();
}
updateActived() {
@ -55,11 +63,12 @@ class GroupChatProvider extends ChangeNotifier { @@ -55,11 +63,12 @@ class GroupChatProvider extends ChangeNotifier {
updateActivedGroup(int id) {
this.actived = id;
// TODO load
rpc.send('group-chat-detail', [id]);
}
clearActivedGroup() {
// TODO
this.activedMessages.clear();
this.activedMembers.clear();
}
check(String addr) {
@ -77,9 +86,13 @@ class GroupChatProvider extends ChangeNotifier { @@ -77,9 +86,13 @@ class GroupChatProvider extends ChangeNotifier {
_list(List params) {
this.clear();
params.forEach((params) {
// if (params.length == 6) {
// this.devices[params[0]] = Device.fromList(params);
// }
final gc = GroupChat.fromList(params);
if (gc.isOk) {
this.orderKeys.add(gc.id);
} else {
this.createKeys.add(gc.id);
}
this.groups[gc.id] = gc;
});
notifyListeners();
}
@ -115,4 +128,15 @@ class GroupChatProvider extends ChangeNotifier { @@ -115,4 +128,15 @@ class GroupChatProvider extends ChangeNotifier {
}
notifyListeners();
}
_detail(List params) {
this.clearActivedGroup();
params[0].forEach((param) {
this.activedMembers[param[0]] = Member.fromList(param);
});
params[1].forEach((param) {
this.activedMessages[param[0]] = Message.fromList(param);
});
notifyListeners();
}
}

2
lib/l10n/localizations.dart

@ -78,6 +78,7 @@ abstract class AppLocalizations { @@ -78,6 +78,7 @@ abstract class AppLocalizations {
String get open;
String get unknown;
String get create;
String get exit;
// theme
String get themeDark;
@ -165,6 +166,7 @@ abstract class AppLocalizations { @@ -165,6 +166,7 @@ abstract class AppLocalizations {
String get groupCheckTypeAllow;
String get groupCheckTypeNone;
String get groupCheckTypeDeny;
String get members;
}
class _AppLocalizationsDelegate

4
lib/l10n/localizations_en.dart

@ -80,6 +80,8 @@ class AppLocalizationsEn extends AppLocalizations { @@ -80,6 +80,8 @@ class AppLocalizationsEn extends AppLocalizations {
String get unknown => 'Unknown';
@override
String get create => 'Create';
@override
String get exit => 'Exit';
// theme
@override
@ -240,4 +242,6 @@ class AppLocalizationsEn extends AppLocalizations { @@ -240,4 +242,6 @@ class AppLocalizationsEn extends AppLocalizations {
String get groupCheckTypeNone => 'Restricted, the allowed number is full';
@override
String get groupCheckTypeDeny => 'No permission to create here';
@override
String get members => 'Members';
}

4
lib/l10n/localizations_zh.dart

@ -80,6 +80,8 @@ class AppLocalizationsZh extends AppLocalizations { @@ -80,6 +80,8 @@ class AppLocalizationsZh extends AppLocalizations {
String get unknown => '未知';
@override
String get create => '创建';
@override
String get exit => '退出';
// theme
@override
@ -240,4 +242,6 @@ class AppLocalizationsZh extends AppLocalizations { @@ -240,4 +242,6 @@ class AppLocalizationsZh extends AppLocalizations {
String get groupCheckTypeNone => '创建被限制,允许数目已满';
@override
String get groupCheckTypeDeny => '没有权限在此创建群聊';
@override
String get members => '成员';
}

2
lib/pages/account_generate.dart

@ -23,6 +23,7 @@ import 'package:esse/provider.dart'; @@ -23,6 +23,7 @@ import 'package:esse/provider.dart';
import 'package:esse/apps/device/provider.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/group_chat/provider.dart';
class AccountGeneratePage extends StatefulWidget {
const AccountGeneratePage({Key key}) : super(key: key);
@ -106,6 +107,7 @@ class _AccountGeneratePageState extends State<AccountGeneratePage> { @@ -106,6 +107,7 @@ class _AccountGeneratePageState extends State<AccountGeneratePage> {
Provider.of<AccountProvider>(context, listen: false).addAccount(account);
Provider.of<DeviceProvider>(context, listen: false).updateActived();
Provider.of<ChatProvider>(context, listen: false).updateActived();
Provider.of<GroupChatProvider>(context, listen: false).updateActived();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomePage()));
} else {

2
lib/pages/account_restore.dart

@ -16,6 +16,7 @@ import 'package:esse/provider.dart'; @@ -16,6 +16,7 @@ import 'package:esse/provider.dart';
import 'package:esse/apps/device/provider.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/group_chat/provider.dart';
class AccountRestorePage extends StatefulWidget {
const AccountRestorePage({Key key}) : super(key: key);
@ -380,6 +381,7 @@ class _AccountRestorePageState extends State<AccountRestorePage> { @@ -380,6 +381,7 @@ class _AccountRestorePageState extends State<AccountRestorePage> {
Provider.of<AccountProvider>(context, listen: false).addAccount(account);
Provider.of<DeviceProvider>(context, listen: false).updateActived();
Provider.of<ChatProvider>(context, listen: false).updateActived();
Provider.of<GroupChatProvider>(context, listen: false).updateActived();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomePage()));
} else {

3
lib/security.dart

@ -18,6 +18,7 @@ import 'package:esse/provider.dart'; @@ -18,6 +18,7 @@ import 'package:esse/provider.dart';
import 'package:esse/apps/device/provider.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/group_chat/provider.dart';
class SecurityPage extends StatefulWidget {
const SecurityPage({Key key}) : super(key: key);
@ -167,6 +168,7 @@ class _SecurityPageState extends State<SecurityPage> { @@ -167,6 +168,7 @@ class _SecurityPageState extends State<SecurityPage> {
Provider.of<AccountProvider>(context, listen: false).autoAccounts(mainAccount.gid, accounts);
Provider.of<DeviceProvider>(context, listen: false).updateActived();
Provider.of<ChatProvider>(context, listen: false).updateActived();
Provider.of<GroupChatProvider>(context, listen: false).updateActived();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomePage()));
return;
@ -217,6 +219,7 @@ class _SecurityPageState extends State<SecurityPage> { @@ -217,6 +219,7 @@ class _SecurityPageState extends State<SecurityPage> {
Provider.of<AccountProvider>(context, listen: false).updateActivedAccount(mainAccount.gid);
Provider.of<DeviceProvider>(context, listen: false).updateActived();
Provider.of<ChatProvider>(context, listen: false).updateActived();
Provider.of<GroupChatProvider>(context, listen: false).updateActived();
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomePage()));
} else {

9
lib/widgets/avatar.dart

@ -11,6 +11,7 @@ class Avatar extends StatelessWidget { @@ -11,6 +11,7 @@ class Avatar extends StatelessWidget {
final bool online;
final bool needOnline;
final bool hasNew;
final Color hasNewColor;
const Avatar(
{Key key,
@ -20,7 +21,9 @@ class Avatar extends StatelessWidget { @@ -20,7 +21,9 @@ class Avatar extends StatelessWidget {
this.avatarPath,
this.online = false,
this.needOnline = true,
this.hasNew = false})
this.hasNew = false,
this.hasNewColor = Colors.red,
})
: super(key: key);
@override
@ -60,8 +63,8 @@ class Avatar extends StatelessWidget { @@ -60,8 +63,8 @@ class Avatar extends StatelessWidget {
child: Container(
width: 9.0,
height: 9.0,
decoration: const BoxDecoration(
color: Colors.red,
decoration: BoxDecoration(
color: this.hasNewColor,
shape: BoxShape.circle,
),
),

1
src/apps/group_chat/mod.rs

@ -10,6 +10,7 @@ pub(super) fn add_layer(results: &mut HandleResult, gid: GroupId, msg: SendType) @@ -10,6 +10,7 @@ pub(super) fn add_layer(results: &mut HandleResult, gid: GroupId, msg: SendType)
results.layers.push((gid, GROUP_ID, msg));
}
pub(crate) use models::GroupChat;
pub(crate) mod rpc;
pub(crate) use layer::handle as layer_handle;
pub(crate) use rpc::new_rpc_handler;

155
src/apps/group_chat/models.rs

@ -49,7 +49,7 @@ impl GroupChatKey { @@ -49,7 +49,7 @@ impl GroupChatKey {
}
/// Group Chat Model.
pub(super) struct GroupChat {
pub(crate) struct GroupChat {
/// db auto-increment id.
pub id: i64,
/// group chat owner.
@ -81,9 +81,9 @@ pub(super) struct GroupChat { @@ -81,9 +81,9 @@ pub(super) struct GroupChat {
/// group chat lastest message readed. (only ESSE used)
last_readed: bool,
/// group chat created time.
datetime: i64,
pub datetime: i64,
/// group chat is online.
online: bool,
pub online: bool,
/// is deleted.
is_deleted: bool,
}
@ -203,6 +203,16 @@ impl GroupChat { @@ -203,6 +203,16 @@ impl GroupChat {
}
}
/// use in rpc when load account friends.
pub fn all(db: &DStorage) -> Result<Vec<GroupChat>> {
let matrix = db.query("SELECT id, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE is_deleted = false ORDER BY last_datetime DESC")?;
let mut groups = vec![];
for values in matrix {
groups.push(GroupChat::from_values(values, false));
}
Ok(groups)
}
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<GroupChat>> {
let sql = format!("SELECT id, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE gcd = '{}' AND is_deleted = false", gid.to_hex());
let mut matrix = db.query(&sql)?;
@ -256,12 +266,88 @@ pub(super) struct Member { @@ -256,12 +266,88 @@ pub(super) struct Member {
m_addr: PeerAddr,
/// member's name.
m_name: String,
/// member's remark.
m_remark: String,
/// is group chat manager.
is_manager: bool,
/// member's joined time.
datetime: i64,
/// member is leave or delete.
is_deleted: bool,
}
impl Member {
pub fn new(
fid: i64,
m_id: GroupId,
m_addr: PeerAddr,
m_name: String,
is_manager: bool,
datetime: i64,
) -> Self {
Self {
fid,
m_id,
m_addr,
m_name,
is_manager,
datetime,
id: 0,
is_deleted: false,
}
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
self.fid,
self.m_id.to_hex(),
self.m_addr.to_hex(),
self.m_name,
self.is_manager,
])
}
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Self {
let is_deleted = if contains_deleted {
v.pop().unwrap().as_bool()
} else {
false
};
Self {
is_deleted,
datetime: v.pop().unwrap().as_i64(),
is_manager: v.pop().unwrap().as_bool(),
m_name: v.pop().unwrap().as_string(),
m_addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()),
m_id: GroupId::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()),
fid: v.pop().unwrap().as_i64(),
id: v.pop().unwrap().as_i64(),
}
}
pub fn all(db: &DStorage, fid: &i64) -> Result<Vec<Member>> {
let matrix = db.query(&format!(
"SELECT id, fid, mid, addr, name, is_manager, datetime FROM members WHERE is_deleted = false AND fid = {}", fid))?;
let mut groups = vec![];
for values in matrix {
groups.push(Member::from_values(values, false));
}
Ok(groups)
}
pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO members (fid, mid, addr, name, is_manager, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', {}, {}, false)",
self.fid,
self.m_id.to_hex(),
self.m_addr.to_hex(),
self.m_name,
if self.is_manager { 1 } else { 0 },
self.datetime,
);
let id = db.insert(&sql)?;
self.id = id;
Ok(())
}
}
/// Group Chat Message Model.
@ -270,18 +356,67 @@ pub(super) struct Message { @@ -270,18 +356,67 @@ pub(super) struct Message {
id: i64,
/// group message consensus height.
height: i64,
/// message is mine.
is_me: bool,
/// group's db id.
fid: i64,
/// member's db id.
m_id: i64,
mid: i64,
/// message is mine.
is_me: bool,
/// message type.
m_type: MessageType,
/// message content.
m_content: String,
content: String,
/// message is delivery.
m_delivery: bool,
is_delivery: bool,
/// message created time.
datetime: i64,
/// message is deteled
is_deleted: bool,
}
impl Message {
/// here is zero-copy and unwrap is safe. checked.
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Message {
let is_deleted = if contains_deleted {
v.pop().unwrap().as_bool()
} else {
false
};
Message {
is_deleted,
datetime: v.pop().unwrap().as_i64(),
is_delivery: v.pop().unwrap().as_bool(),
content: v.pop().unwrap().as_string(),
m_type: MessageType::from_int(v.pop().unwrap().as_i64()),
is_me: v.pop().unwrap().as_bool(),
mid: v.pop().unwrap().as_i64(),
fid: v.pop().unwrap().as_i64(),
height: v.pop().unwrap().as_i64(),
id: v.pop().unwrap().as_i64(),
}
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
self.height,
self.fid,
self.mid,
self.is_me,
self.m_type.to_int(),
self.content,
self.is_delivery,
self.datetime,
])
}
pub fn all(db: &DStorage, fid: &i64) -> Result<Vec<Message>> {
let matrix = db.query(&format!("SELECT id, height, fid, mid, is_me, m_type, content, is_delivery, datetime FROM messages WHERE is_deleted = false AND fid = {}", fid))?;
let mut groups = vec![];
for values in matrix {
groups.push(Message::from_values(values, false));
}
Ok(groups)
}
}

50
src/apps/group_chat/rpc.rs

@ -7,13 +7,13 @@ use tdn::types::{ @@ -7,13 +7,13 @@ use tdn::types::{
};
use tdn_did::Proof;
use group_chat_types::{CheckType, GroupConnect, GroupInfo, GroupType};
use group_chat_types::{CheckType, GroupConnect, GroupType};
use crate::rpc::RpcState;
use crate::storage::group_chat_db;
use super::add_layer;
use super::models::GroupChat;
use super::models::{GroupChat, Member, Message};
#[inline]
pub(crate) fn create_check(mgid: GroupId, ct: CheckType, supported: Vec<GroupType>) -> RpcParam {
@ -26,11 +26,55 @@ pub(crate) fn create_result(mgid: GroupId, gid: i64, ok: bool) -> RpcParam { @@ -26,11 +26,55 @@ pub(crate) fn create_result(mgid: GroupId, gid: i64, ok: bool) -> RpcParam {
rpc_response(0, "group-chat-result", json!([gid, ok]), mgid)
}
#[inline]
fn group_list(groups: Vec<GroupChat>) -> RpcParam {
let mut results = vec![];
for group in groups {
results.push(group.to_rpc());
}
json!(results)
}
#[inline]
fn detail_list(members: Vec<Member>, messages: Vec<Message>) -> RpcParam {
let mut member_results = vec![];
for m in members {
member_results.push(m.to_rpc());
}
let mut message_results = vec![];
for msg in messages {
message_results.push(msg.to_rpc());
}
json!([member_results, message_results])
}
pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method("group-chat-echo", |_, params, _| async move {
Ok(HandleResult::rpc(json!(params)))
});
handler.add_method(
"group-chat-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let groups = state.layer.read().await.all_groups_with_online(&gid)?;
Ok(HandleResult::rpc(group_list(groups)))
},
);
handler.add_method(
"group-chat-detail",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let g_did = params[0].as_i64()?;
let db = group_chat_db(state.layer.read().await.base(), &gid)?;
let members = Member::all(&db, &g_did)?;
let messages = Message::all(&db, &g_did)?;
Ok(HandleResult::rpc(detail_list(members, messages)))
},
);
handler.add_method(
"group-chat-check",
|gid: GroupId, params: Vec<RpcParam>, _state: Arc<RpcState>| async move {
@ -62,7 +106,9 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) { @@ -62,7 +106,9 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let _gcd = gc.g_id;
// save db
let me = state.group.read().await.clone_user(&gid)?;
gc.insert(&db)?;
Member::new(gc.id, gid, me.addr, me.name, true, gc.datetime).insert(&db)?;
// TODO save avatar

27
src/layer.rs

@ -13,8 +13,9 @@ use tdn_did::user::User; @@ -13,8 +13,9 @@ use tdn_did::user::User;
use crate::apps::chat::conn_req_message;
use crate::apps::chat::Friend;
use crate::apps::group_chat::GroupChat;
use crate::group::Group;
use crate::storage::{session_db, write_avatar_sync};
use crate::storage::{group_chat_db, session_db, write_avatar_sync};
/// ESSE layers.
pub(crate) struct Layer {
@ -119,12 +120,34 @@ impl Layer { @@ -119,12 +120,34 @@ impl Layer {
.collect();
for fgid in self.running(gid)?.online_groups() {
friends[keys[fgid]].online = true; // safe vec index.
if keys.contains_key(fgid) {
friends[keys[fgid]].online = true; // safe vec index.
}
}
Ok(friends)
}
pub fn all_groups_with_online(&self, gid: &GroupId) -> Result<Vec<GroupChat>> {
let db = group_chat_db(&self.base, &gid)?;
let mut groups = GroupChat::all(&db)?;
drop(db);
let keys: HashMap<GroupId, usize> = groups
.iter()
.enumerate()
.map(|(i, g)| (g.g_id, i))
.collect();
for fgid in self.running(gid)?.online_groups() {
if keys.contains_key(fgid) {
groups[keys[fgid]].online = true;
}
}
Ok(groups)
}
pub fn update_friend(&self, gid: &GroupId, fid: i64, remote: User) -> Result<Friend> {
let db = session_db(&self.base, &gid)?;
if let Some(mut friend) = Friend::get_id(&db, fid)? {

5
src/migrate/group_chat.rs

@ -22,6 +22,7 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [ @@ -22,6 +22,7 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
fid INTEGER NOT NULL,
mid TEXT NOT NULL,
addr TEXT NOT NULL,
name TEXT NOT NULL,
remark TEXT,
is_ok INTEGER NOT NULL,
@ -32,16 +33,16 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [ @@ -32,16 +33,16 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
fid INTEGER NOT NULL,
mid TEXT NOT NULL,
addr TEXT NOT NULL,
name TEXT NOT NULL,
remark TEXT,
is_manager INTEGER NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
"CREATE TABLE IF NOT EXISTS messages(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
height INTEGER NOT NULL,
fid INTEGER NOT NULL,
mid INTEGER NOT NULL,
height INTEGER NOT NULL,
is_me INTEGER NOT NULL,
m_type INTEGER NOT NULL,
content TEXT NOT NULL,

Loading…
Cancel
Save