Browse Source

Add Session model for all chats and services

pull/18/head
Sun 4 years ago
parent
commit
71859fec18
  1. 6
      lib/apps/chat/detail.dart
  2. 19
      lib/apps/chat/list.dart
  3. 41
      lib/apps/chat/models.dart
  4. 41
      lib/apps/chat/provider.dart
  5. 49
      lib/apps/group_chat/add.dart
  6. 7
      lib/apps/group_chat/detail.dart
  7. 28
      lib/apps/group_chat/models.dart
  8. 16
      lib/apps/group_chat/page.dart
  9. 56
      lib/apps/group_chat/provider.dart
  10. 1
      lib/l10n/localizations.dart
  11. 4
      lib/l10n/localizations_en.dart
  12. 2
      lib/l10n/localizations_zh.dart
  13. 25
      lib/pages/home.dart
  14. 90
      lib/provider.dart
  15. 122
      lib/session.dart
  16. 143
      lib/widgets/default_home_show.dart
  17. 18
      pubspec.lock
  18. 4
      src/apps/assistant/models.rs
  19. 42
      src/apps/chat/layer.rs
  20. 76
      src/apps/chat/models.rs
  21. 52
      src/apps/chat/rpc.rs
  22. 116
      src/apps/group_chat/models.rs
  23. 44
      src/apps/group_chat/rpc.rs
  24. 1
      src/daemon.rs
  25. 83
      src/event.rs
  26. 35
      src/layer.rs
  27. 1
      src/lib.rs
  28. 20
      src/migrate.rs
  29. 11
      src/migrate/account.rs
  30. 34
      src/migrate/chat.rs
  31. 4
      src/migrate/group_chat.rs
  32. 37
      src/migrate/session.rs
  33. 95
      src/rpc.rs
  34. 171
      src/session.rs
  35. 11
      src/storage.rs

6
lib/apps/chat/detail.dart

