Distinguish between Archive and Delete

This commit is contained in:
Sarah Jamie Lewis 2021-08-27 13:46:13 -07:00
parent 00e4ed30f0
commit 4e3bd696ed
16 changed files with 135 additions and 53 deletions

View File

@ -1 +1 @@
v1.1.1-16-g7376218-2021-08-25-16-54
v1.1.1-18-g300b68c-2021-08-27-20-42

View File

@ -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) ?: ""

View File

@ -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);

View File

@ -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']}");
}

View File

@ -376,30 +376,31 @@ class CwtchFfi implements Cwtch {
@override
// ignore: non_constant_identifier_names
void LeaveConversation(String profileOnion, String handle) {
var leaveConversation = library.lookup<NativeFunction<string_string_to_void_function>>("c_LeaveConversation");
void ArchiveConversation(String profileOnion, String handle) {
var archiveConversation = library.lookup<NativeFunction<string_string_to_void_function>>("c_ArchiveConversation");
// ignore: non_constant_identifier_names
final LeaveConversation = leaveConversation.asFunction<VoidFromStringStringFn>();
final ArchiveConversation = archiveConversation.asFunction<VoidFromStringStringFn>();
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<NativeFunction<string_string_to_void_function>>("c_LeaveGroup");
void DeleteContact(String profileOnion, String handle) {
var deleteContact = library.lookup<NativeFunction<string_string_to_void_function>>("c_DeleteContact");
// ignore: non_constant_identifier_names
final LeaveGroup = leaveGroup.asFunction<VoidFromStringStringFn>();
final DeleteContact = deleteContact.asFunction<VoidFromStringStringFn>();
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) {

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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"

View File

@ -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",

View File

@ -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, GlobalKey<MessageRowState>>();
}
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();

View File

@ -136,14 +136,39 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
height: 20,
),
Tooltip(
message: AppLocalizations.of(context)!.leaveGroup,
message: AppLocalizations.of(context)!.archiveConversation,
child: ElevatedButton.icon(
onPressed: () {
showAlertDialog(context);
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
// locally update cache...
Provider.of<ContactInfoState>(context, listen: false).isArchived = true;
Provider.of<FlwtchState>(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(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<GroupSettingsView> {
onPressed: () {
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.LeaveGroup(profileOnion, handle);
// locally update cache...
Provider.of<ContactInfoState>(context, listen: false).isArchived = true;
Provider.of<FlwtchState>(context, listen: false).cwtch.DeleteContact(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(context, listen: false).selectedConversation = null;
Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog

View File

@ -196,13 +196,21 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
),
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<ContactInfoState>(context, listen: false).profileOnion;
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
// locally update cache...
Provider.of<ContactInfoState>(context, listen: false).isArchived = true;
Provider.of<FlwtchState>(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(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<PeerSettingsView> {
onPressed: () {
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.LeaveConversation(profileOnion, handle);
// locally update cache...
Provider.of<ContactInfoState>(context, listen: false).isArchived = true;
Provider.of<FlwtchState>(context, listen: false).cwtch.DeleteContact(profileOnion, handle);
Future.delayed(Duration(milliseconds: 500), () {
Provider.of<AppState>(context, listen: false).selectedConversation = null;
Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog