From ddefcb8ff289aaae0ec70a63db01c45b84dd195b Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 7 Feb 2022 21:13:49 -0500 Subject: [PATCH] rejig notification policy globally and conversationally --- lib/cwtch/cwtchNotifier.dart | 53 +++++++++++++++++--------------- lib/models/contact.dart | 51 +++++++++++++++++++++--------- lib/models/profile.dart | 10 ++++-- lib/settings.dart | 16 +++++----- lib/views/groupsettingsview.dart | 46 ++++++++++----------------- lib/views/peersettingsview.dart | 48 ++++++++++------------------- 6 files changed, 114 insertions(+), 110 deletions(-) diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 4136644d..2f22b89c 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -60,25 +60,24 @@ class CwtchNotifier { case "ContactCreated": EnvironmentConfig.debugLog("ContactCreated $data"); - profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState( - data["ProfileOnion"], - int.parse(data["ConversationID"]), - data["RemotePeer"], - nickname: data["nick"], - status: data["status"], - imagePath: data["picture"], - defaultImagePath: data["defaultPicture"], - blocked: data["blocked"] == "true", - accepted: data["accepted"] == "true", - savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"], - numMessages: int.parse(data["numMessages"]), - numUnread: int.parse(data["unread"]), - isGroup: false, // by definition - server: null, - archived: false, - lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet - options: data["options"] - )); + profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], int.parse(data["ConversationID"]), data["RemotePeer"], + nickname: data["nick"], + status: data["status"], + imagePath: data["picture"], + defaultImagePath: data["defaultPicture"], + blocked: data["blocked"] == "true", + accepted: data["accepted"] == "true", + savePeerHistory: data["saveConversationHistory"] == null ? "DeleteHistoryConfirmed" : data["saveConversationHistory"], + numMessages: int.parse(data["numMessages"]), + numUnread: int.parse(data["unread"]), + isGroup: false, + // by definition + server: null, + archived: false, + lastMessageTime: DateTime.now(), + //show at the top of the contact list even if no messages yet + notificationPolicy: data["notificationPolicy"] ?? "ConversationNotificationPolicy.Default")); + break; case "NewServer": EnvironmentConfig.debugLog("NewServer $data"); @@ -106,8 +105,10 @@ class CwtchNotifier { } if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(int.parse(data["ConversationID"])) == null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], int.parse(data["ConversationID"]), data["GroupID"], - blocked: false, // we created - accepted: true, // we created + blocked: false, + // we created + accepted: true, + // we created imagePath: data["picture"], defaultImagePath: data["picture"], nickname: data["GroupName"], @@ -115,7 +116,8 @@ class CwtchNotifier { server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now(), - options: data["options"])); + notificationPolicy: data["notificationPolicy"] ?? "ConversationNotificationPolicy.Default")); + profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(int.parse(data["ConversationID"]), DateTime.now()); } break; @@ -146,7 +148,6 @@ class CwtchNotifier { } break; case "NewMessageFromPeer": - var identifier = int.parse(data["ConversationID"]); var messageID = int.parse(data["Index"]); var timestamp = DateTime.tryParse(data['TimestampReceived'])!; @@ -319,8 +320,10 @@ class CwtchNotifier { if (profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(groupInvite["GroupID"]) == null) { var identifier = int.parse(data["ConversationID"]); profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], identifier, groupInvite["GroupID"], - blocked: false, // NewGroup only issued on accepting invite - accepted: true, // NewGroup only issued on accepting invite + blocked: false, + // NewGroup only issued on accepting invite + accepted: true, + // NewGroup only issued on accepting invite imagePath: data["picture"], nickname: groupInvite["GroupName"], server: groupInvite["ServerHost"], diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 3cfd604d..78ac97b8 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -4,14 +4,32 @@ import 'package:flutter/widgets.dart'; import 'message.dart'; import 'messagecache.dart'; +enum ConversationNotificationPolicy { + Default, + OptIn, + Never, +} + +extension Nameable on ConversationNotificationPolicy { + String get toName { + switch (this) { + case ConversationNotificationPolicy.Default: + return "Default"; + case ConversationNotificationPolicy.OptIn: + return "Opt In"; + case ConversationNotificationPolicy.Never: + return "Never"; + } + } +} + class ContactInfoState extends ChangeNotifier { final String profileOnion; final int identifier; final String onion; late String _nickname; - late bool _notificationsOptIn; - late bool _notificationsOptOut; + late ConversationNotificationPolicy _notificationPolicy; late bool _accepted; late bool _blocked; @@ -48,7 +66,7 @@ class ContactInfoState extends ChangeNotifier { lastMessageTime, server, archived = false, - options = const {} }) { + notificationPolicy = "ConversationNotificationPolicy.Default"}) { this._nickname = nickname; this._isGroup = isGroup; this._accepted = accepted; @@ -62,9 +80,7 @@ class ContactInfoState extends ChangeNotifier { this._lastMessageTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime; this._server = server; this._archived = archived; - print("Contact: $_nickname, Options: $options opt-in: ${options["notification-opt-in"]} opt-out: ${options["notification-opt-out"]} "); - this._notificationsOptIn = (options["notification-opt-in"] ?? "false") == "true"; - this._notificationsOptOut = (options["notification-opt-out"] ?? "false") == "true"; + this._notificationPolicy = notificationPolicyFromString(notificationPolicy); this.messageCache = new MessageCache(); keys = Map>(); } @@ -202,15 +218,10 @@ class ContactInfoState extends ChangeNotifier { } } - bool get notificationsOptIn => _notificationsOptIn; - set notificationsOptIn(bool newVal) { - _notificationsOptIn = newVal; - notifyListeners(); - } + ConversationNotificationPolicy get notificationsPolicy => _notificationPolicy; - bool get notificationsOptOut => _notificationsOptOut; - set notificationsOptOut(bool newVal) { - _notificationsOptOut = newVal; + set notificationsPolicy(ConversationNotificationPolicy newVal) { + _notificationPolicy = newVal; notifyListeners(); } @@ -257,4 +268,16 @@ class ContactInfoState extends ChangeNotifier { this.messageCache.ackCache(messageID); notifyListeners(); } + + static ConversationNotificationPolicy notificationPolicyFromString(String val) { + switch (val) { + case "ConversationNotificationPolicy.Default": + return ConversationNotificationPolicy.Default; + case "ConversationNotificationPolicy.OptIn": + return ConversationNotificationPolicy.OptIn; + case "ConversationNotificationPolicy.Never": + return ConversationNotificationPolicy.Never; + } + return ConversationNotificationPolicy.Never; + } } diff --git a/lib/models/profile.dart b/lib/models/profile.dart index a50c9922..1f287401 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -64,7 +64,7 @@ class ProfileInfoState extends ChangeNotifier { server: contact["groupServer"], archived: contact["isArchived"] == true, lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])), - options: contact["options"]); + notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default"); })); // dummy set to invoke sort-on-load @@ -101,6 +101,7 @@ class ProfileInfoState extends ChangeNotifier { // Getters and Setters for Online Status bool get isOnline => this._online; + set isOnline(bool newValue) { this._online = newValue; notifyListeners(); @@ -110,24 +111,28 @@ class ProfileInfoState extends ChangeNotifier { bool get isEncrypted => this._encrypted; String get nickname => this._nickname; + set nickname(String newValue) { this._nickname = newValue; notifyListeners(); } String get imagePath => this._imagePath; + set imagePath(String newVal) { this._imagePath = newVal; notifyListeners(); } String get defaultImagePath => this._defaultImagePath; + set defaultImagePath(String newVal) { this._defaultImagePath = newVal; notifyListeners(); } int get unreadMessages => this._unreadMessages; + set unreadMessages(int newVal) { this._unreadMessages = newVal; notifyListeners(); @@ -145,6 +150,7 @@ class ProfileInfoState extends ChangeNotifier { } ContactListState get contactList => this._contacts; + ProfileServerListState get serverList => this._servers; @override @@ -184,7 +190,7 @@ class ProfileInfoState extends ChangeNotifier { isGroup: contact["isGroup"], server: contact["groupServer"], lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])), - options: contact["options"], + notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default", )); } unreadMessages += int.parse(contact["numUnread"]); diff --git a/lib/settings.dart b/lib/settings.dart index b13eaea6..154af0b9 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -23,9 +23,9 @@ enum DualpaneMode { } enum NotificationPolicy { - None, + Mute, OptIn, - OptOut, + DefaultAll, } enum NotificationContent { @@ -47,7 +47,7 @@ class Settings extends ChangeNotifier { DualpaneMode _uiColumnModePortrait = DualpaneMode.Single; DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait; - NotificationPolicy _notificationPolicy = NotificationPolicy.OptOut; + NotificationPolicy _notificationPolicy = NotificationPolicy.DefaultAll; NotificationContent _notificationContent = NotificationContent.SimpleEvent; bool blockUnknownConnections = false; @@ -275,13 +275,13 @@ class Settings extends ChangeNotifier { static NotificationPolicy notificationPolicyFromString(String? np) { switch (np) { case "NotificationPolicy.None": - return NotificationPolicy.None; + return NotificationPolicy.Mute; case "NotificationPolicy.OptIn": return NotificationPolicy.OptIn; case "NotificationPolicy.OptOut": - return NotificationPolicy.OptOut; + return NotificationPolicy.DefaultAll; } - return NotificationPolicy.OptOut; + return NotificationPolicy.DefaultAll; } static NotificationContent notificationContentFromString(String? nc) { @@ -296,9 +296,9 @@ class Settings extends ChangeNotifier { static String notificationPolicyToString(NotificationPolicy np, BuildContext context) { switch (np) { - case NotificationPolicy.None: return "None"; + case NotificationPolicy.Mute: return "Mute"; case NotificationPolicy.OptIn: return "OptIn"; - case NotificationPolicy.OptOut: return "OptOut"; + case NotificationPolicy.DefaultAll: return "DefaultAll"; } } diff --git a/lib/views/groupsettingsview.dart b/lib/views/groupsettingsview.dart index 9d7f2bf9..6d31535d 100644 --- a/lib/views/groupsettingsview.dart +++ b/lib/views/groupsettingsview.dart @@ -130,40 +130,26 @@ class _GroupSettingsViewState extends State { SizedBox( height: 20, ), - Visibility( - visible: Provider.of(context, listen: false).notificationPolicy == NotificationPolicy.OptOut, - child: SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/"Notifications Opt Out", style: TextStyle(color: settings.current().mainTextColor)), - subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/"The system blah blah..."), - secondary: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), - value: Provider.of(context).notificationsOptOut, - onChanged: (bool optOut) { - Provider.of(context, listen: false).notificationsOptOut = optOut; + ListTile( + title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/ "Conversation Notification Policy", style: TextStyle(color: settings.current().mainTextColor)), + subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/ "The system blah blah..."), + leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), + trailing: DropdownButton( + value: Provider.of(context).notificationsPolicy, + items: ConversationNotificationPolicy.values.map>((ConversationNotificationPolicy value) { + return DropdownMenuItem( + value: value, + child: Text(value.toName), + ); + }).toList(), + onChanged: (ConversationNotificationPolicy? newVal) { + Provider.of(context, listen: false).notificationsPolicy = newVal!; var profileOnion = Provider.of(context, listen: false).profileOnion; var identifier = Provider.of(context, listen: false).identifier; - const NotificationOptOutKey = "profile.notification-opt-out"; - Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationOptOutKey, optOut.toString()); + const NotificationPolicyKey = "profile.notification-policy"; + Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationPolicyKey, newVal.toString()); }, - activeTrackColor: settings.theme.defaultButtonColor, - inactiveTrackColor: settings.theme.defaultButtonDisabledColor, )), - Visibility( - visible: Provider.of(context, listen: false).notificationPolicy == NotificationPolicy.OptIn, - child: SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/"Notifications Opt In", style: TextStyle(color: settings.current().mainTextColor)), - subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/"The system blah blah..."), - secondary: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), - value: Provider.of(context).notificationsOptIn, - onChanged: (bool optIn) { - Provider.of(context, listen: false).notificationsOptIn = optIn; - var profileOnion = Provider.of(context, listen: false).profileOnion; - var identifier = Provider.of(context, listen: false).identifier; - const NotificationOptInKey = "profile.notification-opt-in"; - Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationOptInKey, optIn.toString()); - }, - activeTrackColor: settings.theme.defaultButtonColor, - inactiveTrackColor: settings.theme.defaultButtonDisabledColor, - )) ]), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index b45954c8..112c2c3a 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -201,40 +201,26 @@ class _PeerSettingsViewState extends State { child: Text(value), ); }).toList())), - Visibility( - visible: Provider.of(context, listen: false).notificationPolicy == NotificationPolicy.OptOut, - child: SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/"Notifications Opt Out", style: TextStyle(color: settings.current().mainTextColor)), - subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/"The system blah blah..."), - secondary: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), - value: Provider.of(context).notificationsOptOut, - onChanged: (bool optOut) { - Provider.of(context, listen: false).notificationsOptOut = optOut; - var profileOnion = Provider.of(context, listen: false).profileOnion; - var identifier = Provider.of(context, listen: false).identifier; - const NotificationOptOutKey = "profile.notification-opt-out"; - Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationOptOutKey, optOut.toString()); - }, - activeTrackColor: settings.theme.defaultButtonColor, - inactiveTrackColor: settings.theme.defaultButtonDisabledColor, - )), - Visibility( - visible: Provider.of(context, listen: false).notificationPolicy == NotificationPolicy.OptIn, - child: SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/"Notifications Opt In", style: TextStyle(color: settings.current().mainTextColor)), - subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/"The system blah blah..."), - secondary: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), - value: Provider.of(context).notificationsOptIn, - onChanged: (bool optIn) { - Provider.of(context, listen: false).notificationsOptIn = optIn; + ListTile( + title: Text(/*AppLocalizations.of(context)!.savePeerHistory*/ "Conversation Notification Policy", style: TextStyle(color: settings.current().mainTextColor)), + subtitle: Text(/*AppLocalizations.of(context)!.savePeerHistoryDescription*/ "The system blah blah..."), + leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor), + trailing: DropdownButton( + value: Provider.of(context).notificationsPolicy, + items: ConversationNotificationPolicy.values.map>((ConversationNotificationPolicy value) { + return DropdownMenuItem( + value: value, + child: Text(value.toName), + ); + }).toList(), + onChanged: (ConversationNotificationPolicy? newVal) { + Provider.of(context, listen: false).notificationsPolicy = newVal!; var profileOnion = Provider.of(context, listen: false).profileOnion; var identifier = Provider.of(context, listen: false).identifier; - const NotificationOptInKey = "profile.notification-opt-in"; - Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationOptInKey, optIn.toString()); + const NotificationPolicyKey = "profile.notification-policy"; + Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationPolicyKey, newVal.toString()); }, - activeTrackColor: settings.theme.defaultButtonColor, - inactiveTrackColor: settings.theme.defaultButtonDisabledColor, - )) + )), ]), Column(mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ SizedBox(