@ -272,10 +272,7 @@ class _ChatDetailState extends State<ChatDetail> {
color: const Color(0xFFEDEDED), color: const Color(0xFFEDEDED),
child: Icon(Icons.more_vert_rounded, color: color.primary), child: Icon(Icons.more_vert_rounded, color: color.primary),
onSelected: (int value) { onSelected: (int value) {
if (value == 1) { if (value == 2) {
Provider.of<ChatProvider>(context, listen: false).friendUpdate(
this.friend.id, isTop: !this.friend.isTop);
} else if (value == 2) {
showShadowDialog( showShadowDialog(
context, context,
Icons.info, Icons.info,
@ -350,7 +347,6 @@ class _ChatDetailState extends State<ChatDetail> {
}, },
itemBuilder: (context) { itemBuilder: (context) {
return <PopupMenuEntry<int>>[ return <PopupMenuEntry<int>>[
_menuItem(Color(0xFF6174FF), 1, Icons.vertical_align_top_rounded, this.friend.isTop ? lang.cancelTop : lang.setTop),
_menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo), _menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo),
//_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), //_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark),
this.friend.isClosed this.friend.isClosed

19
lib/apps/chat/list.dart

@ -90,29 +90,12 @@ class ListChat extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Text(friend.name, child: Text(friend.name,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0)) style: const TextStyle(fontSize: 16.0)),
),
Container(
margin: const EdgeInsets.only(left: 15.0, right: 20.0),
child: Text(friend.lastMessageTime.toString(),
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0),
),
)
]),
const SizedBox(height: 4.0),
Row(
children: [
Expanded(
child: Text(friend.lastMessageContent,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0)),
), ),
if (this.friend.isClosed) if (this.friend.isClosed)
Container( Container(

41
lib/apps/chat/models.dart

@ -10,64 +10,33 @@ class Friend {
String name; String name;
String addr; String addr;
String remark; String remark;
bool isTop;
bool isClosed; bool isClosed;
RelativeTime lastMessageTime; RelativeTime time;
String lastMessageContent;
bool lastMessageReaded;
bool online = false; bool online = false;
// new friend from network // new friend from network
Friend(this.gid, this.name, this.addr) { Friend(this.gid, this.name, this.addr) {
this.isTop = false;
this.isClosed = false; this.isClosed = false;
this.lastMessageTime = RelativeTime();
this.lastMessageContent = '';
this.lastMessageReaded = true;
} }
Avatar showAvatar({double width = 45.0, bool needOnline = true}) { Avatar showAvatar({double width = 45.0}) {
final avatar = Global.avatarPath + this.gid + '.png'; final avatar = Global.avatarPath + this.gid + '.png';
return Avatar( return Avatar(
width: width, width: width,
name: this.name, name: this.name,
avatarPath: avatar, avatarPath: avatar,
online: this.online, needOnline: false,
needOnline: needOnline,
hasNew: !this.lastMessageReaded,
); );
} }
updateLastMessage(Message msg, bool isReaded) {
this.lastMessageTime = msg.time;
this.lastMessageContent = msg.shortShow();
this.lastMessageReaded = isReaded;
}
static String betterPrint(String info) {
if (info == null) {
return '';
}
final len = info.length;
if (len > 8) {
return info.substring(0, 8) + '...' + info.substring(len - 6, len);
} else {
return info;
}
}
Friend.fromList(List params) { Friend.fromList(List params) {
this.id = params[0]; this.id = params[0];
this.gid = params[1]; this.gid = params[1];
this.addr = params[2]; this.addr = params[2];
this.name = params[3]; this.name = params[3];
this.remark = params[4]; this.remark = params[4];
this.isTop = params[5] == "1"; this.isClosed = params[5];
this.isClosed = params[6] == "1"; this.time = RelativeTime.fromInt(params[6]);
this.lastMessageTime = RelativeTime.fromInt(params[7]);
this.lastMessageContent = params[8];
this.lastMessageReaded = params[9];
this.online = params[10] == "1";
} }
} }

41
lib/apps/chat/provider.dart

@ -18,8 +18,6 @@ class ChatProvider extends ChangeNotifier {
List<int> orderKeys = []; // ordered chat friends with last message. List<int> orderKeys = []; // ordered chat friends with last message.
Map<int, RelativeTime> topKeys = {}; // Set toped friends.
/// all requests. request have order. /// all requests. request have order.
SplayTreeMap<int, Request> requests = SplayTreeMap(); SplayTreeMap<int, Request> requests = SplayTreeMap();
@ -59,7 +57,6 @@ class ChatProvider extends ChangeNotifier {
this.orderKeys.clear(); this.orderKeys.clear();
this.requests.clear(); this.requests.clear();
this.activedMessages.clear(); this.activedMessages.clear();
this.topKeys.clear();
} }
updateActived() { updateActived() {
@ -73,7 +70,6 @@ class ChatProvider extends ChangeNotifier {
updateActivedFriend(int id) { updateActivedFriend(int id) {
this.activedFriendId = id; this.activedFriendId = id;
this.activedMessages.clear(); this.activedMessages.clear();
this.friends[id].lastMessageReaded = true;
rpc.send('chat-message-list', [this.activedFriendId]); rpc.send('chat-message-list', [this.activedFriendId]);
notifyListeners(); notifyListeners();
@ -85,22 +81,9 @@ class ChatProvider extends ChangeNotifier {
} }
/// delete a friend. /// delete a friend.
friendUpdate(int id, {String remark, bool isTop}) { friendUpdate(int id, String remark) {
if (remark != null) {
this.friends[id].remark = remark; this.friends[id].remark = remark;
} rpc.send('chat-friend-update', [id, remark]);
if (isTop != null) {
this.friends[id].isTop = isTop;
if (isTop) {
this.topKeys[id] = this.friends[id].lastMessageTime;
} else {
this.topKeys.remove(id);
}
}
final friend = this.friends[id];
rpc.send('chat-friend-update', [id, friend.remark, friend.isTop]);
notifyListeners(); notifyListeners();
} }
@ -122,7 +105,6 @@ class ChatProvider extends ChangeNotifier {
this.friends.remove(id); this.friends.remove(id);
this.orderKeys.remove(id); this.orderKeys.remove(id);
this.topKeys.remove(id);
rpc.send('chat-friend-delete', [id]); rpc.send('chat-friend-delete', [id]);
notifyListeners(); notifyListeners();
@ -196,9 +178,6 @@ class ChatProvider extends ChangeNotifier {
final id = params[0]; final id = params[0];
this.friends[id] = Friend.fromList(params); this.friends[id] = Friend.fromList(params);
this.orderKeys.add(id); this.orderKeys.add(id);
if (this.friends[id].isTop) {
this.topKeys[id] = this.friends[id].lastMessageTime;
}
}); });
notifyListeners(); notifyListeners();
} }
@ -225,21 +204,13 @@ class ChatProvider extends ChangeNotifier {
_friendInfo(List params) { _friendInfo(List params) {
final id = params[0]; final id = params[0];
this.friends[id] = Friend.fromList(params); this.friends[id] = Friend.fromList(params);
if (this.friends[id].isTop) {
this.topKeys[id] = this.friends[id].lastMessageTime;
}
notifyListeners(); notifyListeners();
} }
_friendUpdate(List params) { _friendUpdate(List params) {
final id = params[0]; final id = params[0];
if (this.friends.containsKey(id)) { if (this.friends.containsKey(id)) {
this.friends[id].isTop = params[1];
this.friends[id].remark = params[2]; this.friends[id].remark = params[2];
if (params[1]) {
this.topKeys[id] = this.friends[id].lastMessageTime;
}
notifyListeners(); notifyListeners();
} }
} }
@ -287,9 +258,6 @@ class ChatProvider extends ChangeNotifier {
this.requests[id].overIt(true); this.requests[id].overIt(true);
} }
var friend = Friend.fromList(params[1]); var friend = Friend.fromList(params[1]);
if (friend.isTop) {
this.topKeys[friend.id] = friend.lastMessageTime;
}
this.friends[friend.id] = friend; this.friends[friend.id] = friend;
orderFriends(friend.id); orderFriends(friend.id);
notifyListeners(); notifyListeners();
@ -326,13 +294,8 @@ class ChatProvider extends ChangeNotifier {
if (!msg.isDelivery) { if (!msg.isDelivery) {
msg.isDelivery = null; // When message create, set is is none; msg.isDelivery = null; // When message create, set is is none;
} }
this.friends[msg.fid].updateLastMessage(msg, true);
this.activedMessages[msg.id] = msg; this.activedMessages[msg.id] = msg;
rpc.send('chat-friend-readed', [this.activedFriendId]); rpc.send('chat-friend-readed', [this.activedFriendId]);
} else {
if (this.friends.containsKey(msg.fid)) {
this.friends[msg.fid].updateLastMessage(msg, false);
}
} }
orderFriends(msg.fid); orderFriends(msg.fid);
notifyListeners(); notifyListeners();

49
lib/apps/group_chat/add.dart

@ -55,6 +55,8 @@ class _GroupAddPageState extends State<GroupAddPage> {
bool _addrChecked = false; bool _addrChecked = false;
String _myName = ''; String _myName = '';
bool _requestsLoadMore = true;
// 0 => encrypted, 1 => common, 2 => open. // 0 => encrypted, 1 => common, 2 => open.
Widget _groupTypeWidget(String text, int value, ColorScheme color) { Widget _groupTypeWidget(String text, int value, ColorScheme color) {
return Row( return Row(
@ -179,7 +181,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
setState(() {}); setState(() {});
}); });
new Future.delayed(Duration.zero, () { new Future.delayed(Duration.zero, () {
//context.read<ChatProvider>().requestList(); context.read<GroupChatProvider>().requestList(false);
_myName = context.read<AccountProvider>().activedAccount.name; _myName = context.read<AccountProvider>().activedAccount.name;
}); });
} }
@ -198,6 +200,9 @@ class _GroupAddPageState extends State<GroupAddPage> {
final groups = provider.groups; final groups = provider.groups;
final createKeys = provider.createKeys; final createKeys = provider.createKeys;
final requests = provider.requests;
final requestKeys = requests.keys.toList().reversed.toList();
return SafeArea( return SafeArea(
child: DefaultTabController( child: DefaultTabController(
initialIndex: 0, initialIndex: 0,
@ -263,7 +268,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
InputText( InputText(
icon: Icons.person, icon: Icons.groups,
text: 'Group ID', text: 'Group ID',
controller: _joinIdController, controller: _joinIdController,
focus: _joinIdFocus), focus: _joinIdFocus),
@ -285,18 +290,28 @@ class _GroupAddPageState extends State<GroupAddPage> {
const SizedBox(height: 20.0), const SizedBox(height: 20.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),
// if (requests.isNotEmpty) if (requests.isNotEmpty)
// Container( Container(
// width: 600.0, width: 600.0,
// child: ListView.builder( child: ListView.builder(
// itemCount: requestKeys.length, itemCount: requestKeys.length,
// shrinkWrap: true, shrinkWrap: true,
// physics: ClampingScrollPhysics(), physics: ClampingScrollPhysics(),
// scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
// itemBuilder: (BuildContext context, int index) => itemBuilder: (BuildContext context, int index) =>
// _RequestItem(request: requests[requestKeys[index]]), _RequestItem(request: requests[requestKeys[index]]),
// ), ),
// ) ),
if (_requestsLoadMore)
TextButton(
onPressed: () {
provider.requestList(true);
setState(() {
_requestsLoadMore = false;
});
},
child: Text('load more...', style: TextStyle(fontSize: 14.0)),
),
], ],
), ),
), ),
@ -410,7 +425,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
Container( Container(
padding: EdgeInsets.symmetric(vertical: 10.0), padding: EdgeInsets.symmetric(vertical: 10.0),
child: InputText( child: InputText(
icon: Icons.person, icon: Icons.account_box,
text: 'Group Name', text: 'Group Name',
controller: _createNameController, controller: _createNameController,
focus: _createNameFocus), focus: _createNameFocus),
@ -418,7 +433,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
Container( Container(
padding: EdgeInsets.symmetric(vertical: 10.0), padding: EdgeInsets.symmetric(vertical: 10.0),
child: InputText( child: InputText(
icon: Icons.location_on, icon: Icons.campaign,
text: 'Group Bio', text: 'Group Bio',
controller: _createBioController, controller: _createBioController,
focus: _createBioFocus), focus: _createBioFocus),
@ -427,7 +442,7 @@ class _GroupAddPageState extends State<GroupAddPage> {
Container( Container(
padding: EdgeInsets.symmetric(vertical: 10.0), padding: EdgeInsets.symmetric(vertical: 10.0),
child: InputText( child: InputText(
icon: Icons.turned_in, icon: Icons.enhanced_encryption,
text: 'Encrypted Key', text: 'Encrypted Key',
controller: _createKeyController, controller: _createKeyController,
focus: _createKeyFocus), focus: _createKeyFocus),

7
lib/apps/group_chat/detail.dart

@ -214,7 +214,7 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
child: Text('Waiting...') child: Text('Waiting...')
); );
} }
final isOnline = this.group.online; final isOnline = provider.online;
return Column( return Column(
children: [ children: [
@ -276,9 +276,7 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
color: const Color(0xFFEDEDED), color: const Color(0xFFEDEDED),
child: Icon(Icons.more_vert_rounded, color: color.primary), child: Icon(Icons.more_vert_rounded, color: color.primary),
onSelected: (int value) { onSelected: (int value) {
if (value == 1) { if (value == 2) {
//Provider.of<GroupChatProvider>(context, listen: false).groupUpdate(this.group.id, isTop: !this.group.isTop);
} else if (value == 2) {
showShadowDialog( showShadowDialog(
context, context,
Icons.info, Icons.info,
@ -368,7 +366,6 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
}, },
itemBuilder: (context) { itemBuilder: (context) {
return <PopupMenuEntry<int>>[ 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.info), _menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.info),
_menuItem(Color(0xFF6174FF), 3, Icons.group_rounded, lang.members), _menuItem(Color(0xFF6174FF), 3, Icons.group_rounded, lang.members),
// _menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), // _menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark),

28
lib/apps/group_chat/models.dart

@ -89,14 +89,9 @@ class GroupChat {
String addr; String addr;
String name; String name;
String bio; String bio;
bool isTop;
bool isOk; bool isOk;
bool isClosed; bool isClosed;
bool isNeedAgree; bool isNeedAgree;
RelativeTime lastTime;
String lastContent;
bool lastReaded;
bool online = false;
GroupChat.fromList(List params) { GroupChat.fromList(List params) {
this.id = params[0]; this.id = params[0];
@ -106,33 +101,20 @@ class GroupChat {
this.addr = params[4]; this.addr = params[4];
this.name = params[5]; this.name = params[5];
this.bio = params[6]; this.bio = params[6];
this.isTop = params[7] == "1"; this.isOk = params[7];
this.isOk = params[8] == "1"; this.isClosed = params[8];
this.isClosed = params[9] == "1"; this.isNeedAgree = params[9];
this.isNeedAgree = params[10] == "1";
this.lastTime = RelativeTime.fromInt(params[11]);
this.lastContent = params[12];
this.lastReaded = params[13] == "1";
this.online = params[14] == "1";
} }
Avatar showAvatar({double width = 45.0, bool needOnline = true}) { Avatar showAvatar({double width = 45.0}) {
final avatar = Global.avatarPath + this.gid + '.png'; final avatar = Global.avatarPath + this.gid + '.png';
return Avatar( return Avatar(
width: width, width: width,
name: this.name, name: this.name,
avatarPath: avatar, avatarPath: avatar,
online: this.online, needOnline: false,
needOnline: needOnline,
hasNew: !this.lastReaded,
); );
} }
updateLastMessage(Message msg, bool isReaded) {
this.lastTime = msg.time;
this.lastContent = msg.shortShow();
this.lastReaded = isReaded;
}
} }
class Request { class Request {

16
lib/apps/group_chat/page.dart

@ -97,22 +97,6 @@ class ListChat extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0)) style: TextStyle(fontSize: 16.0))
), ),
Container(
margin: const EdgeInsets.only(left: 15.0, right: 20.0),
child: Text(group.lastTime.toString(),
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0),
),
)
]),
const SizedBox(height: 4.0),
Row(
children: [
Expanded(
child: Text(group.lastContent,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0)),
),
if (group.isClosed) if (group.isClosed)
Container( Container(
margin: const EdgeInsets.only(left: 15.0, right: 20.0), margin: const EdgeInsets.only(left: 15.0, right: 20.0),

56
lib/apps/group_chat/provider.dart

@ -18,6 +18,7 @@ class GroupChatProvider extends ChangeNotifier {
SplayTreeMap<int, Request> requests = SplayTreeMap(); SplayTreeMap<int, Request> requests = SplayTreeMap();
int actived; int actived;
bool activedOnline;
SplayTreeMap<int, Message> activedMessages = SplayTreeMap(); SplayTreeMap<int, Message> activedMessages = SplayTreeMap();
SplayTreeMap<int, Member> activedMembers = SplayTreeMap(); SplayTreeMap<int, Member> activedMembers = SplayTreeMap();
@ -45,6 +46,7 @@ class GroupChatProvider extends ChangeNotifier {
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-request-list', _requestList, 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);
@ -102,6 +104,10 @@ class GroupChatProvider extends ChangeNotifier {
rpc.send('group-chat-message-create', [gid, mtype.toInt(), content]); rpc.send('group-chat-message-create', [gid, mtype.toInt(), content]);
} }
requestList(bool all) {
rpc.send('group-chat-request-list', [all]);
}
_list(List params) { _list(List params) {
this.clear(); this.clear();
params.forEach((params) { params.forEach((params) {
@ -116,6 +122,20 @@ class GroupChatProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
_online(List params) {
if (this.actived == params[0]) {
this.online = true;
notifyListeners();
}
}
_offline(List params) {
if (this.actived == params[0]) {
this.online = false;
notifyListeners();
}
}
_check(List params) { _check(List params) {
this.createSupported.clear(); this.createSupported.clear();
this.createCheckType = CheckTypeExtension.fromInt(params[0]); this.createCheckType = CheckTypeExtension.fromInt(params[0]);
@ -140,7 +160,6 @@ class GroupChatProvider extends ChangeNotifier {
_result(List params) { _result(List params) {
final id = params[0]; final id = params[0];
this.groups[id].isOk = params[1]; this.groups[id].isOk = params[1];
this.groups[id].online = true;
if (params[1]) { if (params[1]) {
//this.createKeys.remove(id); //this.createKeys.remove(id);
this.orderKeys.add(id); this.orderKeys.add(id);
@ -159,24 +178,6 @@ class GroupChatProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
_online(List params) {
final id = params[0];
if (this.groups.containsKey(id)) {
this.groups[id].online = true;
notifyListeners();
}
}
_offline(List params) {
final id = params[0];
if (this.groups.containsKey(id)) {
if (this.groups[id].gid == params[1]) {
this.groups[id].online = false;
notifyListeners();
}
}
}
_join(List params) { _join(List params) {
this.requests[params[0]] = Request.fromList(params); this.requests[params[0]] = Request.fromList(params);
notifyListeners(); notifyListeners();
@ -202,6 +203,14 @@ class GroupChatProvider extends ChangeNotifier {
} }
} }
_requestList(List params) {
this.requests.clear();
params.forEach((param) {
this.requests[param[0]] = Request.fromList(param);
});
notifyListeners();
}
_memberJoin(List params) { _memberJoin(List params) {
final member = Member.fromList(params); final member = Member.fromList(params);
if (this.actived == member.fid) { if (this.actived == member.fid) {
@ -234,15 +243,8 @@ class GroupChatProvider extends ChangeNotifier {
if (!msg.isDelivery) { if (!msg.isDelivery) {
msg.isDelivery = null; // When message create, set is is none; msg.isDelivery = null; // When message create, set is is none;
} }
this.groups[msg.fid].updateLastMessage(msg, true);
this.activedMessages[msg.id] = msg; this.activedMessages[msg.id] = msg;
rpc.send('group-chat-readed', [this.actived]);
} else {
if (this.groups.containsKey(msg.fid)) {
this.groups[msg.fid].updateLastMessage(msg, false);
}
}
//orderGroups(msg.fid);
notifyListeners(); notifyListeners();
} }
}
} }

1
lib/l10n/localizations.dart

@ -105,7 +105,6 @@ abstract class AppLocalizations {
// homeage // homeage
String get addFriend; String get addFriend;
String get addGroup; String get addGroup;
String get chats;
String get groups; String get groups;
String get devices; String get devices;
String get nightly; String get nightly;

4
lib/l10n/localizations_en.dart

@ -21,7 +21,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get info => 'Info'; String get info => 'Info';
@override @override
String get contact => 'Contact'; String get contact => 'Contacts';
@override @override
String get friend => 'Friend'; String get friend => 'Friend';
@override @override
@ -127,8 +127,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get addGroup => 'Add Service'; String get addGroup => 'Add Service';
@override @override
String get chats => 'Sessions';
@override
String get groups => 'Services'; String get groups => 'Services';
@override @override
String get devices => 'Devices'; String get devices => 'Devices';

2
lib/l10n/localizations_zh.dart

@ -127,8 +127,6 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get addGroup => '添加服务'; String get addGroup => '添加服务';
@override @override
String get chats => '聊天列表';
@override
String get groups => '服务列表'; String get groups => '服务列表';
@override @override
String get devices => '关联设备'; String get devices => '关联设备';

25
lib/pages/home.dart

@ -156,9 +156,6 @@ class _HomeListState extends State<HomeList> with SingleTickerProviderStateMixin
final color = Theme.of(context).colorScheme; final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context); final lang = AppLocalizations.of(context);
final provider = context.watch<AccountProvider>(); final provider = context.watch<AccountProvider>();
final chatProvider = context.watch<ChatProvider>();
final chatTops = chatProvider.topKeys;
final friends = chatProvider.friends;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0), padding: const EdgeInsets.symmetric(vertical: 10.0),
@ -384,7 +381,27 @@ class DrawerWidget extends StatelessWidget {
), ),
const SizedBox(height: 5.0), const SizedBox(height: 5.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)), const Divider(height: 1.0, color: Color(0x40ADB0BB)),
const SizedBox(height: 10.0), ListTile(
leading: Icon(Icons.people_rounded, color: color.primary),
title: Text(lang.contact, textAlign: TextAlign.left,
style: TextStyle(fontSize: 16.0)),
onTap: () {
Navigator.pop(context);
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(
null, lang.contact, ChatList()
);
}),
ListTile(
leading: Icon(Icons.grid_view_rounded, color: color.primary),
title: Text(lang.groups, textAlign: TextAlign.left,
style: TextStyle(fontSize: 16.0)),
onTap: () {
Navigator.pop(context);
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(
null, lang.groups, ServiceList()
);
}),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
ListTile( ListTile(
leading: Icon(Icons.person, color: color.primary), leading: Icon(Icons.person, color: color.primary),
title: Text(lang.profile, title: Text(lang.profile,

90
lib/provider.dart

@ -10,6 +10,7 @@ import 'package:esse/widgets/default_core_show.dart';
import 'package:esse/widgets/default_home_show.dart'; import 'package:esse/widgets/default_home_show.dart';
import 'package:esse/global.dart'; import 'package:esse/global.dart';
import 'package:esse/rpc.dart'; import 'package:esse/rpc.dart';
import 'package:esse/session.dart';
const DEFAULT_ONLINE_INIT = 8; const DEFAULT_ONLINE_INIT = 8;
const DEFAULT_ONLINE_DELAY = 5; const DEFAULT_ONLINE_DELAY = 5;
@ -19,7 +20,10 @@ class AccountProvider extends ChangeNotifier {
String activedAccountId; // actived account gid. String activedAccountId; // actived account gid.
Account get activedAccount => this.accounts[activedAccountId]; Account get activedAccount => this.accounts[activedAccountId];
Set<int> topKeys = Set(); /// home sessions. sorded by last_time.
Map<int, Session> sessions = {};
List<int> topKeys = [];
List<int> orderKeys = [];
/// current user's did. /// current user's did.
String get id => this.activedAccount.id; String get id => this.activedAccount.id;
@ -32,6 +36,13 @@ class AccountProvider extends ChangeNotifier {
Widget get homeShowWidget => this.currentListShow ?? this.defaultListShow; Widget get homeShowWidget => this.currentListShow ?? this.defaultListShow;
void orderSessions(int id) {
if (this.orderKeys.length == 0 || this.orderKeys[0] != id) {
this.orderKeys.remove(id);
this.orderKeys.insert(0, id);
}
}
AccountProvider() { AccountProvider() {
// rpc notice when account not actived. // rpc notice when account not actived.
rpc.addNotice(_accountNotice, _newRequestNotice); rpc.addNotice(_accountNotice, _newRequestNotice);
@ -41,11 +52,13 @@ class AccountProvider extends ChangeNotifier {
rpc.addListener('account-update', _accountUpdate, false); rpc.addListener('account-update', _accountUpdate, false);
rpc.addListener('account-login', _accountLogin, false); rpc.addListener('account-login', _accountLogin, false);
systemInfo(); rpc.addListener('session-list', _sessionList, false);
} rpc.addListener('session-last', _sessionLast, true);
rpc.addListener('session-create', _sessionCreate, true);
rpc.addListener('session-update', _sessionUpdate, false);
rpc.addListener('session-delete', _sessionDelete, false);
handleTops() { systemInfo();
//
} }
/// when security load accounts from rpc. /// when security load accounts from rpc.
@ -62,6 +75,8 @@ class AccountProvider extends ChangeNotifier {
this.activedAccount.online = true; this.activedAccount.online = true;
rpc.send('account-login', [gid, this.activedAccount.lock]); rpc.send('account-login', [gid, this.activedAccount.lock]);
rpc.send('session-list', []);
new Future.delayed(Duration(seconds: DEFAULT_ONLINE_INIT), new Future.delayed(Duration(seconds: DEFAULT_ONLINE_INIT),
() => rpc.send('account-online', [gid])); () => rpc.send('account-online', [gid]));
@ -81,6 +96,8 @@ class AccountProvider extends ChangeNotifier {
this.accounts[account.gid] = account; this.accounts[account.gid] = account;
rpc.send('account-login', [account.gid, account.lock]); rpc.send('account-login', [account.gid, account.lock]);
rpc.send('session-list', []);
new Future.delayed(Duration(seconds: DEFAULT_ONLINE_DELAY), new Future.delayed(Duration(seconds: DEFAULT_ONLINE_DELAY),
() => rpc.send('account-online', [account.gid])); () => rpc.send('account-online', [account.gid]));
updateLogined(account); updateLogined(account);
@ -94,6 +111,11 @@ class AccountProvider extends ChangeNotifier {
this.coreShowWidget = DefaultCoreShow(); this.coreShowWidget = DefaultCoreShow();
this.currentListShow = null; this.currentListShow = null;
// load sessions.
this.sessions.clear();
this.orderKeys.clear();
rpc.send('session-list', []);
if (!this.activedAccount.online) { if (!this.activedAccount.online) {
this.activedAccount.online = true; this.activedAccount.online = true;
rpc.send('account-login', [gid, this.activedAccount.lock]); rpc.send('account-login', [gid, this.activedAccount.lock]);
@ -109,6 +131,10 @@ class AccountProvider extends ChangeNotifier {
this.accounts.clear(); this.accounts.clear();
this.clearActivedAccount(); this.clearActivedAccount();
this.currentListShow = null; this.currentListShow = null;
this.sessions.clear();
this.orderKeys.clear();
this.topKeys.clear();
rpc.send('account-logout', []); rpc.send('account-logout', []);
clearLogined(); clearLogined();
} }
@ -213,4 +239,58 @@ class AccountProvider extends ChangeNotifier {
} }
notifyListeners(); notifyListeners();
} }
_sessionList(List params) {
this.sessions.clear();
this.orderKeys.clear();
this.topKeys.clear();
params.forEach((params) {
final id = params[0];
this.sessions[id] = Session.fromList(params);
if (this.sessions[id].isTop) {
this.topKeys.add(id);
} else {
this.orderKeys.add(id);
}
});
notifyListeners();
}
_sessionCreate(List params) {
final id = params[0];
this.sessions[id] = Session.fromList(params);
orderSessions(id);
notifyListeners();
}
_sessionLast(List params) {
final id = params[0];
this.sessions[id].last(params);
orderSessions(id);
notifyListeners();
}
_sessionUpdate(List params) {
final id = params[0];
this.sessions[id].update(params);
if (this.sessions[id].isTop) {
this.topKeys.add(id);
this.orderKeys.remove(id);
} else {
orderSessions(id);
this.topKeys.remove(id);
}
notifyListeners();
}
_sessionDelete(List params) {
final id = params[1];
this.sessions.remove(id);
this.orderKeys.remove(id);
this.topKeys.remove(id);
notifyListeners();
}
} }

122
lib/session.dart

@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/utils/relative_time.dart';
import 'package:esse/widgets/avatar.dart';
import 'package:esse/global.dart';
import 'package:esse/apps/service/models.dart';
import 'package:esse/apps/primitives.dart';
enum SessionType {
Chat,
Group,
Files,
Device,
Assistant,
Domain,
Service,
}
extension SessionTypeExtension on SessionType {
static SessionType fromInt(int s) {
switch (s) {
case 0:
return SessionType.Chat;
case 1:
return SessionType.Group;
case 2:
return SessionType.Files;
case 3:
return SessionType.Device;
case 4:
return SessionType.Assistant;
case 5:
return SessionType.Domain;
case 6:
return SessionType.Service;
default:
return SessionType.Chat;
}
}
}
class Session {
int id;
int fid;
String gid;
String addr;
SessionType type;
String name;
bool isTop;
RelativeTime lastTime;
String lastContent;
bool lastReaded;
bool online = false;
static List innerService(InnerService service, AppLocalizations lang) {
final params = service.params(lang);
final avatar = Container(
padding: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
),
child: Image.asset(params[2]),
);
final name = params[0];
final bio = params[1];
return [avatar, name, bio];
}
List parse(AppLocalizations lang) {
switch (this.type) {
case SessionType.Chat:
return [showAvatar(), this.name, this.lastContent, this.lastTime.toString()];
case SessionType.Assistant:
final params = Session.innerService(InnerService.Assistant, lang);
return [params[0], params[1], params[2], ''];
case SessionType.Files:
final params = Session.innerService(InnerService.Files, lang);
return [params[0], params[1], params[2], ''];
}
}
Avatar showAvatar({double width = 45.0, bool needOnline = true}) {
final avatar = Global.avatarPath + this.gid + '.png';
return Avatar(
width: width,
name: this.name,
avatarPath: avatar,
online: this.online,
needOnline: needOnline,
hasNew: !this.lastReaded,
);
}
last(List params) {
this.lastTime = RelativeTime.fromInt(params[1]);
this.lastContent = params[2];
this.lastReaded = params[3];
}
update(List params) {
this.addr = params[1];
this.name = params[2];
this.isTop = params[3];
this.online = params[4];
}
Session.fromList(List params) {
this.id = params[0];
this.fid = params[1];
this.gid = params[2];
this.addr = params[3];
this.type = SessionTypeExtension.fromInt(params[4]);
this.name = params[5];
this.isTop = params[6];
this.lastTime = RelativeTime.fromInt(params[7]);
this.lastContent = params[8];
this.lastReaded = params[9];
this.online = params[10];
}
}

143
lib/widgets/default_home_show.dart

@ -6,11 +6,7 @@ import 'package:esse/utils/adaptive.dart';
import 'package:esse/widgets/list_system_app.dart'; import 'package:esse/widgets/list_system_app.dart';
import 'package:esse/options.dart'; import 'package:esse/options.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/session.dart';
import 'package:esse/apps/service/list.dart';
import 'package:esse/apps/service/models.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/chat/list.dart';
class DefaultHomeShow extends StatelessWidget { class DefaultHomeShow extends StatelessWidget {
const DefaultHomeShow({Key key}): super(key: key); const DefaultHomeShow({Key key}): super(key: key);
@ -19,39 +15,118 @@ class DefaultHomeShow extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDesktop = isDisplayDesktop(context); final isDesktop = isDisplayDesktop(context);
final lang = AppLocalizations.of(context); final lang = AppLocalizations.of(context);
final chatProvider = context.watch<ChatProvider>(); final provider = context.watch<AccountProvider>();
final chatTops = chatProvider.topKeys; final allKeys = provider.topKeys + provider.orderKeys;
final friends = chatProvider.friends; final sessions = provider.sessions;
return Column(children: [ return Scaffold(
ListSystemApp(name: lang.chats, icon: Icons.people_rounded, body: ListView.builder(
callback: () => Provider.of<AccountProvider>(context, listen: false).updateActivedApp( itemCount: allKeys.length,
null, lang.chats, ChatList())), itemBuilder: (BuildContext ctx, int index) => _SessionWidget(session: sessions[allKeys[index]]),
ListSystemApp(name: lang.groups, icon: Icons.grid_view_rounded, ),
callback: () => Provider.of<AccountProvider>(context, listen: false).updateActivedApp( // floatingActionButton: FloatingActionButton(
null, lang.groups, ServiceList())), // onPressed: () {
const SizedBox(height: 5.0), // final widget = Text('');
const Divider(height: 1.0, color: Color(0x40ADB0BB)), // if (isDesktop) {
const SizedBox(height: 5.0), // Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget);
Column( // } else {
children: INNER_SERVICES.map((v) { // Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
final params = v.params(lang); // }
return ListInnerService( // },
name: params[0], // child: const Icon(Icons.add, color: Colors.white),
bio: params[1], // backgroundColor: Color(0xFF6174FF),
logo: params[2], // ),
callback: () => v.callback(context, isDesktop, lang),
isDesktop: isDesktop,
); );
}).toList() }
}
class _SessionWidget extends StatelessWidget {
final Session session;
const _SessionWidget({Key key, this.session}) : super(key: key);
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
final params = session.parse(lang);
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
// TODO
// context.read<ChatProvider>().updateActivedFriend(friend.id);
// if (!isDesktop) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (_) => ChatPage(),
// ),
// );
// } else {
// context.read<AccountProvider>().updateActivedApp(ChatDetail());
// }
},
child: Container(
height: 55.0,
child: Row(
children: [
Container(
width: 45.0,
height: 45.0,
margin: const EdgeInsets.only(left: 20.0, right: 15.0),
child: params[0],
), ),
Expanded( Expanded(
child: ListView.builder( child: Container(
itemCount: chatTops.length, height: 55.0,
itemBuilder: (BuildContext ctx, int index) => ListChat( child: Column(
friend: friends[chatTops.keys.elementAt(index)]), mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(params[1],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0))
),
Container(
margin: const EdgeInsets.only(left: 15.0, right: 20.0),
child: Text(params[3],
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0),
), ),
) )
]); ]),
const SizedBox(height: 4.0),
Row(
children: [
Expanded(
child: Text(params[2],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0)),
),
// if (session.isClosed)
// Container(
// margin: const EdgeInsets.only(left: 15.0, right: 20.0),
// child: Text(lang.unfriended,
// style: TextStyle(color: color.primary, fontSize: 12.0),
// ),
// )
]),
],
),
),
),
],
),
),
);
} }
} }

18
pubspec.lock

@ -161,7 +161,7 @@ packages:
name: file name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.0" version: "6.1.1"
file_picker: file_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -302,7 +302,7 @@ packages:
name: image_picker name: image_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.5" version: "0.7.5+2"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
@ -447,14 +447,14 @@ packages:
name: permission_handler name: permission_handler
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "7.1.0" version: "8.0.0+2"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.3.0" version: "3.5.0"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -641,7 +641,7 @@ packages:
name: url_launcher name: url_launcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.3" version: "6.0.4"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -662,7 +662,7 @@ packages:
name: url_launcher_platform_interface name: url_launcher_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
@ -718,7 +718,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.1.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -732,7 +732,7 @@ packages:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.0" version: "5.1.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@ -741,5 +741,5 @@ packages:
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
sdks: sdks:
dart: ">=2.12.1 <3.0.0" dart: ">=2.13.0 <3.0.0"
flutter: ">=2.0.2" flutter: ">=2.0.2"

4
src/apps/assistant/models.rs

@ -9,7 +9,7 @@ use tdn::types::{
use tdn_storage::local::{DStorage, DsValue}; use tdn_storage::local::{DStorage, DsValue};
use crate::apps::chat::Friend; use crate::apps::chat::Friend;
use crate::storage::{read_file, session_db, write_file, write_image}; use crate::storage::{chat_db, read_file, write_file, write_image};
#[derive(Eq, PartialEq, Clone)] #[derive(Eq, PartialEq, Clone)]
pub(crate) enum MessageType { pub(crate) enum MessageType {
@ -69,7 +69,7 @@ impl MessageType {
} }
MessageType::Contact => { MessageType::Contact => {
let cid: i64 = content.parse().map_err(|_e| new_io_error("id error"))?; let cid: i64 = content.parse().map_err(|_e| new_io_error("id error"))?;
let db = session_db(base, mgid)?; let db = chat_db(base, mgid)?;
let contact = Friend::get_id(&db, cid)??; let contact = Friend::get_id(&db, cid)??;
db.close()?; db.close()?;
let tmp_name = contact.name.replace(";", "-;"); let tmp_name = contact.name.replace(";", "-;");

42
src/apps/chat/layer.rs

@ -15,7 +15,7 @@ use crate::event::{InnerEvent, StatusEvent};
use crate::layer::{Layer, Online}; use crate::layer::{Layer, Online};
use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH}; use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH};
use crate::storage::{ use crate::storage::{
read_avatar, read_file, read_record, session_db, write_avatar_sync, write_file, write_image, chat_db, read_avatar, read_file, read_record, write_avatar_sync, write_file, write_image,
}; };
use super::models::{Friend, Message, MessageType, NetworkMessage, Request}; use super::models::{Friend, Message, MessageType, NetworkMessage, Request};
@ -100,7 +100,7 @@ pub(crate) async fn handle(
.check_add_online(fgid, Online::Direct(addr), f.id)?; .check_add_online(fgid, Online::Direct(addr), f.id)?;
// 3. update remote addr. TODO // 3. update remote addr. TODO
if f.addr != addr { if f.addr != addr {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
let _ = Friend::addr_update(&db, f.id, &addr); let _ = Friend::addr_update(&db, f.id, &addr);
drop(db); drop(db);
} }
@ -119,7 +119,7 @@ pub(crate) async fn handle(
let some_friend = load_friend(&layer.base, &mgid, &fgid)?; let some_friend = load_friend(&layer.base, &mgid, &fgid)?;
if some_friend.is_none() { if some_friend.is_none() {
// check if exist request. // check if exist request.
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(req) = Request::get(&db, &remote.id)? { if let Some(req) = Request::get(&db, &remote.id)? {
req.delete(&db)?; // delete the old request. req.delete(&db)?; // delete the old request.
results.rpcs.push(rpc::request_delete(mgid, req.id)); results.rpcs.push(rpc::request_delete(mgid, req.id));
@ -161,13 +161,15 @@ pub(crate) async fn handle(
// 2. update remote user. // 2. update remote user.
friend.name = remote.name; friend.name = remote.name;
friend.addr = remote.addr; friend.addr = remote.addr;
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
friend.remote_update(&db)?; friend.remote_update(&db)?;
drop(db); drop(db);
write_avatar_sync(&layer.base, &mgid, &remote.id, remote.avatar)?; write_avatar_sync(&layer.base, &mgid, &remote.id, remote.avatar)?;
// 3. online to UI. // 3. online to UI.
friend.online = true;
// TODO UPDATE SESSION
results.rpcs.push(rpc::friend_info(mgid, &friend)); results.rpcs.push(rpc::friend_info(mgid, &friend));
// 4. connected. // 4. connected.
let msg = conn_agree_message(&mut layer, 0, &mgid, addr).await?; let msg = conn_agree_message(&mut layer, 0, &mgid, addr).await?;
@ -204,7 +206,7 @@ pub(crate) async fn handle(
RecvType::Result(addr, is_ok, data) => { RecvType::Result(addr, is_ok, data) => {
// check to close. // check to close.
if !is_ok { if !is_ok {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(friend) = Friend::get_it(&db, &fgid)? { if let Some(friend) = Friend::get_it(&db, &fgid)? {
if friend.contains_addr(&addr) { if friend.contains_addr(&addr) {
results.rpcs.push(rpc::friend_close(mgid, friend.id)); results.rpcs.push(rpc::friend_close(mgid, friend.id));
@ -217,7 +219,7 @@ pub(crate) async fn handle(
.map_err(|_e| new_io_error("Deseralize result failure"))?; .map_err(|_e| new_io_error("Deseralize result failure"))?;
match response { match response {
LayerResponse::Reject => { LayerResponse::Reject => {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(mut request) = Request::get(&db, &fgid)? { if let Some(mut request) = Request::get(&db, &fgid)? {
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
&mgid, &mgid,
@ -258,7 +260,7 @@ pub(crate) async fn handle(
.running_mut(&mgid)? .running_mut(&mgid)?
.check_add_online(fgid, Online::Direct(addr), fid)?; .check_add_online(fgid, Online::Direct(addr), fid)?;
// 4. update remote addr. // 4. update remote addr.
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
Friend::addr_update(&db, fid, &addr)?; Friend::addr_update(&db, fid, &addr)?;
drop(db); drop(db);
// 5. online to UI. // 5. online to UI.
@ -287,7 +289,7 @@ pub(crate) async fn handle(
)?; )?;
} else { } else {
// agree request for friend. // agree request for friend.
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(mut request) = Request::get(&db, &remote.id)? { if let Some(mut request) = Request::get(&db, &remote.id)? {
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
&mgid, &mgid,
@ -340,7 +342,7 @@ pub(crate) async fn handle(
.running_mut(&mgid)? .running_mut(&mgid)?
.check_add_online(fgid, Online::Direct(addr), fid)?; .check_add_online(fgid, Online::Direct(addr), fid)?;
// 4. update remote addr. // 4. update remote addr.
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
Friend::addr_update(&db, fid, &addr)?; Friend::addr_update(&db, fid, &addr)?;
drop(db); drop(db);
// 5. online to UI. // 5. online to UI.
@ -372,7 +374,7 @@ pub(crate) async fn handle(
)?; )?;
} else { } else {
// agree request for friend. // agree request for friend.
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(mut request) = Request::get(&db, &remote.id)? { if let Some(mut request) = Request::get(&db, &remote.id)? {
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
&mgid, &mgid,
@ -402,7 +404,7 @@ pub(crate) async fn handle(
results.layers.push((mgid, fgid, msg)); results.layers.push((mgid, fgid, msg));
} }
LayerResponse::Reject => { LayerResponse::Reject => {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if let Some(mut request) = Request::get(&db, &fgid)? { if let Some(mut request) = Request::get(&db, &fgid)? {
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
&mgid, &mgid,
@ -431,7 +433,7 @@ pub(crate) async fn handle(
// TODO maybe send failure need handle. // TODO maybe send failure need handle.
if is_ok { if is_ok {
if let Some((gid, db_id)) = layer.delivery.remove(&tid) { if let Some((gid, db_id)) = layer.delivery.remove(&tid) {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
let resp = match t { let resp = match t {
DeliveryType::Event => { DeliveryType::Event => {
Message::delivery(&db, db_id, true)?; Message::delivery(&db, db_id, true)?;
@ -474,7 +476,7 @@ impl LayerEvent {
match event { match event {
LayerEvent::Message(hash, m) => { LayerEvent::Message(hash, m) => {
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if !Message::exist(&db, &hash)? { if !Message::exist(&db, &hash)? {
let msg = m.clone().handle(false, mgid, &layer.base, &db, fid, hash)?; let msg = m.clone().handle(false, mgid, &layer.base, &db, fid, hash)?;
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
@ -489,7 +491,7 @@ impl LayerEvent {
} }
LayerEvent::Info(remote) => { LayerEvent::Info(remote) => {
let avatar = remote.avatar.clone(); let avatar = remote.avatar.clone();
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
let mut f = Friend::get_id(&db, fid)?.ok_or(new_io_error(""))?; let mut f = Friend::get_id(&db, fid)?.ok_or(new_io_error(""))?;
f.name = remote.name; f.name = remote.name;
f.addr = remote.addr; f.addr = remote.addr;
@ -549,7 +551,7 @@ impl LayerEvent {
&mut results, &mut results,
)?; )?;
layer.remove_online(&mgid, &fgid); layer.remove_online(&mgid, &fgid);
let db = session_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
Friend::id_close(&db, fid)?; Friend::id_close(&db, fid)?;
drop(db); drop(db);
results.rpcs.push(rpc::friend_close(mgid, fid)); results.rpcs.push(rpc::friend_close(mgid, fid));
@ -571,7 +573,7 @@ impl LayerEvent {
m_type: MessageType, m_type: MessageType,
content: String, content: String,
) -> std::result::Result<(Message, NetworkMessage), tdn::types::rpc::RpcError> { ) -> std::result::Result<(Message, NetworkMessage), tdn::types::rpc::RpcError> {
let db = session_db(&base, &mgid)?; let db = chat_db(&base, &mgid)?;
// handle message's type. // handle message's type.
let (nm_type, raw) = match m_type { let (nm_type, raw) = match m_type {
@ -630,7 +632,9 @@ impl LayerEvent {
let mut msg = Message::new(&mgid, fid, true, m_type, raw, false); let mut msg = Message::new(&mgid, fid, true, m_type, raw, false);
msg.insert(&db)?; msg.insert(&db)?;
Friend::update_last_message(&db, fid, &msg, true)?;
// TODO UPDATE SESSION
drop(db); drop(db);
Ok((msg, nm_type)) Ok((msg, nm_type))
} }
@ -638,7 +642,7 @@ impl LayerEvent {
#[inline] #[inline]
fn load_friend(base: &PathBuf, mgid: &GroupId, fgid: &GroupId) -> Result<Option<Friend>> { fn load_friend(base: &PathBuf, mgid: &GroupId, fgid: &GroupId) -> Result<Option<Friend>> {
let db = session_db(base, mgid)?; let db = chat_db(base, mgid)?;
Friend::get(&db, fgid) Friend::get(&db, fgid)
} }

76
src/apps/chat/models.rs

@ -19,13 +19,9 @@ pub(crate) struct Friend {
pub addr: PeerAddr, pub addr: PeerAddr,
pub name: String, pub name: String,
pub remark: String, pub remark: String,
pub is_top: bool,
pub is_closed: bool, pub is_closed: bool,
pub last_message_datetime: i64,
pub last_message_content: String,
pub last_message_readed: bool,
pub online: bool,
pub is_deleted: bool, pub is_deleted: bool,
pub datetime: i64,
} }
#[derive(Clone)] #[derive(Clone)]
@ -114,7 +110,9 @@ impl NetworkMessage {
let mut msg = Message::new_with_id(hash, fid, is_me, m_type, raw, true); let mut msg = Message::new_with_id(hash, fid, is_me, m_type, raw, true);
msg.insert(db)?; msg.insert(db)?;
Friend::update_last_message(db, fid, &msg, false)?;
// TODO UPDATE SESSION
Ok(msg) Ok(msg)
} }
@ -221,15 +219,11 @@ impl Friend {
Friend { Friend {
id: 0, id: 0,
last_message_datetime: datetime,
last_message_content: "".to_owned(),
last_message_readed: true,
gid, gid,
addr, addr,
name, name,
remark, remark,
online: false, datetime,
is_top: false,
is_closed: false, is_closed: false,
is_deleted: false, is_deleted: false,
} }
@ -249,17 +243,13 @@ impl Friend {
Friend { Friend {
is_deleted, is_deleted,
last_message_readed: v.pop().unwrap().as_bool(), datetime: v.pop().unwrap().as_i64(),
last_message_content: v.pop().unwrap().as_string(),
last_message_datetime: v.pop().unwrap().as_i64(),
is_closed: v.pop().unwrap().as_bool(), is_closed: v.pop().unwrap().as_bool(),
is_top: v.pop().unwrap().as_bool(),
remark: v.pop().unwrap().as_string(), remark: v.pop().unwrap().as_string(),
name: v.pop().unwrap().as_string(), name: v.pop().unwrap().as_string(),
addr: PeerAddr::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerAddr::default()), addr: PeerAddr::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerAddr::default()),
gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()), gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()),
id: v.pop().unwrap().as_i64(), id: v.pop().unwrap().as_i64(),
online: false,
} }
} }
@ -284,17 +274,13 @@ impl Friend {
self.addr.to_hex(), self.addr.to_hex(),
self.name, self.name,
self.remark, self.remark,
if self.is_top { "1" } else { "0" }, self.is_closed,
if self.is_closed { "1" } else { "0" }, self.datetime
self.last_message_datetime,
self.last_message_content,
self.last_message_readed,
if self.online { "1" } else { "0" },
]) ])
} }
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> { pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> {
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends WHERE gid = '{}' and is_deleted = false", gid.to_hex()); let sql = format!("SELECT id, gid, addr, name, remark, is_closed, datetime FROM friends WHERE gid = '{}' and is_deleted = false", gid.to_hex());
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), false))); // safe unwrap() return Ok(Some(Friend::from_values(matrix.pop().unwrap(), false))); // safe unwrap()
@ -303,7 +289,7 @@ impl Friend {
} }
pub fn get_it(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> { pub fn get_it(db: &DStorage, gid: &GroupId) -> Result<Option<Friend>> {
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted FROM friends WHERE gid = '{}'", gid.to_hex()); let sql = format!("SELECT id, gid, addr, name, remark, is_closed, datetime, is_deleted FROM friends WHERE gid = '{}'", gid.to_hex());
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap() return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap()
@ -312,7 +298,7 @@ impl Friend {
} }
pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Friend>> { pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Friend>> {
let sql = format!("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted FROM friends WHERE id = {}", id); let sql = format!("SELECT id, gid, addr, name, remark, is_closed, datetime, is_deleted FROM friends WHERE id = {}", id);
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap() return Ok(Some(Friend::from_values(matrix.pop().unwrap(), true))); // safe unwrap()
@ -322,7 +308,7 @@ impl Friend {
/// use in rpc when load account friends. /// use in rpc when load account friends.
pub fn all(db: &DStorage) -> Result<Vec<Friend>> { pub fn all(db: &DStorage) -> Result<Vec<Friend>> {
let matrix = db.query("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends where is_deleted = false ORDER BY last_message_datetime DESC")?; let matrix = db.query("SELECT id, gid, addr, name, remark, is_closed, datetime FROM friends where is_deleted = false")?;
let mut friends = vec![]; let mut friends = vec![];
for values in matrix { for values in matrix {
friends.push(Friend::from_values(values, false)); friends.push(Friend::from_values(values, false));
@ -332,7 +318,7 @@ impl Friend {
/// use in rpc when load account friends. /// use in rpc when load account friends.
pub fn all_ok(db: &DStorage) -> Result<Vec<Friend>> { pub fn all_ok(db: &DStorage) -> Result<Vec<Friend>> {
let matrix = db.query("SELECT id, gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed FROM friends where is_closed = false ORDER BY last_message_datetime DESC")?; let matrix = db.query("SELECT id, gid, addr, name, remark, is_closed, datetime FROM friends where is_closed = false")?;
let mut friends = vec![]; let mut friends = vec![];
for values in matrix { for values in matrix {
friends.push(Friend::from_values(values, false)); friends.push(Friend::from_values(values, false));
@ -355,16 +341,13 @@ impl Friend {
} }
pub fn insert(&mut self, db: &DStorage) -> Result<()> { pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO friends (gid, addr, name, remark, is_top, is_closed, last_message_datetime, last_message_content, last_message_readed, is_deleted) VALUES ('{}', '{}', '{}', '{}', {}, {}, {}, '{}', {}, false)", let sql = format!("INSERT INTO friends (gid, addr, name, remark, is_closed, datetime, is_deleted) VALUES ('{}', '{}', '{}', '{}', {}, {}, false)",
self.gid.to_hex(), self.gid.to_hex(),
self.addr.to_hex(), self.addr.to_hex(),
self.name, self.name,
self.remark, self.remark,
if self.is_top { 1 } else { 0 },
if self.is_closed { 1 } else { 0 }, if self.is_closed { 1 } else { 0 },
self.last_message_datetime, self.datetime,
self.last_message_content,
if self.last_message_readed { 1 } else { 0 }
); );
let id = db.insert(&sql)?; let id = db.insert(&sql)?;
self.id = id; self.id = id;
@ -372,15 +355,11 @@ impl Friend {
} }
pub fn update(&self, db: &DStorage) -> Result<usize> { pub fn update(&self, db: &DStorage) -> Result<usize> {
let sql = format!("UPDATE friends SET addr = '{}', name = '{}', remark = '{}', is_top = {}, is_closed = {}, last_message_datetime = {}, last_message_content = '{}', last_message_readed = {}, is_deleted = {} WHERE id = {}", let sql = format!("UPDATE friends SET addr = '{}', name = '{}', remark = '{}', is_closed = {}, is_deleted = {} WHERE id = {}",
self.addr.to_hex(), self.addr.to_hex(),
self.name, self.name,
self.remark, self.remark,
if self.is_top { 1 } else { 0 },
if self.is_closed { 1 } else { 0 }, if self.is_closed { 1 } else { 0 },
self.last_message_datetime,
self.last_message_content,
if self.last_message_readed { 1 } else { 0 },
if self.is_deleted { 1 } else { 0 }, if self.is_deleted { 1 } else { 0 },
self.id self.id
); );
@ -389,8 +368,8 @@ impl Friend {
pub fn me_update(&mut self, db: &DStorage) -> Result<usize> { pub fn me_update(&mut self, db: &DStorage) -> Result<usize> {
let sql = format!( let sql = format!(
"UPDATE friends SET remark='{}', is_top={} WHERE id = {}", "UPDATE friends SET remark='{}' WHERE id = {}",
self.remark, self.is_top, self.id, self.remark, self.id,
); );
db.update(&sql) db.update(&sql)
} }
@ -414,21 +393,6 @@ impl Friend {
db.update(&sql) db.update(&sql)
} }
pub fn update_last_message(db: &DStorage, id: i64, msg: &Message, read: bool) -> Result<usize> {
let sql = format!("UPDATE friends SET last_message_datetime={}, last_message_content='{}', last_message_readed={} WHERE id = {}",
msg.datetime,
msg.content,
if read { 1 } else { 0 },
id,
);
db.update(&sql)
}
pub fn readed(db: &DStorage, id: i64) -> Result<usize> {
let sql = format!("UPDATE friends SET last_message_readed=1 WHERE id = {}", id);
db.update(&sql)
}
/// used in rpc, when what to delete a friend. /// used in rpc, when what to delete a friend.
pub fn close(&self, db: &DStorage) -> Result<usize> { pub fn close(&self, db: &DStorage) -> Result<usize> {
let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", self.id); let sql = format!("UPDATE friends SET is_closed = true WHERE id = {}", self.id);
@ -492,7 +456,9 @@ impl Request {
pub fn to_friend(self) -> Friend { pub fn to_friend(self) -> Friend {
let mut friend = Friend::new(self.gid, self.addr, self.name, self.remark); let mut friend = Friend::new(self.gid, self.addr, self.name, self.remark);
friend.is_top = true; // default set to top page.
// TODO add new session.
friend friend
} }

52
src/apps/chat/rpc.rs

@ -11,7 +11,7 @@ use tdn_did::user::User;
use crate::event::InnerEvent; use crate::event::InnerEvent;
use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH}; use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH};
use crate::rpc::{sleep_waiting_close_stable, RpcState}; use crate::rpc::{sleep_waiting_close_stable, RpcState};
use crate::storage::{delete_avatar, session_db}; use crate::storage::{chat_db, delete_avatar};
use super::layer::LayerEvent; use super::layer::LayerEvent;
use super::{Friend, Message, MessageType, Request}; use super::{Friend, Message, MessageType, Request};
@ -32,8 +32,8 @@ pub(crate) fn friend_info(mgid: GroupId, friend: &Friend) -> RpcParam {
} }
#[inline] #[inline]
pub(crate) fn friend_update(mgid: GroupId, fid: i64, is_top: bool, remark: &str) -> RpcParam { pub(crate) fn friend_update(mgid: GroupId, fid: i64, remark: &str) -> RpcParam {
rpc_response(0, "chat-friend-update", json!([fid, is_top, remark]), mgid) rpc_response(0, "chat-friend-update", json!([fid, remark]), mgid)
} }
#[inline] #[inline]
@ -123,16 +123,10 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
"chat-friend-list", "chat-friend-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = session_db(&layer_lock.base, &gid)?; let db = chat_db(&layer_lock.base, &gid)?;
let mut friends = Friend::all(&db)?; let mut friends = Friend::all(&db)?;
drop(db); drop(db);
let gids: Vec<&GroupId> = friends.iter().map(|f| &f.gid).collect();
let onlines = layer_lock.merge_online(&gid, gids)?;
for (index, online) in onlines.iter().enumerate() {
friends[index].online = *online;
}
Ok(HandleResult::rpc(friend_list(friends))) Ok(HandleResult::rpc(friend_list(friends)))
}, },
); );
@ -142,12 +136,10 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let id = params[0].as_i64()?; let id = params[0].as_i64()?;
let remark = params[1].as_str()?; let remark = params[1].as_str()?;
let is_top = params[2].as_bool()?;
let mut results = HandleResult::new(); let mut results = HandleResult::new();
let db = session_db(state.layer.read().await.base(), &gid)?; let db = chat_db(state.layer.read().await.base(), &gid)?;
let f = if let Some(mut f) = Friend::get_id(&db, id)? { let f = if let Some(mut f) = Friend::get_id(&db, id)? {
f.is_top = is_top;
f.remark = remark.to_owned(); f.remark = remark.to_owned();
f.me_update(&db)?; f.me_update(&db)?;
f f
@ -157,7 +149,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
drop(db); drop(db);
state.group.write().await.broadcast( state.group.write().await.broadcast(
&gid, &gid,
InnerEvent::SessionFriendUpdate(f.gid, f.is_top, f.remark), InnerEvent::SessionFriendUpdate(f.gid, f.remark),
FRIEND_TABLE_PATH, FRIEND_TABLE_PATH,
f.id, f.id,
&mut results, &mut results,
@ -166,19 +158,6 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
}, },
); );
handler.add_method(
"chat-friend-readed",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let fid = params[0].as_i64()?;
let db = session_db(state.layer.read().await.base(), &gid)?;
Friend::readed(&db, fid)?;
drop(db);
Ok(HandleResult::new())
},
);
handler.add_method( handler.add_method(
"chat-friend-close", "chat-friend-close",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
@ -187,7 +166,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let mut results = HandleResult::new(); let mut results = HandleResult::new();
let mut layer_lock = state.layer.write().await; let mut layer_lock = state.layer.write().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
let friend = Friend::get_id(&db, id)??; let friend = Friend::get_id(&db, id)??;
friend.close(&db)?; friend.close(&db)?;
drop(db); drop(db);
@ -227,7 +206,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let mut results = HandleResult::new(); let mut results = HandleResult::new();
let mut layer_lock = state.layer.write().await; let mut layer_lock = state.layer.write().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
let friend = Friend::get_id(&db, id)??; let friend = Friend::get_id(&db, id)??;
friend.delete(&db)?; friend.delete(&db)?;
drop(db); drop(db);
@ -264,7 +243,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
"chat-request-list", "chat-request-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
drop(layer_lock); drop(layer_lock);
let requests = Request::all(&db)?; let requests = Request::all(&db)?;
drop(db); drop(db);
@ -292,7 +271,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let me = state.group.read().await.clone_user(&gid)?; let me = state.group.read().await.clone_user(&gid)?;
let mut layer_lock = state.layer.write().await; let mut layer_lock = state.layer.write().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
if Friend::is_friend(&db, &request.gid)? { if Friend::is_friend(&db, &request.gid)? {
debug!("had friend."); debug!("had friend.");
drop(layer_lock); drop(layer_lock);
@ -340,7 +319,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let mut group_lock = state.group.write().await; let mut group_lock = state.group.write().await;
let me = group_lock.clone_user(&gid)?; let me = group_lock.clone_user(&gid)?;
let mut layer_lock = state.layer.write().await; let mut layer_lock = state.layer.write().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
let mut results = HandleResult::new(); let mut results = HandleResult::new();
if let Some(mut request) = Request::get_id(&db, id)? { if let Some(mut request) = Request::get_id(&db, id)? {
@ -376,7 +355,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let id = params[0].as_i64()?; let id = params[0].as_i64()?;
let mut layer_lock = state.layer.write().await; let mut layer_lock = state.layer.write().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
let mut req = Request::get_id(&db, id)??; let mut req = Request::get_id(&db, id)??;
req.is_ok = false; req.is_ok = false;
req.is_over = true; req.is_over = true;
@ -403,7 +382,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let id = params[0].as_i64()?; let id = params[0].as_i64()?;
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
let base = layer_lock.base().clone(); let base = layer_lock.base().clone();
drop(layer_lock); drop(layer_lock);
let req = Request::get_id(&db, id)??; let req = Request::get_id(&db, id)??;
@ -433,10 +412,9 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let fid = params[0].as_i64()?; let fid = params[0].as_i64()?;
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = session_db(layer_lock.base(), &gid)?; let db = chat_db(layer_lock.base(), &gid)?;
drop(layer_lock); drop(layer_lock);
Friend::readed(&db, fid)?;
let messages = Message::get(&db, &fid)?; let messages = Message::get(&db, &fid)?;
drop(db); drop(db);
Ok(HandleResult::rpc(message_list(messages))) Ok(HandleResult::rpc(message_list(messages)))
@ -486,7 +464,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
let id = params[0].as_i64()?; let id = params[0].as_i64()?;
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = session_db(&layer_lock.base(), &gid)?; let db = chat_db(&layer_lock.base(), &gid)?;
drop(layer_lock); drop(layer_lock);
let msg = Message::get_id(&db, id)??; let msg = Message::get_id(&db, id)??;

116
src/apps/group_chat/models.rs

@ -71,8 +71,6 @@ pub(crate) struct GroupChat {
g_name: String, g_name: String,
/// group chat simple intro. /// group chat simple intro.
g_bio: String, g_bio: String,
/// group chat is set top sessions.
is_top: bool,
/// group chat is created ok. /// group chat is created ok.
is_ok: bool, is_ok: bool,
/// group chat is closed. /// group chat is closed.
@ -81,16 +79,8 @@ pub(crate) struct GroupChat {
is_need_agree: bool, is_need_agree: bool,
/// group chat encrypted-key. /// group chat encrypted-key.
key: GroupChatKey, key: GroupChatKey,
/// group chat lastest message time. (only ESSE used)
last_datetime: i64,
/// group chat lastest message content. (only ESSE used)
last_content: String,
/// group chat lastest message readed. (only ESSE used)
last_readed: bool,
/// group chat created time. /// group chat created time.
pub datetime: i64, pub datetime: i64,
/// group chat is online.
pub online: bool,
/// is deleted. /// is deleted.
is_deleted: bool, is_deleted: bool,
} }
@ -126,13 +116,8 @@ impl GroupChat {
datetime, datetime,
id: 0, id: 0,
height: 0, height: 0,
is_top: true,
is_ok: false, is_ok: false,
is_closed: false, is_closed: false,
last_datetime: datetime,
last_content: Default::default(),
last_readed: true,
online: false,
is_deleted: false, is_deleted: false,
} }
} }
@ -166,13 +151,8 @@ impl GroupChat {
datetime, datetime,
id: 0, id: 0,
height, height,
is_top: true,
is_ok: true, is_ok: true,
is_closed: false, is_closed: false,
last_datetime: datetime,
last_content: Default::default(),
last_readed: true,
online: false,
is_deleted: false, is_deleted: false,
} }
} }
@ -243,14 +223,9 @@ impl GroupChat {
self.g_addr.to_hex(), self.g_addr.to_hex(),
self.g_name, self.g_name,
self.g_bio, self.g_bio,
if self.is_top { "1" } else { "0" }, self.is_ok,
if self.is_ok { "1" } else { "0" }, self.is_closed,
if self.is_closed { "1" } else { "0" }, self.is_need_agree,
if self.is_need_agree { "1" } else { "0" },
self.last_datetime,
self.last_content,
if self.last_readed { "1" } else { "0" },
if self.online { "1" } else { "0" },
]) ])
} }
@ -263,17 +238,12 @@ impl GroupChat {
Self { Self {
is_deleted, is_deleted,
online: false,
datetime: v.pop().unwrap().as_i64(), datetime: v.pop().unwrap().as_i64(),
last_readed: v.pop().unwrap().as_bool(),
last_content: v.pop().unwrap().as_string(),
last_datetime: v.pop().unwrap().as_i64(),
key: GroupChatKey::from_hex(v.pop().unwrap().as_string()) key: GroupChatKey::from_hex(v.pop().unwrap().as_string())
.unwrap_or(GroupChatKey::new(vec![])), .unwrap_or(GroupChatKey::new(vec![])),
is_closed: v.pop().unwrap().as_bool(), is_closed: v.pop().unwrap().as_bool(),
is_need_agree: v.pop().unwrap().as_bool(), is_need_agree: v.pop().unwrap().as_bool(),
is_ok: v.pop().unwrap().as_bool(), is_ok: v.pop().unwrap().as_bool(),
is_top: v.pop().unwrap().as_bool(),
g_bio: v.pop().unwrap().as_string(), g_bio: v.pop().unwrap().as_string(),
g_name: v.pop().unwrap().as_string(), g_name: v.pop().unwrap().as_string(),
g_addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), g_addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()),
@ -287,7 +257,7 @@ impl GroupChat {
/// use in rpc when load account friends. /// use in rpc when load account friends.
pub fn all(db: &DStorage) -> Result<Vec<GroupChat>> { pub fn all(db: &DStorage) -> Result<Vec<GroupChat>> {
let matrix = db.query("SELECT id, height, 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 matrix = db.query("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_ok, is_need_agree, is_closed, key, datetime FROM groups WHERE is_deleted = false")?;
let mut groups = vec![]; let mut groups = vec![];
for values in matrix { for values in matrix {
groups.push(GroupChat::from_values(values, false)); groups.push(GroupChat::from_values(values, false));
@ -297,7 +267,7 @@ impl GroupChat {
/// use in rpc when load account groups. /// use in rpc when load account groups.
pub fn all_ok(db: &DStorage) -> Result<Vec<GroupChat>> { pub fn all_ok(db: &DStorage) -> Result<Vec<GroupChat>> {
let matrix = db.query("SELECT id, height, 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_closed = false ORDER BY last_datetime DESC")?; let matrix = db.query("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_ok, is_need_agree, is_closed, key, datetime FROM groups WHERE is_closed = false")?;
let mut groups = vec![]; let mut groups = vec![];
for values in matrix { for values in matrix {
groups.push(GroupChat::from_values(values, false)); groups.push(GroupChat::from_values(values, false));
@ -306,7 +276,7 @@ impl GroupChat {
} }
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<GroupChat>> { pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<GroupChat>> {
let sql = format!("SELECT id, height, 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 sql = format!("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_ok, is_need_agree, is_closed, key, datetime FROM groups WHERE gcd = '{}' AND is_deleted = false", gid.to_hex());
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
let values = matrix.pop().unwrap(); // safe unwrap() let values = matrix.pop().unwrap(); // safe unwrap()
@ -316,7 +286,7 @@ impl GroupChat {
} }
pub fn get_id(db: &DStorage, id: &i64) -> Result<Option<GroupChat>> { pub fn get_id(db: &DStorage, id: &i64) -> Result<Option<GroupChat>> {
let sql = format!("SELECT id, height, 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 id = {} AND is_deleted = false", id); let sql = format!("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_ok, is_need_agree, is_closed, key, datetime FROM groups WHERE id = {} AND is_deleted = false", id);
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
let values = matrix.pop().unwrap(); // safe unwrap() let values = matrix.pop().unwrap(); // safe unwrap()
@ -326,7 +296,7 @@ impl GroupChat {
} }
pub fn insert(&mut self, db: &DStorage) -> Result<()> { pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO groups (height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime, is_deleted) VALUES ({}, '{}', '{}', {}, '{}', '{}', '{}', {}, {}, {}, {}, '{}', {}, '{}', {}, {}, false)", let sql = format!("INSERT INTO groups (height, owner, gcd, gtype, addr, name, bio, is_ok, is_need_agree, is_closed, key, datetime, is_deleted) VALUES ({}, '{}', '{}', {}, '{}', '{}', '{}', {}, {}, {}, '{}', {}, false)",
self.height, self.height,
self.owner.to_hex(), self.owner.to_hex(),
self.g_id.to_hex(), self.g_id.to_hex(),
@ -334,14 +304,10 @@ impl GroupChat {
self.g_addr.to_hex(), self.g_addr.to_hex(),
self.g_name, self.g_name,
self.g_bio, self.g_bio,
if self.is_top { 1 } else { 0 },
if self.is_ok { 1 } else { 0 }, if self.is_ok { 1 } else { 0 },
if self.is_need_agree { 1 } else { 0 }, if self.is_need_agree { 1 } else { 0 },
if self.is_closed { 1 } else { 0 }, if self.is_closed { 1 } else { 0 },
self.key.to_hex(), self.key.to_hex(),
self.last_datetime,
self.last_content,
if self.last_readed { 1 } else { 0 },
self.datetime, self.datetime,
); );
let id = db.insert(&sql)?; let id = db.insert(&sql)?;
@ -359,29 +325,6 @@ impl GroupChat {
let sql = format!("UPDATE groups SET height={} WHERE id = {}", height, id,); let sql = format!("UPDATE groups SET height={} WHERE id = {}", height, id,);
db.update(&sql) db.update(&sql)
} }
pub fn update_last_message(
db: &DStorage,
id: i64,
msg: &Message,
read: bool,
height: i64,
) -> Result<usize> {
let sql = format!(
"UPDATE groups SET height={}, last_datetime={}, last_content='{}', last_readed={} WHERE id = {}",
height,
msg.datetime,
msg.content,
if read { 1 } else { 0 },
id,
);
db.update(&sql)
}
pub fn readed(db: &DStorage, id: i64) -> Result<usize> {
let sql = format!("UPDATE groups SET last_readed=1 WHERE id = {}", id);
db.update(&sql)
}
} }
/// Group Join Request model. include my requests and other requests. /// Group Join Request model. include my requests and other requests.
@ -397,6 +340,7 @@ pub(crate) struct Request {
is_ok: bool, is_ok: bool,
is_over: bool, is_over: bool,
datetime: i64, datetime: i64,
is_deleted: bool,
} }
impl Request { impl Request {
@ -418,6 +362,7 @@ impl Request {
key: GroupChatKey(vec![]), key: GroupChatKey(vec![]),
is_ok: false, is_ok: false,
is_over: false, is_over: false,
is_deleted: false,
id: 0, id: 0,
} }
} }
@ -444,6 +389,7 @@ impl Request {
key, key,
is_ok: false, is_ok: false,
is_over: false, is_over: false,
is_deleted: false,
fid: 0, fid: 0,
id: 0, id: 0,
} }
@ -463,6 +409,42 @@ impl Request {
]) ])
} }
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,
key: GroupChatKey(vec![]),
datetime: v.pop().unwrap().as_i64(),
is_over: v.pop().unwrap().as_bool(),
is_ok: v.pop().unwrap().as_bool(),
remark: v.pop().unwrap().as_string(),
name: v.pop().unwrap().as_string(),
addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()),
gid: 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 list(db: &DStorage, is_all: bool) -> Result<Vec<Request>> {
let sql = if is_all {
format!("SELECT id, fid, gid, addr, name, remark, is_ok, is_over, datetime FROM requests WHERE is_deleted = false")
} else {
format!("SELECT id, fid, gid, addr, name, remark, is_ok, is_over, datetime FROM requests WHERE is_deleted = false AND is_over = 0")
};
let matrix = db.query(&sql)?;
let mut requests = vec![];
for values in matrix {
requests.push(Request::from_values(values, false));
}
Ok(requests)
}
pub fn insert(&mut self, db: &DStorage) -> Result<()> { pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO requests (fid, gid, addr, name, remark, key, 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.fid,
@ -826,6 +808,8 @@ pub(super) fn from_network_message(
let mut msg = Message::new_with_time(height, gdid, mdid, is_me, m_type, raw, datetime); let mut msg = Message::new_with_time(height, gdid, mdid, is_me, m_type, raw, datetime);
msg.insert(&db)?; msg.insert(&db)?;
GroupChat::update_last_message(&db, gdid, &msg, false, height)?;
// TODO SESSION UPDATE.
Ok(msg) Ok(msg)
} }

44
src/apps/group_chat/rpc.rs

@ -2,7 +2,7 @@ use std::sync::Arc;
use tdn::types::{ use tdn::types::{
group::GroupId, group::GroupId,
message::SendType, message::SendType,
primitive::{new_io_error, HandleResult, PeerAddr}, primitive::{HandleResult, PeerAddr},
rpc::{json, rpc_response, RpcHandler, RpcParam}, rpc::{json, rpc_response, RpcHandler, RpcParam},
}; };
use tdn_did::Proof; use tdn_did::Proof;
@ -97,6 +97,16 @@ fn group_list(groups: Vec<GroupChat>) -> RpcParam {
json!(results) json!(results)
} }
#[inline]
fn request_list(requests: Vec<Request>) -> RpcParam {
let mut results = vec![];
for request in requests {
results.push(request.to_rpc());
}
json!(results)
}
#[inline] #[inline]
fn detail_list(members: Vec<Member>, messages: Vec<Message>) -> RpcParam { fn detail_list(members: Vec<Member>, messages: Vec<Message>) -> RpcParam {
let mut member_results = vec![]; let mut member_results = vec![];
@ -122,16 +132,17 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let layer_lock = state.layer.read().await; let layer_lock = state.layer.read().await;
let db = group_chat_db(&layer_lock.base, &gid)?; let db = group_chat_db(&layer_lock.base, &gid)?;
let mut groups = GroupChat::all(&db)?; Ok(HandleResult::rpc(group_list(GroupChat::all(&db)?)))
drop(db); },
);
let gids: Vec<&GroupId> = groups.iter().map(|g| &g.g_id).collect();
let onlines = layer_lock.merge_online(&gid, gids)?;
for (index, online) in onlines.iter().enumerate() {
groups[index].online = *online;
}
Ok(HandleResult::rpc(group_list(groups))) handler.add_method(
"group-chat-request-list",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let is_all = params[0].as_bool()?;
let layer_lock = state.layer.read().await;
let db = group_chat_db(&layer_lock.base, &gid)?;
Ok(HandleResult::rpc(request_list(Request::list(&db, is_all)?)))
}, },
); );
@ -266,17 +277,4 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
Ok(results) Ok(results)
}, },
); );
handler.add_method(
"group-chat-readed",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let fid = params[0].as_i64()?;
let db = group_chat_db(state.layer.read().await.base(), &gid)?;
GroupChat::readed(&db, fid)?;
drop(db);
Ok(HandleResult::new())
},
);
} }

1
src/daemon.rs

@ -14,6 +14,7 @@ mod migrate;
mod primitives; mod primitives;
mod rpc; mod rpc;
mod server; mod server;
mod session;
mod storage; mod storage;
mod utils; mod utils;

83
src/event.rs

@ -26,7 +26,7 @@ use crate::apps::chat::{Friend, Message, NetworkMessage, Request};
use crate::apps::file::{FileId, FileType}; use crate::apps::file::{FileId, FileType};
use crate::rpc; use crate::rpc;
use crate::storage::{ use crate::storage::{
account_db, consensus_db, delete_avatar_sync, read_avatar_sync, session_db, write_avatar_sync, account_db, chat_db, consensus_db, delete_avatar_sync, read_avatar_sync, write_avatar_sync,
}; };
/// Event that will update data. /// Event that will update data.
@ -46,8 +46,8 @@ pub(crate) enum InnerEvent {
/// params: f_gid, addr, name, avatar. (TODO addrs as list to store. keep others device) /// params: f_gid, addr, name, avatar. (TODO addrs as list to store. keep others device)
SessionFriendInfo(GroupId, PeerAddr, String, Vec<u8>), SessionFriendInfo(GroupId, PeerAddr, String, Vec<u8>),
/// Session's friend update by me. /// Session's friend update by me.
/// params: f_gid, is_top, remark /// params: f_gid, remark
SessionFriendUpdate(GroupId, bool, String), SessionFriendUpdate(GroupId, String),
/// Session's friend close. /// Session's friend close.
/// params: f_gid. /// params: f_gid.
SessionFriendClose(GroupId), SessionFriendClose(GroupId),
@ -100,7 +100,7 @@ pub(crate) enum SyncEvent {
bool, bool,
), ),
RequestHad(EventId, GroupId), RequestHad(EventId, GroupId),
/// eid, friend_gid, addr, name, avatar, remark, is_top, is_closed, is_deleted /// eid, friend_gid, addr, name, avatar, remark, is_closed, is_deleted
Friend( Friend(
EventId, EventId,
GroupId, GroupId,
@ -110,7 +110,6 @@ pub(crate) enum SyncEvent {
String, String,
bool, bool,
bool, bool,
bool,
), ),
FriendHad(EventId, GroupId), FriendHad(EventId, GroupId),
/// eid, friend_gid, msg_id, is_me, message. /// eid, friend_gid, msg_id, is_me, message.
@ -250,7 +249,7 @@ impl InnerEvent {
(ACCOUNT_TABLE_PATH, 0) (ACCOUNT_TABLE_PATH, 0)
} }
InnerEvent::SessionRequestCreate(is_me, remote, remark) => { InnerEvent::SessionRequestCreate(is_me, remote, remark) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
// check if exist request. // check if exist request.
if Friend::get(&db, &remote.id)?.is_some() { if Friend::get(&db, &remote.id)?.is_some() {
return Ok(()); return Ok(());
@ -270,7 +269,7 @@ impl InnerEvent {
(REQUEST_TABLE_PATH, request.id) (REQUEST_TABLE_PATH, request.id)
} }
InnerEvent::SessionRequestHandle(rgid, is_ok, avatar) => { InnerEvent::SessionRequestHandle(rgid, is_ok, avatar) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if Friend::get(&db, &rgid)?.is_some() { if Friend::get(&db, &rgid)?.is_some() {
return Ok(()); return Ok(());
} }
@ -297,7 +296,7 @@ impl InnerEvent {
} }
} }
InnerEvent::SessionRequestDelete(rgid) => { InnerEvent::SessionRequestDelete(rgid) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(request) = Request::get(&db, &rgid)? { if let Some(request) = Request::get(&db, &rgid)? {
let rid = request.id; let rid = request.id;
request.delete(&db)?; request.delete(&db)?;
@ -312,7 +311,7 @@ impl InnerEvent {
} }
} }
InnerEvent::SessionMessageCreate(rgid, is_me, hash, m) => { InnerEvent::SessionMessageCreate(rgid, is_me, hash, m) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if Message::exist(&db, &hash)? { if Message::exist(&db, &hash)? {
return Ok(()); return Ok(());
} }
@ -342,7 +341,7 @@ impl InnerEvent {
} }
} }
InnerEvent::SessionMessageDelete(hash) => { InnerEvent::SessionMessageDelete(hash) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(m) = Message::get_it(&db, &hash)? { if let Some(m) = Message::get_it(&db, &hash)? {
m.delete(&db)?; m.delete(&db)?;
results.rpcs.push(chat_rpc::message_delete(gid, m.id)); results.rpcs.push(chat_rpc::message_delete(gid, m.id));
@ -352,7 +351,7 @@ impl InnerEvent {
} }
} }
InnerEvent::SessionFriendInfo(rgid, raddr, rname, ravatar) => { InnerEvent::SessionFriendInfo(rgid, raddr, rname, ravatar) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(mut f) = Friend::get_it(&db, &rgid)? { if let Some(mut f) = Friend::get_it(&db, &rgid)? {
f.addr = raddr; f.addr = raddr;
f.name = rname; f.name = rname;
@ -366,22 +365,21 @@ impl InnerEvent {
return Ok(()); return Ok(());
} }
} }
InnerEvent::SessionFriendUpdate(rgid, is_top, remark) => { InnerEvent::SessionFriendUpdate(rgid, remark) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(mut f) = Friend::get_it(&db, &rgid)? { if let Some(mut f) = Friend::get_it(&db, &rgid)? {
f.is_top = is_top;
f.remark = remark; f.remark = remark;
f.me_update(&db)?; f.me_update(&db)?;
results results
.rpcs .rpcs
.push(chat_rpc::friend_update(gid, f.id, is_top, &f.remark)); .push(chat_rpc::friend_update(gid, f.id, &f.remark));
(FRIEND_TABLE_PATH, f.id) (FRIEND_TABLE_PATH, f.id)
} else { } else {
return Ok(()); return Ok(());
} }
} }
InnerEvent::SessionFriendClose(rgid) => { InnerEvent::SessionFriendClose(rgid) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(f) = Friend::get_it(&db, &rgid)? { if let Some(f) = Friend::get_it(&db, &rgid)? {
f.close(&db)?; f.close(&db)?;
results.rpcs.push(chat_rpc::friend_close(gid, f.id)); results.rpcs.push(chat_rpc::friend_close(gid, f.id));
@ -410,7 +408,7 @@ impl InnerEvent {
} }
} }
InnerEvent::SessionFriendDelete(rgid) => { InnerEvent::SessionFriendDelete(rgid) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(f) = Friend::get_it(&db, &rgid)? { if let Some(f) = Friend::get_it(&db, &rgid)? {
f.delete(&db)?; f.delete(&db)?;
results.rpcs.push(chat_rpc::friend_delete(gid, f.id)); results.rpcs.push(chat_rpc::friend_delete(gid, f.id));
@ -487,7 +485,7 @@ impl StatusEvent {
) -> Result<()> { ) -> Result<()> {
match self { match self {
StatusEvent::SessionFriendOnline(rgid) => { StatusEvent::SessionFriendOnline(rgid) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(f) = Friend::get_it(&db, &rgid)? { if let Some(f) = Friend::get_it(&db, &rgid)? {
results results
.rpcs .rpcs
@ -505,7 +503,7 @@ impl StatusEvent {
} }
} }
StatusEvent::SessionFriendOffline(rgid) => { StatusEvent::SessionFriendOffline(rgid) => {
let db = session_db(group.base(), &gid)?; let db = chat_db(group.base(), &gid)?;
if let Some(f) = Friend::get_it(&db, &rgid)? { if let Some(f) = Friend::get_it(&db, &rgid)? {
let layer_lock = layer.clone(); let layer_lock = layer.clone();
let rgid = f.gid; let rgid = f.gid;
@ -574,7 +572,7 @@ impl SyncEvent {
events.push(SyncEvent::Account(hash, name, avatar)); events.push(SyncEvent::Account(hash, name, avatar));
} }
REQUEST_TABLE_PATH => { REQUEST_TABLE_PATH => {
let db = session_db(base, gid)?; let db = chat_db(base, gid)?;
let event = if let Some(request) = Request::get_id(&db, row)? { let event = if let Some(request) = Request::get_id(&db, row)? {
if pre_keys.contains(&(path, row)) { if pre_keys.contains(&(path, row)) {
events.push(SyncEvent::RequestHad(hash, request.gid)); events.push(SyncEvent::RequestHad(hash, request.gid));
@ -609,7 +607,7 @@ impl SyncEvent {
events.push(event); events.push(event);
} }
FRIEND_TABLE_PATH => { FRIEND_TABLE_PATH => {
let db = session_db(base, gid)?; let db = chat_db(base, gid)?;
let event = if let Some(friend) = Friend::get_id(&db, row)? { let event = if let Some(friend) = Friend::get_id(&db, row)? {
if pre_keys.contains(&(path, row)) { if pre_keys.contains(&(path, row)) {
events.push(SyncEvent::FriendHad(hash, friend.gid)); events.push(SyncEvent::FriendHad(hash, friend.gid));
@ -631,7 +629,6 @@ impl SyncEvent {
friend.name, friend.name,
avatar, avatar,
friend.remark, friend.remark,
friend.is_top,
friend.is_closed, friend.is_closed,
friend.is_deleted, friend.is_deleted,
) )
@ -642,7 +639,7 @@ impl SyncEvent {
events.push(event); events.push(event);
} }
MESSAGE_TABLE_PATH => { MESSAGE_TABLE_PATH => {
let db = session_db(base, gid)?; let db = chat_db(base, gid)?;
let event = if let Some(msg) = Message::get_id(&db, row)? { let event = if let Some(msg) = Message::get_id(&db, row)? {
let fgid = if let Some(f) = Friend::get_id(&db, msg.fid)? { let fgid = if let Some(f) = Friend::get_id(&db, msg.fid)? {
f.gid f.gid
@ -746,11 +743,11 @@ impl SyncEvent {
is_over, is_over,
is_delete, is_delete,
) => { ) => {
let session_db = session_db(&base, &gid)?; let chat_db = chat_db(&base, &gid)?;
let request = if let Some(mut req) = Request::get(&session_db, &rgid)? { let request = if let Some(mut req) = Request::get(&chat_db, &rgid)? {
if is_delete { if is_delete {
req.is_deleted = true; req.is_deleted = true;
if Friend::get(&session_db, &rgid)?.is_none() { if Friend::get(&chat_db, &rgid)?.is_none() {
delete_avatar_sync(&base, &gid, &rgid)?; delete_avatar_sync(&base, &gid, &rgid)?;
} }
results.rpcs.push(chat_rpc::request_delete(gid, req.id)); results.rpcs.push(chat_rpc::request_delete(gid, req.id));
@ -758,7 +755,7 @@ impl SyncEvent {
req.is_ok = is_ok; req.is_ok = is_ok;
req.is_over = is_over; req.is_over = is_over;
req.update(&session_db)?; req.update(&chat_db)?;
req req
} else { } else {
let mut request = Request::new(rgid, raddr, rname, remark, is_me, true); let mut request = Request::new(rgid, raddr, rname, remark, is_me, true);
@ -767,12 +764,12 @@ impl SyncEvent {
request.is_deleted = is_delete; request.is_deleted = is_delete;
// save to db. // save to db.
request.insert(&session_db)?; request.insert(&chat_db)?;
let rid = request.id; let rid = request.id;
results.rpcs.push(chat_rpc::request_create(gid, &request)); results.rpcs.push(chat_rpc::request_create(gid, &request));
if is_delete { if is_delete {
if Friend::get(&session_db, &rgid)?.is_none() { if Friend::get(&chat_db, &rgid)?.is_none() {
delete_avatar_sync(&base, &gid, &rgid)?; delete_avatar_sync(&base, &gid, &rgid)?;
} }
results.rpcs.push(chat_rpc::request_delete(gid, rid)); results.rpcs.push(chat_rpc::request_delete(gid, rid));
@ -786,20 +783,20 @@ impl SyncEvent {
if avatar.len() > 0 { if avatar.len() > 0 {
write_avatar_sync(&base, &gid, &request.gid, avatar)?; write_avatar_sync(&base, &gid, &request.gid, avatar)?;
} }
let friend = Friend::from_request(&session_db, request)?; let friend = Friend::from_request(&chat_db, request)?;
results results
.rpcs .rpcs
.push(chat_rpc::request_agree(gid, rid, &friend)); .push(chat_rpc::request_agree(gid, rid, &friend));
} else { } else {
results.rpcs.push(chat_rpc::request_reject(gid, rid)); results.rpcs.push(chat_rpc::request_reject(gid, rid));
} }
session_db.close()?; chat_db.close()?;
(eid, REQUEST_TABLE_PATH, rid) (eid, REQUEST_TABLE_PATH, rid)
} }
SyncEvent::RequestHad(eid, rgid) => { SyncEvent::RequestHad(eid, rgid) => {
let session_db = session_db(&base, &gid)?; let chat_db = chat_db(&base, &gid)?;
let id = if let Some(req) = Request::get(&session_db, &rgid)? { let id = if let Some(req) = Request::get(&chat_db, &rgid)? {
req.id req.id
} else { } else {
-1 -1
@ -813,19 +810,17 @@ impl SyncEvent {
fname, fname,
avatar, avatar,
remark, remark,
is_top,
is_closed, is_closed,
is_deleted, is_deleted,
) => { ) => {
let session_db = session_db(&base, &gid)?; let chat_db = chat_db(&base, &gid)?;
let id = if let Some(mut friend) = Friend::get(&session_db, &fgid)? { let id = if let Some(mut friend) = Friend::get(&chat_db, &fgid)? {
friend.addr = faddr; friend.addr = faddr;
friend.name = fname; friend.name = fname;
friend.remark = remark; friend.remark = remark;
friend.is_top = is_top;
friend.is_closed = is_closed; friend.is_closed = is_closed;
friend.is_deleted = is_deleted; friend.is_deleted = is_deleted;
friend.update(&session_db)?; friend.update(&chat_db)?;
if !is_deleted && avatar.len() > 0 { if !is_deleted && avatar.len() > 0 {
write_avatar_sync(&base, &gid, &friend.gid, avatar)?; write_avatar_sync(&base, &gid, &friend.gid, avatar)?;
@ -865,8 +860,8 @@ impl SyncEvent {
(eid, FRIEND_TABLE_PATH, id) (eid, FRIEND_TABLE_PATH, id)
} }
SyncEvent::FriendHad(eid, fgid) => { SyncEvent::FriendHad(eid, fgid) => {
let session_db = session_db(&base, &gid)?; let chat_db = chat_db(&base, &gid)?;
let id = if let Some(friend) = Friend::get(&session_db, &fgid)? { let id = if let Some(friend) = Friend::get(&chat_db, &fgid)? {
friend.id friend.id
} else { } else {
-1 -1
@ -874,13 +869,13 @@ impl SyncEvent {
(eid, FRIEND_TABLE_PATH, id) (eid, FRIEND_TABLE_PATH, id)
} }
SyncEvent::Message(eid, fgid, meid, is_me, m) => { SyncEvent::Message(eid, fgid, meid, is_me, m) => {
let session_db = session_db(&base, &gid)?; let chat_db = chat_db(&base, &gid)?;
if Message::exist(&session_db, &meid)? { if Message::exist(&chat_db, &meid)? {
continue; continue;
} }
let id = if let Some(f) = Friend::get_it(&session_db, &fgid)? { let id = if let Some(f) = Friend::get_it(&chat_db, &fgid)? {
let msg = m.handle(is_me, gid, &base, &session_db, f.id, eid)?; let msg = m.handle(is_me, gid, &base, &chat_db, f.id, eid)?;
results.rpcs.push(chat_rpc::message_create(gid, &msg)); results.rpcs.push(chat_rpc::message_create(gid, &msg));
msg.id msg.id
} else { } else {

35
src/layer.rs

@ -10,12 +10,12 @@ use tdn::{
primitive::{new_io_error, PeerAddr, Result}, primitive::{new_io_error, PeerAddr, Result},
}, },
}; };
use tdn_did::user::User;
use crate::apps::chat::{chat_conn, Friend}; use crate::apps::chat::chat_conn;
use crate::apps::group_chat::{group_chat_conn, GroupChat, GROUP_ID}; use crate::apps::group_chat::{group_chat_conn, GROUP_ID};
use crate::group::Group; use crate::group::Group;
use crate::storage::{group_chat_db, session_db, write_avatar_sync}; use crate::session::{Session, SessionType};
use crate::storage::session_db;
/// ESSE app's BaseLayerEvent. /// ESSE app's BaseLayerEvent.
/// EVERY LAYER APP MUST EQUAL THE FIRST THREE FIELDS. /// EVERY LAYER APP MUST EQUAL THE FIRST THREE FIELDS.
@ -123,22 +123,23 @@ impl Layer {
for mgid in self.runnings.keys() { for mgid in self.runnings.keys() {
let mut vecs = vec![]; let mut vecs = vec![];
// load friend chat. let db = session_db(&self.base, &mgid)?;
let db = session_db(&self.base, mgid)?; let sessions = Session::list(&db)?;
for friend in Friend::all_ok(&db)? {
let proof = group_lock.prove_addr(mgid, &friend.addr).unwrap();
vecs.push((friend.gid, chat_conn(proof, friend.addr)));
}
drop(db); drop(db);
// load group chat. for s in sessions {
let db = group_chat_db(&self.base, mgid)?; match s.s_type {
let groups = GroupChat::all_ok(&db)?; SessionType::Chat => {
for g in groups { let proof = group_lock.prove_addr(mgid, &s.addr)?;
let proof = group_lock.prove_addr(mgid, &g.g_addr)?; vecs.push((s.gid, chat_conn(proof, s.addr)));
vecs.push((GROUP_ID, group_chat_conn(proof, g.g_addr, g.g_id))); }
SessionType::Group => {
let proof = group_lock.prove_addr(mgid, &s.addr)?;
vecs.push((GROUP_ID, group_chat_conn(proof, s.addr, s.gid)));
}
_ => {}
}
} }
drop(db);
conns.insert(*mgid, vecs); conns.insert(*mgid, vecs);
} }

1
src/lib.rs

@ -15,6 +15,7 @@ mod migrate;
mod primitives; mod primitives;
mod rpc; mod rpc;
mod server; mod server;
mod session;
mod storage; mod storage;
mod utils; mod utils;

20
src/migrate.rs

@ -4,12 +4,14 @@ use tdn_storage::local::DStorage;
pub mod consensus; pub mod consensus;
mod account; mod account;
mod chat;
mod file; mod file;
mod group_chat; mod group_chat;
mod service; mod service;
mod session; mod session;
use account::ACCOUNT_VERSIONS; use account::ACCOUNT_VERSIONS;
use chat::CHAT_VERSIONS;
use consensus::CONSENSUS_VERSIONS; use consensus::CONSENSUS_VERSIONS;
use file::FILE_VERSIONS; use file::FILE_VERSIONS;
use group_chat::GROUP_CHAT_VERSIONS; use group_chat::GROUP_CHAT_VERSIONS;
@ -27,6 +29,9 @@ pub(crate) const CONSENSUS_DB: &'static str = "consensus.db";
/// Account's session database name /// Account's session database name
pub(crate) const SESSION_DB: &'static str = "session.db"; pub(crate) const SESSION_DB: &'static str = "session.db";
/// Account's chat database name
pub(crate) const CHAT_DB: &'static str = "chat.db";
/// Account's consensus database name /// Account's consensus database name
pub(crate) const FILE_DB: &'static str = "file.db"; pub(crate) const FILE_DB: &'static str = "file.db";
@ -105,6 +110,7 @@ pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> {
SERVICE_DB => SERVICE_VERSIONS.as_ref(), SERVICE_DB => SERVICE_VERSIONS.as_ref(),
ASSISTANT_DB => ASSISTANT_VERSIONS.as_ref(), ASSISTANT_DB => ASSISTANT_VERSIONS.as_ref(),
GROUP_CHAT_DB => GROUP_CHAT_VERSIONS.as_ref(), GROUP_CHAT_DB => GROUP_CHAT_VERSIONS.as_ref(),
CHAT_DB => CHAT_VERSIONS.as_ref(),
_ => { _ => {
continue; continue;
} }
@ -182,6 +188,12 @@ pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> {
GROUP_CHAT_DB, GROUP_CHAT_DB,
))?; ))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
CHAT_VERSIONS.len(),
CHAT_DB,
))?;
db.close()?; db.close()?;
} }
@ -235,5 +247,13 @@ pub(crate) fn account_init_migrate(path: &PathBuf) -> std::io::Result<()> {
for i in &GROUP_CHAT_VERSIONS { for i in &GROUP_CHAT_VERSIONS {
db.execute(i)?; db.execute(i)?;
} }
db.close()?;
let mut db_path = path.clone();
db_path.push(CHAT_DB);
let db = DStorage::open(db_path)?;
for i in &CHAT_VERSIONS {
db.execute(i)?;
}
db.close() db.close()
} }

11
src/migrate/account.rs

@ -1,5 +1,5 @@
#[rustfmt::skip] #[rustfmt::skip]
pub(super) const ACCOUNT_VERSIONS: [&str; 8] = [ pub(super) const ACCOUNT_VERSIONS: [&str; 9] = [
"CREATE TABLE IF NOT EXISTS accounts( "CREATE TABLE IF NOT EXISTS accounts(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL, gid TEXT NOT NULL,
@ -14,10 +14,11 @@ pub(super) const ACCOUNT_VERSIONS: [&str; 8] = [
"CREATE TABLE IF NOT EXISTS migrates( "CREATE TABLE IF NOT EXISTS migrates(
db_name TEXT NOT NULL, db_name TEXT NOT NULL,
version INTEGER NOT NULL);", version INTEGER NOT NULL);",
"INSERT INTO migrates (db_name, version) values ('account.db', 1)", "INSERT INTO migrates (db_name, version) values ('account.db', 0)",
"INSERT INTO migrates (db_name, version) values ('consensus.db', 9)", "INSERT INTO migrates (db_name, version) values ('consensus.db', 0)",
"INSERT INTO migrates (db_name, version) values ('session.db', 3)", "INSERT INTO migrates (db_name, version) values ('session.db', 0)",
"INSERT INTO migrates (db_name, version) values ('file.db', 1)", "INSERT INTO migrates (db_name, version) values ('file.db', 0)",
"INSERT INTO migrates (db_name, version) values ('assistant.db', 0)", "INSERT INTO migrates (db_name, version) values ('assistant.db', 0)",
"INSERT INTO migrates (db_name, version) values ('group_chat.db', 0)", "INSERT INTO migrates (db_name, version) values ('group_chat.db', 0)",
"INSERT INTO migrates (db_name, version) values ('chat.db', 0)",
]; ];

34
src/migrate/chat.rs

@ -0,0 +1,34 @@
#[rustfmt::skip]
pub(super) const CHAT_VERSIONS: [&str; 3] = [
"CREATE TABLE IF NOT EXISTS friends(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
addr TEXT NOT NULL,
name TEXT NOT NULL,
remark TEXT,
is_closed INTEGER NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
"CREATE TABLE IF NOT EXISTS requests(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
addr TEXT NOT NULL,
name TEXT,
remark TEXT,
is_me INTEGER NOT NULL,
is_ok INTEGER NOT NULL,
is_over INTEGER NOT NULL,
is_delivery 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,
hash TEXT NOT NULL,
fid INTEGER NOT NULL,
is_me INTEGER NOT NULL,
m_type INTEGER NOT NULL,
content TEXT NOT NULL,
is_delivery INTEGER NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
];

4
src/migrate/group_chat.rs

@ -9,14 +9,10 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [
addr TEXT NOT NULL, addr TEXT NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
bio TEXT NOT NULL, bio TEXT NOT NULL,
is_top INTEGER NOT NULL,
is_ok INTEGER NOT NULL, is_ok INTEGER NOT NULL,
is_need_agree INTEGER NOT NULL, is_need_agree INTEGER NOT NULL,
is_closed INTEGER NOT NULL, is_closed INTEGER NOT NULL,
key TEXT NOT NULL, key TEXT NOT NULL,
last_datetime INTEGER,
last_content TEXT,
last_readed INTEGER,
datetime INTEGER NOT NULL, datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);", is_deleted INTEGER NOT NULL);",
"CREATE TABLE IF NOT EXISTS requests( "CREATE TABLE IF NOT EXISTS requests(

37
src/migrate/session.rs

@ -1,37 +1,16 @@
#[rustfmt::skip] #[rustfmt::skip]
pub(super) const SESSION_VERSIONS: [&str; 3] = [ pub(super) const SESSION_VERSIONS: [&str; 3] = [
"CREATE TABLE IF NOT EXISTS friends( "CREATE TABLE IF NOT EXISTS sessions(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
fid INTEGER NOT NULL,
gid TEXT NOT NULL, gid TEXT NOT NULL,
addr TEXT NOT NULL, addr TEXT NOT NULL,
s_type INTEGER NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
remark TEXT,
is_top INTEGER NOT NULL, is_top INTEGER NOT NULL,
is_closed INTEGER NOT NULL, last_datetime INTEGER,
last_message_datetime INTEGER, last_content TEXT,
last_message_content TEXT, last_readed INTEGER);",
last_message_readed INTEGER, "INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed) VALUES (0, '', '', 4, '', 0, 0, '', 1);", // Assistant.
is_deleted INTEGER NOT NULL);", "INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed) VALUES (0, '', '', 2, '', 0, 0, '', 1);", // File.
"CREATE TABLE IF NOT EXISTS requests(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
addr TEXT NOT NULL,
name TEXT,
remark TEXT,
is_me INTEGER NOT NULL,
is_ok INTEGER NOT NULL,
is_over INTEGER NOT NULL,
is_delivery 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,
hash TEXT NOT NULL,
fid INTEGER NOT NULL,
is_me INTEGER NOT NULL,
m_type INTEGER NOT NULL,
content TEXT NOT NULL,
is_delivery INTEGER NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
]; ];

95
src/rpc.rs

@ -13,12 +13,13 @@ use tdn::{
}; };
use crate::apps::app_rpc_inject; use crate::apps::app_rpc_inject;
use crate::apps::chat::{chat_conn, Friend}; use crate::apps::chat::chat_conn;
use crate::apps::group_chat::{add_layer, group_chat_conn, GroupChat}; use crate::apps::group_chat::{add_layer, group_chat_conn};
use crate::event::InnerEvent; use crate::event::InnerEvent;
use crate::group::Group; use crate::group::Group;
use crate::layer::{Layer, LayerEvent}; use crate::layer::{Layer, LayerEvent};
use crate::storage::{group_chat_db, session_db}; use crate::session::{Session, SessionType};
use crate::storage::session_db;
pub(crate) fn init_rpc( pub(crate) fn init_rpc(
addr: PeerAddr, addr: PeerAddr,
@ -73,6 +74,48 @@ pub(crate) fn account_update(mgid: GroupId, name: &str, avatar: String) -> RpcPa
) )
} }
#[inline]
pub(crate) fn session_create(mgid: GroupId, session: &Session) -> RpcParam {
rpc_response(0, "session-create", session.to_rpc(), mgid)
}
#[inline]
pub(crate) fn session_last(
mgid: GroupId,
id: &i64,
time: &i64,
content: &str,
readed: bool,
) -> RpcParam {
rpc_response(0, "session-last", json!([id, time, content, readed]), mgid)
}
#[inline]
pub(crate) fn session_update(
mgid: GroupId,
id: &i64,
addr: &PeerAddr,
name: &str,
is_top: bool,
online: bool,
) -> RpcParam {
rpc_response(
0,
"session-update",
json!([id, addr.to_hex(), name, is_top, online]),
mgid,
)
}
#[inline]
fn session_list(sessions: Vec<Session>) -> RpcParam {
let mut results = vec![];
for session in sessions {
results.push(session.to_rpc());
}
json!(results)
}
#[inline] #[inline]
pub(crate) async fn sleep_waiting_close_stable( pub(crate) async fn sleep_waiting_close_stable(
sender: Sender<SendMessage>, sender: Sender<SendMessage>,
@ -364,27 +407,29 @@ fn new_rpc_handler(
let group_lock = state.group.read().await; let group_lock = state.group.read().await;
let db = session_db(group_lock.base(), &gid)?; let db = session_db(group_lock.base(), &gid)?;
let friends = Friend::all_ok(&db)?; let sessions = Session::list(&db)?;
drop(db); drop(db);
for f in friends {
let proof = group_lock.prove_addr(&gid, &f.addr)?;
results.layers.push((gid, f.gid, chat_conn(proof, f.addr)));
}
let db = group_chat_db(group_lock.base(), &gid)?; for s in sessions {
let groups = GroupChat::all_ok(&db)?; match s.s_type {
for g in groups { SessionType::Chat => {
let proof = group_lock.prove_addr(&gid, &g.g_addr)?; let proof = group_lock.prove_addr(&gid, &s.addr)?;
add_layer(&mut results, gid, group_chat_conn(proof, g.g_addr, g.g_id)); results.layers.push((gid, s.gid, chat_conn(proof, s.addr)));
}
SessionType::Group => {
let proof = group_lock.prove_addr(&gid, &s.addr)?;
add_layer(&mut results, gid, group_chat_conn(proof, s.addr, s.gid));
}
_ => {}
}
} }
drop(db);
drop(group_lock);
let devices = state.group.read().await.distribute_conns(&gid); let devices = group_lock.distribute_conns(&gid);
for device in devices { for device in devices {
results.groups.push((gid, device)); results.groups.push((gid, device));
} }
drop(group_lock);
debug!("Account Online: {}.", gid.to_hex()); debug!("Account Online: {}.", gid.to_hex());
Ok(results) Ok(results)
@ -421,5 +466,23 @@ fn new_rpc_handler(
}, },
); );
handler.add_method(
"session-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let layer_lock = state.layer.read().await;
let db = session_db(layer_lock.base(), &gid)?;
let mut sessions = Session::list(&db)?;
drop(db);
let gids: Vec<&GroupId> = sessions.iter().map(|s| &s.gid).collect();
let onlines = layer_lock.merge_online(&gid, gids)?;
for (index, online) in onlines.iter().enumerate() {
sessions[index].online = *online;
}
Ok(HandleResult::rpc(session_list(sessions)))
},
);
handler handler
} }

171
src/session.rs

@ -0,0 +1,171 @@
use tdn::types::{
group::GroupId,
primitive::{new_io_error, PeerAddr, Result},
rpc::{json, RpcParam},
};
use tdn_storage::local::{DStorage, DsValue};
pub(crate) enum SessionType {
Chat,
Group,
Files,
Device,
Assistant,
Domain,
Service,
}
impl SessionType {
fn to_int(&self) -> i64 {
match self {
SessionType::Chat => 0,
SessionType::Group => 1,
SessionType::Files => 2,
SessionType::Device => 3,
SessionType::Assistant => 4,
SessionType::Domain => 5,
SessionType::Service => 6,
}
}
fn from_int(i: i64) -> Self {
match i {
0 => SessionType::Chat,
1 => SessionType::Group,
2 => SessionType::Files,
3 => SessionType::Device,
4 => SessionType::Assistant,
5 => SessionType::Domain,
6 => SessionType::Service,
_ => SessionType::Chat,
}
}
}
pub(crate) struct Session {
id: i64,
fid: i64,
pub gid: GroupId,
pub addr: PeerAddr,
pub s_type: SessionType,
name: String,
is_top: bool,
last_datetime: i64,
last_content: String,
last_readed: bool,
pub online: bool,
}
impl Session {
pub fn new(
fid: i64,
gid: GroupId,
addr: PeerAddr,
s_type: SessionType,
name: String,
datetime: i64,
) -> Self {
Self {
fid,
gid,
addr,
s_type,
name,
id: 0,
is_top: false,
last_datetime: datetime,
last_content: "".to_owned(),
last_readed: true,
online: false,
}
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
self.fid,
self.gid.to_hex(),
self.addr.to_hex(),
self.s_type.to_int(),
self.name,
self.is_top,
self.last_datetime,
self.last_content,
self.last_readed,
self.online
])
}
fn from_values(mut v: Vec<DsValue>) -> Self {
Self {
last_readed: v.pop().unwrap().as_bool(),
last_content: v.pop().unwrap().as_string(),
last_datetime: v.pop().unwrap().as_i64(),
is_top: v.pop().unwrap().as_bool(),
name: v.pop().unwrap().as_string(),
s_type: SessionType::from_int(v.pop().unwrap().as_i64()),
addr: PeerAddr::from_hex(v.pop().unwrap().as_str()).unwrap_or(PeerAddr::default()),
gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()),
fid: v.pop().unwrap().as_i64(),
id: v.pop().unwrap().as_i64(),
online: false,
}
}
pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!("INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed) VALUES ({}, '{}', '{}', {}, '{}', {}, {}, '{}', {})",
self.fid,
self.gid.to_hex(),
self.addr.to_hex(),
self.s_type.to_int(),
self.name,
if self.is_top { 1 } else { 0 },
self.last_datetime,
self.last_content,
if self.last_readed { 1 } else { 0 },
);
let id = db.insert(&sql)?;
self.id = id;
Ok(())
}
pub fn get(db: &DStorage, id: i64) -> Result<Session> {
let sql = format!("SELECT id, fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed FROM sessions WHERE id = {}", id);
let mut matrix = db.query(&sql)?;
if matrix.len() > 0 {
Ok(Session::from_values(matrix.pop().unwrap())) // safe unwrap()
} else {
Err(new_io_error("session missing."))
}
}
pub fn list(db: &DStorage) -> Result<Vec<Session>> {
let matrix = db.query("SELECT id, fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed FROM sessions ORDER BY last_datetime DESC")?;
let mut sessions = vec![];
for values in matrix {
sessions.push(Session::from_values(values));
}
Ok(sessions)
}
pub fn top(db: &DStorage, id: &i64, is_top: bool) -> Result<usize> {
db.update(&format!("UPDATE sessions SET is_top = 1 WHERE id = {}", id))
}
pub fn last(
db: &DStorage,
id: &i64,
datetime: &i64,
content: &str,
readed: bool,
) -> Result<usize> {
db.update(&format!("UPDATE sessions SET last_datetime = {}, last_content = '{}', last_readed = {} WHERE id = {}", datetime, content, if readed { 1 } else { 0 }, id))
}
pub fn read(db: &DStorage, id: &i64) -> Result<usize> {
db.update(&format!(
"UPDATE sessions SET last_readed = 1 WHERE id = {}",
id
))
}
}

11
src/storage.rs

@ -2,6 +2,7 @@ use async_fs as fs;
use image::{load_from_memory, DynamicImage, GenericImageView}; use image::{load_from_memory, DynamicImage, GenericImageView};
use rand::{distributions::Alphanumeric, thread_rng, Rng}; use rand::{distributions::Alphanumeric, thread_rng, Rng};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use tdn::types::{ use tdn::types::{
@ -11,7 +12,7 @@ use tdn::types::{
use tdn_storage::local::DStorage; use tdn_storage::local::DStorage;
use crate::migrate::{ use crate::migrate::{
account_init_migrate, ACCOUNT_DB, ASSISTANT_DB, CONSENSUS_DB, FILE_DB, GROUP_CHAT_DB, account_init_migrate, ACCOUNT_DB, ASSISTANT_DB, CHAT_DB, CONSENSUS_DB, FILE_DB, GROUP_CHAT_DB,
SERVICE_DB, SESSION_DB, SERVICE_DB, SESSION_DB,
}; };
@ -327,6 +328,14 @@ pub(crate) fn session_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> {
DStorage::open(db_path) DStorage::open(db_path)
} }
#[inline]
pub(crate) fn chat_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> {
let mut db_path = base.clone();
db_path.push(gid.to_hex());
db_path.push(CHAT_DB);
DStorage::open(db_path)
}
#[inline] #[inline]
pub(crate) fn _file_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> { pub(crate) fn _file_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> {
let mut db_path = base.clone(); let mut db_path = base.clone();

Loading…
Cancel
Save