Merge pull request 'Support New Groups' (#68) from groups into trunk
continuous-integration/drone/push Build was killed Details

Reviewed-on: #68
This commit is contained in:
erinn 2021-05-05 15:27:26 -07:00
commit 2c9cd7f111
7 changed files with 82 additions and 23 deletions

View File

@ -62,20 +62,42 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
break;
case "PeerAcknowledgement":
// We don't use these anymore, IndexedAcknowledgement is more suited to the UI front end...
break;
case "IndexedAcknowledgement":
var idx = int.parse(data["Index"]);
if (idx < 0) break;
var idx = data["Index"];
// We return -1 for protocol message acks if there is no message
if (idx == "-1") break;
var key = profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).getMessageKey(idx);
if (key == null) break;
Provider.of<MessageState>(key.currentContext, listen: false).ackd = true;
try {
var message = Provider.of<MessageState>(key.currentContext, listen: false);
if (message == null) break;
message.ackd = true;
} catch (e) {
// ignore, we received an ack for a message that hasn't loaded onto the screen yet...
// the protocol was faster than the ui....yay?
}
break;
case "NewMessageFromGroup":
if (data["ProfileOnion"] != data["RemotePeer"]) {//not from me
if (data["ProfileOnion"] != data["RemotePeer"]) {
//not from me
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).unreadMessages++;
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).totalMessages++;
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
} else {// from me (already displayed - do not update counter)
//todo: update ack - once group messages
} else {
// from me (already displayed - do not update counter)
var idx = data["Signature"];
var key = profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).getMessageKey(idx);
if (key == null) break;
try {
var message = Provider.of<MessageState>(key.currentContext, listen: false);
if (message == null) break;
message.ackd = true;
} catch (e) {
// ignore, we likely have an old key that has been replaced with an actual signature
}
}
break;
case "AppError":
@ -103,12 +125,19 @@ class CwtchNotifier {
case "UpdateServerInfo":
profileCN.getProfile(data["ProfileOnion"]).replaceServers(data["ServerList"]);
break;
case "NewGroupInvite":
case "NewGroup":
print("new group invite: $data");
dynamic groupInvite = jsonDecode(data["GroupInvite"]);
profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"],
isInvitation: true, imagePath: data["PicturePath"], nickname: groupInvite["GroupName"], server: groupInvite["ServerHost"], isGroup: true, lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(groupInvite["GroupID"], DateTime.now());
String invite = data["GroupInvite"].toString();
if (invite.startsWith("torv3")) {
String inviteJson = new String.fromCharCodes(base64Decode(invite.substring(5)));
dynamic groupInvite = jsonDecode(inviteJson);
print("new group invite: $groupInvite");
if (profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(groupInvite["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"],
isInvitation: true, imagePath: data["PicturePath"], nickname: groupInvite["GroupName"], server: groupInvite["ServerHost"], isGroup: true, lastMessageTime: DateTime.now()));
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(groupInvite["GroupID"], DateTime.now());
}
}
break;
case "AcceptGroupInvite":
print("accept group invite: $data");

View File

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
@ -41,7 +40,7 @@ class DiskAssetBundle extends CachingAssetBundle {
for (final pattern in globs) {
await for (final path in Glob(pattern).list(root: from)) {
if (path is File) {
final bytes = await (path as File).readAsBytes()/* as Uint8List*/;
final bytes = await (path as File).readAsBytes() /* as Uint8List*/;
cache[path.path] = ByteData.view(bytes.buffer);
}
}

View File

@ -233,7 +233,7 @@ class ContactInfoState extends ChangeNotifier {
int _unreadMessages = 0;
int _totalMessages = 0;
DateTime _lastMessageTime;
Map<int, GlobalKey> keys;
Map<String, GlobalKey> keys;
// todo: a nicer way to model contacts, groups and other "entities"
bool _isGroup;
@ -265,7 +265,7 @@ class ContactInfoState extends ChangeNotifier {
this._savePeerHistory = savePeerHistory;
this._lastMessageTime = lastMessageTime;
this._server = server;
keys = Map<int, GlobalKey>();
keys = Map<String, GlobalKey>();
}
get nickname => this._nickname;
@ -340,7 +340,7 @@ class ContactInfoState extends ChangeNotifier {
}
}
GlobalKey<MessageBubbleState> getMessageKey(int index) {
GlobalKey<MessageBubbleState> getMessageKey(String index) {
if (keys[index] == null) {
keys[index] = GlobalKey<MessageBubbleState>();
}
@ -356,6 +356,7 @@ class MessageState extends ChangeNotifier {
DateTime _timestamp;
String _senderOnion;
String _senderImage;
String _signature = "";
bool _ackd = false;
bool _loaded = false;
@ -374,6 +375,7 @@ class MessageState extends ChangeNotifier {
get senderOnion => this._senderOnion;
get senderImage => this._senderImage;
get loaded => this._loaded;
get signature => this._signature;
set ackd(bool newVal) {
this._ackd = newVal;
@ -397,6 +399,12 @@ class MessageState extends ChangeNotifier {
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp']);
this._senderOnion = messageWrapper['PeerID'];
this._senderImage = messageWrapper['ContactImage'];
// If this is a group, store the signature
if (contactHandle.length == 32) {
this._signature = messageWrapper['Signature'];
}
this._loaded = true;
//update ackd last as it's changenotified
this._ackd = messageWrapper['Acknowledged'];

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/widgets/contactrow.dart';
import 'package:flutter_app/widgets/profileimage.dart';
import 'package:provider/provider.dart';
import '../settings.dart';
import 'addcontactview.dart';
import '../model.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -17,7 +19,21 @@ class _ContactsViewState extends State<ContactsView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("%1's contacts".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname ?? Provider.of<ProfileInfoState>(context).onion ?? '')), //todo
title: Row(children: [
ProfileImage(
imagePath: Provider.of<ProfileInfoState>(context).imagePath,
diameter: 42,
border: Provider.of<Settings>(context).theme.portraitOnlineBorderColor(),
),
SizedBox(
width: 10,
),
Expanded(
child: Text(
"%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname ?? Provider.of<ProfileInfoState>(context).onion ?? '').replaceAll("%2", "Contacts"),
overflow: TextOverflow.ellipsis,
)), //todo
]),
actions: [
IconButton(
icon: Icon(Icons.copy),

View File

@ -51,10 +51,10 @@ class MessageBubbleState extends State<MessageBubble> {
),
textAlign: fromMe ? TextAlign.right : TextAlign.left),
!fromMe
? SizedBox(width:1,height:1)
? SizedBox(width: 1, height: 1)
: Provider.of<MessageState>(context).ackd
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 12)
],
));

View File

@ -49,7 +49,9 @@ class _MessageListState extends State<MessageList> {
child: Container(
// Only show broken heart is the contact is offline...
decoration: BoxDecoration(
image: Provider.of<ContactInfoState>(outerContext).isOnline() ? null : DecorationImage(
image: Provider.of<ContactInfoState>(outerContext).isOnline()
? null
: DecorationImage(
fit: BoxFit.contain,
image: AssetImage("assets/core/negative_heart_512px.png"),
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.mainTextColor(), BlendMode.srcIn))),
@ -64,7 +66,12 @@ class _MessageListState extends State<MessageList> {
contactHandle: Provider.of<ContactInfoState>(outerContext).onion,
messageIndex: index,
),
child: MessageRow(key: Provider.of<ContactInfoState>(outerContext).getMessageKey(index)));
builder: (bcontext, child) {
String idx = Provider.of<ContactInfoState>(outerContext).isGroup == true && Provider.of<MessageState>(bcontext).signature.isEmpty == false
? Provider.of<MessageState>(bcontext).signature
: index.toString();
return MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx));
});
},
),
)));

View File

@ -7,7 +7,7 @@ import '../settings.dart';
import 'messagebubble.dart';
class MessageRow extends StatefulWidget {
MessageRow({Key key}): super(key: key);
MessageRow({Key key}) : super(key: key);
@override
_MessageRowState createState() => _MessageRowState();