Browse Source

apply new session online design

pull/18/head
Sun 4 years ago
parent
commit
1cc084627c
  1. 60
      lib/apps/chat/detail.dart
  2. 8
      lib/apps/chat/list.dart
  3. 4
      lib/apps/device/page.dart
  4. 2
      lib/apps/file/page.dart
  5. 7
      lib/apps/group_chat/page.dart
  6. 4
      lib/apps/service/list.dart
  7. 6
      lib/apps/service/models.dart
  8. 10
      lib/pages/home.dart
  9. 131
      lib/provider.dart
  10. 23
      lib/session.dart
  11. 51
      lib/widgets/default_home_show.dart
  12. 84
      lib/widgets/list_service.dart
  13. 86
      src/apps/chat/layer.rs
  14. 32
      src/apps/group_chat/layer.rs
  15. 10
      src/event.rs
  16. 144
      src/layer.rs
  17. 5
      src/migrate/session.rs
  18. 112
      src/rpc.rs
  19. 19
      src/session.rs

60
lib/apps/chat/detail.dart

@ -13,6 +13,7 @@ import 'package:esse/widgets/user_info.dart';
import 'package:esse/widgets/chat_message.dart'; import 'package:esse/widgets/chat_message.dart';
import 'package:esse/global.dart'; import 'package:esse/global.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/session.dart';
import 'package:esse/apps/primitives.dart'; import 'package:esse/apps/primitives.dart';
import 'package:esse/apps/chat/models.dart'; import 'package:esse/apps/chat/models.dart';
@ -46,7 +47,9 @@ class _ChatDetailState extends State<ChatDetail> {
bool recordShow = false; bool recordShow = false;
String _recordName; String _recordName;
Friend friend; int _actived;
Friend _friend;
String _meName;
@override @override
initState() { initState() {
@ -60,12 +63,21 @@ class _ChatDetailState extends State<ChatDetail> {
}); });
} }
}); });
new Future.delayed(Duration.zero, () {
final accountProvider = context.read<AccountProvider>();
final chatProvider = context.read<ChatProvider>();
this._actived = accountProvider.activedSession.fid;
this._meName = accountProvider.activedAccount.name;
this._friend = chatProvider.friends[this._actived];
chatProvider.updateActivedFriend(this._actived);
setState(() {});
});
} }
_generateRecordPath() { _generateRecordPath() {
this._recordName = DateTime.now().millisecondsSinceEpoch.toString() + this._recordName = DateTime.now().millisecondsSinceEpoch.toString() +
'_' + '_' +
this.friend.id.toString() + this._actived.toString() +
'.m4a'; '.m4a';
} }
@ -74,7 +86,7 @@ class _ChatDetailState extends State<ChatDetail> {
return; return;
} }
context.read<ChatProvider>().messageCreate(Message(friend.id, MessageType.String, textController.text)); context.read<ChatProvider>().messageCreate(Message(_actived, MessageType.String, textController.text));
setState(() { setState(() {
textController.text = ''; textController.text = '';
textFocus.requestFocus(); textFocus.requestFocus();
@ -93,7 +105,7 @@ class _ChatDetailState extends State<ChatDetail> {
void _sendImage() async { void _sendImage() async {
final image = await pickImage(); final image = await pickImage();
if (image != null) { if (image != null) {
context.read<ChatProvider>().messageCreate(Message(friend.id, MessageType.Image, image)); context.read<ChatProvider>().messageCreate(Message(_actived, MessageType.Image, image));
} }
setState(() { setState(() {
textFocus.requestFocus(); textFocus.requestFocus();
@ -107,7 +119,7 @@ class _ChatDetailState extends State<ChatDetail> {
void _sendFile() async { void _sendFile() async {
final file = await pickFile(); final file = await pickFile();
if (file != null) { if (file != null) {
context.read<ChatProvider>().messageCreate(Message(friend.id, MessageType.File, file)); context.read<ChatProvider>().messageCreate(Message(_actived, MessageType.File, file));
} }
setState(() { setState(() {
textFocus.requestFocus(); textFocus.requestFocus();
@ -120,7 +132,7 @@ class _ChatDetailState extends State<ChatDetail> {
void _sendRecord(int time) async { void _sendRecord(int time) async {
final raw = BaseMessage.rawRecordName(time, _recordName); final raw = BaseMessage.rawRecordName(time, _recordName);
context.read<ChatProvider>().messageCreate(Message(friend.id, MessageType.Record, raw)); context.read<ChatProvider>().messageCreate(Message(_actived, MessageType.Record, raw));
setState(() { setState(() {
textFocus.requestFocus(); textFocus.requestFocus();
@ -165,7 +177,7 @@ class _ChatDetailState extends State<ChatDetail> {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () async { onTap: () async {
context.read<ChatProvider>().messageCreate(Message(friend.id, MessageType.Contact, "${contact.id}")); context.read<ChatProvider>().messageCreate(Message(_actived, MessageType.Contact, "${contact.id}"));
Navigator.of(context).pop(); Navigator.of(context).pop();
setState(() { setState(() {
textFocus.requestFocus(); textFocus.requestFocus();
@ -201,16 +213,16 @@ class _ChatDetailState extends State<ChatDetail> {
final recentMessages = provider.activedMessages; final recentMessages = provider.activedMessages;
final recentMessageKeys = recentMessages.keys.toList().reversed.toList(); final recentMessageKeys = recentMessages.keys.toList().reversed.toList();
final meName = context.read<AccountProvider>().activedAccount.name; final session = context.watch<AccountProvider>().activedSession;
this.friend = provider.activedFriend;
if (this.friend == null) { if (this._friend == null) {
return Container( return Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0),
child: Text('Waiting...') child: Text('Waiting...')
); );
} }
final isOnline = this.friend.online;
final isOnline = session.online == OnlineType.Active;
return Column( return Column(
children: [ children: [
@ -235,11 +247,11 @@ class _ChatDetailState extends State<ChatDetail> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
this.friend.name, this._friend.name,
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
SizedBox(height: 6.0), SizedBox(height: 6.0),
Text(this.friend.isClosed Text(this._friend.isClosed
? lang.unfriended ? lang.unfriended
: (isOnline ? lang.online : lang.offline), : (isOnline ? lang.online : lang.offline),
style: TextStyle( style: TextStyle(
@ -278,9 +290,9 @@ class _ChatDetailState extends State<ChatDetail> {
Icons.info, Icons.info,
lang.friendInfo, lang.friendInfo,
UserInfo( UserInfo(
id: 'EH' + this.friend.gid.toUpperCase(), id: 'EH' + this._friend.gid.toUpperCase(),
name: this.friend.name, name: this._friend.name,
addr: '0x' + this.friend.addr) addr: '0x' + this._friend.addr)
); );
} else if (value == 3) { } else if (value == 3) {
print('TODO remark'); print('TODO remark');
@ -290,7 +302,7 @@ class _ChatDetailState extends State<ChatDetail> {
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text(lang.unfriend), title: Text(lang.unfriend),
content: Text(this.friend.name, content: Text(this._friend.name,
style: TextStyle(color: color.primary)), style: TextStyle(color: color.primary)),
actions: [ actions: [
TextButton( TextButton(
@ -302,7 +314,7 @@ class _ChatDetailState extends State<ChatDetail> {
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<ChatProvider>( Provider.of<ChatProvider>(
context, listen: false).friendClose(this.friend.id); context, listen: false).friendClose(this._friend.id);
if (!isDesktop) { if (!isDesktop) {
Navigator.pop(context); Navigator.pop(context);
} }
@ -314,14 +326,14 @@ class _ChatDetailState extends State<ChatDetail> {
); );
} else if (value == 5) { } else if (value == 5) {
Provider.of<ChatProvider>(context, listen: false).requestCreate( Provider.of<ChatProvider>(context, listen: false).requestCreate(
Request(this.friend.gid, this.friend.addr, this.friend.name, lang.fromContactCard(meName))); Request(this._friend.gid, this._friend.addr, this._friend.name, lang.fromContactCard(this._meName)));
} else if (value == 6) { } else if (value == 6) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text(lang.delete + " " + lang.friend), title: Text(lang.delete + " " + lang.friend),
content: Text(this.friend.name, content: Text(this._friend.name,
style: TextStyle(color: Colors.red)), style: TextStyle(color: Colors.red)),
actions: [ actions: [
TextButton( TextButton(
@ -333,7 +345,7 @@ class _ChatDetailState extends State<ChatDetail> {
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<ChatProvider>( Provider.of<ChatProvider>(
context, listen: false).friendDelete(this.friend.id); context, listen: false).friendDelete(this._friend.id);
if (!isDesktop) { if (!isDesktop) {
Navigator.pop(context); Navigator.pop(context);
} }
@ -349,7 +361,7 @@ class _ChatDetailState extends State<ChatDetail> {
return <PopupMenuEntry<int>>[ return <PopupMenuEntry<int>>[
_menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo), _menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo),
//_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), //_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark),
this.friend.isClosed this._friend.isClosed
? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addFriend) ? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addFriend)
: _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend), : _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend),
_menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete), _menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete),
@ -366,11 +378,11 @@ class _ChatDetailState extends State<ChatDetail> {
itemCount: recentMessageKeys.length, itemCount: recentMessageKeys.length,
reverse: true, reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage( itemBuilder: (BuildContext context, index) => ChatMessage(
name: this.friend.name, name: this._friend.name,
message: recentMessages[recentMessageKeys[index]], message: recentMessages[recentMessageKeys[index]],
) )
)), )),
if (!this.friend.isClosed) if (!this._friend.isClosed)
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row( child: Row(

8
lib/apps/chat/list.dart

@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
import 'package:esse/utils/adaptive.dart'; import 'package:esse/utils/adaptive.dart';
import 'package:esse/l10n/localizations.dart'; import 'package:esse/l10n/localizations.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/session.dart';
import 'package:esse/apps/chat/provider.dart'; import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/chat/models.dart'; import 'package:esse/apps/chat/models.dart';
@ -34,7 +35,7 @@ class _ChatListState extends State<ChatList> {
onPressed: () { onPressed: () {
final widget = ChatAddPage(); final widget = ChatAddPage();
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
} }
@ -60,7 +61,6 @@ class ListChat extends StatelessWidget {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
context.read<ChatProvider>().updateActivedFriend(friend.id);
if (!isDesktop) { if (!isDesktop) {
Navigator.push( Navigator.push(
context, context,
@ -69,7 +69,9 @@ class ListChat extends StatelessWidget {
), ),
); );
} else { } else {
context.read<AccountProvider>().updateActivedApp(ChatDetail()); context.read<AccountProvider>().updateActivedSessionFromList(
friend.id, SessionType.Chat, ChatDetail()
);
} }
}, },
child: Container( child: Container(

4
lib/apps/device/page.dart

@ -152,7 +152,7 @@ class _DevicesPageState extends State<DevicesPage> {
Provider.of<DeviceProvider>(context, listen: false).updateActivedDevice(device.id); Provider.of<DeviceProvider>(context, listen: false).updateActivedDevice(device.id);
final widget = DeviceListenPage(); final widget = DeviceListenPage();
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
} }
@ -355,7 +355,7 @@ class _DeviceListenPageState extends State<DeviceListenPage> {
onTap: () { onTap: () {
Provider.of<DeviceProvider>(context, listen: false).clear(); Provider.of<DeviceProvider>(context, listen: false).clear();
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(DevicesPage()); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, DevicesPage());
} else { } else {
Navigator.pop(context); Navigator.pop(context);
} }

2
lib/apps/file/page.dart

@ -39,7 +39,7 @@ class _FolderListState extends State<FolderList> {
loadFolder(bool isDesktop, int index) async { loadFolder(bool isDesktop, int index) async {
final widget = FilePage(title: FILE_DIRECTORY[index][0]); final widget = FilePage(title: FILE_DIRECTORY[index][0]);
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
} }

7
lib/apps/group_chat/page.dart

@ -5,6 +5,7 @@ import 'package:esse/utils/adaptive.dart';
import 'package:esse/utils/file_image.dart'; import 'package:esse/utils/file_image.dart';
import 'package:esse/l10n/localizations.dart'; import 'package:esse/l10n/localizations.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/session.dart';
import 'package:esse/apps/group_chat/add.dart'; import 'package:esse/apps/group_chat/add.dart';
import 'package:esse/apps/group_chat/detail.dart'; import 'package:esse/apps/group_chat/detail.dart';
@ -34,7 +35,7 @@ class _GroupChatListState extends State<GroupChatList> {
onPressed: () { onPressed: () {
final widget = GroupAddPage(); final widget = GroupAddPage();
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
} }
@ -68,7 +69,9 @@ class ListChat extends StatelessWidget {
), ),
); );
} else { } else {
context.read<AccountProvider>().updateActivedApp(GroupChatDetail()); context.read<AccountProvider>().updateActivedSessionFromList(
group.id, SessionType.Group, GroupChatDetail()
);
} }
}, },
child: Container( child: Container(

4
lib/apps/service/list.dart

@ -73,10 +73,10 @@ class ListInnerService extends StatelessWidget {
final widgets = this.callback(); final widgets = this.callback();
if (widgets != null) { if (widgets != null) {
if (this.isDesktop) { if (this.isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widgets[0], widgets[1], widgets[2]); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widgets[0], widgets[1], widgets[2]);
} else { } else {
if (widgets[2] != null) { if (widgets[2] != null) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(null, widgets[1], widgets[2]); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, null, widgets[1], widgets[2]);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widgets[0])); Navigator.push(context, MaterialPageRoute(builder: (_) => widgets[0]));
} }

6
lib/apps/service/models.dart

@ -46,17 +46,17 @@ extension InnerServiceExtension on InnerService {
listHome = GroupChatList(); listHome = GroupChatList();
break; break;
} }
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(coreWidget, listTitle, listHome); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, coreWidget, listTitle, listHome);
} else { } else {
switch (this) { switch (this) {
case InnerService.Files: case InnerService.Files:
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(null, lang.files, FolderList()); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, null, lang.files, FolderList());
break; break;
case InnerService.Assistant: case InnerService.Assistant:
Navigator.push(context, MaterialPageRoute(builder: (_) => AssistantPage())); Navigator.push(context, MaterialPageRoute(builder: (_) => AssistantPage()));
break; break;
case InnerService.GroupChat: case InnerService.GroupChat:
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(null, lang.groupChat, GroupChatList()); Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, null, lang.groupChat, GroupChatList());
break; break;
} }
} }

10
lib/pages/home.dart

@ -135,7 +135,7 @@ class _HomeListState extends State<HomeList> with SingleTickerProviderStateMixin
.systemAppFriendAddNew = false; .systemAppFriendAddNew = false;
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false) Provider.of<AccountProvider>(context, listen: false)
.updateActivedApp(widget); .updateActivedSession(0, widget);
} else { } else {
Navigator.push( Navigator.push(
context, MaterialPageRoute(builder: (_) => widget)); context, MaterialPageRoute(builder: (_) => widget));
@ -199,7 +199,7 @@ class _HomeListState extends State<HomeList> with SingleTickerProviderStateMixin
} else if (value == 1) { } else if (value == 1) {
final widget = ChatAddPage(); final widget = ChatAddPage();
if (isDesktop) { if (isDesktop) {
provider.updateActivedApp(widget); provider.updateActivedSession(0, widget);
} else { } else {
provider.systemAppFriendAddNew = false; provider.systemAppFriendAddNew = false;
setState(() {}); setState(() {});
@ -319,7 +319,7 @@ class DrawerWidget extends StatelessWidget {
final widget = DevicesPage(); final widget = DevicesPage();
if (isDesktop) { if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false) Provider.of<AccountProvider>(context, listen: false)
.updateActivedApp(widget); .updateActivedSession(0, widget);
} else { } else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
} }
@ -387,7 +387,7 @@ class DrawerWidget extends StatelessWidget {
style: TextStyle(fontSize: 16.0)), style: TextStyle(fontSize: 16.0)),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<AccountProvider>(context, listen: false).updateActivedApp( Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0,
null, lang.contact, ChatList() null, lang.contact, ChatList()
); );
}), }),
@ -397,7 +397,7 @@ class DrawerWidget extends StatelessWidget {
style: TextStyle(fontSize: 16.0)), style: TextStyle(fontSize: 16.0)),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Provider.of<AccountProvider>(context, listen: false).updateActivedApp( Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0,
null, lang.groups, ServiceList() null, lang.groups, ServiceList()
); );
}), }),

131
lib/provider.dart

@ -20,22 +20,30 @@ class AccountProvider extends ChangeNotifier {
String activedAccountId; // actived account gid. String activedAccountId; // actived account gid.
Account get activedAccount => this.accounts[activedAccountId]; Account get activedAccount => this.accounts[activedAccountId];
/// current user's did.
String get id => this.activedAccount.id;
bool systemAppFriendAddNew = false;
/// home sessions. sorded by last_time. /// home sessions. sorded by last_time.
Map<int, Session> sessions = {}; Map<int, Session> sessions = {};
List<int> topKeys = []; List<int> topKeys = [];
List<int> orderKeys = []; List<int> orderKeys = [];
/// current user's did. /// actived session.
String get id => this.activedAccount.id; int actived = 0;
Session get activedSession => this.sessions[actived];
/// left home list sessions widget.
String homeShowTitle = ''; String homeShowTitle = '';
Widget defaultListShow = DefaultHomeShow(); Widget defaultListShow = DefaultHomeShow();
Widget currentListShow = null; Widget currentListShow = null;
Widget coreShowWidget = DefaultCoreShow();
bool systemAppFriendAddNew = false;
Widget get homeShowWidget => this.currentListShow ?? this.defaultListShow; Widget get homeShowWidget => this.currentListShow ?? this.defaultListShow;
/// right main screen show session details.
Widget coreShowWidget = DefaultCoreShow();
void orderSessions(int id) { void orderSessions(int id) {
if (this.orderKeys.length == 0 || this.orderKeys[0] != id) { if (this.orderKeys.length == 0 || this.orderKeys[0] != id) {
this.orderKeys.remove(id); this.orderKeys.remove(id);
@ -56,7 +64,10 @@ class AccountProvider extends ChangeNotifier {
rpc.addListener('session-last', _sessionLast, true); rpc.addListener('session-last', _sessionLast, true);
rpc.addListener('session-create', _sessionCreate, true); rpc.addListener('session-create', _sessionCreate, true);
rpc.addListener('session-update', _sessionUpdate, false); rpc.addListener('session-update', _sessionUpdate, false);
rpc.addListener('session-delete', _sessionDelete, false); rpc.addListener('session-close', _sessionClose, false);
rpc.addListener('session-connect', _sessionConnect, false);
rpc.addListener('session-suspend', _sessionSuspend, false);
rpc.addListener('session-lost', _sessionLost, false);
systemInfo(); systemInfo();
} }
@ -162,24 +173,6 @@ class AccountProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
updateToHome() {
this.homeShowTitle = '';
this.currentListShow = null;
notifyListeners();
}
updateActivedApp([Widget coreWidget, String title, Widget homeWidget]) {
if (homeWidget != null && title != null) {
this.homeShowTitle = title;
this.currentListShow = homeWidget;
}
if (coreWidget != null) {
this.coreShowWidget = coreWidget;
}
this.systemAppFriendAddNew = false;
notifyListeners();
}
clearActivedAccount() { clearActivedAccount() {
this.topKeys.clear(); this.topKeys.clear();
} }
@ -208,6 +201,58 @@ class AccountProvider extends ChangeNotifier {
rpc.send('account-system-info', []); rpc.send('account-system-info', []);
} }
updateToHome() {
this.homeShowTitle = '';
this.currentListShow = null;
notifyListeners();
}
updateActivedSessionFromList(int fid, SessionType type, Widget coreWidget) {
int id = 0;
for (int k in this.sessions.keys) {
final v = this.sessions[k];
if (v.type == type && v.fid == fid) {
id = k;
break;
}
}
if (id > 0) {
if (this.actived > 0) {
rpc.send('session-suspend', [this.actived, this.activedSession.gid]);
}
this.actived = id;
if (this.activedSession.online == OnlineType.Lost) {
rpc.send('session-connect', [id, this.activedSession.gid]);
}
}
}
updateActivedSession(int id, [Widget coreWidget, String title, Widget homeWidget]) {
if (id > 0) {
if (this.actived > 0) {
rpc.send('session-suspend', [this.actived, this.activedSession.gid]);
}
this.actived = id;
if (this.activedSession.online == OnlineType.Lost) {
rpc.send('session-connect', [id, this.activedSession.gid]);
}
}
if (homeWidget != null && title != null) {
this.homeShowTitle = title;
this.currentListShow = homeWidget;
}
if (coreWidget != null) {
this.coreShowWidget = coreWidget;
}
this.systemAppFriendAddNew = false;
notifyListeners();
}
// -- callback when receive rpc info. -- // // -- callback when receive rpc info. -- //
_systemInfo(List params) { _systemInfo(List params) {
Global.addr = '0x' + params[0]; Global.addr = '0x' + params[0];
@ -248,13 +293,13 @@ class AccountProvider extends ChangeNotifier {
params.forEach((params) { params.forEach((params) {
final id = params[0]; final id = params[0];
this.sessions[id] = Session.fromList(params); this.sessions[id] = Session.fromList(params);
if (!this.sessions[id].isClose) {
if (this.sessions[id].isTop) { if (this.sessions[id].isTop) {
this.topKeys.add(id); this.topKeys.add(id);
} else { } else {
this.orderKeys.add(id); this.orderKeys.add(id);
}
} }
}); });
notifyListeners(); notifyListeners();
} }
@ -286,11 +331,31 @@ class AccountProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
_sessionDelete(List params) { _sessionClose(List params) {
final id = params[1]; final id = params[0];
this.sessions.remove(id); this.sessions[id].isClose = true;
this.orderKeys.remove(id); this.orderKeys.remove(id);
this.topKeys.remove(id); this.topKeys.remove(id);
notifyListeners(); notifyListeners();
} }
_sessionConnect(List params) {
final id = params[0];
final addr = params[1];
this.sessions[id].addr = addr;
this.sessions[id].online = OnlineType.Active;
notifyListeners();
}
_sessionSuspend(List params) {
final id = params[0];
this.sessions[id].online = OnlineType.Suspend;
notifyListeners();
}
_sessionLost(List params) {
final id = params[0];
this.sessions[id].online = OnlineType.Lost;
notifyListeners();
}
} }

23
lib/session.dart

@ -40,6 +40,13 @@ extension SessionTypeExtension on SessionType {
} }
} }
enum OnlineType {
Waiting,
Active,
Suspend,
Lost,
}
class Session { class Session {
int id; int id;
int fid; int fid;
@ -48,10 +55,11 @@ class Session {
SessionType type; SessionType type;
String name; String name;
bool isTop; bool isTop;
bool isClose;
RelativeTime lastTime; RelativeTime lastTime;
String lastContent; String lastContent;
bool lastReaded; bool lastReaded;
bool online = false; OnlineType online;
static List innerService(InnerService service, AppLocalizations lang) { static List innerService(InnerService service, AppLocalizations lang) {
final params = service.params(lang); final params = service.params(lang);
@ -87,13 +95,14 @@ class Session {
width: width, width: width,
name: this.name, name: this.name,
avatarPath: avatar, avatarPath: avatar,
online: this.online, online: true,
needOnline: needOnline, needOnline: needOnline,
hasNew: !this.lastReaded, hasNew: !this.lastReaded,
); );
} }
last(List params) { last(List params) {
this.isClose = false;
this.lastTime = RelativeTime.fromInt(params[1]); this.lastTime = RelativeTime.fromInt(params[1]);
this.lastContent = params[2]; this.lastContent = params[2];
this.lastReaded = params[3]; this.lastReaded = params[3];
@ -103,7 +112,6 @@ class Session {
this.addr = params[1]; this.addr = params[1];
this.name = params[2]; this.name = params[2];
this.isTop = params[3]; this.isTop = params[3];
this.online = params[4];
} }
Session.fromList(List params) { Session.fromList(List params) {
@ -114,9 +122,10 @@ class Session {
this.type = SessionTypeExtension.fromInt(params[4]); this.type = SessionTypeExtension.fromInt(params[4]);
this.name = params[5]; this.name = params[5];
this.isTop = params[6]; this.isTop = params[6];
this.lastTime = RelativeTime.fromInt(params[7]); this.isClose = params[7];
this.lastContent = params[8]; this.lastTime = RelativeTime.fromInt(params[8]);
this.lastReaded = params[9]; this.lastContent = params[9];
this.online = params[10]; this.lastReaded = params[10];
this.online = OnlineType.Lost;
} }
} }

51
lib/widgets/default_home_show.dart

@ -8,6 +8,9 @@ import 'package:esse/options.dart';
import 'package:esse/provider.dart'; import 'package:esse/provider.dart';
import 'package:esse/session.dart'; import 'package:esse/session.dart';
import 'package:esse/apps/chat/detail.dart';
import 'package:esse/apps/assistant/page.dart';
class DefaultHomeShow extends StatelessWidget { class DefaultHomeShow extends StatelessWidget {
const DefaultHomeShow({Key key}): super(key: key); const DefaultHomeShow({Key key}): super(key: key);
@ -28,7 +31,7 @@ class DefaultHomeShow extends StatelessWidget {
// onPressed: () { // onPressed: () {
// final widget = Text(''); // final widget = Text('');
// if (isDesktop) { // if (isDesktop) {
// Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget); // Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
// } else { // } else {
// Navigator.push(context, MaterialPageRoute(builder: (_) => widget)); // Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
// } // }
@ -54,21 +57,41 @@ class _SessionWidget extends StatelessWidget {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
String listTitle = "";
Widget listWidget = null;
Widget coreWidget = null;
// TODO switch (session.type) {
case SessionType.Chat:
// context.read<ChatProvider>().updateActivedFriend(friend.id); if (!isDesktop) {
coreWidget = ChatPage();
} else {
coreWidget = ChatDetail();
}
break;
case SessionType.Group:
break;
case SessionType.Assistant:
if (!isDesktop) {
coreWidget = AssistantPage();
} else {
coreWidget = AssistantDetail();
}
break;
}
// if (!isDesktop) { if (!isDesktop) {
// Navigator.push( Navigator.push(
// context, context,
// MaterialPageRoute( MaterialPageRoute(
// builder: (_) => ChatPage(), builder: (_) => coreWidget,
// ), ),
// ); );
// } else { } else {
// context.read<AccountProvider>().updateActivedApp(ChatDetail()); context.read<AccountProvider>().updateActivedSession(
// } session.id, coreWidget, listTitle, listWidget
);
}
}, },
child: Container( child: Container(
height: 55.0, height: 55.0,

84
lib/widgets/list_service.dart

@ -1,84 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:esse/utils/adaptive.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/provider/account.dart';
import 'package:esse/apps/assistant/page.dart';
class ListService extends StatelessWidget {
const ListService({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
final widget = AssistantPage();
if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget);
} else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
}
},
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),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/logo/logo_light.png'),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(15.0)
),
),
Expanded(
child: Container(
height: 55.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text('esse',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0))
),
Container(
margin: const EdgeInsets.only(left: 15.0, right: 20.0),
child: Text('2021-11-12',
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0),
),
)
]),
SizedBox(height: 5.0),
Expanded(
child: Text('esse is a echo robot',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Color(0xFFADB0BB), fontSize: 12.0)),
),
],
),
),
),
],
),
),
);
}
}

86
src/apps/chat/layer.rs

@ -14,7 +14,7 @@ use tdn_did::{user::User, Proof};
use crate::event::{InnerEvent, StatusEvent}; use crate::event::{InnerEvent, StatusEvent};
use crate::layer::{Layer, Online}; use crate::layer::{Layer, Online};
use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH}; use crate::migrate::consensus::{FRIEND_TABLE_PATH, MESSAGE_TABLE_PATH, REQUEST_TABLE_PATH};
use crate::rpc::{session_create, session_last}; use crate::rpc::{session_connect, session_create, session_last, session_lost, session_suspend};
use crate::session::{Session, SessionType}; use crate::session::{Session, SessionType};
use crate::storage::{ use crate::storage::{
chat_db, read_avatar, read_file, read_record, session_db, write_avatar_sync, write_file, chat_db, read_avatar, read_file, read_record, session_db, write_avatar_sync, write_file,
@ -56,8 +56,12 @@ pub(crate) enum LayerResponse {
/// ESSE chat layer Event. /// ESSE chat layer Event.
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub(crate) enum LayerEvent { pub(crate) enum LayerEvent {
/// receiver gid, sender gid. as BaseLayerEvent. /// offline. extend BaseLayerEvent.
Offline(GroupId), Offline(GroupId),
/// suspend. extend BaseLayerEvent.
Suspend(GroupId),
/// actived. extend BaseLayerEvent.
Actived(GroupId),
/// receiver gid, sender gid. as BaseLayerEvent. /// receiver gid, sender gid. as BaseLayerEvent.
OnlinePing, OnlinePing,
/// receiver gid, sender gid. as BaseLayerEvent. /// receiver gid, sender gid. as BaseLayerEvent.
@ -95,12 +99,18 @@ pub(crate) async fn handle(
} }
let f = friend.unwrap(); // safe. let f = friend.unwrap(); // safe.
// 0. get session. TODO
let sid = 0;
// 1. check verify. // 1. check verify.
proof.verify(&fgid, &addr, &layer.addr)?; proof.verify(&fgid, &addr, &layer.addr)?;
// 2. online this group. // 2. online this group.
layer layer.running_mut(&mgid)?.check_add_online(
.running_mut(&mgid)? fgid,
.check_add_online(fgid, Online::Direct(addr), f.id)?; Online::Direct(addr),
sid,
f.id,
)?;
// 3. update remote addr. TODO // 3. update remote addr. TODO
if f.addr != addr { if f.addr != addr {
let db = chat_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
@ -155,10 +165,15 @@ pub(crate) async fn handle(
let mut friend = some_friend.unwrap(); // safe checked. let mut friend = some_friend.unwrap(); // safe checked.
// already friendship & update. // already friendship & update.
// 0. get session. TODO
let sid = 0;
// 1. online this group. // 1. online this group.
layer.running_mut(&mgid)?.check_add_online( layer.running_mut(&mgid)?.check_add_online(
fgid, fgid,
Online::Direct(addr), Online::Direct(addr),
sid,
friend.id, friend.id,
)?; )?;
// 2. update remote user. // 2. update remote user.
@ -258,10 +273,17 @@ pub(crate) async fn handle(
} }
let fid = some_friend.unwrap().id; // safe. let fid = some_friend.unwrap().id; // safe.
// 0. get session. TODO
let sid = 0;
// 3. online this group. // 3. online this group.
layer layer.running_mut(&mgid)?.check_add_online(
.running_mut(&mgid)? fgid,
.check_add_online(fgid, Online::Direct(addr), fid)?; Online::Direct(addr),
sid,
fid,
)?;
// 4. update remote addr. // 4. update remote addr.
let db = chat_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
Friend::addr_update(&db, fid, &addr)?; Friend::addr_update(&db, fid, &addr)?;
@ -278,10 +300,14 @@ pub(crate) async fn handle(
// 1. check verify. // 1. check verify.
proof.verify(&fgid, &addr, &layer.addr)?; proof.verify(&fgid, &addr, &layer.addr)?;
if let Some(friend) = load_friend(&layer.base, &mgid, &fgid)? { if let Some(friend) = load_friend(&layer.base, &mgid, &fgid)? {
// 0. get session. TODO
let sid = 0;
// already friendship. // already friendship.
layer.running_mut(&mgid)?.check_add_online( layer.running_mut(&mgid)?.check_add_online(
fgid, fgid,
Online::Direct(addr), Online::Direct(addr),
sid,
friend.id, friend.id,
)?; )?;
results.rpcs.push(rpc::friend_online(mgid, friend.id, addr)); results.rpcs.push(rpc::friend_online(mgid, friend.id, addr));
@ -353,10 +379,16 @@ pub(crate) async fn handle(
} }
let fid = some_friend.unwrap().id; // safe. let fid = some_friend.unwrap().id; // safe.
// 0. get session. TODO
let sid = 0;
// 3. online this group. // 3. online this group.
layer layer.running_mut(&mgid)?.check_add_online(
.running_mut(&mgid)? fgid,
.check_add_online(fgid, Online::Direct(addr), fid)?; Online::Direct(addr),
sid,
fid,
)?;
// 4. update remote addr. // 4. update remote addr.
let db = chat_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
Friend::addr_update(&db, fid, &addr)?; Friend::addr_update(&db, fid, &addr)?;
@ -376,10 +408,14 @@ pub(crate) async fn handle(
// 1. check verify. // 1. check verify.
proof.verify(&fgid, &addr, &layer.addr)?; proof.verify(&fgid, &addr, &layer.addr)?;
if let Some(friend) = load_friend(&layer.base, &mgid, &fgid)? { if let Some(friend) = load_friend(&layer.base, &mgid, &fgid)? {
// 0. get session. TODO
let sid = 0;
// already friendship. // already friendship.
layer.running_mut(&mgid)?.check_add_online( layer.running_mut(&mgid)?.check_add_online(
fgid, fgid,
Online::Direct(addr), Online::Direct(addr),
sid,
friend.id, friend.id,
)?; )?;
results.rpcs.push(rpc::friend_online(mgid, friend.id, addr)); results.rpcs.push(rpc::friend_online(mgid, friend.id, addr));
@ -499,11 +535,24 @@ impl LayerEvent {
) -> Result<HandleResult> { ) -> Result<HandleResult> {
let event: LayerEvent = let event: LayerEvent =
postcard::from_bytes(&bytes).map_err(|_| new_io_error("serialize event error."))?; postcard::from_bytes(&bytes).map_err(|_| new_io_error("serialize event error."))?;
let fid = layer.get_running_remote_id(&mgid, &fgid)?; let (sid, fid) = layer.get_running_remote_id(&mgid, &fgid)?;
let mut results = HandleResult::new(); let mut results = HandleResult::new();
match event { match event {
LayerEvent::Offline(_) => {
layer.running_mut(&mgid)?.check_offline(&fgid, &addr);
results.rpcs.push(session_lost(mgid, &sid));
}
LayerEvent::Suspend(_) => {
if layer.running_mut(&mgid)?.suspend(&fgid, false)? {
results.rpcs.push(session_suspend(mgid, &sid));
}
}
LayerEvent::Actived(_) => {
let _ = layer.running_mut(&mgid)?.active(&fgid, false);
results.rpcs.push(session_connect(mgid, &sid, &addr));
}
LayerEvent::Message(hash, m) => { LayerEvent::Message(hash, m) => {
let db = chat_db(&layer.base, &mgid)?; let db = chat_db(&layer.base, &mgid)?;
if !Message::exist(&db, &hash)? { if !Message::exist(&db, &hash)? {
@ -579,7 +628,7 @@ impl LayerEvent {
)?; )?;
layer layer
.running_mut(&mgid)? .running_mut(&mgid)?
.check_add_online(fgid, Online::Direct(addr), fid)?; .check_add_online(fgid, Online::Direct(addr), sid, fid)?;
results.rpcs.push(rpc::friend_online(mgid, fid, addr)); results.rpcs.push(rpc::friend_online(mgid, fid, addr));
let data = postcard::to_allocvec(&LayerEvent::OnlinePong).unwrap_or(vec![]); let data = postcard::to_allocvec(&LayerEvent::OnlinePong).unwrap_or(vec![]);
let msg = SendType::Event(0, addr, data); let msg = SendType::Event(0, addr, data);
@ -593,18 +642,9 @@ impl LayerEvent {
)?; )?;
layer layer
.running_mut(&mgid)? .running_mut(&mgid)?
.check_add_online(fgid, Online::Direct(addr), fid)?; .check_add_online(fgid, Online::Direct(addr), sid, fid)?;
results.rpcs.push(rpc::friend_online(mgid, fid, addr)); results.rpcs.push(rpc::friend_online(mgid, fid, addr));
} }
LayerEvent::Offline(_) => {
layer.group.write().await.status(
&mgid,
StatusEvent::SessionFriendOffline(fgid),
&mut results,
)?;
layer.running_mut(&mgid)?.check_offline(&fgid, &addr);
results.rpcs.push(rpc::friend_offline(mgid, fid, &fgid));
}
LayerEvent::Close => { LayerEvent::Close => {
layer.group.write().await.broadcast( layer.group.write().await.broadcast(
&mgid, &mgid,

32
src/apps/group_chat/layer.rs

@ -14,6 +14,7 @@ use tdn_did::Proof;
use tdn_storage::local::DStorage; use tdn_storage::local::DStorage;
use crate::layer::{Layer, Online}; use crate::layer::{Layer, Online};
use crate::rpc::{session_connect, session_lost, session_suspend};
use crate::storage::{group_chat_db, write_avatar_sync}; use crate::storage::{group_chat_db, write_avatar_sync};
use super::models::{from_network_message, GroupChat, Member, Request}; use super::models::{from_network_message, GroupChat, Member, Request};
@ -46,10 +47,14 @@ pub(crate) async fn handle(
gc.ok(&db)?; gc.ok(&db)?;
results.rpcs.push(rpc::create_result(mgid, gc.id, ok)); results.rpcs.push(rpc::create_result(mgid, gc.id, ok));
// 0. get session. TODO
let sid = 0;
// online this group. // online this group.
layer.write().await.running_mut(&mgid)?.check_add_online( layer.write().await.running_mut(&mgid)?.check_add_online(
gcd, gcd,
Online::Direct(addr), Online::Direct(addr),
sid,
gc.id, gc.id,
)?; )?;
} }
@ -65,10 +70,15 @@ pub(crate) async fn handle(
if group.g_addr != addr { if group.g_addr != addr {
return Ok(results); return Ok(results);
} }
// 2. get group session.
let sid = 0; // TODO
// 2. online this group. // 2. online this group.
layer_lock.running_mut(&mgid)?.check_add_online( layer_lock.running_mut(&mgid)?.check_add_online(
gcd, gcd,
Online::Direct(addr), Online::Direct(addr),
sid,
group.id, group.id,
)?; )?;
// 3. online to UI. // 3. online to UI.
@ -148,8 +158,10 @@ async fn handle_event(
results: &mut HandleResult, results: &mut HandleResult,
) -> Result<()> { ) -> Result<()> {
println!("Got event......."); println!("Got event.......");
let gid = match event { let (sid, gid) = match event {
LayerEvent::Offline(gcd) LayerEvent::Offline(gcd)
| LayerEvent::Suspend(gcd)
| LayerEvent::Actived(gcd)
| LayerEvent::OnlinePing(gcd) | LayerEvent::OnlinePing(gcd)
| LayerEvent::OnlinePong(gcd) | LayerEvent::OnlinePong(gcd)
| LayerEvent::MemberOnline(gcd, ..) | LayerEvent::MemberOnline(gcd, ..)
@ -161,7 +173,23 @@ async fn handle_event(
match event { match event {
LayerEvent::Offline(gcd) => { LayerEvent::Offline(gcd) => {
results.rpcs.push(rpc::group_offline(mgid, gid, &gcd)); let mut layer_lock = layer.write().await;
layer_lock.running_mut(&mgid)?.check_offline(&gcd, &addr);
drop(layer_lock);
results.rpcs.push(session_lost(mgid, &sid));
}
LayerEvent::Suspend(gcd) => {
let mut layer_lock = layer.write().await;
if layer_lock.running_mut(&mgid)?.suspend(&gcd, false)? {
results.rpcs.push(session_suspend(mgid, &sid));
}
drop(layer_lock);
}
LayerEvent::Actived(gcd) => {
let mut layer_lock = layer.write().await;
let _ = layer_lock.running_mut(&mgid)?.active(&gcd, false);
drop(layer_lock);
results.rpcs.push(session_connect(mgid, &sid, &addr));
} }
LayerEvent::OnlinePing(gcd) => { LayerEvent::OnlinePing(gcd) => {
results.rpcs.push(rpc::group_online(mgid, gid)); results.rpcs.push(rpc::group_online(mgid, gid));

10
src/event.rs

@ -490,16 +490,6 @@ impl StatusEvent {
results results
.rpcs .rpcs
.push(chat_rpc::friend_online(gid, f.id, f.addr)); .push(chat_rpc::friend_online(gid, f.id, f.addr));
let layer_lock = layer.clone();
let rgid = f.gid;
let fid = f.id;
let ggid = gid.clone();
tdn::smol::spawn(async move {
if let Ok(running) = layer_lock.write().await.running_mut(&ggid) {
let _ = running.check_add_online(rgid, Online::Relay(addr), fid);
}
})
.detach();
} }
} }
StatusEvent::SessionFriendOffline(rgid) => { StatusEvent::SessionFriendOffline(rgid) => {

144
src/layer.rs

@ -7,7 +7,7 @@ use tdn::{
types::{ types::{
group::GroupId, group::GroupId,
message::SendType, message::SendType,
primitive::{new_io_error, PeerAddr, Result}, primitive::{new_io_error, HandleResult, PeerAddr, Result},
}, },
}; };
@ -21,8 +21,12 @@ use crate::storage::session_db;
/// EVERY LAYER APP MUST EQUAL THE FIRST THREE FIELDS. /// EVERY LAYER APP MUST EQUAL THE FIRST THREE FIELDS.
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub(crate) enum LayerEvent { pub(crate) enum LayerEvent {
/// offline, remote_gid. /// Offline. params: remote_id.
Offline(GroupId), Offline(GroupId),
/// Suspend. params: remote_id.
Suspend(GroupId),
/// Actived. params: remote_id.
Actived(GroupId),
} }
/// ESSE layers. /// ESSE layers.
@ -104,7 +108,7 @@ impl Layer {
addrs addrs
} }
pub fn get_running_remote_id(&self, mgid: &GroupId, fgid: &GroupId) -> Result<i64> { pub fn get_running_remote_id(&self, mgid: &GroupId, fgid: &GroupId) -> Result<(i64, i64)> {
self.running(mgid)?.get_online_id(fgid) self.running(mgid)?.get_online_id(fgid)
} }
@ -156,7 +160,7 @@ impl Layer {
} }
/// online info. /// online info.
pub enum Online { pub(crate) enum Online {
/// connected to this device. /// connected to this device.
Direct(PeerAddr), Direct(PeerAddr),
/// connected to other device. /// connected to other device.
@ -171,42 +175,98 @@ impl Online {
} }
} }
pub(crate) struct OnlineSession {
pub online: Online,
pub db_id: i64,
pub db_fid: i64,
pub suspend_me: bool,
pub suspend_remote: bool,
pub remain: u16, // keep-alive remain minutes
}
impl OnlineSession {
fn new(online: Online, db_id: i64, db_fid: i64) -> Self {
Self {
online,
db_id,
db_fid,
suspend_me: false,
suspend_remote: false,
remain: 0,
}
}
}
pub(crate) struct RunningAccount { pub(crate) struct RunningAccount {
/// online group (friends/services) => (group's address, group's db id) /// online group (friends/services) => (group's address, group's db id)
onlines: HashMap<GroupId, (Online, i64)>, sessions: HashMap<GroupId, OnlineSession>,
} }
impl RunningAccount { impl RunningAccount {
pub fn init() -> Self { pub fn init() -> Self {
RunningAccount { RunningAccount {
onlines: HashMap::new(), sessions: HashMap::new(),
}
}
pub fn active(&mut self, gid: &GroupId, is_me: bool) -> Option<PeerAddr> {
if let Some(online) = self.sessions.get_mut(gid) {
if is_me {
online.suspend_me = false;
} else {
online.suspend_remote = false;
}
online.remain = 0;
Some(*online.online.addr())
} else {
None
}
}
pub fn suspend(&mut self, gid: &GroupId, is_me: bool) -> Result<bool> {
if let Some(online) = self.sessions.get_mut(gid) {
if is_me {
online.suspend_me = true;
} else {
online.suspend_remote = true;
}
if online.suspend_remote && online.suspend_me {
online.remain = 120; // keep-alive 2~3 minutes
Ok(true)
} else {
Ok(false)
}
} else {
Err(new_io_error("remote not online"))
} }
} }
pub fn get_online_id(&self, gid: &GroupId) -> Result<i64> { pub fn get_online_id(&self, gid: &GroupId) -> Result<(i64, i64)> {
self.onlines self.sessions
.get(gid) .get(gid)
.map(|(_, id)| *id) .map(|online| (online.db_id, online.db_fid))
.ok_or(new_io_error("remote not online")) .ok_or(new_io_error("remote not online"))
} }
/// get all onlines's groupid /// get all sessions's groupid
pub fn is_online(&self, gid: &GroupId) -> bool { pub fn is_online(&self, gid: &GroupId) -> bool {
self.onlines.contains_key(gid) self.sessions.contains_key(gid)
} }
/// get online peer's addr. /// get online peer's addr.
pub fn online(&self, gid: &GroupId) -> Result<PeerAddr> { pub fn online(&self, gid: &GroupId) -> Result<PeerAddr> {
self.onlines self.sessions
.get(gid) .get(gid)
.map(|(online, _)| *online.addr()) .map(|online| *online.online.addr())
.ok_or(new_io_error("remote not online")) .ok_or(new_io_error("remote not online"))
} }
pub fn online_direct(&self, gid: &GroupId) -> Result<PeerAddr> { pub fn online_direct(&self, gid: &GroupId) -> Result<PeerAddr> {
if let Some((online, _)) = self.onlines.get(gid) { if let Some(online) = self.sessions.get(gid) {
match online { match online.online {
Online::Direct(addr) => return Ok(*addr), Online::Direct(addr) => return Ok(addr),
_ => {} _ => {}
} }
} }
@ -215,36 +275,44 @@ impl RunningAccount {
/// get all online peer. /// get all online peer.
pub fn onlines(&self) -> Vec<(&GroupId, &PeerAddr)> { pub fn onlines(&self) -> Vec<(&GroupId, &PeerAddr)> {
self.onlines self.sessions
.iter() .iter()
.map(|(fgid, (online, _))| (fgid, online.addr())) .map(|(fgid, online)| (fgid, online.online.addr()))
.collect() .collect()
} }
/// check add online. /// check add online.
pub fn check_add_online(&mut self, gid: GroupId, online: Online, id: i64) -> Result<()> { pub fn check_add_online(
if let Some((o, _)) = self.onlines.get(&gid) { &mut self,
match (o, &online) { gid: GroupId,
online: Online,
id: i64,
fid: i64,
) -> Result<()> {
if let Some(o) = self.sessions.get(&gid) {
match (&o.online, &online) {
(Online::Relay(..), Online::Direct(..)) => { (Online::Relay(..), Online::Direct(..)) => {
self.onlines.insert(gid, (online, id)); self.sessions
.insert(gid, OnlineSession::new(online, id, fid));
Ok(()) Ok(())
} }
_ => Err(new_io_error("remote had online")), _ => Err(new_io_error("remote had online")),
} }
} else { } else {
self.onlines.insert(gid, (online, id)); self.sessions
.insert(gid, OnlineSession::new(online, id, fid));
Ok(()) Ok(())
} }
} }
/// check offline, and return is direct. /// check offline, and return is direct.
pub fn check_offline(&mut self, gid: &GroupId, addr: &PeerAddr) -> bool { pub fn check_offline(&mut self, gid: &GroupId, addr: &PeerAddr) -> bool {
if let Some((online, _)) = self.onlines.remove(gid) { if let Some(online) = self.sessions.remove(gid) {
if online.addr() != addr { if online.online.addr() != addr {
return false; return false;
} }
match online { match online.online {
Online::Direct(..) => { Online::Direct(..) => {
return true; return true;
} }
@ -255,14 +323,16 @@ impl RunningAccount {
} }
pub fn remove_online(&mut self, gid: &GroupId) -> Option<PeerAddr> { pub fn remove_online(&mut self, gid: &GroupId) -> Option<PeerAddr> {
self.onlines.remove(gid).map(|(online, _)| *online.addr()) self.sessions
.remove(gid)
.map(|online| *online.online.addr())
} }
/// remove all onlines peer. /// remove all onlines peer.
pub fn remove_onlines(self) -> Vec<(PeerAddr, GroupId)> { pub fn remove_onlines(self) -> Vec<(PeerAddr, GroupId)> {
let mut peers = vec![]; let mut peers = vec![];
for (fgid, (online, _)) in self.onlines { for (fgid, online) in self.sessions {
match online { match online.online {
Online::Direct(addr) => peers.push((addr, fgid)), Online::Direct(addr) => peers.push((addr, fgid)),
_ => {} _ => {}
} }
@ -272,8 +342,8 @@ impl RunningAccount {
/// check if addr is online. /// check if addr is online.
pub fn check_addr_online(&self, addr: &PeerAddr) -> bool { pub fn check_addr_online(&self, addr: &PeerAddr) -> bool {
for (_, (online, _)) in &self.onlines { for (_, online) in &self.sessions {
if online.addr() == addr { if online.online.addr() == addr {
return true; return true;
} }
} }
@ -283,23 +353,23 @@ impl RunningAccount {
/// peer leave, remove online peer. /// peer leave, remove online peer.
pub fn peer_leave(&mut self, addr: &PeerAddr) -> Vec<(GroupId, i64)> { pub fn peer_leave(&mut self, addr: &PeerAddr) -> Vec<(GroupId, i64)> {
let mut peers = vec![]; let mut peers = vec![];
for (fgid, (online, id)) in &self.onlines { for (fgid, online) in &self.sessions {
if online.addr() == addr { if online.online.addr() == addr {
peers.push((*fgid, *id)) peers.push((*fgid, online.db_id))
} }
} }
for i in &peers { for i in &peers {
self.onlines.remove(&i.0); self.sessions.remove(&i.0);
} }
peers peers
} }
/// list all onlines groups. /// list all onlines groups.
pub fn _list_onlines(&self) -> Vec<(&GroupId, &PeerAddr)> { pub fn _list_onlines(&self) -> Vec<(&GroupId, &PeerAddr)> {
self.onlines self.sessions
.iter() .iter()
.map(|(k, (v, _))| (k, v.addr())) .map(|(k, online)| (k, online.online.addr()))
.collect() .collect()
} }
} }

5
src/migrate/session.rs

@ -8,9 +8,10 @@ pub(super) const SESSION_VERSIONS: [&str; 3] = [
s_type INTEGER NOT NULL, s_type INTEGER NOT NULL,
name TEXT NOT NULL, name TEXT NOT NULL,
is_top INTEGER NOT NULL, is_top INTEGER NOT NULL,
is_close INTEGER NOT NULL,
last_datetime INTEGER, last_datetime INTEGER,
last_content TEXT, last_content TEXT,
last_readed INTEGER);", 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, is_close, last_datetime, last_content, last_readed) VALUES (0, '', '', 4, '', 0, 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. "INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, is_close, last_datetime, last_content, last_readed) VALUES (0, '', '', 2, '', 0, 0, 0, '', 1);", // File.
]; ];

112
src/rpc.rs

@ -97,16 +97,30 @@ pub(crate) fn session_update(
addr: &PeerAddr, addr: &PeerAddr,
name: &str, name: &str,
is_top: bool, is_top: bool,
online: bool,
) -> RpcParam { ) -> RpcParam {
rpc_response( rpc_response(
0, 0,
"session-update", "session-update",
json!([id, addr.to_hex(), name, is_top, online]), json!([id, addr.to_hex(), name, is_top]),
mgid, mgid,
) )
} }
#[inline]
pub(crate) fn session_connect(mgid: GroupId, id: &i64, addr: &PeerAddr) -> RpcParam {
rpc_response(0, "session-connect", json!([id, addr.to_hex()]), mgid)
}
#[inline]
pub(crate) fn session_suspend(mgid: GroupId, id: &i64) -> RpcParam {
rpc_response(0, "session-suspend", json!([id]), mgid)
}
#[inline]
pub(crate) fn session_lost(mgid: GroupId, id: &i64) -> RpcParam {
rpc_response(0, "session-lost", json!([id]), mgid)
}
#[inline] #[inline]
fn session_list(sessions: Vec<Session>) -> RpcParam { fn session_list(sessions: Vec<Session>) -> RpcParam {
let mut results = vec![]; let mut results = vec![];
@ -406,24 +420,6 @@ fn new_rpc_handler(
let mut results = HandleResult::new(); let mut results = HandleResult::new();
let group_lock = state.group.read().await; let group_lock = state.group.read().await;
let db = session_db(group_lock.base(), &gid)?;
let sessions = Session::list(&db)?;
drop(db);
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));
}
_ => {}
}
}
let devices = group_lock.distribute_conns(&gid); let devices = group_lock.distribute_conns(&gid);
for device in devices { for device in devices {
results.groups.push((gid, device)); results.groups.push((gid, device));
@ -469,18 +465,78 @@ fn new_rpc_handler(
handler.add_method( handler.add_method(
"session-list", "session-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move { |gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let layer_lock = state.layer.read().await; let db = session_db(state.layer.read().await.base(), &gid)?;
let db = session_db(layer_lock.base(), &gid)?; Ok(HandleResult::rpc(session_list(Session::list(&db)?)))
let mut sessions = Session::list(&db)?; },
);
handler.add_method(
"session-connect",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let id = params[0].as_i64()?;
let remote = GroupId::from_hex(params[1].as_str()?)?;
let mut layer_lock = state.layer.write().await;
let online = layer_lock.running_mut(&gid)?.active(&remote, true);
drop(layer_lock);
if let Some(addr) = online {
return Ok(HandleResult::rpc(json!([id, addr.to_hex()])));
}
let group_lock = state.group.read().await;
let db = session_db(group_lock.base(), &gid)?;
let s = Session::get(&db, &id)?;
drop(db); drop(db);
let gids: Vec<&GroupId> = sessions.iter().map(|s| &s.gid).collect(); let mut results = HandleResult::new();
let onlines = layer_lock.merge_online(&gid, gids)?; match s.s_type {
for (index, online) in onlines.iter().enumerate() { SessionType::Chat => {
sessions[index].online = *online; 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));
}
_ => {}
}
Ok(results)
},
);
handler.add_method(
"session-suspend",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let id = params[0].as_i64()?;
let remote = GroupId::from_hex(params[1].as_str()?)?;
let mut layer_lock = state.layer.write().await;
let suspend = layer_lock.running_mut(&gid)?.suspend(&remote, true)?;
drop(layer_lock);
let mut results = HandleResult::new();
if suspend {
results.rpcs.push(json!([id]))
} }
Ok(HandleResult::rpc(session_list(sessions))) // let group_lock = state.group.read().await;
// let db = session_db(group_lock.base(), &gid)?;
// let s = Session::get(&db, &id)?;
// drop(db);
// 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));
// }
// _ => {}
// }
Ok(results)
}, },
); );

19
src/session.rs

@ -50,10 +50,10 @@ pub(crate) struct Session {
pub s_type: SessionType, pub s_type: SessionType,
name: String, name: String,
is_top: bool, is_top: bool,
is_close: bool,
pub last_datetime: i64, pub last_datetime: i64,
pub last_content: String, pub last_content: String,
pub last_readed: bool, pub last_readed: bool,
pub online: bool,
} }
impl Session { impl Session {
@ -73,10 +73,10 @@ impl Session {
name, name,
id: 0, id: 0,
is_top: false, is_top: false,
is_close: false,
last_datetime: datetime, last_datetime: datetime,
last_content: "".to_owned(), last_content: "".to_owned(),
last_readed: true, last_readed: true,
online: false,
} }
} }
@ -89,10 +89,10 @@ impl Session {
self.s_type.to_int(), self.s_type.to_int(),
self.name, self.name,
self.is_top, self.is_top,
self.is_close,
self.last_datetime, self.last_datetime,
self.last_content, self.last_content,
self.last_readed, self.last_readed,
self.online
]) ])
} }
@ -101,6 +101,7 @@ impl Session {
last_readed: v.pop().unwrap().as_bool(), last_readed: v.pop().unwrap().as_bool(),
last_content: v.pop().unwrap().as_string(), last_content: v.pop().unwrap().as_string(),
last_datetime: v.pop().unwrap().as_i64(), last_datetime: v.pop().unwrap().as_i64(),
is_close: v.pop().unwrap().as_bool(),
is_top: v.pop().unwrap().as_bool(), is_top: v.pop().unwrap().as_bool(),
name: v.pop().unwrap().as_string(), name: v.pop().unwrap().as_string(),
s_type: SessionType::from_int(v.pop().unwrap().as_i64()), s_type: SessionType::from_int(v.pop().unwrap().as_i64()),
@ -108,18 +109,18 @@ impl Session {
gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()), gid: GroupId::from_hex(v.pop().unwrap().as_str()).unwrap_or(GroupId::default()),
fid: v.pop().unwrap().as_i64(), fid: v.pop().unwrap().as_i64(),
id: v.pop().unwrap().as_i64(), id: v.pop().unwrap().as_i64(),
online: false,
} }
} }
pub fn insert(&mut self, db: &DStorage) -> Result<()> { 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 ({}, '{}', '{}', {}, '{}', {}, {}, '{}', {})", let sql = format!("INSERT INTO sessions (fid, gid, addr, s_type, name, is_top, is_close, last_datetime, last_content, last_readed) VALUES ({}, '{}', '{}', {}, '{}', {}, {}, {}, '{}', {})",
self.fid, self.fid,
self.gid.to_hex(), self.gid.to_hex(),
self.addr.to_hex(), self.addr.to_hex(),
self.s_type.to_int(), self.s_type.to_int(),
self.name, self.name,
if self.is_top { 1 } else { 0 }, if self.is_top { 1 } else { 0 },
if self.is_close { 1 } else { 0 },
self.last_datetime, self.last_datetime,
self.last_content, self.last_content,
if self.last_readed { 1 } else { 0 }, if self.last_readed { 1 } else { 0 },
@ -129,8 +130,8 @@ impl Session {
Ok(()) Ok(())
} }
pub fn get(db: &DStorage, id: i64) -> Result<Session> { pub fn get(db: &DStorage, id: &i64) -> Result<Session> {
let sql = format!("SELECT id, fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed FROM sessions WHERE id = {}", id); let sql = format!("SELECT id, fid, gid, addr, s_type, name, is_top, is_close, last_datetime, last_content, last_readed FROM sessions WHERE id = {}", id);
let mut matrix = db.query(&sql)?; let mut matrix = db.query(&sql)?;
if matrix.len() > 0 { if matrix.len() > 0 {
Ok(Session::from_values(matrix.pop().unwrap())) // safe unwrap() Ok(Session::from_values(matrix.pop().unwrap())) // safe unwrap()
@ -140,7 +141,7 @@ impl Session {
} }
pub fn list(db: &DStorage) -> Result<Vec<Session>> { pub fn list(db: &DStorage) -> Result<Vec<Session>> {
let matrix = db.query("SELECT id, fid, gid, addr, s_type, name, is_top, last_datetime, last_content, last_readed FROM sessions ORDER BY last_datetime DESC")?; let matrix = db.query("SELECT id, fid, gid, addr, s_type, name, is_top, is_close, last_datetime, last_content, last_readed FROM sessions ORDER BY last_datetime DESC")?;
let mut sessions = vec![]; let mut sessions = vec![];
for values in matrix { for values in matrix {
sessions.push(Session::from_values(values)); sessions.push(Session::from_values(values));
@ -169,7 +170,7 @@ impl Session {
if let Some(mut values) = matrix.pop() { if let Some(mut values) = matrix.pop() {
let id = values.pop().unwrap().as_i64(); let id = values.pop().unwrap().as_i64();
db.update(&format!("UPDATE sessions SET last_datetime = {}, last_content = '{}', last_readed = {} WHERE id = {}", datetime, content, if readed { 1 } else { 0 }, id))?; db.update(&format!("UPDATE sessions SET is_close = 0, last_datetime = {}, last_content = '{}', last_readed = {} WHERE id = {}", datetime, content, if readed { 1 } else { 0 }, id))?;
Ok(id) Ok(id)
} else { } else {
Err(new_io_error("session missing")) Err(new_io_error("session missing"))

Loading…
Cancel
Save