From 71859fec18667c68c0f3e3dd3865b27f09ab73ab Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 25 May 2021 17:38:41 +0800 Subject: [PATCH] Add Session model for all chats and services --- lib/apps/chat/detail.dart | 6 +- lib/apps/chat/list.dart | 19 +--- lib/apps/chat/models.dart | 41 +------ lib/apps/chat/provider.dart | 43 +------- lib/apps/group_chat/add.dart | 49 ++++++--- lib/apps/group_chat/detail.dart | 7 +- lib/apps/group_chat/models.dart | 28 +---- lib/apps/group_chat/page.dart | 16 --- lib/apps/group_chat/provider.dart | 56 +++++----- lib/l10n/localizations.dart | 1 - lib/l10n/localizations_en.dart | 4 +- lib/l10n/localizations_zh.dart | 2 - lib/pages/home.dart | 63 +++++++---- lib/provider.dart | 90 ++++++++++++++- lib/session.dart | 122 ++++++++++++++++++++ lib/widgets/default_home_show.dart | 151 ++++++++++++++++++------- pubspec.lock | 18 +-- src/apps/assistant/models.rs | 4 +- src/apps/chat/layer.rs | 42 +++---- src/apps/chat/models.rs | 76 ++++--------- src/apps/chat/rpc.rs | 52 +++------ src/apps/group_chat/models.rs | 116 +++++++++---------- src/apps/group_chat/rpc.rs | 44 ++++---- src/daemon.rs | 1 + src/event.rs | 83 +++++++------- src/layer.rs | 35 +++--- src/lib.rs | 1 + src/migrate.rs | 20 ++++ src/migrate/account.rs | 11 +- src/migrate/chat.rs | 34 ++++++ src/migrate/group_chat.rs | 4 - src/migrate/session.rs | 37 ++----- src/rpc.rs | 95 +++++++++++++--- src/session.rs | 171 +++++++++++++++++++++++++++++ src/storage.rs | 11 +- 35 files changed, 967 insertions(+), 586 deletions(-) create mode 100644 lib/session.dart create mode 100644 src/migrate/chat.rs create mode 100644 src/session.rs diff --git a/lib/apps/chat/detail.dart b/lib/apps/chat/detail.dart index bb6a3f3..31ba5f9 100644 --- a/lib/apps/chat/detail.dart +++ b/lib/apps/chat/detail.dart @@ -272,10 +272,7 @@ class _ChatDetailState extends State { color: const Color(0xFFEDEDED), child: Icon(Icons.more_vert_rounded, color: color.primary), onSelected: (int value) { - if (value == 1) { - Provider.of(context, listen: false).friendUpdate( - this.friend.id, isTop: !this.friend.isTop); - } else if (value == 2) { + if (value == 2) { showShadowDialog( context, Icons.info, @@ -350,7 +347,6 @@ class _ChatDetailState extends State { }, itemBuilder: (context) { return >[ - _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.primary, 3, Icons.turned_in_rounded, lang.remark), this.friend.isClosed diff --git a/lib/apps/chat/list.dart b/lib/apps/chat/list.dart index 955d814..04803aa 100644 --- a/lib/apps/chat/list.dart +++ b/lib/apps/chat/list.dart @@ -90,29 +90,12 @@ class ListChat extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text(friend.name, maxLines: 1, overflow: TextOverflow.ellipsis, - style: 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)), + style: const TextStyle(fontSize: 16.0)), ), if (this.friend.isClosed) Container( diff --git a/lib/apps/chat/models.dart b/lib/apps/chat/models.dart index 2814444..cd434d7 100644 --- a/lib/apps/chat/models.dart +++ b/lib/apps/chat/models.dart @@ -10,64 +10,33 @@ class Friend { String name; String addr; String remark; - bool isTop; bool isClosed; - RelativeTime lastMessageTime; - String lastMessageContent; - bool lastMessageReaded; + RelativeTime time; bool online = false; // new friend from network Friend(this.gid, this.name, this.addr) { - this.isTop = 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'; return Avatar( width: width, name: this.name, avatarPath: avatar, - online: this.online, - needOnline: needOnline, - hasNew: !this.lastMessageReaded, + needOnline: false, ); } - 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) { this.id = params[0]; this.gid = params[1]; this.addr = params[2]; this.name = params[3]; this.remark = params[4]; - this.isTop = params[5] == "1"; - this.isClosed = params[6] == "1"; - this.lastMessageTime = RelativeTime.fromInt(params[7]); - this.lastMessageContent = params[8]; - this.lastMessageReaded = params[9]; - this.online = params[10] == "1"; + this.isClosed = params[5]; + this.time = RelativeTime.fromInt(params[6]); } } diff --git a/lib/apps/chat/provider.dart b/lib/apps/chat/provider.dart index 599f04e..66b54b5 100644 --- a/lib/apps/chat/provider.dart +++ b/lib/apps/chat/provider.dart @@ -18,8 +18,6 @@ class ChatProvider extends ChangeNotifier { List orderKeys = []; // ordered chat friends with last message. - Map topKeys = {}; // Set toped friends. - /// all requests. request have order. SplayTreeMap requests = SplayTreeMap(); @@ -59,7 +57,6 @@ class ChatProvider extends ChangeNotifier { this.orderKeys.clear(); this.requests.clear(); this.activedMessages.clear(); - this.topKeys.clear(); } updateActived() { @@ -73,7 +70,6 @@ class ChatProvider extends ChangeNotifier { updateActivedFriend(int id) { this.activedFriendId = id; this.activedMessages.clear(); - this.friends[id].lastMessageReaded = true; rpc.send('chat-message-list', [this.activedFriendId]); notifyListeners(); @@ -85,22 +81,9 @@ class ChatProvider extends ChangeNotifier { } /// delete a friend. - friendUpdate(int id, {String remark, bool isTop}) { - if (remark != null) { - this.friends[id].remark = 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]); + friendUpdate(int id, String remark) { + this.friends[id].remark = remark; + rpc.send('chat-friend-update', [id, remark]); notifyListeners(); } @@ -122,7 +105,6 @@ class ChatProvider extends ChangeNotifier { this.friends.remove(id); this.orderKeys.remove(id); - this.topKeys.remove(id); rpc.send('chat-friend-delete', [id]); notifyListeners(); @@ -196,9 +178,6 @@ class ChatProvider extends ChangeNotifier { final id = params[0]; this.friends[id] = Friend.fromList(params); this.orderKeys.add(id); - if (this.friends[id].isTop) { - this.topKeys[id] = this.friends[id].lastMessageTime; - } }); notifyListeners(); } @@ -225,21 +204,13 @@ class ChatProvider extends ChangeNotifier { _friendInfo(List params) { final id = params[0]; this.friends[id] = Friend.fromList(params); - if (this.friends[id].isTop) { - this.topKeys[id] = this.friends[id].lastMessageTime; - } notifyListeners(); } _friendUpdate(List params) { final id = params[0]; if (this.friends.containsKey(id)) { - this.friends[id].isTop = params[1]; this.friends[id].remark = params[2]; - - if (params[1]) { - this.topKeys[id] = this.friends[id].lastMessageTime; - } notifyListeners(); } } @@ -287,9 +258,6 @@ class ChatProvider extends ChangeNotifier { this.requests[id].overIt(true); } var friend = Friend.fromList(params[1]); - if (friend.isTop) { - this.topKeys[friend.id] = friend.lastMessageTime; - } this.friends[friend.id] = friend; orderFriends(friend.id); notifyListeners(); @@ -326,13 +294,8 @@ class ChatProvider extends ChangeNotifier { if (!msg.isDelivery) { msg.isDelivery = null; // When message create, set is is none; } - this.friends[msg.fid].updateLastMessage(msg, true); this.activedMessages[msg.id] = msg; rpc.send('chat-friend-readed', [this.activedFriendId]); - } else { - if (this.friends.containsKey(msg.fid)) { - this.friends[msg.fid].updateLastMessage(msg, false); - } } orderFriends(msg.fid); notifyListeners(); diff --git a/lib/apps/group_chat/add.dart b/lib/apps/group_chat/add.dart index b60240c..25df764 100644 --- a/lib/apps/group_chat/add.dart +++ b/lib/apps/group_chat/add.dart @@ -55,6 +55,8 @@ class _GroupAddPageState extends State { bool _addrChecked = false; String _myName = ''; + bool _requestsLoadMore = true; + // 0 => encrypted, 1 => common, 2 => open. Widget _groupTypeWidget(String text, int value, ColorScheme color) { return Row( @@ -179,7 +181,7 @@ class _GroupAddPageState extends State { setState(() {}); }); new Future.delayed(Duration.zero, () { - //context.read().requestList(); + context.read().requestList(false); _myName = context.read().activedAccount.name; }); } @@ -198,6 +200,9 @@ class _GroupAddPageState extends State { final groups = provider.groups; final createKeys = provider.createKeys; + final requests = provider.requests; + final requestKeys = requests.keys.toList().reversed.toList(); + return SafeArea( child: DefaultTabController( initialIndex: 0, @@ -263,7 +268,7 @@ class _GroupAddPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ InputText( - icon: Icons.person, + icon: Icons.groups, text: 'Group ID', controller: _joinIdController, focus: _joinIdFocus), @@ -285,18 +290,28 @@ class _GroupAddPageState extends State { const SizedBox(height: 20.0), const Divider(height: 1.0, color: Color(0x40ADB0BB)), const SizedBox(height: 10.0), - // if (requests.isNotEmpty) - // Container( - // width: 600.0, - // child: ListView.builder( - // itemCount: requestKeys.length, - // shrinkWrap: true, - // physics: ClampingScrollPhysics(), - // scrollDirection: Axis.vertical, - // itemBuilder: (BuildContext context, int index) => - // _RequestItem(request: requests[requestKeys[index]]), - // ), - // ) + if (requests.isNotEmpty) + Container( + width: 600.0, + child: ListView.builder( + itemCount: requestKeys.length, + shrinkWrap: true, + physics: ClampingScrollPhysics(), + scrollDirection: Axis.vertical, + itemBuilder: (BuildContext context, int 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 { Container( padding: EdgeInsets.symmetric(vertical: 10.0), child: InputText( - icon: Icons.person, + icon: Icons.account_box, text: 'Group Name', controller: _createNameController, focus: _createNameFocus), @@ -418,7 +433,7 @@ class _GroupAddPageState extends State { Container( padding: EdgeInsets.symmetric(vertical: 10.0), child: InputText( - icon: Icons.location_on, + icon: Icons.campaign, text: 'Group Bio', controller: _createBioController, focus: _createBioFocus), @@ -427,7 +442,7 @@ class _GroupAddPageState extends State { Container( padding: EdgeInsets.symmetric(vertical: 10.0), child: InputText( - icon: Icons.turned_in, + icon: Icons.enhanced_encryption, text: 'Encrypted Key', controller: _createKeyController, focus: _createKeyFocus), diff --git a/lib/apps/group_chat/detail.dart b/lib/apps/group_chat/detail.dart index 7d64c61..4acd7d4 100644 --- a/lib/apps/group_chat/detail.dart +++ b/lib/apps/group_chat/detail.dart @@ -214,7 +214,7 @@ class _GroupChatDetailState extends State { child: Text('Waiting...') ); } - final isOnline = this.group.online; + final isOnline = provider.online; return Column( children: [ @@ -276,9 +276,7 @@ class _GroupChatDetailState extends State { color: const Color(0xFFEDEDED), child: Icon(Icons.more_vert_rounded, color: color.primary), onSelected: (int value) { - if (value == 1) { - //Provider.of(context, listen: false).groupUpdate(this.group.id, isTop: !this.group.isTop); - } else if (value == 2) { + if (value == 2) { showShadowDialog( context, Icons.info, @@ -368,7 +366,6 @@ class _GroupChatDetailState extends State { }, itemBuilder: (context) { return >[ - _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), 3, Icons.group_rounded, lang.members), // _menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), diff --git a/lib/apps/group_chat/models.dart b/lib/apps/group_chat/models.dart index 68d854d..1a315b6 100644 --- a/lib/apps/group_chat/models.dart +++ b/lib/apps/group_chat/models.dart @@ -89,14 +89,9 @@ class GroupChat { String addr; String name; String bio; - bool isTop; bool isOk; bool isClosed; bool isNeedAgree; - RelativeTime lastTime; - String lastContent; - bool lastReaded; - bool online = false; GroupChat.fromList(List params) { this.id = params[0]; @@ -106,33 +101,20 @@ class GroupChat { this.addr = params[4]; this.name = params[5]; this.bio = params[6]; - this.isTop = params[7] == "1"; - this.isOk = params[8] == "1"; - this.isClosed = params[9] == "1"; - 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"; + this.isOk = params[7]; + this.isClosed = params[8]; + this.isNeedAgree = params[9]; } - Avatar showAvatar({double width = 45.0, bool needOnline = true}) { + Avatar showAvatar({double width = 45.0}) { final avatar = Global.avatarPath + this.gid + '.png'; return Avatar( width: width, name: this.name, avatarPath: avatar, - online: this.online, - needOnline: needOnline, - hasNew: !this.lastReaded, + needOnline: false, ); } - - updateLastMessage(Message msg, bool isReaded) { - this.lastTime = msg.time; - this.lastContent = msg.shortShow(); - this.lastReaded = isReaded; - } } class Request { diff --git a/lib/apps/group_chat/page.dart b/lib/apps/group_chat/page.dart index ead6b29..7b17083 100644 --- a/lib/apps/group_chat/page.dart +++ b/lib/apps/group_chat/page.dart @@ -97,22 +97,6 @@ class ListChat extends StatelessWidget { overflow: TextOverflow.ellipsis, 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) Container( margin: const EdgeInsets.only(left: 15.0, right: 20.0), diff --git a/lib/apps/group_chat/provider.dart b/lib/apps/group_chat/provider.dart index 5761c8c..9a79ca0 100644 --- a/lib/apps/group_chat/provider.dart +++ b/lib/apps/group_chat/provider.dart @@ -18,6 +18,7 @@ class GroupChatProvider extends ChangeNotifier { SplayTreeMap requests = SplayTreeMap(); int actived; + bool activedOnline; SplayTreeMap activedMessages = SplayTreeMap(); SplayTreeMap activedMembers = SplayTreeMap(); @@ -45,6 +46,7 @@ class GroupChatProvider extends ChangeNotifier { rpc.addListener('group-chat-join', _join, true); rpc.addListener('group-chat-agree', _agree, true); 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-info', _memberInfo, 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]); } + requestList(bool all) { + rpc.send('group-chat-request-list', [all]); + } + _list(List params) { this.clear(); params.forEach((params) { @@ -116,6 +122,20 @@ class GroupChatProvider extends ChangeNotifier { 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) { this.createSupported.clear(); this.createCheckType = CheckTypeExtension.fromInt(params[0]); @@ -140,7 +160,6 @@ class GroupChatProvider extends ChangeNotifier { _result(List params) { final id = params[0]; this.groups[id].isOk = params[1]; - this.groups[id].online = true; if (params[1]) { //this.createKeys.remove(id); this.orderKeys.add(id); @@ -159,24 +178,6 @@ class GroupChatProvider extends ChangeNotifier { 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) { this.requests[params[0]] = Request.fromList(params); 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) { final member = Member.fromList(params); if (this.actived == member.fid) { @@ -234,15 +243,8 @@ class GroupChatProvider extends ChangeNotifier { if (!msg.isDelivery) { msg.isDelivery = null; // When message create, set is is none; } - this.groups[msg.fid].updateLastMessage(msg, true); 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); - } + notifyListeners(); } - //orderGroups(msg.fid); - notifyListeners(); } } diff --git a/lib/l10n/localizations.dart b/lib/l10n/localizations.dart index 5f7f9c6..8a0b50a 100644 --- a/lib/l10n/localizations.dart +++ b/lib/l10n/localizations.dart @@ -105,7 +105,6 @@ abstract class AppLocalizations { // homeage String get addFriend; String get addGroup; - String get chats; String get groups; String get devices; String get nightly; diff --git a/lib/l10n/localizations_en.dart b/lib/l10n/localizations_en.dart index 1745ad8..6483d7c 100644 --- a/lib/l10n/localizations_en.dart +++ b/lib/l10n/localizations_en.dart @@ -21,7 +21,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get info => 'Info'; @override - String get contact => 'Contact'; + String get contact => 'Contacts'; @override String get friend => 'Friend'; @override @@ -127,8 +127,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get addGroup => 'Add Service'; @override - String get chats => 'Sessions'; - @override String get groups => 'Services'; @override String get devices => 'Devices'; diff --git a/lib/l10n/localizations_zh.dart b/lib/l10n/localizations_zh.dart index b7fb008..80d1208 100644 --- a/lib/l10n/localizations_zh.dart +++ b/lib/l10n/localizations_zh.dart @@ -127,8 +127,6 @@ class AppLocalizationsZh extends AppLocalizations { @override String get addGroup => '添加服务'; @override - String get chats => '聊天列表'; - @override String get groups => '服务列表'; @override String get devices => '关联设备'; diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 31876e5..2c95545 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -156,9 +156,6 @@ class _HomeListState extends State with SingleTickerProviderStateMixin final color = Theme.of(context).colorScheme; final lang = AppLocalizations.of(context); final provider = context.watch(); - final chatProvider = context.watch(); - final chatTops = chatProvider.topKeys; - final friends = chatProvider.friends; return Padding( padding: const EdgeInsets.symmetric(vertical: 10.0), @@ -377,34 +374,54 @@ class DrawerWidget extends StatelessWidget { child: Text( "${me.name}", style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16.0), + fontWeight: FontWeight.bold, fontSize: 16.0), )), children: accountsWidget, ), ), const SizedBox(height: 5.0), const Divider(height: 1.0, color: Color(0x40ADB0BB)), - const SizedBox(height: 10.0), ListTile( - leading: Icon(Icons.person, color: color.primary), - title: Text(lang.profile, - textAlign: TextAlign.left, - style: TextStyle(fontSize: 16.0)), - onTap: () { - Navigator.pop(context); - showShadowDialog(context, Icons.person, lang.profile, - ProfileDetail()); - }), + 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(context, listen: false).updateActivedApp( + null, lang.contact, ChatList() + ); + }), ListTile( - leading: Icon(Icons.devices_other_rounded, - color: color.primary), - title: Text(lang.devices, - textAlign: TextAlign.left, - style: TextStyle(fontSize: 16.0)), - onTap: () { - Navigator.pop(context); - _showDevices(context, isDesktop); - }), + 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(context, listen: false).updateActivedApp( + null, lang.groups, ServiceList() + ); + }), + const Divider(height: 1.0, color: Color(0x40ADB0BB)), + ListTile( + leading: Icon(Icons.person, color: color.primary), + title: Text(lang.profile, + textAlign: TextAlign.left, + style: TextStyle(fontSize: 16.0)), + onTap: () { + Navigator.pop(context); + showShadowDialog(context, Icons.person, lang.profile, + ProfileDetail()); + }), + ListTile( + leading: Icon(Icons.devices_other_rounded, + color: color.primary), + title: Text(lang.devices, + textAlign: TextAlign.left, + style: TextStyle(fontSize: 16.0)), + onTap: () { + Navigator.pop(context); + _showDevices(context, isDesktop); + }), ListTile( leading: Icon(Icons.language, color: color.primary), title: Text(lang.preference, diff --git a/lib/provider.dart b/lib/provider.dart index e349b12..a4f28ab 100644 --- a/lib/provider.dart +++ b/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/global.dart'; import 'package:esse/rpc.dart'; +import 'package:esse/session.dart'; const DEFAULT_ONLINE_INIT = 8; const DEFAULT_ONLINE_DELAY = 5; @@ -19,7 +20,10 @@ class AccountProvider extends ChangeNotifier { String activedAccountId; // actived account gid. Account get activedAccount => this.accounts[activedAccountId]; - Set topKeys = Set(); + /// home sessions. sorded by last_time. + Map sessions = {}; + List topKeys = []; + List orderKeys = []; /// current user's did. String get id => this.activedAccount.id; @@ -32,6 +36,13 @@ class AccountProvider extends ChangeNotifier { 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() { // rpc notice when account not actived. rpc.addNotice(_accountNotice, _newRequestNotice); @@ -41,11 +52,13 @@ class AccountProvider extends ChangeNotifier { rpc.addListener('account-update', _accountUpdate, 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. @@ -62,6 +75,8 @@ class AccountProvider extends ChangeNotifier { this.activedAccount.online = true; rpc.send('account-login', [gid, this.activedAccount.lock]); + rpc.send('session-list', []); + new Future.delayed(Duration(seconds: DEFAULT_ONLINE_INIT), () => rpc.send('account-online', [gid])); @@ -81,6 +96,8 @@ class AccountProvider extends ChangeNotifier { this.accounts[account.gid] = account; rpc.send('account-login', [account.gid, account.lock]); + rpc.send('session-list', []); + new Future.delayed(Duration(seconds: DEFAULT_ONLINE_DELAY), () => rpc.send('account-online', [account.gid])); updateLogined(account); @@ -94,6 +111,11 @@ class AccountProvider extends ChangeNotifier { this.coreShowWidget = DefaultCoreShow(); this.currentListShow = null; + // load sessions. + this.sessions.clear(); + this.orderKeys.clear(); + rpc.send('session-list', []); + if (!this.activedAccount.online) { this.activedAccount.online = true; rpc.send('account-login', [gid, this.activedAccount.lock]); @@ -109,6 +131,10 @@ class AccountProvider extends ChangeNotifier { this.accounts.clear(); this.clearActivedAccount(); this.currentListShow = null; + this.sessions.clear(); + this.orderKeys.clear(); + this.topKeys.clear(); + rpc.send('account-logout', []); clearLogined(); } @@ -213,4 +239,58 @@ class AccountProvider extends ChangeNotifier { } 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(); + } } diff --git a/lib/session.dart b/lib/session.dart new file mode 100644 index 0000000..0a81a4a --- /dev/null +++ b/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]; + } +} diff --git a/lib/widgets/default_home_show.dart b/lib/widgets/default_home_show.dart index 262346c..ad6171d 100644 --- a/lib/widgets/default_home_show.dart +++ b/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/options.dart'; import 'package:esse/provider.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'; +import 'package:esse/session.dart'; class DefaultHomeShow extends StatelessWidget { const DefaultHomeShow({Key key}): super(key: key); @@ -19,39 +15,118 @@ class DefaultHomeShow extends StatelessWidget { Widget build(BuildContext context) { final isDesktop = isDisplayDesktop(context); final lang = AppLocalizations.of(context); - final chatProvider = context.watch(); - final chatTops = chatProvider.topKeys; - final friends = chatProvider.friends; - - return Column(children: [ - ListSystemApp(name: lang.chats, icon: Icons.people_rounded, - callback: () => Provider.of(context, listen: false).updateActivedApp( - null, lang.chats, ChatList())), - ListSystemApp(name: lang.groups, icon: Icons.grid_view_rounded, - callback: () => Provider.of(context, listen: false).updateActivedApp( - null, lang.groups, ServiceList())), - const SizedBox(height: 5.0), - const Divider(height: 1.0, color: Color(0x40ADB0BB)), - const SizedBox(height: 5.0), - Column( - children: INNER_SERVICES.map((v) { - final params = v.params(lang); - return ListInnerService( - name: params[0], - bio: params[1], - logo: params[2], - callback: () => v.callback(context, isDesktop, lang), - isDesktop: isDesktop, - ); - }).toList() + final provider = context.watch(); + final allKeys = provider.topKeys + provider.orderKeys; + final sessions = provider.sessions; + + return Scaffold( + body: ListView.builder( + itemCount: allKeys.length, + itemBuilder: (BuildContext ctx, int index) => _SessionWidget(session: sessions[allKeys[index]]), + ), + // floatingActionButton: FloatingActionButton( + // onPressed: () { + // final widget = Text(''); + // if (isDesktop) { + // Provider.of(context, listen: false).updateActivedApp(widget); + // } else { + // Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); + // } + // }, + // child: const Icon(Icons.add, color: Colors.white), + // backgroundColor: Color(0xFF6174FF), + // ), + ); + } +} + +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().updateActivedFriend(friend.id); + + // if (!isDesktop) { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (_) => ChatPage(), + // ), + // ); + // } else { + // context.read().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( + child: Container( + height: 55.0, + child: Column( + 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), + // ), + // ) + ]), + ], + ), + ), + ), + ], ), - Expanded( - child: ListView.builder( - itemCount: chatTops.length, - itemBuilder: (BuildContext ctx, int index) => ListChat( - friend: friends[chatTops.keys.elementAt(index)]), - ), - ) - ]); + ), + ); } } diff --git a/pubspec.lock b/pubspec.lock index afb0123..5e77623 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -161,7 +161,7 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" + version: "6.1.1" file_picker: dependency: "direct main" description: @@ -302,7 +302,7 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.7.5" + version: "0.7.5+2" image_picker_for_web: dependency: transitive description: @@ -447,14 +447,14 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "7.1.0" + version: "8.0.0+2" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "3.5.0" petitparser: dependency: transitive description: @@ -641,7 +641,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.3" + version: "6.0.4" url_launcher_linux: dependency: transitive description: @@ -662,7 +662,7 @@ packages: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" url_launcher_web: dependency: transitive description: @@ -718,7 +718,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.1" xdg_directories: dependency: transitive description: @@ -732,7 +732,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "5.1.1" yaml: dependency: transitive description: @@ -741,5 +741,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.1 <3.0.0" + dart: ">=2.13.0 <3.0.0" flutter: ">=2.0.2" diff --git a/src/apps/assistant/models.rs b/src/apps/assistant/models.rs index a28222f..10bcbad 100644 --- a/src/apps/assistant/models.rs +++ b/src/apps/assistant/models.rs @@ -9,7 +9,7 @@ use tdn::types::{ use tdn_storage::local::{DStorage, DsValue}; 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)] pub(crate) enum MessageType { @@ -69,7 +69,7 @@ impl MessageType { } MessageType::Contact => { 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)??; db.close()?; let tmp_name = contact.name.replace(";", "-;"); diff --git a/src/apps/chat/layer.rs b/src/apps/chat/layer.rs index 039b6aa..b5eccf8 100644 --- a/src/apps/chat/layer.rs +++ b/src/apps/chat/layer.rs @@ -15,7 +15,7 @@ use crate::event::{InnerEvent, StatusEvent}; use crate::layer::{Layer, Online}; use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH}; 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}; @@ -100,7 +100,7 @@ pub(crate) async fn handle( .check_add_online(fgid, Online::Direct(addr), f.id)?; // 3. update remote addr. TODO 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); drop(db); } @@ -119,7 +119,7 @@ pub(crate) async fn handle( let some_friend = load_friend(&layer.base, &mgid, &fgid)?; if some_friend.is_none() { // 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)? { req.delete(&db)?; // delete the old request. results.rpcs.push(rpc::request_delete(mgid, req.id)); @@ -161,13 +161,15 @@ pub(crate) async fn handle( // 2. update remote user. friend.name = remote.name; friend.addr = remote.addr; - let db = session_db(&layer.base, &mgid)?; + let db = chat_db(&layer.base, &mgid)?; friend.remote_update(&db)?; drop(db); write_avatar_sync(&layer.base, &mgid, &remote.id, remote.avatar)?; // 3. online to UI. - friend.online = true; + + // TODO UPDATE SESSION + results.rpcs.push(rpc::friend_info(mgid, &friend)); // 4. connected. 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) => { // check to close. 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 friend.contains_addr(&addr) { 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"))?; match response { 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)? { layer.group.write().await.broadcast( &mgid, @@ -258,7 +260,7 @@ pub(crate) async fn handle( .running_mut(&mgid)? .check_add_online(fgid, Online::Direct(addr), fid)?; // 4. update remote addr. - let db = session_db(&layer.base, &mgid)?; + let db = chat_db(&layer.base, &mgid)?; Friend::addr_update(&db, fid, &addr)?; drop(db); // 5. online to UI. @@ -287,7 +289,7 @@ pub(crate) async fn handle( )?; } else { // 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)? { layer.group.write().await.broadcast( &mgid, @@ -340,7 +342,7 @@ pub(crate) async fn handle( .running_mut(&mgid)? .check_add_online(fgid, Online::Direct(addr), fid)?; // 4. update remote addr. - let db = session_db(&layer.base, &mgid)?; + let db = chat_db(&layer.base, &mgid)?; Friend::addr_update(&db, fid, &addr)?; drop(db); // 5. online to UI. @@ -372,7 +374,7 @@ pub(crate) async fn handle( )?; } else { // 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)? { layer.group.write().await.broadcast( &mgid, @@ -402,7 +404,7 @@ pub(crate) async fn handle( results.layers.push((mgid, fgid, msg)); } 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)? { layer.group.write().await.broadcast( &mgid, @@ -431,7 +433,7 @@ pub(crate) async fn handle( // TODO maybe send failure need handle. if is_ok { 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 { DeliveryType::Event => { Message::delivery(&db, db_id, true)?; @@ -474,7 +476,7 @@ impl LayerEvent { match event { LayerEvent::Message(hash, m) => { - let db = session_db(&layer.base, &mgid)?; + let db = chat_db(&layer.base, &mgid)?; if !Message::exist(&db, &hash)? { let msg = m.clone().handle(false, mgid, &layer.base, &db, fid, hash)?; layer.group.write().await.broadcast( @@ -489,7 +491,7 @@ impl LayerEvent { } LayerEvent::Info(remote) => { 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(""))?; f.name = remote.name; f.addr = remote.addr; @@ -549,7 +551,7 @@ impl LayerEvent { &mut results, )?; layer.remove_online(&mgid, &fgid); - let db = session_db(&layer.base, &mgid)?; + let db = chat_db(&layer.base, &mgid)?; Friend::id_close(&db, fid)?; drop(db); results.rpcs.push(rpc::friend_close(mgid, fid)); @@ -571,7 +573,7 @@ impl LayerEvent { m_type: MessageType, content: String, ) -> 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. 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); msg.insert(&db)?; - Friend::update_last_message(&db, fid, &msg, true)?; + + // TODO UPDATE SESSION + drop(db); Ok((msg, nm_type)) } @@ -638,7 +642,7 @@ impl LayerEvent { #[inline] fn load_friend(base: &PathBuf, mgid: &GroupId, fgid: &GroupId) -> Result> { - let db = session_db(base, mgid)?; + let db = chat_db(base, mgid)?; Friend::get(&db, fgid) } diff --git a/src/apps/chat/models.rs b/src/apps/chat/models.rs index 22b5915..64f6135 100644 --- a/src/apps/chat/models.rs +++ b/src/apps/chat/models.rs @@ -19,13 +19,9 @@ pub(crate) struct Friend { pub addr: PeerAddr, pub name: String, pub remark: String, - pub is_top: 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 datetime: i64, } #[derive(Clone)] @@ -114,7 +110,9 @@ impl NetworkMessage { let mut msg = Message::new_with_id(hash, fid, is_me, m_type, raw, true); msg.insert(db)?; - Friend::update_last_message(db, fid, &msg, false)?; + + // TODO UPDATE SESSION + Ok(msg) } @@ -221,15 +219,11 @@ impl Friend { Friend { id: 0, - last_message_datetime: datetime, - last_message_content: "".to_owned(), - last_message_readed: true, gid, addr, name, remark, - online: false, - is_top: false, + datetime, is_closed: false, is_deleted: false, } @@ -249,17 +243,13 @@ impl Friend { Friend { is_deleted, - last_message_readed: v.pop().unwrap().as_bool(), - last_message_content: v.pop().unwrap().as_string(), - last_message_datetime: v.pop().unwrap().as_i64(), + datetime: v.pop().unwrap().as_i64(), is_closed: v.pop().unwrap().as_bool(), - is_top: 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_str()).unwrap_or(PeerAddr::default()), gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()), id: v.pop().unwrap().as_i64(), - online: false, } } @@ -284,17 +274,13 @@ impl Friend { self.addr.to_hex(), self.name, self.remark, - if self.is_top { "1" } else { "0" }, - if self.is_closed { "1" } else { "0" }, - self.last_message_datetime, - self.last_message_content, - self.last_message_readed, - if self.online { "1" } else { "0" }, + self.is_closed, + self.datetime ]) } pub fn get(db: &DStorage, gid: &GroupId) -> Result> { - 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)?; if matrix.len() > 0 { 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> { - 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)?; if matrix.len() > 0 { 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> { - 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)?; if matrix.len() > 0 { 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. pub fn all(db: &DStorage) -> Result> { - 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![]; for values in matrix { friends.push(Friend::from_values(values, false)); @@ -332,7 +318,7 @@ impl Friend { /// use in rpc when load account friends. pub fn all_ok(db: &DStorage) -> Result> { - 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![]; for values in matrix { friends.push(Friend::from_values(values, false)); @@ -355,16 +341,13 @@ impl Friend { } 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.addr.to_hex(), self.name, self.remark, - if self.is_top { 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 } + self.datetime, ); let id = db.insert(&sql)?; self.id = id; @@ -372,15 +355,11 @@ impl Friend { } pub fn update(&self, db: &DStorage) -> Result { - 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.name, self.remark, - if self.is_top { 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 }, self.id ); @@ -389,8 +368,8 @@ impl Friend { pub fn me_update(&mut self, db: &DStorage) -> Result { let sql = format!( - "UPDATE friends SET remark='{}', is_top={} WHERE id = {}", - self.remark, self.is_top, self.id, + "UPDATE friends SET remark='{}' WHERE id = {}", + self.remark, self.id, ); db.update(&sql) } @@ -414,21 +393,6 @@ impl Friend { db.update(&sql) } - pub fn update_last_message(db: &DStorage, id: i64, msg: &Message, read: bool) -> Result { - 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 { - 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. pub fn close(&self, db: &DStorage) -> Result { 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 { 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 } diff --git a/src/apps/chat/rpc.rs b/src/apps/chat/rpc.rs index 45c929f..ba30bd2 100644 --- a/src/apps/chat/rpc.rs +++ b/src/apps/chat/rpc.rs @@ -11,7 +11,7 @@ use tdn_did::user::User; use crate::event::InnerEvent; use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH}; 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::{Friend, Message, MessageType, Request}; @@ -32,8 +32,8 @@ pub(crate) fn friend_info(mgid: GroupId, friend: &Friend) -> RpcParam { } #[inline] -pub(crate) fn friend_update(mgid: GroupId, fid: i64, is_top: bool, remark: &str) -> RpcParam { - rpc_response(0, "chat-friend-update", json!([fid, is_top, remark]), mgid) +pub(crate) fn friend_update(mgid: GroupId, fid: i64, remark: &str) -> RpcParam { + rpc_response(0, "chat-friend-update", json!([fid, remark]), mgid) } #[inline] @@ -123,16 +123,10 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { "chat-friend-list", |gid: GroupId, _params: Vec, state: Arc| async move { 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)?; 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))) }, ); @@ -142,12 +136,10 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { |gid: GroupId, params: Vec, state: Arc| async move { let id = params[0].as_i64()?; let remark = params[1].as_str()?; - let is_top = params[2].as_bool()?; 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)? { - f.is_top = is_top; f.remark = remark.to_owned(); f.me_update(&db)?; f @@ -157,7 +149,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { drop(db); state.group.write().await.broadcast( &gid, - InnerEvent::SessionFriendUpdate(f.gid, f.is_top, f.remark), + InnerEvent::SessionFriendUpdate(f.gid, f.remark), FRIEND_TABLE_PATH, f.id, &mut results, @@ -166,19 +158,6 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { }, ); - handler.add_method( - "chat-friend-readed", - |gid: GroupId, params: Vec, state: Arc| 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( "chat-friend-close", |gid: GroupId, params: Vec, state: Arc| async move { @@ -187,7 +166,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let mut results = HandleResult::new(); 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)??; friend.close(&db)?; drop(db); @@ -227,7 +206,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let mut results = HandleResult::new(); 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)??; friend.delete(&db)?; drop(db); @@ -264,7 +243,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { "chat-request-list", |gid: GroupId, _params: Vec, state: Arc| async move { 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); let requests = Request::all(&db)?; drop(db); @@ -292,7 +271,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let me = state.group.read().await.clone_user(&gid)?; 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)? { debug!("had friend."); drop(layer_lock); @@ -340,7 +319,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let mut group_lock = state.group.write().await; let me = group_lock.clone_user(&gid)?; 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(); if let Some(mut request) = Request::get_id(&db, id)? { @@ -376,7 +355,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let id = params[0].as_i64()?; 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)??; req.is_ok = false; req.is_over = true; @@ -403,7 +382,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let id = params[0].as_i64()?; 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(); drop(layer_lock); let req = Request::get_id(&db, id)??; @@ -433,10 +412,9 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let fid = params[0].as_i64()?; 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); - Friend::readed(&db, fid)?; let messages = Message::get(&db, &fid)?; drop(db); Ok(HandleResult::rpc(message_list(messages))) @@ -486,7 +464,7 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { let id = params[0].as_i64()?; 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); let msg = Message::get_id(&db, id)??; diff --git a/src/apps/group_chat/models.rs b/src/apps/group_chat/models.rs index 3508f3f..8bd44e2 100644 --- a/src/apps/group_chat/models.rs +++ b/src/apps/group_chat/models.rs @@ -71,8 +71,6 @@ pub(crate) struct GroupChat { g_name: String, /// group chat simple intro. g_bio: String, - /// group chat is set top sessions. - is_top: bool, /// group chat is created ok. is_ok: bool, /// group chat is closed. @@ -81,16 +79,8 @@ pub(crate) struct GroupChat { is_need_agree: bool, /// group chat encrypted-key. 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. pub datetime: i64, - /// group chat is online. - pub online: bool, /// is deleted. is_deleted: bool, } @@ -126,13 +116,8 @@ impl GroupChat { datetime, id: 0, height: 0, - is_top: true, is_ok: false, is_closed: false, - last_datetime: datetime, - last_content: Default::default(), - last_readed: true, - online: false, is_deleted: false, } } @@ -166,13 +151,8 @@ impl GroupChat { datetime, id: 0, height, - is_top: true, is_ok: true, is_closed: false, - last_datetime: datetime, - last_content: Default::default(), - last_readed: true, - online: false, is_deleted: false, } } @@ -243,14 +223,9 @@ impl GroupChat { self.g_addr.to_hex(), self.g_name, self.g_bio, - if self.is_top { "1" } else { "0" }, - if self.is_ok { "1" } else { "0" }, - if self.is_closed { "1" } else { "0" }, - 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" }, + self.is_ok, + self.is_closed, + self.is_need_agree, ]) } @@ -263,17 +238,12 @@ impl GroupChat { Self { is_deleted, - online: false, 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()) .unwrap_or(GroupChatKey::new(vec![])), is_closed: v.pop().unwrap().as_bool(), is_need_agree: 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_name: v.pop().unwrap().as_string(), 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. pub fn all(db: &DStorage) -> Result> { - 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![]; for values in matrix { groups.push(GroupChat::from_values(values, false)); @@ -297,7 +267,7 @@ impl GroupChat { /// use in rpc when load account groups. pub fn all_ok(db: &DStorage) -> Result> { - 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![]; for values in matrix { groups.push(GroupChat::from_values(values, false)); @@ -306,7 +276,7 @@ impl GroupChat { } pub fn get(db: &DStorage, gid: &GroupId) -> Result> { - 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)?; if matrix.len() > 0 { let values = matrix.pop().unwrap(); // safe unwrap() @@ -316,7 +286,7 @@ impl GroupChat { } pub fn get_id(db: &DStorage, id: &i64) -> Result> { - 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)?; if matrix.len() > 0 { let values = matrix.pop().unwrap(); // safe unwrap() @@ -326,7 +296,7 @@ impl GroupChat { } 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.owner.to_hex(), self.g_id.to_hex(), @@ -334,14 +304,10 @@ impl GroupChat { self.g_addr.to_hex(), self.g_name, self.g_bio, - if self.is_top { 1 } else { 0 }, if self.is_ok { 1 } else { 0 }, if self.is_need_agree { 1 } else { 0 }, if self.is_closed { 1 } else { 0 }, self.key.to_hex(), - self.last_datetime, - self.last_content, - if self.last_readed { 1 } else { 0 }, self.datetime, ); let id = db.insert(&sql)?; @@ -359,29 +325,6 @@ impl GroupChat { let sql = format!("UPDATE groups SET height={} WHERE id = {}", height, id,); db.update(&sql) } - - pub fn update_last_message( - db: &DStorage, - id: i64, - msg: &Message, - read: bool, - height: i64, - ) -> Result { - 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 { - 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. @@ -397,6 +340,7 @@ pub(crate) struct Request { is_ok: bool, is_over: bool, datetime: i64, + is_deleted: bool, } impl Request { @@ -418,6 +362,7 @@ impl Request { key: GroupChatKey(vec![]), is_ok: false, is_over: false, + is_deleted: false, id: 0, } } @@ -444,6 +389,7 @@ impl Request { key, is_ok: false, is_over: false, + is_deleted: false, fid: 0, id: 0, } @@ -463,6 +409,42 @@ impl Request { ]) } + fn from_values(mut v: Vec, 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> { + 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<()> { let sql = format!("INSERT INTO requests (fid, gid, addr, name, remark, key, is_ok, is_over, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', '{}', '{}', {}, {}, {}, false)", 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); msg.insert(&db)?; - GroupChat::update_last_message(&db, gdid, &msg, false, height)?; + + // TODO SESSION UPDATE. + Ok(msg) } diff --git a/src/apps/group_chat/rpc.rs b/src/apps/group_chat/rpc.rs index 92b26ff..112f32a 100644 --- a/src/apps/group_chat/rpc.rs +++ b/src/apps/group_chat/rpc.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use tdn::types::{ group::GroupId, message::SendType, - primitive::{new_io_error, HandleResult, PeerAddr}, + primitive::{HandleResult, PeerAddr}, rpc::{json, rpc_response, RpcHandler, RpcParam}, }; use tdn_did::Proof; @@ -97,6 +97,16 @@ fn group_list(groups: Vec) -> RpcParam { json!(results) } +#[inline] +fn request_list(requests: Vec) -> RpcParam { + let mut results = vec![]; + for request in requests { + results.push(request.to_rpc()); + } + + json!(results) +} + #[inline] fn detail_list(members: Vec, messages: Vec) -> RpcParam { let mut member_results = vec![]; @@ -122,16 +132,17 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler) { |gid: GroupId, _params: Vec, state: Arc| async move { let layer_lock = state.layer.read().await; let db = group_chat_db(&layer_lock.base, &gid)?; - let mut groups = 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(GroupChat::all(&db)?))) + }, + ); - Ok(HandleResult::rpc(group_list(groups))) + handler.add_method( + "group-chat-request-list", + |gid: GroupId, params: Vec, state: Arc| 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) { Ok(results) }, ); - - handler.add_method( - "group-chat-readed", - |gid: GroupId, params: Vec, state: Arc| 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()) - }, - ); } diff --git a/src/daemon.rs b/src/daemon.rs index 2d2a35f..39f88b9 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -14,6 +14,7 @@ mod migrate; mod primitives; mod rpc; mod server; +mod session; mod storage; mod utils; diff --git a/src/event.rs b/src/event.rs index 6434e48..684f7f4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -26,7 +26,7 @@ use crate::apps::chat::{Friend, Message, NetworkMessage, Request}; use crate::apps::file::{FileId, FileType}; use crate::rpc; 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. @@ -46,8 +46,8 @@ pub(crate) enum InnerEvent { /// params: f_gid, addr, name, avatar. (TODO addrs as list to store. keep others device) SessionFriendInfo(GroupId, PeerAddr, String, Vec), /// Session's friend update by me. - /// params: f_gid, is_top, remark - SessionFriendUpdate(GroupId, bool, String), + /// params: f_gid, remark + SessionFriendUpdate(GroupId, String), /// Session's friend close. /// params: f_gid. SessionFriendClose(GroupId), @@ -100,7 +100,7 @@ pub(crate) enum SyncEvent { bool, ), 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( EventId, GroupId, @@ -110,7 +110,6 @@ pub(crate) enum SyncEvent { String, bool, bool, - bool, ), FriendHad(EventId, GroupId), /// eid, friend_gid, msg_id, is_me, message. @@ -250,7 +249,7 @@ impl InnerEvent { (ACCOUNT_TABLE_PATH, 0) } InnerEvent::SessionRequestCreate(is_me, remote, remark) => { - let db = session_db(group.base(), &gid)?; + let db = chat_db(group.base(), &gid)?; // check if exist request. if Friend::get(&db, &remote.id)?.is_some() { return Ok(()); @@ -270,7 +269,7 @@ impl InnerEvent { (REQUEST_TABLE_PATH, request.id) } 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() { return Ok(()); } @@ -297,7 +296,7 @@ impl InnerEvent { } } 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)? { let rid = request.id; request.delete(&db)?; @@ -312,7 +311,7 @@ impl InnerEvent { } } 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)? { return Ok(()); } @@ -342,7 +341,7 @@ impl InnerEvent { } } 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)? { m.delete(&db)?; results.rpcs.push(chat_rpc::message_delete(gid, m.id)); @@ -352,7 +351,7 @@ impl InnerEvent { } } 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)? { f.addr = raddr; f.name = rname; @@ -366,22 +365,21 @@ impl InnerEvent { return Ok(()); } } - InnerEvent::SessionFriendUpdate(rgid, is_top, remark) => { - let db = session_db(group.base(), &gid)?; + InnerEvent::SessionFriendUpdate(rgid, remark) => { + let db = chat_db(group.base(), &gid)?; if let Some(mut f) = Friend::get_it(&db, &rgid)? { - f.is_top = is_top; f.remark = remark; f.me_update(&db)?; results .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) } else { return Ok(()); } } 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)? { f.close(&db)?; results.rpcs.push(chat_rpc::friend_close(gid, f.id)); @@ -410,7 +408,7 @@ impl InnerEvent { } } 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)? { f.delete(&db)?; results.rpcs.push(chat_rpc::friend_delete(gid, f.id)); @@ -487,7 +485,7 @@ impl StatusEvent { ) -> Result<()> { match self { 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)? { results .rpcs @@ -505,7 +503,7 @@ impl StatusEvent { } } 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)? { let layer_lock = layer.clone(); let rgid = f.gid; @@ -574,7 +572,7 @@ impl SyncEvent { events.push(SyncEvent::Account(hash, name, avatar)); } 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)? { if pre_keys.contains(&(path, row)) { events.push(SyncEvent::RequestHad(hash, request.gid)); @@ -609,7 +607,7 @@ impl SyncEvent { events.push(event); } 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)? { if pre_keys.contains(&(path, row)) { events.push(SyncEvent::FriendHad(hash, friend.gid)); @@ -631,7 +629,6 @@ impl SyncEvent { friend.name, avatar, friend.remark, - friend.is_top, friend.is_closed, friend.is_deleted, ) @@ -642,7 +639,7 @@ impl SyncEvent { events.push(event); } 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 fgid = if let Some(f) = Friend::get_id(&db, msg.fid)? { f.gid @@ -746,11 +743,11 @@ impl SyncEvent { is_over, is_delete, ) => { - let session_db = session_db(&base, &gid)?; - let request = if let Some(mut req) = Request::get(&session_db, &rgid)? { + let chat_db = chat_db(&base, &gid)?; + let request = if let Some(mut req) = Request::get(&chat_db, &rgid)? { if is_delete { 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)?; } results.rpcs.push(chat_rpc::request_delete(gid, req.id)); @@ -758,7 +755,7 @@ impl SyncEvent { req.is_ok = is_ok; req.is_over = is_over; - req.update(&session_db)?; + req.update(&chat_db)?; req } else { let mut request = Request::new(rgid, raddr, rname, remark, is_me, true); @@ -767,12 +764,12 @@ impl SyncEvent { request.is_deleted = is_delete; // save to db. - request.insert(&session_db)?; + request.insert(&chat_db)?; let rid = request.id; results.rpcs.push(chat_rpc::request_create(gid, &request)); 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)?; } results.rpcs.push(chat_rpc::request_delete(gid, rid)); @@ -786,20 +783,20 @@ impl SyncEvent { if avatar.len() > 0 { 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 .rpcs .push(chat_rpc::request_agree(gid, rid, &friend)); } else { results.rpcs.push(chat_rpc::request_reject(gid, rid)); } - session_db.close()?; + chat_db.close()?; (eid, REQUEST_TABLE_PATH, rid) } SyncEvent::RequestHad(eid, rgid) => { - let session_db = session_db(&base, &gid)?; - let id = if let Some(req) = Request::get(&session_db, &rgid)? { + let chat_db = chat_db(&base, &gid)?; + let id = if let Some(req) = Request::get(&chat_db, &rgid)? { req.id } else { -1 @@ -813,19 +810,17 @@ impl SyncEvent { fname, avatar, remark, - is_top, is_closed, is_deleted, ) => { - let session_db = session_db(&base, &gid)?; - let id = if let Some(mut friend) = Friend::get(&session_db, &fgid)? { + let chat_db = chat_db(&base, &gid)?; + let id = if let Some(mut friend) = Friend::get(&chat_db, &fgid)? { friend.addr = faddr; friend.name = fname; friend.remark = remark; - friend.is_top = is_top; friend.is_closed = is_closed; friend.is_deleted = is_deleted; - friend.update(&session_db)?; + friend.update(&chat_db)?; if !is_deleted && avatar.len() > 0 { write_avatar_sync(&base, &gid, &friend.gid, avatar)?; @@ -865,8 +860,8 @@ impl SyncEvent { (eid, FRIEND_TABLE_PATH, id) } SyncEvent::FriendHad(eid, fgid) => { - let session_db = session_db(&base, &gid)?; - let id = if let Some(friend) = Friend::get(&session_db, &fgid)? { + let chat_db = chat_db(&base, &gid)?; + let id = if let Some(friend) = Friend::get(&chat_db, &fgid)? { friend.id } else { -1 @@ -874,13 +869,13 @@ impl SyncEvent { (eid, FRIEND_TABLE_PATH, id) } SyncEvent::Message(eid, fgid, meid, is_me, m) => { - let session_db = session_db(&base, &gid)?; - if Message::exist(&session_db, &meid)? { + let chat_db = chat_db(&base, &gid)?; + if Message::exist(&chat_db, &meid)? { continue; } - let id = if let Some(f) = Friend::get_it(&session_db, &fgid)? { - let msg = m.handle(is_me, gid, &base, &session_db, f.id, eid)?; + let id = if let Some(f) = Friend::get_it(&chat_db, &fgid)? { + let msg = m.handle(is_me, gid, &base, &chat_db, f.id, eid)?; results.rpcs.push(chat_rpc::message_create(gid, &msg)); msg.id } else { diff --git a/src/layer.rs b/src/layer.rs index 951355a..fd903bd 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -10,12 +10,12 @@ use tdn::{ primitive::{new_io_error, PeerAddr, Result}, }, }; -use tdn_did::user::User; -use crate::apps::chat::{chat_conn, Friend}; -use crate::apps::group_chat::{group_chat_conn, GroupChat, GROUP_ID}; +use crate::apps::chat::chat_conn; +use crate::apps::group_chat::{group_chat_conn, GROUP_ID}; 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. /// EVERY LAYER APP MUST EQUAL THE FIRST THREE FIELDS. @@ -123,22 +123,23 @@ impl Layer { for mgid in self.runnings.keys() { let mut vecs = vec![]; - // load friend chat. - let db = session_db(&self.base, mgid)?; - 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))); - } + let db = session_db(&self.base, &mgid)?; + let sessions = Session::list(&db)?; drop(db); - // load group chat. - let db = group_chat_db(&self.base, mgid)?; - let groups = GroupChat::all_ok(&db)?; - for g in groups { - let proof = group_lock.prove_addr(mgid, &g.g_addr)?; - vecs.push((GROUP_ID, group_chat_conn(proof, g.g_addr, g.g_id))); + for s in sessions { + match s.s_type { + SessionType::Chat => { + let proof = group_lock.prove_addr(mgid, &s.addr)?; + vecs.push((s.gid, chat_conn(proof, s.addr))); + } + 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); } diff --git a/src/lib.rs b/src/lib.rs index 6c66541..c585e87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ mod migrate; mod primitives; mod rpc; mod server; +mod session; mod storage; mod utils; diff --git a/src/migrate.rs b/src/migrate.rs index 5f56a02..735eb60 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -4,12 +4,14 @@ use tdn_storage::local::DStorage; pub mod consensus; mod account; +mod chat; mod file; mod group_chat; mod service; mod session; use account::ACCOUNT_VERSIONS; +use chat::CHAT_VERSIONS; use consensus::CONSENSUS_VERSIONS; use file::FILE_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 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 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(), ASSISTANT_DB => ASSISTANT_VERSIONS.as_ref(), GROUP_CHAT_DB => GROUP_CHAT_VERSIONS.as_ref(), + CHAT_DB => CHAT_VERSIONS.as_ref(), _ => { continue; } @@ -182,6 +188,12 @@ pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> { GROUP_CHAT_DB, ))?; + db.update(&format!( + "UPDATE migrates SET version = {} where db_name = '{}'", + CHAT_VERSIONS.len(), + CHAT_DB, + ))?; + db.close()?; } @@ -235,5 +247,13 @@ pub(crate) fn account_init_migrate(path: &PathBuf) -> std::io::Result<()> { for i in &GROUP_CHAT_VERSIONS { 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() } diff --git a/src/migrate/account.rs b/src/migrate/account.rs index d2c0135..0974894 100644 --- a/src/migrate/account.rs +++ b/src/migrate/account.rs @@ -1,5 +1,5 @@ #[rustfmt::skip] -pub(super) const ACCOUNT_VERSIONS: [&str; 8] = [ +pub(super) const ACCOUNT_VERSIONS: [&str; 9] = [ "CREATE TABLE IF NOT EXISTS accounts( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, gid TEXT NOT NULL, @@ -14,10 +14,11 @@ pub(super) const ACCOUNT_VERSIONS: [&str; 8] = [ "CREATE TABLE IF NOT EXISTS migrates( db_name TEXT NOT NULL, version INTEGER NOT NULL);", - "INSERT INTO migrates (db_name, version) values ('account.db', 1)", - "INSERT INTO migrates (db_name, version) values ('consensus.db', 9)", - "INSERT INTO migrates (db_name, version) values ('session.db', 3)", - "INSERT INTO migrates (db_name, version) values ('file.db', 1)", + "INSERT INTO migrates (db_name, version) values ('account.db', 0)", + "INSERT INTO migrates (db_name, version) values ('consensus.db', 0)", + "INSERT INTO migrates (db_name, version) values ('session.db', 0)", + "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 ('group_chat.db', 0)", + "INSERT INTO migrates (db_name, version) values ('chat.db', 0)", ]; diff --git a/src/migrate/chat.rs b/src/migrate/chat.rs new file mode 100644 index 0000000..526b9dd --- /dev/null +++ b/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);", +]; diff --git a/src/migrate/group_chat.rs b/src/migrate/group_chat.rs index e8a5d09..80c8d84 100644 --- a/src/migrate/group_chat.rs +++ b/src/migrate/group_chat.rs @@ -9,14 +9,10 @@ pub(super) const GROUP_CHAT_VERSIONS: [&str; 4] = [ addr TEXT NOT NULL, name TEXT NOT NULL, bio TEXT NOT NULL, - is_top INTEGER NOT NULL, is_ok INTEGER NOT NULL, is_need_agree INTEGER NOT NULL, is_closed INTEGER NOT NULL, key TEXT NOT NULL, - last_datetime INTEGER, - last_content TEXT, - last_readed INTEGER, datetime INTEGER NOT NULL, is_deleted INTEGER NOT NULL);", "CREATE TABLE IF NOT EXISTS requests( diff --git a/src/migrate/session.rs b/src/migrate/session.rs index 1efd421..e668746 100644 --- a/src/migrate/session.rs +++ b/src/migrate/session.rs @@ -1,37 +1,16 @@ #[rustfmt::skip] 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, + fid INTEGER NOT NULL, gid TEXT NOT NULL, addr TEXT NOT NULL, + s_type INTEGER NOT NULL, name TEXT NOT NULL, - remark TEXT, is_top INTEGER NOT NULL, - is_closed INTEGER NOT NULL, - last_message_datetime INTEGER, - last_message_content TEXT, - last_message_readed INTEGER, - 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);", + last_datetime INTEGER, + last_content TEXT, + last_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. + "INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed) VALUES (0, '', '', 2, '', 0, 0, '', 1);", // File. ]; diff --git a/src/rpc.rs b/src/rpc.rs index 1e1881c..ecd2c02 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -13,12 +13,13 @@ use tdn::{ }; use crate::apps::app_rpc_inject; -use crate::apps::chat::{chat_conn, Friend}; -use crate::apps::group_chat::{add_layer, group_chat_conn, GroupChat}; +use crate::apps::chat::chat_conn; +use crate::apps::group_chat::{add_layer, group_chat_conn}; use crate::event::InnerEvent; use crate::group::Group; 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( 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) -> RpcParam { + let mut results = vec![]; + for session in sessions { + results.push(session.to_rpc()); + } + json!(results) +} + #[inline] pub(crate) async fn sleep_waiting_close_stable( sender: Sender, @@ -364,27 +407,29 @@ fn new_rpc_handler( let group_lock = state.group.read().await; let db = session_db(group_lock.base(), &gid)?; - let friends = Friend::all_ok(&db)?; + let sessions = Session::list(&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)?; - let groups = GroupChat::all_ok(&db)?; - for g in groups { - let proof = group_lock.prove_addr(&gid, &g.g_addr)?; - add_layer(&mut results, gid, group_chat_conn(proof, g.g_addr, g.g_id)); + for s in sessions { + match s.s_type { + SessionType::Chat => { + let proof = group_lock.prove_addr(&gid, &s.addr)?; + 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 { results.groups.push((gid, device)); } + drop(group_lock); debug!("Account Online: {}.", gid.to_hex()); Ok(results) @@ -421,5 +466,23 @@ fn new_rpc_handler( }, ); + handler.add_method( + "session-list", + |gid: GroupId, _params: Vec, state: Arc| 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 } diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 0000000..f60b96a --- /dev/null +++ b/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) -> 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 { + 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> { + 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 { + 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 { + 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 { + db.update(&format!( + "UPDATE sessions SET last_readed = 1 WHERE id = {}", + id + )) + } +} diff --git a/src/storage.rs b/src/storage.rs index 6f5bd77..96b9665 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -2,6 +2,7 @@ use async_fs as fs; use image::{load_from_memory, DynamicImage, GenericImageView}; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use std::path::PathBuf; +use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use tdn::types::{ @@ -11,7 +12,7 @@ use tdn::types::{ use tdn_storage::local::DStorage; 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, }; @@ -327,6 +328,14 @@ pub(crate) fn session_db(base: &PathBuf, gid: &GroupId) -> Result { DStorage::open(db_path) } +#[inline] +pub(crate) fn chat_db(base: &PathBuf, gid: &GroupId) -> Result { + let mut db_path = base.clone(); + db_path.push(gid.to_hex()); + db_path.push(CHAT_DB); + DStorage::open(db_path) +} + #[inline] pub(crate) fn _file_db(base: &PathBuf, gid: &GroupId) -> Result { let mut db_path = base.clone();