Merge pull request 'acknowledgements' (#66) from messagefixes into trunk
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
Reviewed-on: #66
This commit is contained in:
commit
4b5958644c
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:flutter_app/torstatus.dart';
|
import 'package:flutter_app/torstatus.dart';
|
||||||
|
|
||||||
|
@ -62,10 +62,21 @@ class CwtchNotifier {
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
|
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
||||||
break;
|
break;
|
||||||
|
case "IndexedAcknowledgement":
|
||||||
|
var idx = int.parse(data["Index"]);
|
||||||
|
if (idx < 0) 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;
|
||||||
|
break;
|
||||||
case "NewMessageFromGroup":
|
case "NewMessageFromGroup":
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).unreadMessages++;
|
if (data["ProfileOnion"] != data["RemotePeer"]) {//not from me
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).totalMessages++;
|
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).unreadMessages++;
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
|
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
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "AppError":
|
case "AppError":
|
||||||
print("New App Error: $data");
|
print("New App Error: $data");
|
||||||
|
|
|
@ -305,6 +305,7 @@ class CwtchFfi implements Cwtch {
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void ResetTor() {
|
void ResetTor() {
|
||||||
var resetTor = library.lookup<NativeFunction<Void Function()>>("c_ResetTor");
|
var resetTor = library.lookup<NativeFunction<Void Function()>>("c_ResetTor");
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
final ResetTor = resetTor.asFunction<void Function()>();
|
final ResetTor = resetTor.asFunction<void Function()>();
|
||||||
ResetTor();
|
ResetTor();
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,7 @@ class CwtchGomobile implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
void SetGroupAttribute(String profileOnion, String groupHandle, String key, String value) {
|
void SetGroupAttribute(String profileOnion, String groupHandle, String key, String value) {
|
||||||
cwtchPlatform.invokeMethod("SetGroupAttribute", {"ProfileOnion": profileOnion, "groupHandle": groupHandle, "key": key, "value": value});
|
cwtchPlatform.invokeMethod("SetGroupAttribute", {"ProfileOnion": profileOnion, "groupHandle": groupHandle, "key": key, "value": value});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -13,7 +12,6 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:glob/glob.dart';
|
import 'package:glob/glob.dart';
|
||||||
import 'package:glob/list_local_fs.dart';
|
import 'package:glob/list_local_fs.dart';
|
||||||
|
@ -43,7 +41,7 @@ class DiskAssetBundle extends CachingAssetBundle {
|
||||||
for (final pattern in globs) {
|
for (final pattern in globs) {
|
||||||
await for (final path in Glob(pattern).list(root: from)) {
|
await for (final path in Glob(pattern).list(root: from)) {
|
||||||
if (path is File) {
|
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);
|
cache[path.path] = ByteData.view(bytes.buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter_app/models/servers.dart';
|
import 'package:flutter_app/models/servers.dart';
|
||||||
|
import 'package:flutter_app/widgets/messagebubble.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
@ -232,6 +233,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
int _unreadMessages = 0;
|
int _unreadMessages = 0;
|
||||||
int _totalMessages = 0;
|
int _totalMessages = 0;
|
||||||
DateTime _lastMessageTime;
|
DateTime _lastMessageTime;
|
||||||
|
Map<int, GlobalKey> keys;
|
||||||
|
|
||||||
// todo: a nicer way to model contacts, groups and other "entities"
|
// todo: a nicer way to model contacts, groups and other "entities"
|
||||||
bool _isGroup;
|
bool _isGroup;
|
||||||
|
@ -249,7 +251,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
savePeerHistory = "DeleteHistoryConfirmed",
|
savePeerHistory = "DeleteHistoryConfirmed",
|
||||||
numMessages = 0,
|
numMessages = 0,
|
||||||
numUnread = 0,
|
numUnread = 0,
|
||||||
lastMessageTime = null,
|
lastMessageTime,
|
||||||
server = "",
|
server = "",
|
||||||
}) {
|
}) {
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
|
@ -263,6 +265,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
this._savePeerHistory = savePeerHistory;
|
this._savePeerHistory = savePeerHistory;
|
||||||
this._lastMessageTime = lastMessageTime;
|
this._lastMessageTime = lastMessageTime;
|
||||||
this._server = server;
|
this._server = server;
|
||||||
|
keys = Map<int, GlobalKey>();
|
||||||
}
|
}
|
||||||
|
|
||||||
get nickname => this._nickname;
|
get nickname => this._nickname;
|
||||||
|
@ -336,6 +339,13 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
return this.status == "Authenticated";
|
return this.status == "Authenticated";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlobalKey<MessageBubbleState> getMessageKey(int index) {
|
||||||
|
if (keys[index] == null) {
|
||||||
|
keys[index] = GlobalKey<MessageBubbleState>();
|
||||||
|
}
|
||||||
|
return keys[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageState extends ChangeNotifier {
|
class MessageState extends ChangeNotifier {
|
||||||
|
@ -345,7 +355,9 @@ class MessageState extends ChangeNotifier {
|
||||||
String _message;
|
String _message;
|
||||||
DateTime _timestamp;
|
DateTime _timestamp;
|
||||||
String _senderOnion;
|
String _senderOnion;
|
||||||
|
String _senderImage;
|
||||||
bool _ackd = false;
|
bool _ackd = false;
|
||||||
|
bool _loaded = false;
|
||||||
|
|
||||||
MessageState({
|
MessageState({
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
@ -353,26 +365,43 @@ class MessageState extends ChangeNotifier {
|
||||||
this.contactHandle,
|
this.contactHandle,
|
||||||
this.messageIndex,
|
this.messageIndex,
|
||||||
}) {
|
}) {
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) {
|
tryLoad(context);
|
||||||
dynamic messageWrapper = jsonDecode(jsonMessage);
|
|
||||||
dynamic message = jsonDecode(messageWrapper['Message']);
|
|
||||||
this._message = message['d'];
|
|
||||||
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp']);
|
|
||||||
this._senderOnion = messageWrapper['PeerID'];
|
|
||||||
//update ackd last as it's changenotified
|
|
||||||
this._ackd = messageWrapper['Acknowledged'];
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get message => this._message;
|
get message => this._message;
|
||||||
get timestamp => this._timestamp;
|
get timestamp => this._timestamp;
|
||||||
get ackd => this._ackd;
|
get ackd => this._ackd;
|
||||||
get senderOnion => this._senderOnion;
|
get senderOnion => this._senderOnion;
|
||||||
|
get senderImage => this._senderImage;
|
||||||
|
get loaded => this._loaded;
|
||||||
|
|
||||||
set ackd(bool newVal) {
|
set ackd(bool newVal) {
|
||||||
this._ackd = newVal;
|
this._ackd = newVal;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tryLoad(BuildContext context) {
|
||||||
|
Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) {
|
||||||
|
dynamic messageWrapper = jsonDecode(jsonMessage);
|
||||||
|
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
|
||||||
|
this._senderOnion = profileOnion;
|
||||||
|
//todo: remove once sent group messages are prestored
|
||||||
|
Future.delayed(const Duration(milliseconds: 200), () {
|
||||||
|
tryLoad(context);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||||
|
this._message = message['d'];
|
||||||
|
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp']);
|
||||||
|
this._senderOnion = messageWrapper['PeerID'];
|
||||||
|
this._senderImage = messageWrapper['ContactImage'];
|
||||||
|
this._loaded = true;
|
||||||
|
//update ackd last as it's changenotified
|
||||||
|
this._ackd = messageWrapper['Acknowledged'];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////
|
/////////////
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/widgets/contactrow.dart';
|
import 'package:flutter_app/widgets/contactrow.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../main.dart';
|
|
||||||
import 'addcontactview.dart';
|
import 'addcontactview.dart';
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_app/model.dart';
|
import 'package:flutter_app/model.dart';
|
||||||
import 'package:flutter_app/widgets/buttontextfield.dart';
|
import 'package:flutter_app/widgets/buttontextfield.dart';
|
||||||
|
@ -72,6 +71,18 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
|
||||||
controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).onion),
|
controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).onion),
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
|
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchLabel(label: AppLocalizations.of(context).server),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchTextField(
|
||||||
|
controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).server),
|
||||||
|
)
|
||||||
|
]),
|
||||||
// Nickname Save Button
|
// Nickname Save Button
|
||||||
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
|
@ -6,7 +6,6 @@ import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
import '../opaque.dart';
|
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import '../widgets/messagelist.dart';
|
import '../widgets/messagelist.dart';
|
||||||
import 'groupsettingsview.dart';
|
import 'groupsettingsview.dart';
|
||||||
|
@ -84,9 +83,9 @@ class _MessageViewState extends State<MessageView> {
|
||||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||||
ctrlrCompose.clear();
|
ctrlrCompose.clear();
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
if (Provider.of<ContactInfoState>(context, listen: false).isGroup == false) {
|
Future.delayed(const Duration(milliseconds: 80), () {
|
||||||
Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
|
Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposeBox() {
|
Widget _buildComposeBox() {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/settings.dart';
|
import 'package:flutter_app/settings.dart';
|
||||||
import 'package:flutter_app/torstatus.dart';
|
|
||||||
import 'package:flutter_app/views/torstatusview.dart';
|
import 'package:flutter_app/views/torstatusview.dart';
|
||||||
import 'package:flutter_app/widgets/passwordfield.dart';
|
import 'package:flutter_app/widgets/passwordfield.dart';
|
||||||
import 'package:flutter_app/widgets/tor_icon.dart';
|
import 'package:flutter_app/widgets/tor_icon.dart';
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/settings.dart';
|
|
||||||
import 'package:flutter_app/torstatus.dart';
|
import 'package:flutter_app/torstatus.dart';
|
||||||
import 'package:flutter_app/widgets/tor_icon.dart';
|
import 'package:flutter_app/widgets/tor_icon.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
|
@ -7,10 +7,10 @@ import '../settings.dart';
|
||||||
|
|
||||||
class MessageBubble extends StatefulWidget {
|
class MessageBubble extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_MessageBubbleState createState() => _MessageBubbleState();
|
MessageBubbleState createState() => MessageBubbleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MessageBubbleState extends State<MessageBubble> {
|
class MessageBubbleState extends State<MessageBubble> {
|
||||||
FocusNode _focus = FocusNode();
|
FocusNode _focus = FocusNode();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -24,6 +24,40 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||||
// user-configurable timestamps prolly ideal? #todo
|
// user-configurable timestamps prolly ideal? #todo
|
||||||
prettyDate = DateFormat.yMd().add_jm().format(Provider.of<MessageState>(context).timestamp);
|
prettyDate = DateFormat.yMd().add_jm().format(Provider.of<MessageState>(context).timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wdgSender = Text(Provider.of<MessageState>(context).senderOnion ?? "",
|
||||||
|
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()));
|
||||||
|
|
||||||
|
var wdgMessage = SelectableText(
|
||||||
|
(Provider.of<MessageState>(context).message ?? "") + '\u202F',
|
||||||
|
key: Key(myKey),
|
||||||
|
focusNode: _focus,
|
||||||
|
style: TextStyle(
|
||||||
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
);
|
||||||
|
|
||||||
|
var wdgDecorations = Center(
|
||||||
|
widthFactor: 1.0,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(prettyDate,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 9.0,
|
||||||
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
||||||
|
),
|
||||||
|
textAlign: fromMe ? TextAlign.right : TextAlign.left),
|
||||||
|
!fromMe
|
||||||
|
? 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)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
//print(constraints.toString()+", "+constraints.maxWidth.toString());
|
//print(constraints.toString()+", "+constraints.maxWidth.toString());
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -45,36 +79,7 @@ class _MessageBubbleState extends State<MessageBubble> {
|
||||||
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations]))));
|
||||||
//Flexible(
|
|
||||||
//fit: BoxFit.contain,
|
|
||||||
SelectableText(
|
|
||||||
(Provider.of<MessageState>(context).message ?? "") + '\u202F',
|
|
||||||
key: Key(myKey),
|
|
||||||
focusNode: _focus,
|
|
||||||
style: TextStyle(
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
widthFactor: 1.0,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(prettyDate,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 9.0,
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
|
||||||
),
|
|
||||||
textAlign: fromMe ? TextAlign.right : TextAlign.left),
|
|
||||||
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)
|
|
||||||
],
|
|
||||||
))
|
|
||||||
]))));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class _MessageListState extends State<MessageList> {
|
||||||
if (force || Provider.of<ContactInfoState>(context, listen: false).totalMessages > lastMessageCount) {
|
if (force || Provider.of<ContactInfoState>(context, listen: false).totalMessages > lastMessageCount) {
|
||||||
if (ctrlr1.position.pixels == lastMaxScroll) {
|
if (ctrlr1.position.pixels == lastMaxScroll) {
|
||||||
ctrlr1.jumpTo(ctrlr1.position.maxScrollExtent);
|
ctrlr1.jumpTo(ctrlr1.position.maxScrollExtent);
|
||||||
|
Provider.of<ContactInfoState>(context, listen: false).unreadMessages = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -63,7 +64,7 @@ class _MessageListState extends State<MessageList> {
|
||||||
contactHandle: Provider.of<ContactInfoState>(outerContext).onion,
|
contactHandle: Provider.of<ContactInfoState>(outerContext).onion,
|
||||||
messageIndex: index,
|
messageIndex: index,
|
||||||
),
|
),
|
||||||
child: MessageRow());
|
child: MessageRow(key: Provider.of<ContactInfoState>(outerContext).getMessageKey(index)));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/views/messageview.dart';
|
|
||||||
import 'package:flutter_app/widgets/profileimage.dart';
|
import 'package:flutter_app/widgets/profileimage.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../main.dart';
|
|
||||||
import '../model.dart';
|
import '../model.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import 'messagebubble.dart';
|
import 'messagebubble.dart';
|
||||||
|
|
||||||
class MessageRow extends StatefulWidget {
|
class MessageRow extends StatefulWidget {
|
||||||
|
MessageRow({Key key}): super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MessageRowState createState() => _MessageRowState();
|
_MessageRowState createState() => _MessageRowState();
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class _MessageRowState extends State<MessageRow> {
|
||||||
var contact = Provider.of<ContactInfoState>(context);
|
var contact = Provider.of<ContactInfoState>(context);
|
||||||
Widget wdgPortrait = ProfileImage(
|
Widget wdgPortrait = ProfileImage(
|
||||||
diameter: 48.0,
|
diameter: 48.0,
|
||||||
imagePath: contact.imagePath,
|
imagePath: Provider.of<MessageState>(context).senderImage ?? contact.imagePath,
|
||||||
//maskOut: contact.status != "Authenticated",
|
//maskOut: contact.status != "Authenticated",
|
||||||
border: contact.status == "Authenticated" ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor());
|
border: contact.status == "Authenticated" ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor());
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/opaque.dart';
|
import 'package:flutter_app/opaque.dart';
|
||||||
import 'package:flutter_app/settings.dart';
|
import 'package:flutter_app/settings.dart';
|
||||||
import 'package:flutter_app/widgets/cwtchlabel.dart';
|
|
||||||
import 'package:flutter_app/widgets/profileimage.dart';
|
import 'package:flutter_app/widgets/profileimage.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
Loading…
Reference in New Issue