diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 81b6e224..9c48c0ed 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.1.1-16-g7376218-2021-08-25-16-54 +v1.1.1-18-g300b68c-2021-08-27-20-42 \ No newline at end of file diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt index 9f4f8247..4bdb87b5 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -192,15 +192,15 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : val pass = (a.get("pass") as? String) ?: "" Cwtch.deleteProfile(profile, pass) } - "LeaveConversation" -> { + "ArchiveConversation" -> { val profile = (a.get("ProfileOnion") as? String) ?: "" - val contactHandle = (a.get("contactHandle") as? String) ?: "" - Cwtch.leaveConversation(profile, contactHandle) + val contactHandle = (a.get("handle") as? String) ?: "" + Cwtch.archiveConversation(profile, contactHandle) } - "LeaveGroup" -> { + "DeleteContact" -> { val profile = (a.get("ProfileOnion") as? String) ?: "" - val groupHandle = (a.get("groupHandle") as? String) ?: "" - Cwtch.leaveGroup(profile, groupHandle) + val handle = (a.get("handle") as? String) ?: "" + Cwtch.deleteConversation(profile, handle) } "RejectInvite" -> { val profile = (a.get("ProfileOnion") as? String) ?: "" diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 25359975..22868c44 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -41,12 +41,12 @@ abstract class Cwtch { void SendInvitation(String profile, String handle, String target); // ignore: non_constant_identifier_names - void LeaveConversation(String profile, String handle); + void ArchiveConversation(String profile, String handle); + // ignore: non_constant_identifier_names + void DeleteContact(String profile, String handle); // ignore: non_constant_identifier_names void CreateGroup(String profile, String server, String groupName); - // ignore: non_constant_identifier_names - void LeaveGroup(String profile, String groupID); // ignore: non_constant_identifier_names void ImportBundle(String profile, String bundle); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index b079c73e..77d8e336 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -55,6 +55,7 @@ class CwtchNotifier { numUnread: int.parse(data["unread"]), isGroup: data["isGroup"] == true, server: data["groupServer"], + archived: data["isArchived"] == true, lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet )); break; @@ -289,6 +290,23 @@ class CwtchNotifier { if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) != null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.nickname = data["Data"]; } + } else if (data["Key"] == "local.archived") { + if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) != null) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.isArchived = data["Data"] == "true"; + } + } else { + EnvironmentConfig.debugLog("unhandled set group attribute event: ${data['Key']}"); + } + break; + case "SetContactAttribute": + if (data["Key"] == "local.name") { + if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.nickname = data["Data"]; + } + } else if (data["Key"] == "local.archived") { + if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.isArchived = data["Data"] == "true"; + } } else { EnvironmentConfig.debugLog("unhandled set group attribute event: ${data['Key']}"); } diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 6c5d4802..007c0f21 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -376,30 +376,31 @@ class CwtchFfi implements Cwtch { @override // ignore: non_constant_identifier_names - void LeaveConversation(String profileOnion, String handle) { - var leaveConversation = library.lookup>("c_LeaveConversation"); + void ArchiveConversation(String profileOnion, String handle) { + var archiveConversation = library.lookup>("c_ArchiveConversation"); // ignore: non_constant_identifier_names - final LeaveConversation = leaveConversation.asFunction(); + final ArchiveConversation = archiveConversation.asFunction(); final u1 = profileOnion.toNativeUtf8(); final u2 = handle.toNativeUtf8(); - LeaveConversation(u1, u1.length, u2, u2.length); + ArchiveConversation(u1, u1.length, u2, u2.length); malloc.free(u1); malloc.free(u2); } @override // ignore: non_constant_identifier_names - void LeaveGroup(String profileOnion, String groupHandle) { - var leaveGroup = library.lookup>("c_LeaveGroup"); + void DeleteContact(String profileOnion, String handle) { + var deleteContact = library.lookup>("c_DeleteContact"); // ignore: non_constant_identifier_names - final LeaveGroup = leaveGroup.asFunction(); + final DeleteContact = deleteContact.asFunction(); final u1 = profileOnion.toNativeUtf8(); - final u2 = groupHandle.toNativeUtf8(); - LeaveGroup(u1, u1.length, u2, u2.length); + final u2 = handle.toNativeUtf8(); + DeleteContact(u1, u1.length, u2, u2.length); malloc.free(u1); malloc.free(u2); } + @override // ignore: non_constant_identifier_names void UpdateMessageFlags(String profile, String handle, int index, int flags) { diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 4717f7e5..1ca6b1d2 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -162,14 +162,14 @@ class CwtchGomobile implements Cwtch { @override // ignore: non_constant_identifier_names - void LeaveGroup(String profileOnion, String groupHandle) { - cwtchPlatform.invokeMethod("LeaveGroup", {"ProfileOnion": profileOnion, "groupHandle": groupHandle}); + void DeleteContact(String profileOnion, String handle) { + cwtchPlatform.invokeMethod("DeleteContact", {"ProfileOnion": profileOnion, "handle": handle}); } @override // ignore: non_constant_identifier_names - void LeaveConversation(String profileOnion, String contactHandle) { - cwtchPlatform.invokeMethod("LeaveConversation", {"ProfileOnion": profileOnion, "contactHandle": contactHandle}); + void ArchiveConversation(String profileOnion, String contactHandle) { + cwtchPlatform.invokeMethod("ArchiveConversation", {"ProfileOnion": profileOnion, "handle": contactHandle}); } @override diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 4b780758..306c098e 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,7 @@ { "@@locale": "de", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten", "addPeerTab": "Einen anderen Nutzer hinzufügen", "addPeer": "Anderen Nutzer hinzufügen", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index cc5b5e01..2b8309f3 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,7 @@ { "@@locale": "en", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Send this address to people you want to connect with", "addPeerTab": "Add a contact", "addPeer": "Add Contact", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 52427f6f..ceeddacd 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,7 @@ { "@@locale": "es", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte", "addPeerTab": "Agregar Contacto", "addPeer": "Agregar Contacto", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index cc64f3c5..c152cc4f 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,7 @@ { "@@locale": "fr", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.", "addPeerTab": "Ajouter un pair", "addPeer": "Ajouter un pair", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 26995b98..6419453d 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,7 @@ { "@@locale": "it", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi", "addPeerTab": "Aggiungi un peer", "addPeer": "Aggiungi peer", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 867272c8..79e76b57 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,21 @@ { "@@locale": "pl", - "@@last_modified": "2021-07-14T23:49:07+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", + "profileOnionLabel": "Send this address to contacts you want to connect with", + "addPeerTab": "Add a contact", + "addPeer": "Add Contact", + "peerNotOnline": "Contact is offline. Applications cannot be used right now.", + "peerBlockedMessage": "Contact is blocked", + "peerOfflineMessage": "Contact is offline, messages can't be delivered right now", + "blockBtn": "Block Contact", + "savePeerHistory": "Save History", + "savePeerHistoryDescription": "Determines whether or not to delete any history associated with the contact.", + "dontSavePeerHistory": "Delete History", + "unblockBtn": "Unblock Contact", + "blockUnknownLabel": "Block Unknown Contacts", + "blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings", + "networkStatusConnecting": "Connecting to network and contacts...", "showMessageButton": "Show Message", "blockedMessageMessage": "This message is from a profile you have blocked.", "placeholderEnterMessage": "Type a message...", @@ -84,7 +99,6 @@ "todoPlaceholder": "Todo...", "newConnectionPaneTitle": "New Connection", "networkStatusOnline": "Online", - "networkStatusConnecting": "Connecting to network and peers...", "networkStatusAttemptingTor": "Attempting to connect to Tor network", "networkStatusDisconnected": "Disconnected from the internet, check your connection", "viewGroupMembershipTooltip": "View Group Membership", @@ -104,7 +118,6 @@ "localeFr": "Frances", "localeEn": "English", "settingLanguage": "Language", - "blockUnknownLabel": "Block Unknown Peers", "zoomLabel": "Interface zoom (mostly affects text and button sizes)", "versionBuilddate": "Version: %1 Built on: %2", "cwtchSettingsTitle": "Cwtch Settings", @@ -128,7 +141,6 @@ "password1Label": "Password", "currentPasswordLabel": "Current Password", "yourDisplayName": "Your Display Name", - "profileOnionLabel": "Send this address to peers you want to connect with", "noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted", "radioNoPassword": "Unencrypted (No password)", "radioUsePassword": "Password", @@ -141,11 +153,6 @@ "editProfileTitle": "Edit Profile", "addProfileTitle": "Add new profile", "deleteBtn": "Delete", - "unblockBtn": "Unblock Peer", - "dontSavePeerHistory": "Delete Peer History", - "savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.", - "savePeerHistory": "Save Peer History", - "blockBtn": "Block Peer", "saveBtn": "Save", "displayNameLabel": "Display Name", "addressLabel": "Address", @@ -158,15 +165,12 @@ "acceptGroupInviteLabel": "Do you want to accept the invitation to", "newGroupBtn": "Create new group", "copiedClipboardNotification": "Copied to clipboard", - "peerOfflineMessage": "Peer is offline, messages can't be delivered right now", - "peerBlockedMessage": "Peer is blocked", "pendingLabel": "Pending", "acknowledgedLabel": "Acknowledged", "couldNotSendMsgError": "Could not send this message", "dmTooltip": "Click to DM", "membershipDescription": "Below is a list of users who have sent messages to the group. This list may not reflect all users who have access to the group.", "addListItemBtn": "Add Item", - "peerNotOnline": "Peer is Offline. Applications cannot be used right now.", "searchList": "Search List", "update": "Update", "inviteBtn": "Invite", @@ -192,7 +196,6 @@ "newBulletinLabel": "New Bulletin", "joinGroup": "Join group", "createGroup": "Create group", - "addPeer": "Add Peer", "groupAddr": "Address", "invitation": "Invitation", "server": "Server", @@ -201,7 +204,6 @@ "peerAddress": "Address", "joinGroupTab": "Join a group", "createGroupTab": "Create a group", - "addPeerTab": "Add a peer", "createGroupBtn": "Create", "defaultGroupName": "Awesome Group", "createGroupTitle": "Create Group" diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 6f7c38f2..58b8396c 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,7 @@ { "@@locale": "pt", - "@@last_modified": "2021-08-26T23:44:51+02:00", + "@@last_modified": "2021-08-27T21:54:52+02:00", + "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Send this address to contacts you want to connect with", "addPeerTab": "Add a contact", "addPeer": "Add Contact", diff --git a/lib/model.dart b/lib/model.dart index 75b39197..603fd0bb 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -142,6 +142,9 @@ class ContactListState extends ChangeNotifier { // blocked contacts last if (a.isBlocked == true && b.isBlocked != true) return 1; if (a.isBlocked != true && b.isBlocked == true) return -1; + // archive is next... + if (!a.isArchived && b.isArchived) return -1; + if (a.isArchived && !b.isArchived) return 1; // special sorting for contacts with no messages in either history if (a.lastMessageTime.millisecondsSinceEpoch == 0 && b.lastMessageTime.millisecondsSinceEpoch == 0) { // online contacts first @@ -235,6 +238,7 @@ class ProfileInfoState extends ChangeNotifier { numUnread: contact["numUnread"], isGroup: contact["isGroup"], server: contact["groupServer"], + archived: contact["isArchived"] == true, lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"]))); })); @@ -375,6 +379,7 @@ class ContactInfoState extends ChangeNotifier { // todo: a nicer way to model contacts, groups and other "entities" late bool _isGroup; String? _server; + late bool _archived; ContactInfoState( this.profileOnion, @@ -389,6 +394,7 @@ class ContactInfoState extends ChangeNotifier { numUnread = 0, lastMessageTime, server, + archived = false }) { this._nickname = nickname; this._isGroup = isGroup; @@ -400,12 +406,24 @@ class ContactInfoState extends ChangeNotifier { this._savePeerHistory = savePeerHistory; this._lastMessageTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime; this._server = server; + this._archived = archived; keys = Map>(); } String get nickname => this._nickname; String get savePeerHistory => this._savePeerHistory; + + // Indicated whether the conversation is archived, in which case it will + // be moved to the very bottom of the active conversations list until + // new messages appear + set isArchived(bool archived) { + this._archived = archived; + notifyListeners(); + } + bool get isArchived => this._archived; + + set savePeerHistory(String newVal) { this._savePeerHistory = newVal; notifyListeners(); diff --git a/lib/views/groupsettingsview.dart b/lib/views/groupsettingsview.dart index d36cc1e0..6d5acfe9 100644 --- a/lib/views/groupsettingsview.dart +++ b/lib/views/groupsettingsview.dart @@ -136,14 +136,39 @@ class _GroupSettingsViewState extends State { height: 20, ), Tooltip( - message: AppLocalizations.of(context)!.leaveGroup, + message: AppLocalizations.of(context)!.archiveConversation, child: ElevatedButton.icon( onPressed: () { - showAlertDialog(context); + var profileOnion = Provider.of(context, listen: false).profileOnion; + var handle = Provider.of(context, listen: false).onion; + // locally update cache... + Provider.of(context, listen: false).isArchived = true; + Provider.of(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle); + Future.delayed(Duration(milliseconds: 500), () { + Provider.of(context, listen: false).selectedConversation = null; + Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog + }); }, - icon: Icon(CwtchIcons.leave_group), - label: Text(AppLocalizations.of(context)!.leaveGroup), - )) + icon: Icon(CwtchIcons.leave_chat), + label: Text(AppLocalizations.of(context)!.archiveConversation), + )), + SizedBox( + height: 20, + ), + Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ + Tooltip( + message: AppLocalizations.of(context)!.leaveGroup, + child: TextButton.icon( + onPressed: () { + showAlertDialog(context); + }, + style: ButtonStyle ( + backgroundColor: MaterialStateProperty.all(Colors.transparent) + ), + icon: Icon(CwtchIcons.leave_group), + label: Text(AppLocalizations.of(context)!.leaveGroup, style: TextStyle(decoration: TextDecoration.underline),), + )) + ]) ]) ]))))); }); @@ -170,7 +195,9 @@ class _GroupSettingsViewState extends State { onPressed: () { var profileOnion = Provider.of(context, listen: false).profileOnion; var handle = Provider.of(context, listen: false).onion; - Provider.of(context, listen: false).cwtch.LeaveGroup(profileOnion, handle); + // locally update cache... + Provider.of(context, listen: false).isArchived = true; + Provider.of(context, listen: false).cwtch.DeleteContact(profileOnion, handle); Future.delayed(Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index da8f0b50..7b59a604 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -196,13 +196,21 @@ class _PeerSettingsViewState extends State { ), Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ Tooltip( - message: AppLocalizations.of(context)!.leaveGroup, + message: AppLocalizations.of(context)!.archiveConversation, child: ElevatedButton.icon( onPressed: () { - showAlertDialog(context); + var profileOnion = Provider.of(context, listen: false).profileOnion; + var handle = Provider.of(context, listen: false).onion; + // locally update cache... + Provider.of(context, listen: false).isArchived = true; + Provider.of(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle); + Future.delayed(Duration(milliseconds: 500), () { + Provider.of(context, listen: false).selectedConversation = null; + Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog + }); }, icon: Icon(CwtchIcons.leave_chat), - label: Text(AppLocalizations.of(context)!.leaveGroup), + label: Text(AppLocalizations.of(context)!.archiveConversation), )) ]) ]), @@ -232,7 +240,9 @@ class _PeerSettingsViewState extends State { onPressed: () { var profileOnion = Provider.of(context, listen: false).profileOnion; var handle = Provider.of(context, listen: false).onion; - Provider.of(context, listen: false).cwtch.LeaveConversation(profileOnion, handle); + // locally update cache... + Provider.of(context, listen: false).isArchived = true; + Provider.of(context, listen: false).cwtch.DeleteContact(profileOnion, handle); Future.delayed(Duration(milliseconds: 500), () { Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog