diff --git a/android/app/build.gradle b/android/app/build.gradle index 4899ea9e..abbe8f45 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -65,8 +65,8 @@ android { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - // signingConfig signingConfigs.debug - signingConfig signingConfigs.release + signingConfig signingConfigs.debug + // signingConfig signingConfigs.release } } diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 0367c7ee..5584ae35 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -8,6 +8,7 @@ import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/models/remoteserver.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/notification_manager.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:cwtch/torstatus.dart'; @@ -145,9 +146,10 @@ class CwtchNotifier { var senderImage = data['Picture']; var isAuto = data['Auto'] == "true"; String? contenthash = data['ContentHash']; - var selectedConversation = appState.selectedProfile == data["ProfileOnion"] && appState.selectedConversation == identifier; + var selectedProfile = appState.selectedProfile == data["ProfileOnion"]; + var selectedConversation = selectedProfile && appState.selectedConversation == identifier; - profileCN.getProfile(data["ProfileOnion"])?.contactList.newMessage( + profileCN.getProfile(data["ProfileOnion"])?.newMessage( identifier, messageID, timestamp, @@ -156,9 +158,10 @@ class CwtchNotifier { isAuto, data["Data"], contenthash, + selectedProfile, selectedConversation, ); - + appState.notifyProfileUnread(); break; case "PeerAcknowledgement": // We don't use these anymore, IndexedAcknowledgement is more suited to the UI front end... @@ -198,7 +201,8 @@ class CwtchNotifier { var currentTotal = contact!.totalMessages; var isAuto = data['Auto'] == "true"; String? contenthash = data['ContentHash']; - var selectedConversation = appState.selectedProfile == data["ProfileOnion"] && appState.selectedConversation == identifier; + var selectedProfile = appState.selectedProfile == data["ProfileOnion"]; + var selectedConversation = selectedProfile && appState.selectedConversation == identifier; // Only bother to do anything if we know about the group and the provided index is greater than our current total... if (currentTotal != null && idx >= currentTotal) { @@ -212,9 +216,10 @@ class CwtchNotifier { // For now we perform some minimal checks on the sent timestamp to use to provide a useful ordering for honest contacts // and ensure that malicious contacts in groups can only set this timestamp to a value within the range of `last seen message time` // and `local now`. - profileCN.getProfile(data["ProfileOnion"])?.contactList.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedConversation); + profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation); notificationManager.notify("New Message From Group!"); + appState.notifyProfileUnread(); } RemoteServerInfoState? server = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(contact.server); server?.updateSyncProgressFor(timestampSent); diff --git a/lib/models/appstate.dart b/lib/models/appstate.dart index 5a2293c3..9d78dcbb 100644 --- a/lib/models/appstate.dart +++ b/lib/models/appstate.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/widgets.dart'; enum ModalState { none, storageMigration } @@ -16,6 +18,13 @@ class AppState extends ChangeNotifier { bool _disableFilePicker = false; bool _focus = true; + StreamController _profilesUnreadNotifyControler = StreamController(); + late Stream profilesUnreadNotify; + + AppState() { + profilesUnreadNotify = _profilesUnreadNotifyControler.stream.asBroadcastStream(); + } + void SetCwtchInit() { cwtchInit = true; notifyListeners(); @@ -82,4 +91,12 @@ class AppState extends ChangeNotifier { } bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height; + + void notifyProfileUnread() { + _profilesUnreadNotifyControler.add(true); + } + + Stream getUnreadProfileNotifyStream() { + return profilesUnreadNotify; + } } diff --git a/lib/models/profile.dart b/lib/models/profile.dart index 268eeb78..2c193865 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -173,6 +173,24 @@ class ProfileInfoState extends ChangeNotifier { this._contacts.resort(); } + void newMessage(int identifier, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String? contenthash, bool selectedProfile, bool selectedConversation) { + if (!selectedProfile) { + unreadMessages++; + notifyListeners(); + } + + contactList.newMessage( + identifier, + messageID, + timestamp, + senderHandle, + senderImage, + isAuto, + data, + contenthash, + selectedConversation); + } + void downloadInit(String fileKey, int numChunks) { this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); notifyListeners(); diff --git a/lib/models/profilelist.dart b/lib/models/profilelist.dart index 1e0eb3e2..8cae18b5 100644 --- a/lib/models/profilelist.dart +++ b/lib/models/profilelist.dart @@ -27,4 +27,8 @@ class ProfileListState extends ChangeNotifier { _profiles.removeWhere((element) => element.onion == onion); notifyListeners(); } + + int generateUnreadCount(String selectedProfile) => _profiles.where( (p) => p.onion != selectedProfile ).fold(0, (i, p) => i + p.unreadMessages); + + } diff --git a/lib/themes/cwtch.dart b/lib/themes/cwtch.dart index e4a660c5..5d0eb3e1 100644 --- a/lib/themes/cwtch.dart +++ b/lib/themes/cwtch.dart @@ -68,8 +68,8 @@ class CwtchDark extends OpaqueThemeType { get portraitBlockedTextColor => lightGrey; get portraitContactBadgeColor => hotPink; get portraitContactBadgeTextColor => whiteishPurple; - get portraitProfileBadgeColor => mauvePurple; - get portraitProfileBadgeTextColor => darkGreyPurple; + get portraitProfileBadgeColor => hotPink; + get portraitProfileBadgeTextColor => whiteishPurple; get dropShadowColor => mauvePurple; get toolbarIconColor => settings; //whiteishPurple; get messageFromMeBackgroundColor => userBubble; // mauvePurple; @@ -112,7 +112,7 @@ class CwtchLight extends OpaqueThemeType { get portraitBlockedTextColor => softGrey; get portraitContactBadgeColor => accent; get portraitContactBadgeTextColor => whitePurple; - get portraitProfileBadgeColor => brightPurple; + get portraitProfileBadgeColor => accent; get portraitProfileBadgeTextColor => whitePurple; get dropShadowColor => purple; get toolbarIconColor => settings; //darkPurple; diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 7f6b2236..c7ab0271 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -76,6 +76,30 @@ class _ContactsViewState extends State { endDrawerEnableOpenDragGesture: false, drawerEnableOpenDragGesture: false, appBar: AppBar( + leading: Row(children: [ + IconButton( + icon: Icon(Icons.arrow_back), + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + onPressed: () { + Provider.of(context, listen: false).selectedProfile = ""; + Navigator.of(context).pop(); + }, + ), + + StreamBuilder( + stream: Provider.of(context).getUnreadProfileNotifyStream(), + builder: (BuildContext context, AsyncSnapshot unreadCountSnapshot) { + int unreadCount = Provider.of(context).generateUnreadCount(Provider.of(context).selectedProfile ?? "") ; + + return Visibility( + visible: unreadCount > 0, + child: CircleAvatar( + radius: 10.0, + backgroundColor: Provider.of(context).theme.portraitProfileBadgeColor, + child: Text(unreadCount > 99 ? "99+" : unreadCount.toString(), style: TextStyle(color: Provider.of(context).theme.portraitProfileBadgeTextColor, fontSize: 8.0)), + )); + }), + ]), title: RepaintBoundary( child: Row(children: [ ProfileImage( diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index acbdce0a..2ddda6d9 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -33,7 +33,7 @@ class _ProfileRowState extends State { Padding( padding: const EdgeInsets.all(6.0), //border size child: ProfileImage( - badgeCount: 0, + badgeCount: profile.unreadMessages, badgeColor: Provider.of(context).theme.portraitProfileBadgeColor, badgeTextColor: Provider.of(context).theme.portraitProfileBadgeTextColor, diameter: 64.0, @@ -83,6 +83,7 @@ class _ProfileRowState extends State { } void _pushContactList(ProfileInfoState profile, bool isLandscape) { + Provider.of(context, listen: false).unreadMessages = 0; Navigator.of(context).push( MaterialPageRoute( settings: RouteSettings(name: "conversations"),