From e582599d23d85fc0dcc30c9e95f033cef5bd36d1 Mon Sep 17 00:00:00 2001 From: erinn Date: Fri, 9 Apr 2021 19:31:27 -0700 Subject: [PATCH] mo wirin --- integration_test/app_test.dart | 1 - lib/cwtch/cwtchNotifier.dart | 17 +++++++------- lib/cwtch/gomobile.dart | 4 ++++ lib/model.dart | 36 ++++++++++++++++++++++++++++ lib/opaque.dart | 1 + lib/views/addcontactview.dart | 1 - lib/views/contactsview.dart | 2 -- lib/views/messageview.dart | 40 ++++++++++++++++++++++--------- lib/widgets/messagebubble.dart | 33 +++----------------------- lib/widgets/messagelist.dart | 43 ++++++++++++++++++++++++---------- pubspec.lock | 2 +- pubspec.yaml | 6 ++--- test/buttontextfield_test.dart | 1 - test/textfield_test.dart | 2 -- 14 files changed, 115 insertions(+), 74 deletions(-) diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 9dd90a4..3071725 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -6,7 +6,6 @@ // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; -import 'package:flutter_app/views/addeditprofileview.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 3d614c9..2ec2644 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -28,24 +28,25 @@ class CwtchNotifier { )); break; case "PeerCreated": - print("xx peercreated $data"); profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState( profileOnion: data["ProfileOnion"], onion: data["RemotePeer"], nickname: data["nick"], status: data["status"], imagePath: data["picture"], + isBlocked: data["authorization"] == "blocked", + isInvitation: data["authorization"] == "unknown", + savePeerHistory: data["saveConversationHistory"], + numMessages: int.parse(data["numMessages"]), + numUnread: int.parse(data["unread"]), )); break; case "PeerStateChange": ContactInfoState contact = profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]); - if (contact == null) { - //todo: stopgap, as lc-g is supposed to handle this - print("PSC -> adding " + data["ProfileOnion"] + " :: " + data["RemotePeer"]); - profileCN.getProfile(data["ProfileOnion"]).contactList.add( - ContactInfoState(profileOnion: data["ProfileOnion"], onion: data["RemotePeer"], nickname: data["name"], status: data["ConnectionState"], isBlocked: data["authorization"] == "blocked", isInvitation: data["authorization"] == "unknown")); - } else { - contact.status = data["ConnectionState"]; + if (contact != null) { + if (data["ConnectionState"] != null) { + contact.status = data["ConnectionState"]; + } if (data["authorization"] != null) { contact.isInvitation = data["authorization"] == "unknown"; contact.isBlocked = data["authorization"] == "blocked"; diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index ed38cc7..f168d61 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -120,21 +120,25 @@ class CwtchGomobile implements Cwtch { void dispose() => {}; @override + // ignore: non_constant_identifier_names void AcceptContact(String profileOnion, String contactHandle) { cwtchPlatform.invokeMethod("AcceptContact", {"ProfileOnion": profileOnion, "handle": contactHandle}); } @override + // ignore: non_constant_identifier_names void BlockContact(String profileOnion, String contactHandle) { cwtchPlatform.invokeMethod("BlockContact", {"ProfileOnion": profileOnion, "handle": contactHandle}); } @override + // ignore: non_constant_identifier_names void DebugResetContact(String profileOnion, String contactHandle) { cwtchPlatform.invokeMethod("DebugResetContact", {"ProfileOnion": profileOnion, "handle": contactHandle}); } @override + // ignore: non_constant_identifier_names void SendMessage(String profileOnion, String contactHandle, String message) { cwtchPlatform.invokeMethod("SendMessage", {"ProfileOnion": profileOnion, "handle": contactHandle, "message": message}); } diff --git a/lib/model.dart b/lib/model.dart index 66bf826..e84163b 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -1,10 +1,12 @@ import 'dart:convert'; import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; import 'dart:async'; import 'dart:collection'; import 'cwtch/cwtch.dart'; +import 'main.dart'; //////////////////// /// UI State /// @@ -274,6 +276,40 @@ class ContactInfoState extends ChangeNotifier { } } + +class MessageState extends ChangeNotifier { + final String profileOnion; + final String contactHandle; + final int messageIndex; + String _message; + String _timestamp; + bool _ackd = false; + + MessageState({ + BuildContext context, + this.profileOnion, + this.contactHandle, + this.messageIndex, + }) { + Provider.of(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) { + dynamic messageWrapper = jsonDecode(jsonMessage); + dynamic message = jsonDecode(messageWrapper['Message']); + this._message = message['d']; + this._timestamp = messageWrapper['Timestamp']; + this._ackd = messageWrapper['Acknowledged']; + }); + } + + get message => this._message; + get timestamp => this._timestamp; + get ackd => this._ackd; + + set ackd(bool newVal) { + this._ackd = newVal; + notifyListeners(); + } +} + ///////////// /// ACN /// ///////////// diff --git a/lib/opaque.dart b/lib/opaque.dart index 4373440..56e7e28 100644 --- a/lib/opaque.dart +++ b/lib/opaque.dart @@ -1350,6 +1350,7 @@ ThemeData mkThemeData(Settings opaque) { accentColor: opaque.current().defaultButtonColor(), buttonColor: opaque.current().defaultButtonColor(), backgroundColor: opaque.current().backgroundMainColor(), + highlightColor: opaque.current().hilightElementTextColor(), iconTheme: IconThemeData( color: opaque.current().mainTextColor(), ), diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index 34fadfa..abebcea 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_app/errorHandler.dart'; import 'package:flutter_app/settings.dart'; diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index a82c2dc..1c06af3 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/widgets/contactrow.dart'; import 'package:provider/provider.dart'; -import '../errorHandler.dart'; -import '../main.dart'; import 'addcontactview.dart'; import '../model.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 4383f68..a0c22af 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -16,9 +16,11 @@ class MessageView extends StatefulWidget { class _MessageViewState extends State { final ctrlrCompose = TextEditingController(); + final focusNode = FocusNode(); @override void dispose() { + focusNode.dispose(); ctrlrCompose.dispose(); super.dispose(); } @@ -65,13 +67,14 @@ class _MessageViewState extends State { )); } - void _sendMessage() { + void _sendMessage([String ignoredParam]) { ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text); Provider.of(context, listen:false).cwtch.SendMessage( Provider.of(context, listen:false).profileOnion, Provider.of(context, listen:false).onion, jsonEncode(cm)); ctrlrCompose.clear(); + focusNode.requestFocus(); Provider.of(context, listen:false).totalMessages++; } @@ -79,33 +82,48 @@ class _MessageViewState extends State { return Container( color: Opaque.current().backgroundMainColor(), height: 100, + padding: EdgeInsets.all(8.0), child: Row( children: [ - Expanded(child: TextField(controller: ctrlrCompose)), + Expanded(child: TextField( + controller: ctrlrCompose, + focusNode: focusNode, + textInputAction: TextInputAction.send, + onSubmitted: _sendMessage, + )), SizedBox( - width: 100, + width: 90, height: 80, child: Column(children: [ - ElevatedButton( + Padding(padding: EdgeInsets.fromLTRB(2, 2, 2, 2), child: ElevatedButton( child: Icon(Icons.send, color: Opaque.current().mainTextColor()), style: ButtonStyle( + fixedSize: MaterialStateProperty.all(Size(86, 40)), backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()), ), onPressed: _sendMessage, - ), + )), Row(children: [ - SizedBox( - width: 45, + Padding(padding: EdgeInsets.all(2), child: SizedBox( + width: 41, child: ElevatedButton( child: Icon(Icons.emoji_emotions_outlined, color: Opaque.current().mainTextColor()), + style: ButtonStyle( + fixedSize: MaterialStateProperty.all(Size(41, 40)), + backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()), + ), onPressed: placeHolder, - )), - SizedBox( - width: 45, + ))), + Padding(padding: EdgeInsets.all(2), child: SizedBox( + width: 41, child: ElevatedButton( child: Icon(Icons.attach_file, color: Opaque.current().mainTextColor()), + style: ButtonStyle( + fixedSize: MaterialStateProperty.all(Size(41, 40)), + backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()), + ), onPressed: placeHolder, - )), + ))), ]) ]), ), diff --git a/lib/widgets/messagebubble.dart b/lib/widgets/messagebubble.dart index 839bca3..0019d9c 100644 --- a/lib/widgets/messagebubble.dart +++ b/lib/widgets/messagebubble.dart @@ -1,41 +1,14 @@ -import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import '../main.dart'; import '../model.dart'; import '../opaque.dart'; class MessageBubble extends StatefulWidget { - MessageBubble({Key key, this.profile, this.contactOnion, this.messageIndex}); - final ProfileInfoState profile; - final String contactOnion; - final int messageIndex; - @override _MessageBubbleState createState() => _MessageBubbleState(); } class _MessageBubbleState extends State { - String d = "", ts = ""; - bool ack = false; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - - print("requesting message " + widget.messageIndex.toString()); - Provider.of(context, listen: false).cwtch.GetMessage(widget.profile.onion, widget.contactOnion, widget.messageIndex).then((jsonMessage) { - print("got message: " + widget.messageIndex.toString() + ": " + jsonMessage); - dynamic messageWrapper = jsonDecode(jsonMessage); - dynamic message = jsonDecode(messageWrapper['Message']); - setState(() { - d = message['d']; - ts = messageWrapper['Timestamp']; - ack = messageWrapper['Acknowledged']; - }); - }); - } - @override Widget build(BuildContext context) { return Padding( @@ -50,13 +23,13 @@ class _MessageBubbleState extends State { ), child: ListTile( title: Text( - d, + Provider.of(context).message, textAlign: TextAlign.left, ), subtitle: Row( children: [ - Text("" + widget.messageIndex.toString()), - ack ? Icon(Icons.check_circle_outline, color: Opaque.current().mainTextColor()) : Icon(Icons.hourglass_bottom_outlined, color: Opaque.current().mainTextColor()) + Text(Provider.of(context).timestamp), + Provider.of(context).ackd ? Icon(Icons.check_circle_outline, color: Opaque.current().mainTextColor()) : Icon(Icons.hourglass_bottom_outlined, color: Opaque.current().mainTextColor()) ], ), ), diff --git a/lib/widgets/messagelist.dart b/lib/widgets/messagelist.dart index 48cb68f..064cd4f 100644 --- a/lib/widgets/messagelist.dart +++ b/lib/widgets/messagelist.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; import '../model.dart'; @@ -12,6 +11,8 @@ class MessageList extends StatefulWidget { class _MessageListState extends State { ScrollController ctrlr1 = ScrollController(); + int lastMessageCount = 0; + double lastMaxScroll = 0.0; @override void initState() { @@ -20,24 +21,40 @@ class _MessageListState extends State { } void _scrollToBottom(bool force) { - if (force || ctrlr1.position.pixels >= ctrlr1.position.maxScrollExtent - 3) { - ctrlr1.jumpTo(ctrlr1.position.maxScrollExtent); + if (ctrlr1.hasClients) { + if (force || Provider.of(context, listen: false).totalMessages > lastMessageCount) { + if (ctrlr1.position.pixels == lastMaxScroll) { + ctrlr1.jumpTo(ctrlr1.position.maxScrollExtent); + } + + setState(() { + lastMessageCount = Provider.of(context, listen: false).totalMessages; + lastMaxScroll = ctrlr1.position.maxScrollExtent; + }); + } + } else { + setState(() => null); } } @override Widget build(BuildContext outerContext) { WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom(false)); - return ListView.builder( + return Card(child: Scrollbar( + isAlwaysShown: true, controller: ctrlr1, - itemCount: Provider.of(context).totalMessages, - itemBuilder: (context, index) { - return MessageBubble( - profile: Provider.of(outerContext), - contactOnion: Provider.of(outerContext).onion, - messageIndex: index, - ); - }, - ); + child: ListView.builder( + controller: ctrlr1, + itemCount: Provider.of(context).totalMessages, + itemBuilder: (context, index) { + return ChangeNotifierProvider(create: (_) => MessageState( + context: context, + profileOnion: Provider.of(outerContext).onion, + contactHandle: Provider.of(outerContext).onion, + messageIndex: index, + ), child: MessageBubble()); + }, + ), + )); } } diff --git a/pubspec.lock b/pubspec.lock index 6528649..78cf820 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -378,7 +378,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7507c4b..3343752 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,16 +36,14 @@ dependencies: path_provider: ^2.0.0 # todo: flutter_driver causes version conflict. eg https://github.com/flutter/flutter/issues/44829 - # testing-related deps +# testing-related deps integration_test: ^1.0.0 flutter_test: sdk: flutter flutter_driver: sdk: flutter -dev_dependencies: - flutter_test: - sdk: flutter +#dev_dependencies: # flutter_lokalise: any # alternatively: flutter pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/intl/app_localizations.dart lib/l10n/intl_*.arb --api-token X --project-id Y diff --git a/test/buttontextfield_test.dart b/test/buttontextfield_test.dart index e69d278..cd5f9ad 100644 --- a/test/buttontextfield_test.dart +++ b/test/buttontextfield_test.dart @@ -9,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/opaque.dart'; import 'package:flutter_app/settings.dart'; import 'package:flutter_app/widgets/buttontextfield.dart'; -import 'package:flutter_app/widgets/cwtchlabel.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; diff --git a/test/textfield_test.dart b/test/textfield_test.dart index 6ab6dda..1b4a1e4 100644 --- a/test/textfield_test.dart +++ b/test/textfield_test.dart @@ -6,10 +6,8 @@ // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_app/opaque.dart'; import 'package:flutter_app/settings.dart'; -import 'package:flutter_app/widgets/cwtchlabel.dart'; import 'package:flutter_app/widgets/textfield.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart';