Browse Source

update group chat UI

pull/18/head
Sun 4 years ago
parent
commit
c8f6ee63ba
  1. 454
      lib/apps/assistant/page.dart
  2. 648
      lib/apps/chat/detail.dart
  3. 10
      lib/apps/chat/list.dart
  4. 718
      lib/apps/group_chat/detail.dart
  5. 19
      lib/apps/group_chat/models.dart
  6. 14
      lib/apps/group_chat/page.dart
  7. 30
      lib/apps/group_chat/provider.dart
  8. 45
      lib/apps/service/models.dart
  9. 2
      lib/session.dart
  10. 12
      lib/widgets/avatar.dart
  11. 89
      lib/widgets/chat_message.dart
  12. 49
      lib/widgets/default_home_show.dart

454
lib/apps/assistant/page.dart

@ -19,18 +19,6 @@ import 'package:esse/apps/assistant/provider.dart';
import 'package:esse/apps/assistant/message.dart'; import 'package:esse/apps/assistant/message.dart';
import 'package:esse/apps/assistant/answer.dart'; import 'package:esse/apps/assistant/answer.dart';
class AssistantPage extends StatelessWidget {
const AssistantPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: AssistantDetail(),
));
}
}
class AssistantDetail extends StatefulWidget { class AssistantDetail extends StatefulWidget {
const AssistantDetail({Key key}) : super(key: key); const AssistantDetail({Key key}) : super(key: key);
@ -211,237 +199,241 @@ class _AssistantDetailState extends State<AssistantDetail> {
final recentMessages = context.watch<AssistantProvider>().messages; final recentMessages = context.watch<AssistantProvider>().messages;
final recentMessageKeys = recentMessages.keys.toList().reversed.toList(); final recentMessageKeys = recentMessages.keys.toList().reversed.toList();
return Column( return Scaffold(
children: [ body: SafeArea(
Container( child: Column(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), children: [
child: Row( Container(
children: [ padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0),
if (!isDesktop) child: Row(
GestureDetector( children: [
onTap: () { if (!isDesktop)
Navigator.pop(context); GestureDetector(
}, onTap: () {
child: Container( Navigator.pop(context);
width: 20.0, },
child: child: Container(
Icon(Icons.arrow_back, color: color.primary)), width: 20.0,
), child:
SizedBox(width: 15.0), Icon(Icons.arrow_back, color: color.primary)),
Expanded( ),
child: Column( SizedBox(width: 15.0),
crossAxisAlignment: CrossAxisAlignment.start, Expanded(
children: [ child: Column(
Text('Jarvis', crossAxisAlignment: CrossAxisAlignment.start,
style: TextStyle(fontWeight: FontWeight.bold), children: [
Text('Jarvis',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 6.0),
Text(lang.onlineActive,
style: TextStyle(color: color.onPrimary.withOpacity(0.5), fontSize: 14.0))
],
), ),
SizedBox(height: 6.0), ),
Text(lang.onlineActive, SizedBox(width: 20.0),
style: TextStyle(color: color.onPrimary.withOpacity(0.5), fontSize: 14.0)) GestureDetector(
], onTap: () {},
), child: Container(
), width: 20.0,
SizedBox(width: 20.0), child: Icon(Icons.phone_rounded,
GestureDetector( color: Color(0x26ADB0BB))),
onTap: () {}, ),
child: Container( SizedBox(width: 20.0),
width: 20.0, GestureDetector(
child: Icon(Icons.phone_rounded, onTap: () {},
color: Color(0x26ADB0BB))), child: Container(
), width: 20.0,
SizedBox(width: 20.0), child: Icon(Icons.videocam_rounded,
GestureDetector( color: Color(0x26ADB0BB))),
onTap: () {}, ),
child: Container( SizedBox(width: 20.0),
width: 20.0, // PopupMenuButton<int>(
child: Icon(Icons.videocam_rounded, // shape: RoundedRectangleBorder(
color: Color(0x26ADB0BB))), // borderRadius: BorderRadius.circular(15)
), // ),
SizedBox(width: 20.0), // color: const Color(0xFFEDEDED),
// PopupMenuButton<int>( // child: Icon(Icons.more_vert_rounded, color: color.primary),
// shape: RoundedRectangleBorder( // onSelected: (int value) {
// borderRadius: BorderRadius.circular(15) // if (value == 1) {
// ), // // TODO set top
// color: const Color(0xFFEDEDED), // }
// child: Icon(Icons.more_vert_rounded, color: color.primary), // },
// onSelected: (int value) { // itemBuilder: (context) {
// if (value == 1) { // return <PopupMenuEntry<int>>[
// // TODO set top // _menuItem(Color(0xFF6174FF), 1, Icons.vertical_align_top_rounded, lang.cancelTop),
// } // ];
// }, // },
// itemBuilder: (context) { // )
// return <PopupMenuEntry<int>>[ ]
// _menuItem(Color(0xFF6174FF), 1, Icons.vertical_align_top_rounded, lang.cancelTop),
// ];
// },
// )
]
),
),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => AssistantMessage(
name: 'Jarvis',
message: recentMessages[recentMessageKeys[index]],
answers: this.answers,
)
)),
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
children: [
GestureDetector(
onTap: () async {
if (recordShow) {
recordShow = false;
textFocus.requestFocus();
} else {
_generateRecordPath();
setState(() {
menuShow = false;
emojiShow = false;
recordShow = true;
textFocus.unfocus();
});
}
},
child: Container(width: 20.0,
child: Icon(Icons.mic_rounded, color: color.primary)),
), ),
SizedBox(width: 10.0), ),
Expanded( const Divider(height: 1.0, color: Color(0x40ADB0BB)),
child: Container( Expanded(
height: 40, child: ListView.builder(
decoration: BoxDecoration( padding: EdgeInsets.symmetric(horizontal: 20.0),
color: color.surface, itemCount: recentMessageKeys.length,
borderRadius: BorderRadius.circular(15.0), reverse: true,
itemBuilder: (BuildContext context, index) => AssistantMessage(
name: 'Jarvis',
message: recentMessages[recentMessageKeys[index]],
answers: this.answers,
)
)),
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
children: [
GestureDetector(
onTap: () async {
if (recordShow) {
recordShow = false;
textFocus.requestFocus();
} else {
_generateRecordPath();
setState(() {
menuShow = false;
emojiShow = false;
recordShow = true;
textFocus.unfocus();
});
}
},
child: Container(width: 20.0,
child: Icon(Icons.mic_rounded, color: color.primary)),
),
SizedBox(width: 10.0),
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
color: color.surface,
borderRadius: BorderRadius.circular(15.0),
),
child: TextField(
style: TextStyle(fontSize: 14.0),
textInputAction: TextInputAction.send,
onChanged: (value) {
if (value.length == 0 && sendShow) {
setState(() {
sendShow = false;
});
} else {
if (!sendShow) {
setState(() {
sendShow = true;
});
}
}
},
onSubmitted: (_v) => _sendMessage(),
decoration: InputDecoration(
hintText: 'Aa',
border: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
),
),
), ),
child: TextField( SizedBox(width: 10.0),
style: TextStyle(fontSize: 14.0), GestureDetector(
textInputAction: TextInputAction.send, onTap: () {
onChanged: (value) { if (emojiShow) {
if (value.length == 0 && sendShow) { textFocus.requestFocus();
} else {
setState(() { setState(() {
sendShow = false; menuShow = false;
recordShow = false;
emojiShow = true;
textFocus.unfocus();
}); });
}
},
child: Container(
width: 20.0,
child: Icon(
emojiShow
? Icons.keyboard_rounded
: Icons.emoji_emotions_rounded,
color: color.primary)),
),
SizedBox(width: 10.0),
sendShow
? GestureDetector(
onTap: _sendMessage,
child: Container(
width: 50.0,
height: 30.0,
decoration: BoxDecoration(
color: Color(0xFF6174FF),
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Icon(Icons.send, color: Colors.white, size: 20.0))),
)
: GestureDetector(
onTap: () {
if (menuShow) {
textFocus.requestFocus();
} else { } else {
if (!sendShow) { setState(() {
setState(() { emojiShow = false;
sendShow = true; recordShow = false;
}); menuShow = true;
} textFocus.unfocus();
});
} }
}, },
onSubmitted: (_v) => _sendMessage(), child: Container(
decoration: InputDecoration( width: 20.0,
hintText: 'Aa', child: Icon(Icons.add_circle_rounded, color: color.primary)),
border: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
), ),
), ],
), ),
SizedBox(width: 10.0), ),
GestureDetector( if (emojiShow) Emoji(action: _selectEmoji),
onTap: () { if (recordShow)
if (emojiShow) { Container(
textFocus.requestFocus(); height: 100.0,
} else { child: AudioRecorder(
setState(() { path: Global.recordPath + _recordName, onStop: _sendRecord),
menuShow = false; ),
recordShow = false; if (menuShow)
emojiShow = true; Container(
textFocus.unfocus(); height: 100.0,
}); child: Wrap(
} spacing: 20.0,
}, runSpacing: 20.0,
child: Container( alignment: WrapAlignment.center,
width: 20.0, children: <Widget>[
child: Icon( ExtensionButton(
emojiShow icon: Icons.image_rounded,
? Icons.keyboard_rounded text: lang.album,
: Icons.emoji_emotions_rounded, action: _sendImage,
color: color.primary)), bgColor: color.surface,
), iconColor: color.primary),
SizedBox(width: 10.0), ExtensionButton(
sendShow icon: Icons.folder_rounded,
? GestureDetector( text: lang.file,
onTap: _sendMessage, action: _sendFile,
child: Container( bgColor: color.surface,
width: 50.0, iconColor: color.primary),
height: 30.0, ExtensionButton(
decoration: BoxDecoration( icon: Icons.person_rounded,
color: Color(0xFF6174FF), text: lang.contact,
borderRadius: BorderRadius.circular(10.0), action: () => _sendContact(color, lang,
), context.read<ChatProvider>().friends.values),
child: Center( bgColor: color.surface,
child: Icon(Icons.send, color: Colors.white, size: 20.0))), iconColor: color.primary),
) ],
: GestureDetector(
onTap: () {
if (menuShow) {
textFocus.requestFocus();
} else {
setState(() {
emojiShow = false;
recordShow = false;
menuShow = true;
textFocus.unfocus();
});
}
},
child: Container(
width: 20.0,
child: Icon(Icons.add_circle_rounded, color: color.primary)),
), ),
], )
), ],
),
if (emojiShow) Emoji(action: _selectEmoji),
if (recordShow)
Container(
height: 100.0,
child: AudioRecorder(
path: Global.recordPath + _recordName, onStop: _sendRecord),
),
if (menuShow)
Container(
height: 100.0,
child: Wrap(
spacing: 20.0,
runSpacing: 20.0,
alignment: WrapAlignment.center,
children: <Widget>[
ExtensionButton(
icon: Icons.image_rounded,
text: lang.album,
action: _sendImage,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.folder_rounded,
text: lang.file,
action: _sendFile,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.person_rounded,
text: lang.contact,
action: () => _sendContact(color, lang,
context.read<ChatProvider>().friends.values),
bgColor: color.surface,
iconColor: color.primary),
],
),
) )
], )
); );
} }
} }

648
lib/apps/chat/detail.dart

@ -19,18 +19,6 @@ import 'package:esse/apps/primitives.dart';
import 'package:esse/apps/chat/models.dart'; import 'package:esse/apps/chat/models.dart';
import 'package:esse/apps/chat/provider.dart'; import 'package:esse/apps/chat/provider.dart';
class ChatPage extends StatelessWidget {
const ChatPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ChatDetail(),
));
}
}
class ChatDetail extends StatefulWidget { class ChatDetail extends StatefulWidget {
const ChatDetail({Key key}) : super(key: key); const ChatDetail({Key key}) : super(key: key);
@ -217,336 +205,340 @@ class _ChatDetailState extends State<ChatDetail> {
); );
} }
return Column( return Scaffold(
children: [ body: SafeArea(
Container( child: Column(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), children: [
child: Row( Container(
children: [ padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0),
if (!isDesktop) child: Row(
GestureDetector( children: [
onTap: () { if (!isDesktop)
context.read<ChatProvider>().clearActivedFriend(); GestureDetector(
Navigator.pop(context); onTap: () {
}, context.read<ChatProvider>().clearActivedFriend();
child: Container( Navigator.pop(context);
width: 20.0, },
child: child: Container(
Icon(Icons.arrow_back, color: color.primary)), width: 20.0,
), child:
SizedBox(width: 15.0), Icon(Icons.arrow_back, color: color.primary)),
Expanded( ),
child: Column( SizedBox(width: 15.0),
crossAxisAlignment: CrossAxisAlignment.start, Expanded(
children: [ child: Column(
Text( crossAxisAlignment: CrossAxisAlignment.start,
friend.name, children: [
style: TextStyle(fontWeight: FontWeight.bold), Text(
friend.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 6.0),
Text(friend.isClosed
? lang.unfriended
: session.onlineLang(lang),
style: TextStyle(
color: color.onPrimary.withOpacity(0.5),
fontSize: 14.0))
],
), ),
SizedBox(height: 6.0), ),
Text(friend.isClosed SizedBox(width: 20.0),
? lang.unfriended GestureDetector(
: session.onlineLang(lang), onTap: () {},
style: TextStyle( child: Container(
color: color.onPrimary.withOpacity(0.5), width: 20.0,
fontSize: 14.0)) child: Icon(Icons.phone_rounded,
], color: Color(0x26ADB0BB))),
), ),
), SizedBox(width: 20.0),
SizedBox(width: 20.0), GestureDetector(
GestureDetector( onTap: () {},
onTap: () {}, child: Container(
child: Container( width: 20.0,
width: 20.0, child: Icon(Icons.videocam_rounded,
child: Icon(Icons.phone_rounded, color: Color(0x26ADB0BB))),
color: Color(0x26ADB0BB))), ),
), SizedBox(width: 20.0),
SizedBox(width: 20.0), PopupMenuButton<int>(
GestureDetector( shape: RoundedRectangleBorder(
onTap: () {}, borderRadius: BorderRadius.circular(15)
child: Container( ),
width: 20.0, color: const Color(0xFFEDEDED),
child: Icon(Icons.videocam_rounded, child: Icon(Icons.more_vert_rounded, color: color.primary),
color: Color(0x26ADB0BB))), onSelected: (int value) {
), if (value == 2) {
SizedBox(width: 20.0), showShadowDialog(
PopupMenuButton<int>( context,
shape: RoundedRectangleBorder( Icons.info,
borderRadius: BorderRadius.circular(15) lang.friendInfo,
), UserInfo(
color: const Color(0xFFEDEDED), id: 'EH' + friend.gid.toUpperCase(),
child: Icon(Icons.more_vert_rounded, color: color.primary), name: friend.name,
onSelected: (int value) { addr: '0x' + friend.addr)
if (value == 2) { );
showShadowDialog( } else if (value == 3) {
context, print('TODO remark');
Icons.info, } else if (value == 4) {
lang.friendInfo, showDialog(
UserInfo( context: context,
id: 'EH' + friend.gid.toUpperCase(), builder: (BuildContext context) {
name: friend.name, return AlertDialog(
addr: '0x' + friend.addr) title: Text(lang.unfriend),
); content: Text(friend.name,
} else if (value == 3) { style: TextStyle(color: color.primary)),
print('TODO remark'); actions: [
} else if (value == 4) { TextButton(
showDialog( child: Text(lang.cancel),
context: context, onPressed: () => Navigator.pop(context),
builder: (BuildContext context) { ),
return AlertDialog( TextButton(
title: Text(lang.unfriend), child: Text(lang.ok),
content: Text(friend.name, onPressed: () {
style: TextStyle(color: color.primary)), Navigator.pop(context);
actions: [ Provider.of<ChatProvider>(
TextButton( context, listen: false).friendClose(friend.id);
child: Text(lang.cancel), if (!isDesktop) {
onPressed: () => Navigator.pop(context), Navigator.pop(context);
), }
TextButton( },
child: Text(lang.ok), ),
onPressed: () { ]
Navigator.pop(context); );
Provider.of<ChatProvider>( },
context, listen: false).friendClose(friend.id);
if (!isDesktop) {
Navigator.pop(context);
}
},
),
]
); );
}, } else if (value == 5) {
); Provider.of<ChatProvider>(context, listen: false).requestCreate(
} else if (value == 5) { Request(friend.gid, friend.addr, friend.name, lang.fromContactCard(meName)));
Provider.of<ChatProvider>(context, listen: false).requestCreate( } else if (value == 6) {
Request(friend.gid, friend.addr, friend.name, lang.fromContactCard(meName))); showDialog(
} else if (value == 6) { context: context,
showDialog( builder: (BuildContext context) {
context: context, return AlertDialog(
builder: (BuildContext context) { title: Text(lang.delete + " " + lang.friend),
return AlertDialog( content: Text(friend.name,
title: Text(lang.delete + " " + lang.friend), style: TextStyle(color: Colors.red)),
content: Text(friend.name, actions: [
style: TextStyle(color: Colors.red)), TextButton(
actions: [ child: Text(lang.cancel),
TextButton( onPressed: () => Navigator.pop(context),
child: Text(lang.cancel), ),
onPressed: () => Navigator.pop(context), TextButton(
), child: Text(lang.ok),
TextButton( onPressed: () {
child: Text(lang.ok), Navigator.pop(context);
onPressed: () { Provider.of<ChatProvider>(
Navigator.pop(context); context, listen: false).friendDelete(friend.id);
Provider.of<ChatProvider>( if (!isDesktop) {
context, listen: false).friendDelete(friend.id); Navigator.pop(context);
if (!isDesktop) { }
Navigator.pop(context); },
} ),
}, ]
), );
] },
); );
}, }
); },
} itemBuilder: (context) {
}, return <PopupMenuEntry<int>>[
itemBuilder: (context) { _menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo),
return <PopupMenuEntry<int>>[ //_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark),
_menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.friendInfo), friend.isClosed
//_menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), ? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addFriend)
friend.isClosed : _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend),
? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addFriend) _menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete),
: _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend), ];
_menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete), },
]; )
}, ]
),
),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage(
name: friend.name,
message: recentMessages[recentMessageKeys[index]],
)
)),
if (session.online == OnlineType.Lost)
InkWell(
onTap: () {
context.read<AccountProvider>().updateActivedSession(session.id);
},
hoverColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 8.0),
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border.all(color: color.primary),
borderRadius: BorderRadius.circular(10.0)
),
child: Center(child: Text(lang.reconnect, style: TextStyle(color: color.primary))),
) )
]
),
),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage(
name: friend.name,
message: recentMessages[recentMessageKeys[index]],
)
)),
if (session.online == OnlineType.Lost)
InkWell(
onTap: () {
context.read<AccountProvider>().updateActivedSession(session.id);
},
hoverColor: Colors.transparent,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 8.0),
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border.all(color: color.primary),
borderRadius: BorderRadius.circular(10.0)
), ),
child: Center(child: Text(lang.reconnect, style: TextStyle(color: color.primary))), if (!friend.isClosed && session.online != OnlineType.Lost)
) Container(
), padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
if (!friend.isClosed && session.online != OnlineType.Lost) child: Row(
Container( children: [
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0), GestureDetector(
child: Row( onTap: isOnline ? () async {
children: [ if (recordShow) {
GestureDetector( recordShow = false;
onTap: isOnline ? () async { textFocus.requestFocus();
if (recordShow) { } else {
recordShow = false; _generateRecordPath();
textFocus.requestFocus(); setState(() {
} else { menuShow = false;
_generateRecordPath(); emojiShow = false;
setState(() { recordShow = true;
menuShow = false; textFocus.unfocus();
emojiShow = false; });
recordShow = true; }
textFocus.unfocus(); } : null,
}); child: Container(
} width: 20.0,
} : null, child: Icon(Icons.mic_rounded, color: isOnline ? color.primary : Color(0xFFADB0BB))),
child: Container(
width: 20.0,
child: Icon(Icons.mic_rounded, color: isOnline ? color.primary : Color(0xFFADB0BB))),
),
SizedBox(width: 10.0),
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
color: color.surface,
borderRadius: BorderRadius.circular(15.0),
), ),
child: TextField( SizedBox(width: 10.0),
enabled: isOnline, Expanded(
style: TextStyle(fontSize: 14.0), child: Container(
textInputAction: TextInputAction.send, height: 40,
onChanged: (value) { decoration: BoxDecoration(
if (value.length == 0 && sendShow) { color: color.surface,
borderRadius: BorderRadius.circular(15.0),
),
child: TextField(
enabled: isOnline,
style: TextStyle(fontSize: 14.0),
textInputAction: TextInputAction.send,
onChanged: (value) {
if (value.length == 0 && sendShow) {
setState(() {
sendShow = false;
});
} else {
if (!sendShow) {
setState(() {
sendShow = true;
});
}
}
},
onSubmitted: (_v) => _sendMessage(),
decoration: InputDecoration(
hintText: 'Aa',
border: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
),
),
),
SizedBox(width: 10.0),
GestureDetector(
onTap: isOnline ? () {
if (emojiShow) {
textFocus.requestFocus();
} else {
setState(() { setState(() {
sendShow = false; menuShow = false;
recordShow = false;
emojiShow = true;
textFocus.unfocus();
}); });
}
} : null,
child: Container(
width: 20.0,
child: Icon(
emojiShow
? Icons.keyboard_rounded
: Icons.emoji_emotions_rounded,
color: isOnline ? color.primary : Color(0xFFADB0BB))),
),
SizedBox(width: 10.0),
sendShow
? GestureDetector(
onTap: isOnline ? _sendMessage : null,
child: Container(
width: 50.0,
height: 30.0,
decoration: BoxDecoration(
color: Color(0xFF6174FF),
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Icon(Icons.send,
color: Colors.white, size: 20.0))),
)
: GestureDetector(
onTap: isOnline ? () {
if (menuShow) {
textFocus.requestFocus();
} else { } else {
if (!sendShow) { setState(() {
setState(() { emojiShow = false;
sendShow = true; recordShow = false;
}); menuShow = true;
} textFocus.unfocus();
});
} }
}, } : null,
onSubmitted: (_v) => _sendMessage(), child: Container(
decoration: InputDecoration( width: 20.0,
hintText: 'Aa', child: Icon(Icons.add_circle_rounded,
border: InputBorder.none, color: isOnline ? color.primary : Color(0xFFADB0BB))),
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
), ),
), ],
),
SizedBox(width: 10.0),
GestureDetector(
onTap: isOnline ? () {
if (emojiShow) {
textFocus.requestFocus();
} else {
setState(() {
menuShow = false;
recordShow = false;
emojiShow = true;
textFocus.unfocus();
});
}
} : null,
child: Container(
width: 20.0,
child: Icon(
emojiShow
? Icons.keyboard_rounded
: Icons.emoji_emotions_rounded,
color: isOnline ? color.primary : Color(0xFFADB0BB))),
), ),
SizedBox(width: 10.0), ),
sendShow if (emojiShow && isOnline) Emoji(action: _selectEmoji),
? GestureDetector( if (recordShow && isOnline)
onTap: isOnline ? _sendMessage : null, Container(
child: Container( height: 100.0,
width: 50.0, child: AudioRecorder(
height: 30.0, path: Global.recordPath + _recordName, onStop: _sendRecord),
decoration: BoxDecoration( ),
color: Color(0xFF6174FF), if (menuShow && isOnline)
borderRadius: BorderRadius.circular(10.0), Container(
), height: 100.0,
child: Center( child: Wrap(
child: Icon(Icons.send, spacing: 20.0,
color: Colors.white, size: 20.0))), runSpacing: 20.0,
) alignment: WrapAlignment.center,
: GestureDetector( children: <Widget>[
onTap: isOnline ? () { ExtensionButton(
if (menuShow) { icon: Icons.image_rounded,
textFocus.requestFocus(); text: lang.album,
} else { action: _sendImage,
setState(() { bgColor: color.surface,
emojiShow = false; iconColor: color.primary),
recordShow = false; ExtensionButton(
menuShow = true; icon: Icons.folder_rounded,
textFocus.unfocus(); text: lang.file,
}); action: _sendFile,
} bgColor: color.surface,
} : null, iconColor: color.primary),
child: Container( ExtensionButton(
width: 20.0, icon: Icons.person_rounded,
child: Icon(Icons.add_circle_rounded, text: lang.contact,
color: isOnline ? color.primary : Color(0xFFADB0BB))), action: () => _sendContact(color, lang, context.read<ChatProvider>().friends.values),
bgColor: color.surface,
iconColor: color.primary),
],
), ),
], )
), ],
),
if (emojiShow && isOnline) Emoji(action: _selectEmoji),
if (recordShow && isOnline)
Container(
height: 100.0,
child: AudioRecorder(
path: Global.recordPath + _recordName, onStop: _sendRecord),
),
if (menuShow && isOnline)
Container(
height: 100.0,
child: Wrap(
spacing: 20.0,
runSpacing: 20.0,
alignment: WrapAlignment.center,
children: <Widget>[
ExtensionButton(
icon: Icons.image_rounded,
text: lang.album,
action: _sendImage,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.folder_rounded,
text: lang.file,
action: _sendFile,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.person_rounded,
text: lang.contact,
action: () => _sendContact(color, lang, context.read<ChatProvider>().friends.values),
bgColor: color.surface,
iconColor: color.primary),
],
),
) )
], )
); );
} }
} }

10
lib/apps/chat/list.dart

@ -63,15 +63,11 @@ class ListChat extends StatelessWidget {
onTap: () { onTap: () {
context.read<AccountProvider>().updateActivedSession(0, SessionType.Chat, friend.id); context.read<AccountProvider>().updateActivedSession(0, SessionType.Chat, friend.id);
context.read<ChatProvider>().updateActivedFriend(friend.id); context.read<ChatProvider>().updateActivedFriend(friend.id);
final widget = ChatDetail();
if (!isDesktop) { if (!isDesktop) {
Navigator.push( Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
context,
MaterialPageRoute(
builder: (_) => ChatPage(),
),
);
} else { } else {
context.read<AccountProvider>().updateActivedWidget(ChatDetail()); context.read<AccountProvider>().updateActivedWidget(widget);
} }
}, },
child: Container( child: Container(

718
lib/apps/group_chat/detail.dart

@ -1,3 +1,5 @@
import 'dart:ui' show ImageFilter;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -20,19 +22,9 @@ import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/group_chat/models.dart'; import 'package:esse/apps/group_chat/models.dart';
import 'package:esse/apps/group_chat/provider.dart'; import 'package:esse/apps/group_chat/provider.dart';
class GroupChatPage extends StatelessWidget {
const GroupChatPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GroupChatDetail(),
));
}
}
class GroupChatDetail extends StatefulWidget { class GroupChatDetail extends StatefulWidget {
static GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
const GroupChatDetail({Key key}) : super(key: key); const GroupChatDetail({Key key}) : super(key: key);
@override @override
@ -200,6 +192,7 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
final isDesktop = isDisplayDesktop(context); final isDesktop = isDisplayDesktop(context);
final provider = context.watch<GroupChatProvider>(); final provider = context.watch<GroupChatProvider>();
final members = provider.activedMembers;
final recentMessages = provider.activedMessages; final recentMessages = provider.activedMessages;
final recentMessageKeys = recentMessages.keys.toList().reversed.toList(); final recentMessageKeys = recentMessages.keys.toList().reversed.toList();
@ -219,337 +212,323 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
final meName = accountProvider.activedAccount.name; final meName = accountProvider.activedAccount.name;
final isOnline = session.isActive(); final isOnline = session.isActive();
return Column( return Scaffold(
children: [ key: GroupChatDetail._scaffoldKey,
Container( endDrawer: _MemberDrawerWidget(title: lang.members),
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0), drawerScrimColor: color.background,
child: Row( body: SafeArea(
children: [ child: Column(
if (!isDesktop) children: [
GestureDetector( Container(
onTap: () { padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 10.0),
context.read<GroupChatProvider>().clearActivedGroup(); child: Row(
Navigator.pop(context); children: [
}, if (!isDesktop)
child: Container( GestureDetector(
width: 20.0, onTap: () {
child: context.read<GroupChatProvider>().clearActivedGroup();
Icon(Icons.arrow_back, color: color.primary)), Navigator.pop(context);
), },
SizedBox(width: 15.0), child: Container(
Expanded( width: 20.0,
child: Column( child:
crossAxisAlignment: CrossAxisAlignment.start, Icon(Icons.arrow_back, color: color.primary)),
children: [ ),
Text( SizedBox(width: 15.0),
this.group.name, Expanded(
style: TextStyle(fontWeight: FontWeight.bold), child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
this.group.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 6.0),
Text(this.group.isClosed
? lang.unfriended
: session.onlineLang(lang),
style: TextStyle(
color: color.onPrimary.withOpacity(0.5),
fontSize: 14.0))
],
), ),
SizedBox(height: 6.0), ),
Text(this.group.isClosed SizedBox(width: 20.0),
? lang.unfriended GestureDetector(
: session.onlineLang(lang), onTap: () {},
style: TextStyle( child: Container(
color: color.onPrimary.withOpacity(0.5), width: 20.0,
fontSize: 14.0)) child: Icon(Icons.phone_rounded,
], color: Color(0x26ADB0BB))),
), ),
), SizedBox(width: 20.0),
SizedBox(width: 20.0), GestureDetector(
GestureDetector( onTap: () {},
onTap: () {}, child: Container(
child: Container( width: 20.0,
width: 20.0, child: Icon(Icons.videocam_rounded,
child: Icon(Icons.phone_rounded, color: Color(0x26ADB0BB))),
color: Color(0x26ADB0BB))), ),
), SizedBox(width: 20.0),
SizedBox(width: 20.0), GestureDetector(
GestureDetector( onTap: () {
onTap: () {}, GroupChatDetail._scaffoldKey.currentState.openEndDrawer();
child: Container( },
width: 20.0, child: Container(
child: Icon(Icons.videocam_rounded, width: 20.0,
color: Color(0x26ADB0BB))), child: Icon(Icons.group_rounded, color: color.primary)),
), ),
SizedBox(width: 20.0), SizedBox(width: 20.0),
PopupMenuButton<int>( PopupMenuButton<int>(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15) borderRadius: BorderRadius.circular(15)
), ),
color: const Color(0xFFEDEDED), color: const Color(0xFFEDEDED),
child: Icon(Icons.more_vert_rounded, color: color.primary), child: Icon(Icons.more_vert_rounded, color: color.primary),
onSelected: (int value) { onSelected: (int value) {
if (value == 2) { if (value == 1) {
showShadowDialog( showShadowDialog(context, Icons.info, lang.groupChat,
context, UserInfo(
Icons.info, id: 'EG' + this.group.gid.toUpperCase(),
lang.groupChat, name: this.group.name,
UserInfo( addr: '0x' + this.group.addr
id: 'EG' + this.group.gid.toUpperCase(), )
name: this.group.name,
addr: '0x' + this.group.addr)
);
} else if (value == 3) {
final memberWidgets = provider.activedMembers.values.map((m) {
return MemberAvatar(
member: m, title: lang.members,
isGroupManager: isGroupManager, isGroupOwner: isGroupOwner,
); );
}).toList(); } else if (value == 2) {
showDialog(
showShadowDialog( context: context, builder: (BuildContext context) {
context, return AlertDialog(
Icons.group_rounded, title: Text(lang.exit),
lang.members, content: Text(this.group.name,
Wrap( style: TextStyle(color: color.primary)),
spacing: 10.0, actions: [
runSpacing: 10.0, TextButton(child: Text(lang.cancel),
children: memberWidgets, onPressed: () => Navigator.pop(context),
), ),
10.0, //height TextButton(child: Text(lang.ok),
); onPressed: () {
} else if (value == 4) { Navigator.pop(context);
showDialog( provider.close(this.group.id);
context: context, },
builder: (BuildContext context) { ),
return AlertDialog( ]
title: Text(lang.unfriend), );
content: Text(this.group.name, },
style: TextStyle(color: color.primary)),
actions: [
TextButton(
child: Text(lang.cancel),
onPressed: () => Navigator.pop(context),
),
TextButton(
child: Text(lang.ok),
onPressed: () {
Navigator.pop(context);
//Provider.of<GroupChatProvider>(context, listen: false).groupClose(this.group.id);
if (!isDesktop) {
Navigator.pop(context);
}
},
),
]
); );
}, } else if (value == 3) {
); showDialog(
} else if (value == 5) { context: context, builder: (BuildContext context) {
// Provider.of<GroupChatProvider>(context, listen: false).requestCreate( return AlertDialog(
// Request(this.group.gid, this.group.addr, this.group.name, lang.fromContactCard(meName))); title: Text(lang.delete + ' ' + lang.groupChat),
} else if (value == 6) { content: Text(this.group.name,
showDialog( style: TextStyle(color: Colors.red)),
context: context, actions: [
builder: (BuildContext context) { TextButton(
return AlertDialog( child: Text(lang.cancel),
title: Text(lang.delete + ' ' + lang.groupChat), onPressed: () => Navigator.pop(context),
content: Text(this.group.name, ),
style: TextStyle(color: Colors.red)), TextButton(
actions: [ child: Text(lang.ok),
TextButton( onPressed: () {
child: Text(lang.cancel), Navigator.pop(context);
onPressed: () => Navigator.pop(context), provider.delete(this.group.id);
), },
TextButton( ),
child: Text(lang.ok), ]
onPressed: () { );
Navigator.pop(context); },
//Provider.of<GroupChatProvider>(context, listen: false).groupDelete(this.group.id);
if (!isDesktop) {
Navigator.pop(context);
}
},
),
]
); );
}, } else if (value == 4) {
); provider.reAdd(this.group.id);
} }
}, },
itemBuilder: (context) { itemBuilder: (context) {
return <PopupMenuEntry<int>>[ return <PopupMenuEntry<int>>[
_menuItem(Color(0xFF6174FF), 2, Icons.qr_code_rounded, lang.info), _menuItem(Color(0xFF6174FF), 1, Icons.qr_code_rounded, lang.info),
_menuItem(Color(0xFF6174FF), 3, Icons.group_rounded, lang.members), this.group.isClosed
// _menuItem(color.primary, 3, Icons.turned_in_rounded, lang.remark), ? _menuItem(Color(0xFF6174FF), 4, Icons.send_rounded, lang.addGroup)
// this.group.isClosed : _menuItem(Colors.orange, 2, Icons.block_rounded, lang.exit),
// ? _menuItem(Color(0xFF6174FF), 5, Icons.send_rounded, lang.addGroup) _menuItem(Colors.red, 3, Icons.delete_rounded, lang.delete),
// : _menuItem(Color(0xFF6174FF), 4, Icons.block_rounded, lang.unfriend), ];
_menuItem(Colors.orange, 6, Icons.block_rounded, lang.exit), },
_menuItem(Colors.red, 6, Icons.delete_rounded, lang.delete), )
]; ]
},
)
]
),
),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage(
name: this.group.name,
message: recentMessages[recentMessageKeys[index]],
)
)),
if (!this.group.isClosed)
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
children: [
GestureDetector(
onTap: isOnline ? () async {
if (recordShow) {
recordShow = false;
textFocus.requestFocus();
} else {
_generateRecordPath();
setState(() {
menuShow = false;
emojiShow = false;
recordShow = true;
textFocus.unfocus();
});
}
} : null,
child: Container(
width: 20.0,
child: Icon(Icons.mic_rounded, color: isOnline ? color.primary : Color(0xFFADB0BB))),
), ),
SizedBox(width: 10.0), ),
Expanded( const Divider(height: 1.0, color: Color(0x40ADB0BB)),
child: Container( Expanded(
height: 40, child: ListView.builder(
decoration: BoxDecoration( padding: EdgeInsets.symmetric(horizontal: 20.0),
color: color.surface, itemCount: recentMessageKeys.length,
borderRadius: BorderRadius.circular(15.0), reverse: true,
itemBuilder: (BuildContext context, index) {
final message = recentMessages[recentMessageKeys[index]];
final member = members[message.fid];
return ChatMessage(
avatar: member.showAvatar(),
name: member.name,
message: message,
);
}
)),
if (!this.group.isClosed)
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
children: [
GestureDetector(
onTap: isOnline ? () async {
if (recordShow) {
recordShow = false;
textFocus.requestFocus();
} else {
_generateRecordPath();
setState(() {
menuShow = false;
emojiShow = false;
recordShow = true;
textFocus.unfocus();
});
}
} : null,
child: Container(
width: 20.0,
child: Icon(Icons.mic_rounded, color: isOnline ? color.primary : Color(0xFFADB0BB))),
),
SizedBox(width: 10.0),
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
color: color.surface,
borderRadius: BorderRadius.circular(15.0),
),
child: TextField(
enabled: isOnline,
style: TextStyle(fontSize: 14.0),
textInputAction: TextInputAction.send,
onChanged: (value) {
if (value.length == 0 && sendShow) {
setState(() {
sendShow = false;
});
} else {
if (!sendShow) {
setState(() {
sendShow = true;
});
}
}
},
onSubmitted: (_v) => _sendMessage(),
decoration: InputDecoration(
hintText: 'Aa',
border: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
),
),
), ),
child: TextField( SizedBox(width: 10.0),
enabled: isOnline, GestureDetector(
style: TextStyle(fontSize: 14.0), onTap: isOnline ? () {
textInputAction: TextInputAction.send, if (emojiShow) {
onChanged: (value) { textFocus.requestFocus();
if (value.length == 0 && sendShow) { } else {
setState(() { setState(() {
sendShow = false; menuShow = false;
recordShow = false;
emojiShow = true;
textFocus.unfocus();
}); });
}
} : null,
child: Container(
width: 20.0,
child: Icon(
emojiShow
? Icons.keyboard_rounded
: Icons.emoji_emotions_rounded,
color: isOnline ? color.primary : Color(0xFFADB0BB))),
),
SizedBox(width: 10.0),
sendShow
? GestureDetector(
onTap: isOnline ? _sendMessage : null,
child: Container(
width: 50.0,
height: 30.0,
decoration: BoxDecoration(
color: Color(0xFF6174FF),
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Icon(Icons.send,
color: Colors.white, size: 20.0))),
)
: GestureDetector(
onTap: isOnline ? () {
if (menuShow) {
textFocus.requestFocus();
} else { } else {
if (!sendShow) { setState(() {
setState(() { emojiShow = false;
sendShow = true; recordShow = false;
}); menuShow = true;
} textFocus.unfocus();
});
} }
}, } : null,
onSubmitted: (_v) => _sendMessage(), child: Container(
decoration: InputDecoration( width: 20.0,
hintText: 'Aa', child: Icon(Icons.add_circle_rounded,
border: InputBorder.none, color: isOnline ? color.primary : Color(0xFFADB0BB))),
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
), ),
), ],
), ),
SizedBox(width: 10.0), ),
GestureDetector( if (emojiShow && isOnline) Emoji(action: _selectEmoji),
onTap: isOnline ? () { if (recordShow && isOnline)
if (emojiShow) { Container(
textFocus.requestFocus(); height: 100.0,
} else { child: AudioRecorder(
setState(() { path: Global.recordPath + _recordName, onStop: _sendRecord),
menuShow = false; ),
recordShow = false; if (menuShow && isOnline)
emojiShow = true; Container(
textFocus.unfocus(); height: 100.0,
}); child: Wrap(
} spacing: 20.0,
} : null, runSpacing: 20.0,
child: Container( alignment: WrapAlignment.center,
width: 20.0, children: <Widget>[
child: Icon( ExtensionButton(
emojiShow icon: Icons.image_rounded,
? Icons.keyboard_rounded text: lang.album,
: Icons.emoji_emotions_rounded, action: _sendImage,
color: isOnline ? color.primary : Color(0xFFADB0BB))), bgColor: color.surface,
), iconColor: color.primary),
SizedBox(width: 10.0), ExtensionButton(
sendShow icon: Icons.folder_rounded,
? GestureDetector( text: lang.file,
onTap: isOnline ? _sendMessage : null, action: _sendFile,
child: Container( bgColor: color.surface,
width: 50.0, iconColor: color.primary),
height: 30.0, ExtensionButton(
decoration: BoxDecoration( icon: Icons.person_rounded,
color: Color(0xFF6174FF), text: lang.contact,
borderRadius: BorderRadius.circular(10.0), action: () => _sendContact(color, lang, context.read<ChatProvider>().friends.values),
), bgColor: color.surface,
child: Center( iconColor: color.primary),
child: Icon(Icons.send, ],
color: Colors.white, size: 20.0))),
)
: GestureDetector(
onTap: isOnline ? () {
if (menuShow) {
textFocus.requestFocus();
} else {
setState(() {
emojiShow = false;
recordShow = false;
menuShow = true;
textFocus.unfocus();
});
}
} : null,
child: Container(
width: 20.0,
child: Icon(Icons.add_circle_rounded,
color: isOnline ? color.primary : Color(0xFFADB0BB))),
), ),
], )
), ],
),
if (emojiShow && isOnline) Emoji(action: _selectEmoji),
if (recordShow && isOnline)
Container(
height: 100.0,
child: AudioRecorder(
path: Global.recordPath + _recordName, onStop: _sendRecord),
),
if (menuShow && isOnline)
Container(
height: 100.0,
child: Wrap(
spacing: 20.0,
runSpacing: 20.0,
alignment: WrapAlignment.center,
children: <Widget>[
ExtensionButton(
icon: Icons.image_rounded,
text: lang.album,
action: _sendImage,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.folder_rounded,
text: lang.file,
action: _sendFile,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.person_rounded,
text: lang.contact,
action: () => _sendContact(color, lang, context.read<ChatProvider>().friends.values),
bgColor: color.surface,
iconColor: color.primary),
],
),
) )
], )
); );
} }
} }
@ -607,39 +586,62 @@ Widget _menuItem(Color color, int value, IconData icon, String text) {
); );
} }
class MemberAvatar extends StatelessWidget { class _MemberDrawerWidget extends StatelessWidget {
final String title; final String title;
final Member member; const _MemberDrawerWidget({Key key, this.title}) : super(key: key);
final bool isGroupManager;
final bool isGroupOwner; Widget _item(context, Member member, bool isOwner, Color color) {
return ListTile(
const MemberAvatar({Key key, this.title, this.member, this.isGroupManager, this.isGroupOwner}) : super(key: key); leading: member.showAvatar(),
title: Text(member.name, textAlign: TextAlign.left, style: TextStyle(fontSize: 16.0)),
trailing: Text(isOwner ? 'Owner' : (member.isManager ? 'Manager' : ''),
style: TextStyle(color: color)),
onTap: () {
Navigator.pop(context);
showShadowDialog(context, Icons.group_rounded, title,
MemberDetail(member: member, isGroupOwner: isOwner, isGroupManager: member.isManager),
10.0,
);
}
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( final color = Theme.of(context).colorScheme;
onTap: () => showShadowDialog( final lang = AppLocalizations.of(context);
context, final isLight = color.brightness == Brightness.light;
Icons.group_rounded, final isDesktop = isDisplayDesktop(context);
title,
MemberDetail(member: member, isGroupOwner: isGroupOwner, isGroupManager: isGroupManager), final provider = context.watch<GroupChatProvider>();
10.0, final members = provider.activedMembers;
), final allKeys = provider.activedMemberOrder;
hoverColor: Colors.transparent,
child: Column( return Drawer(
children: [ child: BackdropFilter(
member.showAvatar(), filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
SizedBox(height: 4.0), child: SafeArea(
Container( child: Container(
alignment: Alignment.center, decoration: BoxDecoration(color: color.surface),
width: 60.0, padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Text( child: Column(
member.name, children: [
maxLines: 1, Text(lang.members, style: Theme.of(context).textTheme.title),
overflow: TextOverflow.ellipsis, const SizedBox(height: 10.0),
style: TextStyle(fontSize: 14.0)), const Divider(height: 1.0, color: Color(0x40ADB0BB)),
const SizedBox(height: 10.0),
Expanded(
child: ListView.builder(
itemCount: allKeys.length,
itemBuilder: (BuildContext ctx, int index) => _item(
context, members[allKeys[index]], index == 0, color.primary
),
)
)
]
),
) )
] )
) )
); );
} }
@ -685,7 +687,7 @@ class _MemberDetailState extends State<MemberDetail> {
return Column( return Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
widget.member.showAvatar(width: 100.0), widget.member.showAvatar(width: 100.0, colorSurface: true),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),
Text(widget.member.name), Text(widget.member.name),
const SizedBox(height: 10.0), const SizedBox(height: 10.0),

19
lib/apps/group_chat/models.dart

@ -155,16 +155,7 @@ class Member {
String addr; String addr;
String name; String name;
bool isManager; bool isManager;
bool online = false;
// MOCK need deleted.
Member(String name, bool isManager) {
this.id = 0;
this.fid = 0;
this.mid = 'EH0000000000000000000000000000000000000000000000000000000000000000';
this.addr = '0x0000000000000000000000000000000000000000000000000000000000000000';
this.name = name;
this.isManager = isManager;
}
Member.fromList(List params) { Member.fromList(List params) {
this.id = params[0]; this.id = params[0];
@ -175,15 +166,15 @@ class Member {
this.isManager = params[5]; this.isManager = params[5];
} }
Avatar showAvatar({double width = 45.0}) { Avatar showAvatar({double width = 45.0, colorSurface = false}) {
final avatar = Global.avatarPath + this.mid + '.png'; final avatar = Global.avatarPath + this.mid + '.png';
return Avatar( return Avatar(
width: width, width: width,
name: this.name, name: this.name,
avatarPath: avatar, avatarPath: avatar,
online: false, online: this.online,
hasNew: this.isManager, onlineColor: Color(0xFF0EE50A),
hasNewColor: Color(0xFF6174FF), colorSurface: colorSurface,
); );
} }
} }

14
lib/apps/group_chat/page.dart

@ -60,19 +60,13 @@ class ListChat extends StatelessWidget {
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
context.read<GroupChatProvider>().updateActivedGroup(group.id);
context.read<AccountProvider>().updateActivedSession(0, SessionType.Group, group.id); context.read<AccountProvider>().updateActivedSession(0, SessionType.Group, group.id);
context.read<GroupChatProvider>().updateActivedGroup(group.id);
final widget = GroupChatDetail();
if (!isDesktop) { if (!isDesktop) {
Navigator.push( Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
context,
MaterialPageRoute(
builder: (_) => GroupChatPage(),
),
);
} else { } else {
context.read<AccountProvider>().updateActivedWidget(GroupChatDetail()); context.read<AccountProvider>().updateActivedWidget(widget);
} }
}, },
child: Container( child: Container(

30
lib/apps/group_chat/provider.dart

@ -33,6 +33,24 @@ class GroupChatProvider extends ChangeNotifier {
return false; return false;
} }
List<int> get activedMemberOrder {
List<int> allKeys = [];
List<int> managers = [];
List<int> commons = [];
this.activedMembers.forEach((i, m) {
if (m.isManager) {
if (m.mid == this.activedGroup.owner) {
allKeys.add(i);
} else {
managers.add(i);
}
} else {
commons.add(i);
}
});
return allKeys + managers + commons;
}
GroupChatProvider() { GroupChatProvider() {
// rpc. // rpc.
rpc.addListener('group-chat-list', _list, false); rpc.addListener('group-chat-list', _list, false);
@ -108,6 +126,18 @@ class GroupChatProvider extends ChangeNotifier {
rpc.send('group-chat-request-list', [all]); rpc.send('group-chat-request-list', [all]);
} }
close(int id) {
// rpc.send('group-chat-close', [id]);
}
delete(int id) {
// rpc.send('group-chat-delete', [id]);
}
reAdd(int id) {
// rpc.send('group-chat-readd', [id]);
}
_list(List params) { _list(List params) {
this.clear(); this.clear();
params.forEach((params) { params.forEach((params) {

45
lib/apps/service/models.dart

@ -32,33 +32,26 @@ extension InnerServiceExtension on InnerService {
String listTitle = null; String listTitle = null;
Widget listHome = null; Widget listHome = null;
if (isDesktop) { switch (this) {
switch (this) { case InnerService.Files:
case InnerService.Files: listTitle = lang.files;
listTitle = lang.files; listHome = FolderList();
listHome = FolderList(); break;
break; case InnerService.Assistant:
case InnerService.Assistant: coreWidget = AssistantDetail();
coreWidget = AssistantDetail(); break;
break; case InnerService.GroupChat:
case InnerService.GroupChat: listTitle = lang.groupChat;
listTitle = lang.groupChat; listHome = GroupChatList();
listHome = GroupChatList(); break;
break; }
}
Provider.of<AccountProvider>(context, listen: false).updateActivedWidget(coreWidget, listTitle, listHome); if (this == InnerService.Assistant) {
Navigator.push(context, MaterialPageRoute(builder: (_) => coreWidget));
} else { } else {
switch (this) { Provider.of<AccountProvider>(context, listen: false).updateActivedWidget(
case InnerService.Files: coreWidget, listTitle, listHome
Provider.of<AccountProvider>(context, listen: false).updateActivedWidget(null, lang.files, FolderList()); );
break;
case InnerService.Assistant:
Navigator.push(context, MaterialPageRoute(builder: (_) => AssistantPage()));
break;
case InnerService.GroupChat:
Provider.of<AccountProvider>(context, listen: false).updateActivedWidget(null, lang.groupChat, GroupChatList());
break;
}
} }
} }
} }

2
lib/session.dart

@ -97,6 +97,8 @@ class Session {
switch (this.type) { switch (this.type) {
case SessionType.Chat: case SessionType.Chat:
return [showAvatar(), this.name, this.lastContent, this.lastTime.toString()]; return [showAvatar(), this.name, this.lastContent, this.lastTime.toString()];
case SessionType.Group:
return [showAvatar(), this.name, this.lastContent, this.lastTime.toString()];
case SessionType.Assistant: case SessionType.Assistant:
final params = Session.innerService(InnerService.Assistant, lang); final params = Session.innerService(InnerService.Assistant, lang);
return [params[0], params[1], params[2], '']; return [params[0], params[1], params[2], ''];

12
lib/widgets/avatar.dart

@ -14,6 +14,7 @@ class Avatar extends StatelessWidget {
final bool hasNew; final bool hasNew;
final Color hasNewColor; final Color hasNewColor;
final bool loading; final bool loading;
final bool colorSurface;
const Avatar( const Avatar(
{Key key, {Key key,
@ -26,6 +27,7 @@ class Avatar extends StatelessWidget {
this.hasNew = false, this.hasNew = false,
this.hasNewColor = Colors.red, this.hasNewColor = Colors.red,
this.loading = false, this.loading = false,
this.colorSurface = true,
}) })
: super(key: key); : super(key: key);
@ -46,8 +48,14 @@ class Avatar extends StatelessWidget {
width: this.width, width: this.width,
height: this.width, height: this.width,
decoration: showAvatar != null decoration: showAvatar != null
? BoxDecoration(image: DecorationImage(image: showAvatar, fit: BoxFit.cover), borderRadius: BorderRadius.circular(15.0)) ? BoxDecoration(
: BoxDecoration(color: color.surface, borderRadius: BorderRadius.circular(15.0)), image: DecorationImage(image: showAvatar, fit: BoxFit.cover),
borderRadius: BorderRadius.circular(15.0)
)
: BoxDecoration(
color: this.colorSurface ? color.surface : color.background,
borderRadius: BorderRadius.circular(15.0)
),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[

89
lib/widgets/chat_message.dart

@ -20,10 +20,11 @@ import 'package:esse/apps/chat/models.dart' show Request;
import 'package:esse/apps/chat/provider.dart'; import 'package:esse/apps/chat/provider.dart';
class ChatMessage extends StatelessWidget { class ChatMessage extends StatelessWidget {
final Widget avatar;
final String name; final String name;
final BaseMessage message; final BaseMessage message;
const ChatMessage({Key key, this.name, this.message}): super(key: key); const ChatMessage({Key key, this.name, this.message, this.avatar}): super(key: key);
Widget _showText(context, color, isDesktop) { Widget _showText(context, color, isDesktop) {
final width = MediaQuery.of(context).size.width * 0.6; final width = MediaQuery.of(context).size.width * 0.6;
@ -347,36 +348,66 @@ class ChatMessage extends StatelessWidget {
final lang = AppLocalizations.of(context); final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context); final isDesktop = isDisplayDesktop(context);
final messageShow = _show(context, color, lang, isDesktop); final messageShow = _show(context, color, lang, isDesktop);
final isAvatar = avatar != null && !message.isMe;
return Padding( final timeWidget = Container(
padding: const EdgeInsets.symmetric(vertical: 5.0), padding: EdgeInsets.only(top: 6.0),
child: Column(children: [ child: Row(children: [
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ if (message.isMe) Spacer(),
Expanded( if (isAvatar)
child: Align(
alignment:
message.isMe ? Alignment.topRight : Alignment.topLeft,
child: messageShow,
),
),
]),
Container( Container(
padding: EdgeInsets.only(top: 10.0), width: 50.0,
child: Row(children: [ child: Text(name, maxLines: 1, overflow: TextOverflow.ellipsis,
if (message.isMe) Spacer(), style: TextStyle(color: color.onPrimary.withOpacity(0.5), fontSize: 12.0)
Text(message.time.toString(), )),
style: TextStyle( const SizedBox(width: 4.0),
color: color.onPrimary.withOpacity(0.5), Text(message.time.toString(), style: TextStyle(
fontSize: 12.0)), color: color.onPrimary.withOpacity(0.5),
SizedBox(width: 4.0), fontSize: 12.0)),
Icon( const SizedBox(width: 4.0),
message.isDelivery == null ? Icons.hourglass_top Icon(
: (message.isDelivery ? Icons.done : Icons.error), message.isDelivery == null ? Icons.hourglass_top
size: 12.0, : (message.isDelivery ? Icons.done : Icons.error),
color: message.isDelivery == null ? color.primaryVariant size: 12.0,
: (message.isDelivery ? color.primary : Colors.red) color: message.isDelivery == null ? color.primaryVariant
), : (message.isDelivery ? color.primary : Colors.red)
])) ),
])); ]));
final mainWidget = Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: Align(
alignment: message.isMe ? Alignment.topRight : Alignment.topLeft,
child: messageShow,
),
),
]);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child:
isAvatar
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
avatar,
const SizedBox(width: 4.0),
Expanded(
child: Column(
children: [
mainWidget,
timeWidget,
]
)
)
]
)
: Column(
children: [
mainWidget,
timeWidget,
]
)
);
} }
} }

49
lib/widgets/default_home_show.dart

@ -10,6 +10,8 @@ import 'package:esse/session.dart';
import 'package:esse/apps/chat/detail.dart'; import 'package:esse/apps/chat/detail.dart';
import 'package:esse/apps/chat/provider.dart'; import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/group_chat/detail.dart';
import 'package:esse/apps/group_chat/provider.dart';
import 'package:esse/apps/assistant/page.dart'; import 'package:esse/apps/assistant/page.dart';
import 'package:esse/apps/file/page.dart'; import 'package:esse/apps/file/page.dart';
@ -24,23 +26,11 @@ class DefaultHomeShow extends StatelessWidget {
final allKeys = provider.topKeys + provider.orderKeys; final allKeys = provider.topKeys + provider.orderKeys;
final sessions = provider.sessions; final sessions = provider.sessions;
return Scaffold( return ListView.builder(
body: ListView.builder( itemCount: allKeys.length,
itemCount: allKeys.length, itemBuilder: (BuildContext ctx, int index) => _SessionWidget(
itemBuilder: (BuildContext ctx, int index) => _SessionWidget(session: sessions[allKeys[index]]), session: sessions[allKeys[index]]
), ),
// floatingActionButton: FloatingActionButton(
// onPressed: () {
// final widget = Text('');
// if (isDesktop) {
// Provider.of<AccountProvider>(context, listen: false).updateActivedSession(0, widget);
// } else {
// Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
// }
// },
// child: const Icon(Icons.add, color: Colors.white),
// backgroundColor: Color(0xFF6174FF),
// ),
); );
} }
} }
@ -66,20 +56,14 @@ class _SessionWidget extends StatelessWidget {
switch (session.type) { switch (session.type) {
case SessionType.Chat: case SessionType.Chat:
context.read<ChatProvider>().updateActivedFriend(session.fid); context.read<ChatProvider>().updateActivedFriend(session.fid);
if (!isDesktop) { coreWidget = ChatDetail();
coreWidget = ChatPage();
} else {
coreWidget = ChatDetail();
}
break; break;
case SessionType.Group: case SessionType.Group:
context.read<GroupChatProvider>().updateActivedGroup(session.fid);
coreWidget = GroupChatDetail();
break; break;
case SessionType.Assistant: case SessionType.Assistant:
if (!isDesktop) { coreWidget = AssistantDetail();
coreWidget = AssistantPage();
} else {
coreWidget = AssistantDetail();
}
break; break;
case SessionType.Files: case SessionType.Files:
listTitle = lang.files; listTitle = lang.files;
@ -89,17 +73,10 @@ class _SessionWidget extends StatelessWidget {
context.read<AccountProvider>().updateActivedSession(session.id); context.read<AccountProvider>().updateActivedSession(session.id);
if (!isDesktop) { if (!isDesktop && coreWidget != null) {
Navigator.push( Navigator.push(context, MaterialPageRoute(builder: (_) => coreWidget));
context,
MaterialPageRoute(
builder: (_) => coreWidget,
),
);
} else { } else {
context.read<AccountProvider>().updateActivedWidget( context.read<AccountProvider>().updateActivedWidget(coreWidget, listTitle, listWidget);
coreWidget, listTitle, listWidget
);
} }
}, },
child: Container( child: Container(

Loading…
Cancel
Save