From fe156108b585d824f9768ebb4cc56894721396b8 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Mon, 1 Nov 2021 15:03:05 -0700 Subject: [PATCH 01/26] Rely on Index from NewGroupMessage to sync Timelines --- LIBCWTCH-GO.version | 2 +- lib/cwtch/cwtchNotifier.dart | 51 +++++++++++++++++----------- lib/models/messages/textmessage.dart | 2 +- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 1df52e42..f0e8a400 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.0-3-gfcc9d71-2021-09-30-23-09 \ No newline at end of file +v1.3.1-8-g4529984-2021-11-01-22-03 \ No newline at end of file diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 90071154..7119233a 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -145,24 +145,34 @@ class CwtchNotifier { break; case "NewMessageFromGroup": if (data["ProfileOnion"] != data["RemotePeer"]) { - //if not currently open - if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; + var idx = int.parse(data["Index"]); + var currentTotal = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages; + + // Only bother to do anything if we know about the group and the provided index is greater than our current total... + if (currentTotal != null) { + if (idx >= currentTotal) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages = idx + 1; + + //if not currently open + if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; + } + + var timestampSent = DateTime.tryParse(data['TimestampSent'])!; + // TODO: There are 2 timestamps associated with a new group message - time sent and time received. + // Sent refers to the time a profile alleges they sent a message + // Received refers to the time we actually saw the message from the server + // These can obviously be very different for legitimate reasons. + // We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for + // order. + // In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline + // 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.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); + notificationManager.notify("New Message From Group!"); + } } - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++; - var timestampSent = DateTime.tryParse(data['TimestampSent'])!; - // TODO: There are 2 timestamps associated with a new group message - time sent and time received. - // Sent refers to the time a profile alleges they sent a message - // Received refers to the time we actually saw the message from the server - // These can obviously be very different for legitimate reasons. - // We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for - // order. - // In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline - // 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.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); - notificationManager.notify("New Message From Group!"); } else { // from me (already displayed - do not update counter) var idx = data["Signature"]; @@ -180,7 +190,10 @@ class CwtchNotifier { case "MessageCounterResync": var contactHandle = data["RemotePeer"]; if (contactHandle == null || contactHandle == "") contactHandle = data["GroupID"]; - profileCN.getProfile(data["Identity"])?.contactList.getContact(contactHandle)!.totalMessages = int.parse(data["Data"]); + var total = int.parse(data["Data"]); + if (total != profileCN.getProfile(data["Identity"])?.contactList.getContact(contactHandle)!.totalMessages) { + profileCN.getProfile(data["Identity"])?.contactList.getContact(contactHandle)!.totalMessages = total; + } break; case "SendMessageToPeerError": // Ignore @@ -316,7 +329,7 @@ class CwtchNotifier { } break; case "NewRetValMessageFromPeer": - if (data["Path"] == "name") { + if (data["Path"] == "name" && data["Data"].toString().trim().length > 0) { // Update locally on the UI... if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"]) != null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.nickname = data["Data"]; diff --git a/lib/models/messages/textmessage.dart b/lib/models/messages/textmessage.dart index b4acd725..4f0d4906 100644 --- a/lib/models/messages/textmessage.dart +++ b/lib/models/messages/textmessage.dart @@ -32,7 +32,7 @@ class TextMessage extends Message { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { - String idx = Provider.of(context).isGroup == true && this.metadata.signature != null ? this.metadata.signature! : this.metadata.messageIndex.toString(); + String idx = this.metadata.messageIndex.toString(); return MessageRow(MessageBubble(this.content), key: Provider.of(bcontext).getMessageKey(idx)); }); } From 73690e8bac2841f58bbf29aed8b77cd100439012 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Mon, 1 Nov 2021 15:15:12 -0700 Subject: [PATCH 02/26] Combined if statements --- lib/cwtch/cwtchNotifier.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 7119233a..2f952dd0 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -149,8 +149,7 @@ class CwtchNotifier { var currentTotal = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages; // Only bother to do anything if we know about the group and the provided index is greater than our current total... - if (currentTotal != null) { - if (idx >= currentTotal) { + if (currentTotal != null && idx >= currentTotal) { profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages = idx + 1; //if not currently open @@ -171,7 +170,6 @@ class CwtchNotifier { // and `local now`. profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); notificationManager.notify("New Message From Group!"); - } } } else { // from me (already displayed - do not update counter) From d4b9f1dc559022d03a73b94e4132493c31eb96df Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 13 Oct 2021 18:24:07 -0700 Subject: [PATCH 03/26] lib/cwtch new servers api coverage and Set[Profile/Contact]Attribute --- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 40 +++++++ lib/cwtch/cwtch.dart | 19 ++++ lib/cwtch/ffi.dart | 107 ++++++++++++++++++ lib/cwtch/gomobile.dart | 54 +++++++++ 4 files changed, 220 insertions(+) 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 f4ef3152..3a7e9bc8 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -291,10 +291,50 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : val groupHandle = (a.get("groupHandle") as? String) ?: "" Cwtch.rejectInvite(profile, groupHandle) } + "SetProfileAttribute" -> { + val profile = (a.get("ProfileOnion") as? String) ?: "" + val key = (a.get("Key") as? String) ?: "" + val v = (a.get("Val") as? String) ?: "" + Cwtch.setProfileAttribute(profile, key, v) + } + "SetContactAttribute" -> { + val profile = (a.get("ProfileOnion") as? String) ?: "" + val contact = (a.get("Contact") as? String) ?: "" + val key = (a.get("Key") as? String) ?: "" + val v = (a.get("Val") as? String) ?: "" + Cwtch.setContactAttribute(profile, contact, key, v) + } "Shutdown" -> { Cwtch.shutdownCwtch(); return Result.success() } + "LoadServers" -> { + val password = (a.get("Password") as? String) ?: "" + Cwtch.loadServers(password) + } + "CreateServer" -> { + val password = (a.get("Password") as? String) ?: "" + Cwtch.createServer(password) + } + "DeleteServer" -> { + val serverOnion = (a.get("ServerOnion") as? String) ?: "" + val password = (a.get("Password") as? String) ?: "" + Cwtch.deleteServer(serverOnion, password) + } + "LaunchServers" -> { + Cwtch.launchServers() + } + "LaunchServer" -> { + val serverOnion = (a.get("ServerOnion") as? String) ?: "" + Cwtch.launchServer(serverOnion) + } + "ShutdownServer" -> { + val serverOnion = (a.get("ServerOnion") as? String) ?: "" + Cwtch.shutdownServer(serverOnion) + } + "ShutdownServers" -> { + Cwtch.shutdownServers() + } else -> return Result.failure() } return Result.success() diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 0df9c0b0..be2c466b 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -63,6 +63,25 @@ abstract class Cwtch { void SetGroupAttribute(String profile, String groupHandle, String key, String value); // ignore: non_constant_identifier_names void RejectInvite(String profileOnion, String groupHandle); + // ignore: non_constant_identifier_names + void SetProfileAttribute(String profile, String key, String val); + // ignore: non_constant_identifier_names + void SetContactAttribute(String profile, String contact, String key, String val); + + // ignore: non_constant_identifier_names + void LoadServers(String password); + // ignore: non_constant_identifier_names + void CreateServer(String password); + // ignore: non_constant_identifier_names + void DeleteServer(String serverOnion, String password); + // ignore: non_constant_identifier_names + void LaunchServers(); + // ignore: non_constant_identifier_names + void LaunchServer(String serverOnion); + // ignore: non_constant_identifier_names + void ShutdownServer(String serverOnion); + // ignore: non_constant_identifier_names + void ShutdownServers(); // ignore: non_constant_identifier_names void Shutdown(); diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 6b7cb552..8a76bb2d 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -530,6 +530,113 @@ class CwtchFfi implements Cwtch { malloc.free(u2); } + @override + // ignore: non_constant_identifier_names + void SetProfileAttribute(String profile, String key, String val) { + var setProfileAttribute = library.lookup>("c_SetProfileAttribute"); + // ignore: non_constant_identifier_names + final SetProfileAttribute = setProfileAttribute.asFunction(); + final u1 = profile.toNativeUtf8(); + final u2 = key.toNativeUtf8(); + final u3 = key.toNativeUtf8(); + SetProfileAttribute(u1, u1.length, u2, u2.length, u3, u3.length); + malloc.free(u1); + malloc.free(u2); + malloc.free(u3); + } + + @override + // ignore: non_constant_identifier_names + void SetContactAttribute(String profile, String contact, String key, String val) { + var setContactAttribute = library.lookup>("c_SetContactAttribute"); + // ignore: non_constant_identifier_names + final SetContactAttribute = setContactAttribute.asFunction(); + final u1 = profile.toNativeUtf8(); + final u2 = contact.toNativeUtf8(); + final u3 = key.toNativeUtf8(); + final u4 = key.toNativeUtf8(); + SetContactAttribute(u1, u1.length, u2, u2.length, u3, u3.length, u4, u4.length); + malloc.free(u1); + malloc.free(u2); + malloc.free(u3); + malloc.free(u4); + } + + @override + // ignore: non_constant_identifier_names + void LoadServers(String password) { + var loadServers = library.lookup>("c_LoadServers"); + // ignore: non_constant_identifier_names + final LoadServers = loadServers.asFunction(); + final u1 = password.toNativeUtf8(); + LoadServers(u1, u1.length); + malloc.free(u1); + } + + @override + // ignore: non_constant_identifier_names + void CreateServer(String password) { + var createServer = library.lookup>("c_CreateServer"); + // ignore: non_constant_identifier_names + final CreateServer = createServer.asFunction(); + final u1 = password.toNativeUtf8(); + CreateServer(u1, u1.length); + malloc.free(u1); + } + + @override + // ignore: non_constant_identifier_names + void DeleteServer(String serverOnion, String password) { + var deleteServer = library.lookup>("c_DeleteServer"); + // ignore: non_constant_identifier_names + final DeleteServer = deleteServer.asFunction(); + final u1 = serverOnion.toNativeUtf8(); + final u2 = password.toNativeUtf8(); + DeleteServer(u1, u1.length, u2, u2.length); + malloc.free(u1); + malloc.free(u2); + } + + @override + // ignore: non_constant_identifier_names + void LaunchServers() { + var launchServers = library.lookup>("c_LaunchServers"); + // ignore: non_constant_identifier_names + final LaunchServers = launchServers.asFunction(); + LaunchServers(); + } + + @override + // ignore: non_constant_identifier_names + void LaunchServer(String serverOnion) { + var launchServer = library.lookup>("c_LaunchServer"); + // ignore: non_constant_identifier_names + final LaunchServer = launchServer.asFunction(); + final u1 = serverOnion.toNativeUtf8(); + LaunchServer(u1, u1.length); + malloc.free(u1); + } + + @override + // ignore: non_constant_identifier_names + void ShutdownServer(String serverOnion) { + var shutdownServer = library.lookup>("c_ShutdownServer"); + // ignore: non_constant_identifier_names + final ShutdownServer = shutdownServer.asFunction(); + final u1 = serverOnion.toNativeUtf8(); + ShutdownServer(u1, u1.length); + malloc.free(u1); + } + + @override + // ignore: non_constant_identifier_names + void ShutdownServers() { + var shutdownServers = library.lookup>("c_ShutdownServers"); + // ignore: non_constant_identifier_names + final ShutdownServers = shutdownServers.asFunction(); + ShutdownServers(); + } + @override // ignore: non_constant_identifier_names Future Shutdown() async { diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index bf73813e..ab436570 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -202,6 +202,60 @@ class CwtchGomobile implements Cwtch { } @override + // ignore: non_constant_identifier_names + void SetProfileAttribute(String profile, String key, String val) { + cwtchPlatform.invokeMethod("SetProfileAttribute", {"ProfileOnion": profile, "Key": key, "Val": val}); + } + + @override + // ignore: non_constant_identifier_names + void SetContactAttribute(String profile, String contact, String key, String val) { + cwtchPlatform.invokeMethod("SetContactAttribute", {"ProfileOnion": profile, "Contact": contact, "Key": key, "Val": val}); + } + + @override + // ignore: non_constant_identifier_names + void LoadServers(String password) { + cwtchPlatform.invokeMethod("LoadServers", {"Password": password}); + } + + @override + // ignore: non_constant_identifier_names + void CreateServer(String password) { + cwtchPlatform.invokeMethod("CreateServer", {"Password": password}); + } + + @override + // ignore: non_constant_identifier_names + void DeleteServer(String serverOnion, String password) { + cwtchPlatform.invokeMethod("DeleteServer", {"ServerOnion": serverOnion, "Password": password}); + } + + @override + // ignore: non_constant_identifier_names + void LaunchServers() { + cwtchPlatform.invokeMethod("LaunchServers", {}); + } + + @override + // ignore: non_constant_identifier_names + void LaunchServer(String serverOnion) { + cwtchPlatform.invokeMethod("LaunchServer", {"ServerOnion": serverOnion}); + } + + @override + // ignore: non_constant_identifier_names + void ShutdownServer(String serverOnion) { + cwtchPlatform.invokeMethod("ShutdownServer", {"ServerOnion": serverOnion}); + } + + @override + // ignore: non_constant_identifier_names + void ShutdownServers() { + cwtchPlatform.invokeMethod("ShutdownServers", {}); + } + + @override Future Shutdown() async { print("gomobile.dart Shutdown"); cwtchPlatform.invokeMethod("Shutdown", {}); From 9789a42e94eec4ead5bcce32e83c5f92d495e3af Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 29 Oct 2021 16:37:02 -0700 Subject: [PATCH 04/26] server manager --- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 10 ++++++- lib/cwtch/cwtch.dart | 6 +++- lib/cwtch/cwtchNotifier.dart | 20 +++++++++++-- lib/cwtch/ffi.dart | 28 ++++++++++++++++--- lib/cwtch/gomobile.dart | 10 +++++-- lib/l10n/intl_de.arb | 5 ++-- lib/l10n/intl_en.arb | 5 ++-- lib/l10n/intl_es.arb | 5 ++-- lib/l10n/intl_fr.arb | 27 +++++++++--------- lib/l10n/intl_it.arb | 13 +++++---- lib/l10n/intl_pl.arb | 5 ++-- lib/l10n/intl_pt.arb | 5 ++-- lib/main.dart | 10 +++++-- lib/model.dart | 8 +++--- .../{servers.dart => profileservers.dart} | 16 +++++------ lib/settings.dart | 1 + lib/views/addcontactview.dart | 8 +++--- lib/views/addeditprofileview.dart | 21 ++++---------- lib/views/contactsview.dart | 7 +++-- lib/views/globalsettingsview.dart | 17 +++++++++++ lib/views/profilemgrview.dart | 25 +++++++++++++++-- lib/widgets/profilerow.dart | 4 +-- 22 files changed, 173 insertions(+), 83 deletions(-) rename lib/models/{servers.dart => profileservers.dart} (52%) 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 3a7e9bc8..6ee8a912 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -314,7 +314,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : } "CreateServer" -> { val password = (a.get("Password") as? String) ?: "" - Cwtch.createServer(password) + val desc = (a.get("Description") as? String) ?: "" + val autostart = (a.get("Autostart") as? Byte) ?: "" + Cwtch.createServer(password, desc, autostart) } "DeleteServer" -> { val serverOnion = (a.get("ServerOnion") as? String) ?: "" @@ -335,6 +337,12 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : "ShutdownServers" -> { Cwtch.shutdownServers() } + "SetServerAttribute" -> { + val serverOnion = (a.get("ServerOnion") as? String) ?: "" + val key = (a.get("Key") as? String) ?: "" + val v = (a.get("Val") as? String) ?: "" + Cwtch.setServerAttribute(serverOnion, key, v) + } else -> return Result.failure() } return Result.success() diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index be2c466b..d9763f43 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -1,5 +1,7 @@ import 'package:flutter/src/services/text_input.dart'; +const DefaultPassword = "be gay do crime"; + abstract class Cwtch { // ignore: non_constant_identifier_names Future Start(); @@ -71,7 +73,7 @@ abstract class Cwtch { // ignore: non_constant_identifier_names void LoadServers(String password); // ignore: non_constant_identifier_names - void CreateServer(String password); + void CreateServer(String password, String description, bool autostart); // ignore: non_constant_identifier_names void DeleteServer(String serverOnion, String password); // ignore: non_constant_identifier_names @@ -82,6 +84,8 @@ abstract class Cwtch { void ShutdownServer(String serverOnion); // ignore: non_constant_identifier_names void ShutdownServers(); + // ignore: non_constant_identifier_names + void SetServerAttribute(String serverOnion, String key, String val); // ignore: non_constant_identifier_names void Shutdown(); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 2f952dd0..0ae5d98d 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:cwtch/models/message.dart'; +import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/models/servers.dart'; import 'package:cwtch/notification_manager.dart'; import 'package:provider/provider.dart'; @@ -20,17 +21,20 @@ class CwtchNotifier { late TorStatus torStatus; late NotificationsManager notificationManager; late AppState appState; + late ServerListState serverListState; - CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN) { + CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) { profileCN = pcn; settings = settingsCN; error = errorCN; torStatus = torStatusCN; notificationManager = notificationManagerP; appState = appStateCN; + serverListState = serverListStateCN; } void handleMessage(String type, dynamic data) { + print("EVENT $type $data"); switch (type) { case "CwtchStarted": appState.SetCwtchInit(); @@ -59,11 +63,21 @@ class CwtchNotifier { lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet )); break; + case "NewServer": + var serverData = jsonDecode(data["Data"]); + serverListState.add( + serverData["onion"], + serverData["serverbundle"], + serverData["enabled"] == "true", + serverData["description"], + serverData["autostart"] == "true", + serverData["storageType"] == "storage-password"); + break; case "GroupCreated": // Retrieve Server Status from Cache... String status = ""; - ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(data["GroupServer"]); + RemoteServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(data["GroupServer"]); if (serverInfoState != null) { status = serverInfoState.status; } @@ -263,7 +277,7 @@ class CwtchNotifier { // Retrieve Server Status from Cache... String status = ""; - ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])!.serverList.getServer(groupInvite["ServerHost"]); + RemoteServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])!.serverList.getServer(groupInvite["ServerHost"]); if (serverInfoState != null) { status = serverInfoState.status; } diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 8a76bb2d..e022fe8a 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -42,6 +42,9 @@ typedef VoidFromStringStringStringStringStringFn = void Function(Pointer, typedef void_from_string_string_int_int_function = Void Function(Pointer, Int32, Pointer, Int32, Int64, Int64); typedef VoidFromStringStringIntIntFn = void Function(Pointer, int, Pointer, int, int, int); +typedef void_from_string_string_byte_function = Void Function(Pointer, Int32, Pointer, Int32, Int8); +typedef VoidFromStringStringByteFn = void Function(Pointer, int, Pointer, int, int); + typedef string_to_void_function = Void Function(Pointer str, Int32 length); typedef StringFn = void Function(Pointer dir, int); @@ -575,13 +578,15 @@ class CwtchFfi implements Cwtch { @override // ignore: non_constant_identifier_names - void CreateServer(String password) { - var createServer = library.lookup>("c_CreateServer"); + void CreateServer(String password, String description, bool autostart) { + var createServer = library.lookup>("c_CreateServer"); // ignore: non_constant_identifier_names - final CreateServer = createServer.asFunction(); + final CreateServer = createServer.asFunction(); final u1 = password.toNativeUtf8(); - CreateServer(u1, u1.length); + final u2 = description.toNativeUtf8(); + CreateServer(u1, u1.length, u2, u2.length, autostart ? 1 : 0); malloc.free(u1); + malloc.free(u2); } @override @@ -637,6 +642,21 @@ class CwtchFfi implements Cwtch { ShutdownServers(); } + @override + // ignore: non_constant_identifier_names + void SetServerAttribute(String serverOnion, String key, String val) { + var setServerAttribute = library.lookup>("c_SetServerAttribute"); + // ignore: non_constant_identifier_names + final SetServerAttribute = setServerAttribute.asFunction(); + final u1 = serverOnion.toNativeUtf8(); + final u2 = key.toNativeUtf8(); + final u3 = val.toNativeUtf8(); + SetServerAttribute(u1, u1.length, u2, u2.length, u3, u3.length); + malloc.free(u1); + malloc.free(u2); + malloc.free(u3); + } + @override // ignore: non_constant_identifier_names Future Shutdown() async { diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index ab436570..458defef 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -221,8 +221,8 @@ class CwtchGomobile implements Cwtch { @override // ignore: non_constant_identifier_names - void CreateServer(String password) { - cwtchPlatform.invokeMethod("CreateServer", {"Password": password}); + void CreateServer(String password, String description, bool autostart) { + cwtchPlatform.invokeMethod("CreateServer", {"Password": password, "Description": description, "Autostart": autostart ? 1 : 0}); } @override @@ -255,6 +255,12 @@ class CwtchGomobile implements Cwtch { cwtchPlatform.invokeMethod("ShutdownServers", {}); } + @override + // ignore: non_constant_identifier_names + void SetServerAttribute(String serverOnion, String key, String val) { + cwtchPlatform.invokeMethod("SetServerAttribute", {"ServerOnion": serverOnion, "Key": key, "Val": val}); + } + @override Future Shutdown() async { print("gomobile.dart Shutdown"); diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index e8d93349..bd883204 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,8 @@ { "@@locale": "de", - "@@last_modified": "2021-09-21T23:09:19+02:00", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "settingFileSharing": "File Sharing", "tooltipSendFile": "Send File", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d36736f1..4ff01827 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,8 @@ { "@@locale": "en", - "@@last_modified": "2021-09-21T23:09:19+02:00", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "settingFileSharing": "File Sharing", "tooltipSendFile": "Send File", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Send this address to people you want to connect with", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 449a7d18..5b234376 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,8 @@ { "@@locale": "es", - "@@last_modified": "2021-09-21T23:09:19+02:00", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "settingFileSharing": "File Sharing", "tooltipSendFile": "Send File", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index a6a1ea5d..0add5f25 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,18 +1,19 @@ { "@@locale": "fr", - "@@last_modified": "2021-09-21T23:09:19+02:00", - "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", - "settingFileSharing": "File Sharing", - "tooltipSendFile": "Send File", - "messageFileOffered": "Contact is offering to send you a file", - "messageFileSent": "You sent a file", - "messageEnableFileSharing": "Enable the file sharing experiment to view this message.", - "labelFilesize": "Size", - "labelFilename": "Filename", - "downloadFileButton": "Download", - "openFolderButton": "Open Folder", - "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion ou la présentation, par exemple en masquant les profils et les contacts.", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion en direct ou la présentation, par exemple, en masquant profil et adresses de contacts.", + "descriptionFileSharing": "L'expérience de partage de fichiers vous permet d'envoyer et de recevoir des fichiers à partir de contacts et de groupes Cwtch. Notez que si vous partagez un fichier avec un groupe, les membres de ce groupe se connecteront avec vous directement via Cwtch pour le télécharger.", + "settingFileSharing": "Partage de fichiers", + "tooltipSendFile": "Envoyer le fichier", + "messageFileOffered": "Contact vous propose de vous envoyer un fichier", + "messageFileSent": "Vous avez envoyé un fichier", + "messageEnableFileSharing": "Activez l'expérience de partage de fichiers pour afficher ce message.", + "labelFilesize": "Taille", + "labelFilename": "Nom de fichier", + "downloadFileButton": "Télécharger", + "openFolderButton": "Ouvrir le dossier", + "retrievingManifestMessage": "Récupération des informations sur le fichier...", "streamerModeLabel": "Mode Streamer\/Présentation", "archiveConversation": "Archiver cette conversation", "profileOnionLabel": "Envoyez cette adresse aux personnes avec lesquelles vous souhaitez entrer en contact.", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 51fe624d..95878777 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,10 +1,12 @@ { "@@locale": "it", - "@@last_modified": "2021-09-21T23:09:19+02:00", - "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", - "settingFileSharing": "File Sharing", - "tooltipSendFile": "Send File", - "messageFileOffered": "Contact is offering to send you a file", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", + "descriptionFileSharing": "L'esperimento di condivisione dei file ti consente di inviare e ricevere file dai contatti e dai gruppi di Cwtch. Tieni presente che la condivisione di un file con un gruppo farà sì che i membri di quel gruppo si colleghino con te direttamente su Cwtch per scaricarlo.", + "settingFileSharing": "Condivisione file", + "tooltipSendFile": "Invia file", + "messageFileOffered": "Il contatto offre l'invio di un file", "messageFileSent": "You sent a file", "messageEnableFileSharing": "Enable the file sharing experiment to view this message.", "labelFilesize": "Size", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 84a5523f..84d27a9d 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,8 @@ { "@@locale": "pl", - "@@last_modified": "2021-09-21T23:09:19+02:00", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "settingFileSharing": "File Sharing", "tooltipSendFile": "Send File", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Send this address to contacts you want to connect with", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 3c16f8cc..9e593a6b 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,8 @@ { "@@locale": "pt", - "@@last_modified": "2021-09-21T23:09:19+02:00", + "@@last_modified": "2021-10-15T20:45:29+02:00", + "titleManageProfilesShort": "Profiles", + "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", "settingFileSharing": "File Sharing", "tooltipSendFile": "Send File", @@ -12,7 +14,6 @@ "downloadFileButton": "Download", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", - "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact onions", "streamerModeLabel": "Streamer\/Presentation Mode", "archiveConversation": "Archive this Conversation", "profileOnionLabel": "Send this address to contacts you want to connect with", diff --git a/lib/main.dart b/lib/main.dart index c0c56d69..9ab2ad4b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,6 +17,7 @@ import 'cwtch/cwtch.dart'; import 'cwtch/cwtchNotifier.dart'; import 'licenses.dart'; import 'model.dart'; +import 'models/servers.dart'; import 'views/profilemgrview.dart'; import 'views/splashView.dart'; import 'dart:io' show Platform, exit; @@ -27,6 +28,7 @@ var globalSettings = Settings(Locale("en", ''), OpaqueDark()); var globalErrorHandler = ErrorHandler(); var globalTorStatus = TorStatus(); var globalAppState = AppState(); +var globalServersList = ServerListState(); void main() { print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}"); @@ -62,13 +64,13 @@ class FlwtchState extends State { shutdownMethodChannel.setMethodCallHandler(modalShutdown); print("initState: creating cwtchnotifier, ffi"); if (Platform.isAndroid) { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState); + var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList); cwtch = CwtchGomobile(cwtchNotifier); } else if (Platform.isLinux) { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState); + var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(), globalAppState, globalServersList); cwtch = CwtchFfi(cwtchNotifier); } else { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState); + var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList); cwtch = CwtchFfi(cwtchNotifier); } print("initState: invoking cwtch.Start()"); @@ -82,6 +84,7 @@ class FlwtchState extends State { ChangeNotifierProvider getAppStateProvider() => ChangeNotifierProvider.value(value: globalAppState); Provider getFlwtchStateProvider() => Provider(create: (_) => this); ChangeNotifierProvider getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs); + ChangeNotifierProvider getServerListStateProvider() => ChangeNotifierProvider.value(value: globalServersList); @override Widget build(BuildContext context) { @@ -94,6 +97,7 @@ class FlwtchState extends State { getErrorHandlerProvider(), getTorStatusProvider(), getAppStateProvider(), + getServerListStateProvider(), ], builder: (context, widget) { return Consumer2( diff --git a/lib/model.dart b/lib/model.dart index f036be0f..cb6d2e5d 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:cwtch/widgets/messagerow.dart'; import 'package:flutter/cupertino.dart'; -import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/models/profileservers.dart'; //////////////////// /// UI State /// @@ -207,7 +207,7 @@ class ContactListState extends ChangeNotifier { class ProfileInfoState extends ChangeNotifier { ContactListState _contacts = ContactListState(); - ServerListState _servers = ServerListState(); + ProfileServerListState _servers = ProfileServerListState(); final String onion; String _nickname = ""; String _imagePath = ""; @@ -267,7 +267,7 @@ class ProfileInfoState extends ChangeNotifier { List servers = jsonDecode(serversJson); this._servers.replace(servers.map((server) { // TODO Keys... - return ServerInfoState(onion: server["onion"], status: server["status"]); + return RemoteServerInfoState(onion: server["onion"], status: server["status"]); })); notifyListeners(); } @@ -316,7 +316,7 @@ class ProfileInfoState extends ChangeNotifier { } ContactListState get contactList => this._contacts; - ServerListState get serverList => this._servers; + ProfileServerListState get serverList => this._servers; @override void dispose() { diff --git a/lib/models/servers.dart b/lib/models/profileservers.dart similarity index 52% rename from lib/models/servers.dart rename to lib/models/profileservers.dart index 6996aed9..cb86d392 100644 --- a/lib/models/servers.dart +++ b/lib/models/profileservers.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -class ServerListState extends ChangeNotifier { - List _servers = []; +class ProfileServerListState extends ChangeNotifier { + List _servers = []; - void replace(Iterable newServers) { + void replace(Iterable newServers) { _servers.clear(); _servers.addAll(newServers); notifyListeners(); } - ServerInfoState? getServer(String onion) { + RemoteServerInfoState? getServer(String onion) { int idx = _servers.indexWhere((element) => element.onion == onion); return idx >= 0 ? _servers[idx] : null; } @@ -17,20 +17,20 @@ class ServerListState extends ChangeNotifier { void updateServerCache(String onion, String status) { int idx = _servers.indexWhere((element) => element.onion == onion); if (idx >= 0) { - _servers[idx] = ServerInfoState(onion: onion, status: status); + _servers[idx] = RemoteServerInfoState(onion: onion, status: status); } else { print("Tried to update server cache without a starting state...this is probably an error"); } notifyListeners(); } - List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier + List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier } -class ServerInfoState extends ChangeNotifier { +class RemoteServerInfoState extends ChangeNotifier { final String onion; final String status; - ServerInfoState({required this.onion, required this.status}); + RemoteServerInfoState({required this.onion, required this.status}); } diff --git a/lib/settings.dart b/lib/settings.dart index c0955489..436e7d88 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -9,6 +9,7 @@ import 'opaque.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; const TapirGroupsExperiment = "tapir-groups-experiment"; +const ServerManagementExperiment = "servers-experiment"; const FileSharingExperiment = "filesharing"; enum DualpaneMode { diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index a8a1a422..8485058f 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -4,7 +4,7 @@ import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cwtch/errorHandler.dart'; -import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/models/profileservers.dart'; import 'package:cwtch/settings.dart'; import 'package:cwtch/widgets/buttontextfield.dart'; import 'package:cwtch/widgets/cwtchlabel.dart'; @@ -197,7 +197,7 @@ class _AddContactViewState extends State { }, isExpanded: true, // magic property value: server, - items: Provider.of(context).serverList.servers.map>((ServerInfoState serverInfo) { + items: Provider.of(context).serverList.servers.map>((RemoteServerInfoState serverInfo) { return DropdownMenuItem( value: serverInfo.onion, child: Text( @@ -240,8 +240,8 @@ class _AddContactViewState extends State { /// TODO Manage Servers Tab Widget manageServersTab() { - final tiles = Provider.of(context).serverList.servers.map((ServerInfoState server) { - return ChangeNotifierProvider.value( + final tiles = Provider.of(context).serverList.servers.map((RemoteServerInfoState server) { + return ChangeNotifierProvider.value( value: server, child: ListTile( title: Text(server.onion), diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index 97ebe133..25e0749b 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; +import 'package:cwtch/cwtch/cwtch.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:cwtch/model.dart'; @@ -106,6 +107,7 @@ class _AddEditProfileViewState extends State { labelText: AppLocalizations.of(context)!.yourDisplayName, validator: (value) { if (value.isEmpty) { + // TODO l10n ize return "Please enter a display name"; } return null; @@ -287,32 +289,19 @@ class _AddEditProfileViewState extends State { Provider.of(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, ctrlrPass.value.text); Navigator.of(context).pop(); } else { - Provider.of(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, "be gay do crime"); + Provider.of(context, listen: false).cwtch.CreateProfile(ctrlrNick.value.text, DefaultPassword); Navigator.of(context).pop(); } } else { // Profile Editing if (ctrlrPass.value.text.isEmpty) { // Don't update password, only update name - final event = { - "EventType": "SetAttribute", - "Data": {"Key": "public.name", "Data": ctrlrNick.value.text} - }; - final json = jsonEncode(event); - - Provider.of(context, listen: false).cwtch.SendProfileEvent(Provider.of(context, listen: false).onion, json); + Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "public.name", ctrlrNick.value.text); Navigator.of(context).pop(); } else { // At this points passwords have been validated to be the same and not empty // Update both password and name, even if name hasn't been changed... - final updateNameEvent = { - "EventType": "SetAttribute", - "Data": {"Key": "public.name", "Data": ctrlrNick.value.text} - }; - final updateNameEventJson = jsonEncode(updateNameEvent); - - Provider.of(context, listen: false).cwtch.SendProfileEvent(Provider.of(context, listen: false).onion, updateNameEventJson); - + Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "public.name", ctrlrNick.value.text); final updatePasswordEvent = { "EventType": "ChangePassword", "Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text} diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index f1caeb6d..ae2af434 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -103,9 +103,10 @@ class _ContactsViewState extends State { if (Provider.of(context).blockUnknownConnections) { actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown))); } - actions.add( - IconButton(icon: TorIcon(), onPressed: _pushTorStatus), - ); + + // TODO copy ID + // TODO servers + actions.add(IconButton( // need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset icon: Icon(showSearchBar || Provider.of(context).isFiltered ? Icons.search_off : Icons.search), diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 20695220..8b19e27b 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -188,6 +188,23 @@ class _GlobalSettingsViewState extends State { inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()), ), + SwitchListTile( + title: Text("Enable Servers", /*AppLocalizations.of(context)!.enableGroups,*/ style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text("Enable Servers"), //AppLocalizations.of(context)!.descriptionExperimentsGroups), + value: settings.isExperimentEnabled(ServerManagementExperiment), + onChanged: (bool value) { + if (value) { + settings.enableExperiment(ServerManagementExperiment); + } else { + settings.disableExperiment(ServerManagementExperiment); + } + // Save Settings... + saveSettings(context); + }, + activeTrackColor: settings.theme.defaultButtonActiveColor(), + inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), + secondary: Icon(CwtchIcons.dns_24px, color: settings.current().mainTextColor()), + ), SwitchListTile( title: Text(AppLocalizations.of(context)!.settingFileSharing, style: TextStyle(color: settings.current().mainTextColor())), subtitle: Text(AppLocalizations.of(context)!.descriptionFileSharing), diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index 33f62340..ea896a7d 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -17,6 +17,7 @@ import '../model.dart'; import '../torstatus.dart'; import 'addeditprofileview.dart'; import 'globalsettingsview.dart'; +import 'serversview.dart'; class ProfileMgrView extends StatefulWidget { ProfileMgrView(); @@ -56,12 +57,14 @@ class _ProfileMgrViewState extends State { SizedBox( width: 10, ), - Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor()))) + Expanded(child: Text(MediaQuery.of(context).size.width > 600 ? + AppLocalizations.of(context)!.titleManageProfiles : AppLocalizations.of(context)!.titleManageProfilesShort, + style: TextStyle(color: settings.current().mainTextColor()))) ]), actions: getActions(), ), floatingActionButton: FloatingActionButton( - onPressed: _pushAddEditProfile, + onPressed: _pushAddProfile, tooltip: AppLocalizations.of(context)!.addNewProfileBtn, child: Icon( Icons.add, @@ -95,6 +98,11 @@ class _ProfileMgrViewState extends State { onPressed: _modalUnlockProfiles, )); + // Servers + if (Provider.of(context).isExperimentEnabled(ServerManagementExperiment)) { + actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: "Servers", onPressed: _pushServers)); + } + // Global Settings actions.add(IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, onPressed: _pushGlobalSettings)); @@ -119,6 +127,17 @@ class _ProfileMgrViewState extends State { )); } + void _pushServers() { + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) { + return MultiProvider( + providers: [Provider.value(value: Provider.of(context))], + child: ServersView(), + ); + }, + )); + } + void _pushTorStatus() { Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { @@ -130,7 +149,7 @@ class _ProfileMgrViewState extends State { )); } - void _pushAddEditProfile({onion: ""}) { + void _pushAddProfile({onion: ""}) { Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return MultiProvider( diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index 9d43997f..d2df0f66 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -61,7 +61,7 @@ class _ProfileRowState extends State { tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname, icon: Icon(Icons.create, color: Provider.of(context).current().mainTextColor()), onPressed: () { - _pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted); + _pushEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted); }, ) ], @@ -100,7 +100,7 @@ class _ProfileRowState extends State { ); } - void _pushAddEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) { + void _pushEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) { Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return MultiProvider( From c304e2ec2a4a9126c474e2a97b4c96e8091889e2 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 1 Nov 2021 19:29:58 -0700 Subject: [PATCH 05/26] server list, add edit --- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 7 +- lib/cwtch/cwtch.dart | 6 +- lib/cwtch/cwtchNotifier.dart | 22 +- lib/cwtch/ffi.dart | 17 +- lib/cwtch/gomobile.dart | 14 +- lib/views/addeditservers.dart | 329 ++++++++++++++++++ lib/views/globalsettingsview.dart | 2 + lib/views/serversview.dart | 79 +++++ lib/widgets/serverrow.dart | 95 +++++ 9 files changed, 551 insertions(+), 20 deletions(-) create mode 100644 lib/views/addeditservers.dart create mode 100644 lib/views/serversview.dart create mode 100644 lib/widgets/serverrow.dart 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 6ee8a912..98bbeb79 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -330,13 +330,16 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : val serverOnion = (a.get("ServerOnion") as? String) ?: "" Cwtch.launchServer(serverOnion) } - "ShutdownServer" -> { + "StopServer" -> { val serverOnion = (a.get("ServerOnion") as? String) ?: "" Cwtch.shutdownServer(serverOnion) } - "ShutdownServers" -> { + "StopServers" -> { Cwtch.shutdownServers() } + "DestroyServers" -> { + Cwtch.destroyServers() + } "SetServerAttribute" -> { val serverOnion = (a.get("ServerOnion") as? String) ?: "" val key = (a.get("Key") as? String) ?: "" diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index d9763f43..8ee539fa 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -81,9 +81,11 @@ abstract class Cwtch { // ignore: non_constant_identifier_names void LaunchServer(String serverOnion); // ignore: non_constant_identifier_names - void ShutdownServer(String serverOnion); + void StopServer(String serverOnion); // ignore: non_constant_identifier_names - void ShutdownServers(); + void StopServers(); + // ignore: non_constant_identifier_names + void DestroyServers(); // ignore: non_constant_identifier_names void SetServerAttribute(String serverOnion, String key, String val); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 0ae5d98d..da1dd6f9 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -34,7 +34,6 @@ class CwtchNotifier { } void handleMessage(String type, dynamic data) { - print("EVENT $type $data"); switch (type) { case "CwtchStarted": appState.SetCwtchInit(); @@ -64,14 +63,21 @@ class CwtchNotifier { )); break; case "NewServer": - var serverData = jsonDecode(data["Data"]); + EnvironmentConfig.debugLog("NewServer $data"); serverListState.add( - serverData["onion"], - serverData["serverbundle"], - serverData["enabled"] == "true", - serverData["description"], - serverData["autostart"] == "true", - serverData["storageType"] == "storage-password"); + data["Onion"], + data["ServerBundle"], + data["Running"] == "true", + data["Description"], + data["Autostart"] == "true", + data["StorageType"] == "storage-password"); + break; + case "ServerIntentUpdate": + EnvironmentConfig.debugLog("ServerIntentUpdate $data"); + var server = serverListState.getServer(data["Identity"]); + if (server != null) { + server.setRunning(data["Intent"] == "running"); + } break; case "GroupCreated": diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index e022fe8a..0b775862 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -624,8 +624,8 @@ class CwtchFfi implements Cwtch { @override // ignore: non_constant_identifier_names - void ShutdownServer(String serverOnion) { - var shutdownServer = library.lookup>("c_ShutdownServer"); + void StopServer(String serverOnion) { + var shutdownServer = library.lookup>("c_StopServer"); // ignore: non_constant_identifier_names final ShutdownServer = shutdownServer.asFunction(); final u1 = serverOnion.toNativeUtf8(); @@ -635,13 +635,22 @@ class CwtchFfi implements Cwtch { @override // ignore: non_constant_identifier_names - void ShutdownServers() { - var shutdownServers = library.lookup>("c_ShutdownServers"); + void StopServers() { + var shutdownServers = library.lookup>("c_StopServers"); // ignore: non_constant_identifier_names final ShutdownServers = shutdownServers.asFunction(); ShutdownServers(); } + @override + // ignore: non_constant_identifier_names + void DestroyServers() { + var destroyServers = library.lookup>("c_DestroyServers"); + // ignore: non_constant_identifier_names + final DestroyServers = destroyServers.asFunction(); + DestroyServers(); + } + @override // ignore: non_constant_identifier_names void SetServerAttribute(String serverOnion, String key, String val) { diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 458defef..03efe699 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -245,14 +245,20 @@ class CwtchGomobile implements Cwtch { @override // ignore: non_constant_identifier_names - void ShutdownServer(String serverOnion) { - cwtchPlatform.invokeMethod("ShutdownServer", {"ServerOnion": serverOnion}); + void StopServer(String serverOnion) { + cwtchPlatform.invokeMethod("StopServer", {"ServerOnion": serverOnion}); } @override // ignore: non_constant_identifier_names - void ShutdownServers() { - cwtchPlatform.invokeMethod("ShutdownServers", {}); + void StopServers() { + cwtchPlatform.invokeMethod("StopServers", {}); + } + + @override + // ignore: non_constant_identifier_names + void DestroyServers() { + cwtchPlatform.invokeMethod("DestroyServers", {}); } @override diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart new file mode 100644 index 00000000..32030c30 --- /dev/null +++ b/lib/views/addeditservers.dart @@ -0,0 +1,329 @@ +import 'dart:convert'; +import 'package:cwtch/cwtch/cwtch.dart'; +import 'package:cwtch/cwtch_icons_icons.dart'; +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/widgets/cwtchlabel.dart'; +import 'package:cwtch/widgets/passwordfield.dart'; +import 'package:cwtch/widgets/textfield.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:cwtch/settings.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../errorHandler.dart'; +import '../main.dart'; +import '../config.dart'; + +/// Global Settings View provides access to modify all the Globally Relevant Settings including Locale, Theme and Experiments. +class AddEditServerView extends StatefulWidget { + const AddEditServerView(); + + @override + _AddEditServerViewState createState() => _AddEditServerViewState(); +} + +class _AddEditServerViewState extends State { + final _formKey = GlobalKey(); + + final ctrlrDesc = TextEditingController(text: ""); + final ctrlrOldPass = TextEditingController(text: ""); + final ctrlrPass = TextEditingController(text: ""); + final ctrlrPass2 = TextEditingController(text: ""); + final ctrlrOnion = TextEditingController(text: ""); + + late bool usePassword; + //late bool deleted; + + @override + void initState() { + super.initState(); + var serverInfoState = Provider.of(context, listen: false); + ctrlrOnion.text = serverInfoState.onion; + usePassword = serverInfoState.isEncrypted; + if (serverInfoState.description.isNotEmpty) { + ctrlrDesc.text = serverInfoState.description; + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: ctrlrOnion.text.isEmpty ? Text("Add Server") : Text("Edit Server"), //AppLocalizations.of(context)!.cwtchSettingsTitle), + ), + body: _buildSettingsList(), + ); + } + + void _handleSwitchPassword(bool? value) { + setState(() { + usePassword = value!; + }); + } + + Widget _buildSettingsList() { + return Consumer2(builder: (context, serverInfoState, settings, child) { + return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + return Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + clipBehavior: Clip.antiAlias, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Form( + key: _formKey, + child: Container( + margin: EdgeInsets.fromLTRB(30, 0, 30, 10), + padding: EdgeInsets.fromLTRB(20, 0 , 20, 10), + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + + // Onion + Visibility( + visible: serverInfoState.onion.isNotEmpty, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + height: 20, + ), + CwtchLabel(label: "Onion"), //AppLocalizations.of(context)!.displayNameLabel), + SizedBox( + height: 20, + ), + SelectableText( + serverInfoState.onion + ) + ])), + + // Description + Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + height: 20, + ), + CwtchLabel(label: "Description"), //AppLocalizations.of(context)!.displayNameLabel), + SizedBox( + height: 20, + ), + CwtchTextField( + controller: ctrlrDesc, + labelText: "Description", + autofocus: false, + ) + ]), + + SizedBox( + height: 20, + ), + + // Enabled + Visibility( + visible: serverInfoState.onion.isNotEmpty, + child: SwitchListTile( + title: Text(/*AppLocalizations.of(context)!.blockUnknownLabel*/ "Enabled", style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), + value: serverInfoState.running, + onChanged: (bool value) { + serverInfoState.setRunning(value); + if (value) { + Provider.of(context, listen: false).cwtch.LaunchServer(serverInfoState.onion); + } else { + Provider.of(context, listen: false).cwtch.StopServer(serverInfoState.onion); + } + // ?? serverInfoState.enabled = value; + notify? + }, + activeTrackColor: settings.theme.defaultButtonActiveColor(), + inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), + secondary: Icon(CwtchIcons.negative_heart_24px, color: settings.current().mainTextColor()), + )), + + // Auto start + SwitchListTile( + title: Text(/*AppLocalizations.of(context)!.blockUnknownLabel*/ "Autostart", style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), + value: serverInfoState.autoStart, + onChanged: (bool value) { + serverInfoState.setAutostart(value); + + if (! serverInfoState.onion.isEmpty) { + Provider.of(context, listen: false).cwtch.SetServerAttribute(serverInfoState.onion, "autostart", value ? "true" : "false"); + } + }, + activeTrackColor: settings.theme.defaultButtonActiveColor(), + inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), + secondary: Icon(CwtchIcons.favorite_24dp, color: settings.current().mainTextColor()), + ), + + + // ***** Password ***** + + Visibility( + visible: serverInfoState.onion.isEmpty, + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + SizedBox( + height: 20, + ), + Checkbox( + value: usePassword, + fillColor: MaterialStateProperty.all(settings.current().defaultButtonColor()), + activeColor: settings.current().defaultButtonActiveColor(), + onChanged: _handleSwitchPassword, + ), + Text( + AppLocalizations.of(context)!.radioUsePassword, + style: TextStyle(color: settings.current().mainTextColor()), + ), + SizedBox( + height: 20, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + usePassword ? AppLocalizations.of(context)!.encryptedProfileDescription : AppLocalizations.of(context)!.plainProfileDescription, + textAlign: TextAlign.center, + )) + ])), + SizedBox( + height: 20, + ), + Visibility( + visible: usePassword, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + Visibility( + visible: serverInfoState.onion.isNotEmpty && serverInfoState.isEncrypted, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel), + SizedBox( + height: 20, + ), + CwtchPasswordField( + controller: ctrlrOldPass, + autoFillHints: [AutofillHints.newPassword], + validator: (value) { + // Password field can be empty when just updating the profile, not on creation + if (serverInfoState.isEncrypted && + serverInfoState.onion.isEmpty && + value.isEmpty && + usePassword) { + return AppLocalizations.of(context)!.passwordErrorEmpty; + } + if (Provider.of(context).deleteProfileError == true) { + return AppLocalizations.of(context)!.enterCurrentPasswordForDelete; + } + return null; + }, + ), + SizedBox( + height: 20, + ), + ])), + CwtchLabel(label: AppLocalizations.of(context)!.newPassword), + SizedBox( + height: 20, + ), + CwtchPasswordField( + controller: ctrlrPass, + validator: (value) { + // Password field can be empty when just updating the profile, not on creation + if (serverInfoState.onion.isEmpty && value.isEmpty && usePassword) { + return AppLocalizations.of(context)!.passwordErrorEmpty; + } + if (value != ctrlrPass2.value.text) { + return AppLocalizations.of(context)!.passwordErrorMatch; + } + return null; + }, + ), + SizedBox( + height: 20, + ), + CwtchLabel(label: AppLocalizations.of(context)!.password2Label), + SizedBox( + height: 20, + ), + CwtchPasswordField( + controller: ctrlrPass2, + validator: (value) { + // Password field can be empty when just updating the profile, not on creation + if (serverInfoState.onion.isEmpty && value.isEmpty && usePassword) { + return AppLocalizations.of(context)!.passwordErrorEmpty; + } + if (value != ctrlrPass.value.text) { + return AppLocalizations.of(context)!.passwordErrorMatch; + } + return null; + }), + ]), + ), + SizedBox( + height: 20, + ), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: ElevatedButton( + onPressed: serverInfoState.onion.isEmpty ? _createPressed : _savePressed, + child: Text( + serverInfoState.onion.isEmpty ? "Add Server" : "Save Server",//AppLocalizations.of(context)!.addNewProfileBtn : AppLocalizations.of(context)!.saveProfileBtn, + textAlign: TextAlign.center, + ), + ), + ), + ], + ), + + // ***** END Password ***** + + + + + + ])))))); + }); + }); + } + + void _createPressed() { + // This will run all the validations in the form including + // checking that display name is not empty, and an actual check that the passwords + // match (and are provided if the user has requested an encrypted profile). + if (_formKey.currentState!.validate()) { + if (usePassword) { + Provider + .of(context, listen: false) + .cwtch + .CreateServer(ctrlrPass.value.text, ctrlrDesc.value.text, Provider.of(context, listen: false).autoStart); + } else { + Provider + .of(context, listen: false) + .cwtch + .CreateServer(DefaultPassword, ctrlrDesc.value.text, Provider.of(context, listen: false).autoStart); + } + Navigator.of(context).pop(); + } + } + + void _savePressed() { + + var server = Provider.of(context, listen: false); + + Provider.of(context, listen: false) + .cwtch.SetServerAttribute(server.onion, "description", ctrlrDesc.text); + server.setDescription(ctrlrDesc.text); + + + if (_formKey.currentState!.validate()) { + // TODO change password + } + Navigator.of(context).pop(); + } +} \ No newline at end of file diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 8b19e27b..4525fd88 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:cwtch/cwtch_icons_icons.dart'; +import 'package:cwtch/models/servers.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/settings.dart'; @@ -193,6 +194,7 @@ class _GlobalSettingsViewState extends State { subtitle: Text("Enable Servers"), //AppLocalizations.of(context)!.descriptionExperimentsGroups), value: settings.isExperimentEnabled(ServerManagementExperiment), onChanged: (bool value) { + Provider.of(context, listen: false).clear(); if (value) { settings.enableExperiment(ServerManagementExperiment); } else { diff --git a/lib/views/serversview.dart b/lib/views/serversview.dart new file mode 100644 index 00000000..e1bd89b4 --- /dev/null +++ b/lib/views/serversview.dart @@ -0,0 +1,79 @@ +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/views/addeditservers.dart'; +import 'package:cwtch/widgets/serverrow.dart'; +import 'package:flutter/material.dart'; +import 'package:cwtch/torstatus.dart'; +import 'package:cwtch/widgets/tor_icon.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../main.dart'; + +/// +class ServersView extends StatefulWidget { + @override + _ServersView createState() => _ServersView(); +} + +class _ServersView extends State { + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Servers you host"), //AppLocalizations.of(context)!.torNetworkStatus), + ), + floatingActionButton: FloatingActionButton( + onPressed: _pushAddServer, + tooltip: "Add new Server", //AppLocalizations.of(context)!.addNewProfileBtn, + child: Icon( + Icons.add, + semanticLabel: "Add new Server", //AppLocalizations.of(context)!.addNewProfileBtn, + ), + ), + body: Consumer( + builder: (context, svrs, child) { + final tiles = svrs.servers.map((ServerInfoState server) { + return ChangeNotifierProvider.value( + value: server, + builder: (context, child) => RepaintBoundary(child: ServerRow()), + ); + }, + ); + + final divided = ListTile.divideTiles( + context: context, + tiles: tiles, + ).toList(); + + if (tiles.isEmpty) { + return const Center( + child: const Text( + "Please create or unlock a server to begin!", + textAlign: TextAlign.center, + )); + } + + return ListView(children: divided); + }, + )); + } + + void _pushAddServer() { + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) { + return MultiProvider( + providers: [ChangeNotifierProvider( + create: (_) => ServerInfoState(onion: "", serverBundle: "", description: "", autoStart: true, running: false, isEncrypted: true), + )], + //ChangeNotifierProvider.value(value: Provider.of(context))], + child: AddEditServerView(), + ); + }, + )); + } +} diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart new file mode 100644 index 00000000..0588df82 --- /dev/null +++ b/lib/widgets/serverrow.dart @@ -0,0 +1,95 @@ +import 'package:cwtch/main.dart'; +import 'package:cwtch/models/servers.dart'; +import 'package:cwtch/views/addeditservers.dart'; +import 'package:cwtch/widgets/profileimage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../cwtch_icons_icons.dart'; +import '../model.dart'; +import '../settings.dart'; + +class ServerRow extends StatefulWidget { + @override + _ServerRowState createState() => _ServerRowState(); +} + +class _ServerRowState extends State { + @override + Widget build(BuildContext context) { + var server = Provider.of(context); + return Card(clipBehavior: Clip.antiAlias, + margin: EdgeInsets.all(0.0), + child: InkWell( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(6.0), //border size + child: Icon(CwtchIcons.dns_24px, + color: server.running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor(), + size: 64) + + ), + Expanded( + child: Column( + children: [ + Text( + server.description, + semanticsLabel: server.description, + style: Provider.of(context).biggerFont.apply(color: server.running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor()), + softWrap: true, + overflow: TextOverflow.ellipsis, + ), + Visibility( + visible: !Provider.of(context).streamerMode, + child: ExcludeSemantics( + child: Text( + server.onion, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: server.running ? Provider.of(context).theme.portraitOnlineBorderColor() : Provider.of(context).theme.portraitOfflineBorderColor()), + ))) + ], + )), + + // Copy server button + IconButton( + enableFeedback: true, + tooltip: AppLocalizations.of(context)!.editProfile + " " + server.onion, + icon: Icon(CwtchIcons.address_copy_2, color: Provider.of(context).current().mainTextColor()), + onPressed: () { + Clipboard.setData(new ClipboardData(text: server.serverBundle)); + }, + ), + + // Edit button + IconButton( + enableFeedback: true, + tooltip: AppLocalizations.of(context)!.editProfile + " " + server.onion, + icon: Icon(Icons.create, color: Provider.of(context).current().mainTextColor()), + onPressed: () { + _pushEditServer(server); + }, + ) + + + ]))); + } + + void _pushEditServer(ServerInfoState server ) { + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) { + return MultiProvider( + providers: [ChangeNotifierProvider( + create: (_) => server, + )], + //ChangeNotifierProvider.value(value: Provider.of(context))], + child: AddEditServerView(), + ); + }, + )); + } +} \ No newline at end of file From 562c05183b812fa5fe19389d4554682419b4c877 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 1 Nov 2021 19:45:17 -0700 Subject: [PATCH 06/26] server list, add edit --- lib/models/servers.dart | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 lib/models/servers.dart diff --git a/lib/models/servers.dart b/lib/models/servers.dart new file mode 100644 index 00000000..2cafbe60 --- /dev/null +++ b/lib/models/servers.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +class ServerListState extends ChangeNotifier { + List _servers = []; + + void replace(Iterable newServers) { + _servers.clear(); + _servers.addAll(newServers); + notifyListeners(); + } + + void clear() { + _servers.clear(); + } + + ServerInfoState? getServer(String onion) { + int idx = _servers.indexWhere((element) => element.onion == onion); + return idx >= 0 ? _servers[idx] : null; + } + + void add(String onion, String serverBundle, bool running, String description, bool autoStart, bool isEncrypted) { + print("servers.add desc:$description autostart: $autoStart"); + _servers.add(ServerInfoState(onion: onion, serverBundle: serverBundle, running: running, description: description, autoStart: autoStart, isEncrypted: isEncrypted)); + notifyListeners(); + } + + void updateServer(String onion, String serverBundle, bool running, String description, bool autoStart, bool isEncrypted) { + int idx = _servers.indexWhere((element) => element.onion == onion); + if (idx >= 0) { + _servers[idx] = ServerInfoState(onion: onion, serverBundle: serverBundle, running: running, description: description, autoStart: autoStart, isEncrypted: isEncrypted); + } else { + print("Tried to update server list without a starting state...this is probably an error"); + } + notifyListeners(); + } + + List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier + +} + +class ServerInfoState extends ChangeNotifier { + String onion; + String serverBundle; + String description; + bool running; + bool autoStart; + bool isEncrypted; + + ServerInfoState({required this.onion, required this.serverBundle, required this.running, required this.description, required this.autoStart, required this.isEncrypted}); + + void setAutostart(bool val) { + autoStart = val; + notifyListeners(); + } + + void setRunning(bool val) { + running = val; + notifyListeners(); + } + + void setDescription(String val) { + description = val; + notifyListeners(); + } +} From ed8292ece917c888f614aac58c59174b4b6df28f Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 1 Nov 2021 21:45:27 -0700 Subject: [PATCH 07/26] serverlist check password. also libcwtch-go version --- LIBCWTCH-GO.version | 2 +- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 6 +- lib/cwtch/gomobile.dart | 2 +- lib/views/serversview.dart | 74 +++++++++++++++++++ 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index f0e8a400..09a831fa 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-8-g4529984-2021-11-01-22-03 \ No newline at end of file +v1.3.1-14-g63e96f3-2021-11-02-04-31 \ 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 98bbeb79..3b06e434 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -315,7 +315,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : "CreateServer" -> { val password = (a.get("Password") as? String) ?: "" val desc = (a.get("Description") as? String) ?: "" - val autostart = (a.get("Autostart") as? Byte) ?: "" + val autostart = (a.get("Autostart") as? Boolean) ?: false Cwtch.createServer(password, desc, autostart) } "DeleteServer" -> { @@ -332,10 +332,10 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : } "StopServer" -> { val serverOnion = (a.get("ServerOnion") as? String) ?: "" - Cwtch.shutdownServer(serverOnion) + Cwtch.stopServer(serverOnion) } "StopServers" -> { - Cwtch.shutdownServers() + Cwtch.stopServers() } "DestroyServers" -> { Cwtch.destroyServers() diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 03efe699..dcf868b1 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -222,7 +222,7 @@ class CwtchGomobile implements Cwtch { @override // ignore: non_constant_identifier_names void CreateServer(String password, String description, bool autostart) { - cwtchPlatform.invokeMethod("CreateServer", {"Password": password, "Description": description, "Autostart": autostart ? 1 : 0}); + cwtchPlatform.invokeMethod("CreateServer", {"Password": password, "Description": description, "Autostart": autostart}); } @override diff --git a/lib/views/serversview.dart b/lib/views/serversview.dart index e1bd89b4..dbec10cd 100644 --- a/lib/views/serversview.dart +++ b/lib/views/serversview.dart @@ -1,5 +1,6 @@ import 'package:cwtch/models/servers.dart'; import 'package:cwtch/views/addeditservers.dart'; +import 'package:cwtch/widgets/passwordfield.dart'; import 'package:cwtch/widgets/serverrow.dart'; import 'package:flutter/material.dart'; import 'package:cwtch/torstatus.dart'; @@ -7,7 +8,9 @@ import 'package:cwtch/widgets/tor_icon.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../cwtch_icons_icons.dart'; import '../main.dart'; +import '../settings.dart'; /// class ServersView extends StatefulWidget { @@ -16,8 +19,11 @@ class ServersView extends StatefulWidget { } class _ServersView extends State { + final ctrlrPassword = TextEditingController(); + @override void dispose() { + ctrlrPassword.dispose(); super.dispose(); } @@ -26,6 +32,7 @@ class _ServersView extends State { return Scaffold( appBar: AppBar( title: Text("Servers you host"), //AppLocalizations.of(context)!.torNetworkStatus), + actions: getActions(), ), floatingActionButton: FloatingActionButton( onPressed: _pushAddServer, @@ -63,6 +70,73 @@ class _ServersView extends State { )); } + List getActions() { + List actions = new List.empty(growable: true); + + // Unlock Profiles + actions.add(IconButton( + icon: Icon(CwtchIcons.lock_open_24px), + color: Provider.of(context).servers.isEmpty ? Provider.of(context).theme.defaultButtonColor() : Provider.of(context).theme.mainTextColor(), + tooltip: AppLocalizations.of(context)!.tooltipUnlockProfiles, + onPressed: _modalUnlockServers, + )); + + return actions; + } + + void _modalUnlockServers() { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return Padding( + padding: MediaQuery.of(context).viewInsets, + child: RepaintBoundary( + child: Container( + height: 200, // bespoke value courtesy of the [TextField] docs + child: Center( + child: Padding( + padding: EdgeInsets.all(10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text("Enter password to unlock server"),//AppLocalizations.of(context)!.enterProfilePassword), + SizedBox( + height: 20, + ), + CwtchPasswordField( + autofocus: true, + controller: ctrlrPassword, + action: unlock, + validator: (value) {}, + ), + SizedBox( + height: 20, + ), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + Spacer(), + Expanded( + child: ElevatedButton( + child: Text(AppLocalizations.of(context)!.unlock, semanticsLabel: AppLocalizations.of(context)!.unlock), + onPressed: () { + unlock(ctrlrPassword.value.text); + }, + )), + Spacer() + ]), + ], + ))), + ))); + }); + } + + void unlock(String password) { + Provider.of(context, listen: false).cwtch.LoadServers(password); + ctrlrPassword.text = ""; + Navigator.pop(context); + } + void _pushAddServer() { Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { From b65d16aa8a8934156cbc4f44ea888731e46823e1 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 2 Nov 2021 13:36:59 -0700 Subject: [PATCH 08/26] translations --- lib/l10n/intl_de.arb | 20 +++++++++++++-- lib/l10n/intl_en.arb | 18 ++++++++++++- lib/l10n/intl_es.arb | 18 ++++++++++++- lib/l10n/intl_fr.arb | 20 +++++++++++++-- lib/l10n/intl_it.arb | 18 ++++++++++++- lib/l10n/intl_pl.arb | 48 +++++++++++++++++++++++------------ lib/l10n/intl_pt.arb | 18 ++++++++++++- lib/views/addeditservers.dart | 19 +++++++------- lib/views/profilemgrview.dart | 6 ++--- lib/views/serversview.dart | 14 +++++----- 10 files changed, 156 insertions(+), 43 deletions(-) diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index bd883204..34d6c77f 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,22 @@ { "@@locale": "de", - "@@last_modified": "2021-10-15T20:45:29+02:00", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", "titleManageProfilesShort": "Profiles", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", @@ -11,7 +27,7 @@ "messageEnableFileSharing": "Enable the file sharing experiment to view this message.", "labelFilesize": "Size", "labelFilename": "Filename", - "downloadFileButton": "Download", + "downloadFileButton": "Herunterladen", "openFolderButton": "Open Folder", "retrievingManifestMessage": "Retrieving file information...", "streamerModeLabel": "Streamer\/Presentation Mode", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 4ff01827..e44616f2 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,22 @@ { "@@locale": "en", - "@@last_modified": "2021-10-15T20:45:29+02:00", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", "titleManageProfilesShort": "Profiles", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 5b234376..f5255737 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,22 @@ { "@@locale": "es", - "@@last_modified": "2021-10-15T20:45:29+02:00", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", "titleManageProfilesShort": "Profiles", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 0add5f25..6c083644 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,7 +1,23 @@ { "@@locale": "fr", - "@@last_modified": "2021-10-15T20:45:29+02:00", - "titleManageProfilesShort": "Profiles", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", + "titleManageProfilesShort": "Profils", "descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion en direct ou la présentation, par exemple, en masquant profil et adresses de contacts.", "descriptionFileSharing": "L'expérience de partage de fichiers vous permet d'envoyer et de recevoir des fichiers à partir de contacts et de groupes Cwtch. Notez que si vous partagez un fichier avec un groupe, les membres de ce groupe se connecteront avec vous directement via Cwtch pour le télécharger.", "settingFileSharing": "Partage de fichiers", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 95878777..f0626eb6 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,22 @@ { "@@locale": "it", - "@@last_modified": "2021-10-15T20:45:29+02:00", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", "titleManageProfilesShort": "Profiles", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "L'esperimento di condivisione dei file ti consente di inviare e ricevere file dai contatti e dai gruppi di Cwtch. Tieni presente che la condivisione di un file con un gruppo farà sì che i membri di quel gruppo si colleghino con te direttamente su Cwtch per scaricarlo.", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 84d27a9d..fdfc189a 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,21 +1,37 @@ { "@@locale": "pl", - "@@last_modified": "2021-10-15T20:45:29+02:00", - "titleManageProfilesShort": "Profiles", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", + "titleManageProfilesShort": "Profile", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", - "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", - "settingFileSharing": "File Sharing", - "tooltipSendFile": "Send File", - "messageFileOffered": "Contact is offering to send you a file", - "messageFileSent": "You sent a file", - "messageEnableFileSharing": "Enable the file sharing experiment to view this message.", - "labelFilesize": "Size", - "labelFilename": "Filename", - "downloadFileButton": "Download", - "openFolderButton": "Open Folder", - "retrievingManifestMessage": "Retrieving file information...", - "streamerModeLabel": "Streamer\/Presentation Mode", - "archiveConversation": "Archive this Conversation", + "descriptionFileSharing": "Eksperyment udostępniania plików pozwala na wysyłanie i odbieranie plików od kontaktów i grup Cwtch. Zauważ, że udostępnienie pliku grupie spowoduje, że członkowie tej grupy połączą się z Tobą bezpośrednio przez Cwtch, aby go pobrać.", + "settingFileSharing": "Udostępnianie plików", + "tooltipSendFile": "Wyślij plik", + "messageFileOffered": "Kontakt proponuje wysłanie Ci pliku", + "messageFileSent": "Plik został wysłany", + "messageEnableFileSharing": "Włącz eksperyment udostępniania plików, aby wyświetlić tę wiadomość.", + "labelFilesize": "Rozmiar", + "labelFilename": "Nazwa pliku", + "downloadFileButton": "Pobierz", + "openFolderButton": "Otwórz folder", + "retrievingManifestMessage": "Pobieranie informacji o pliku...", + "streamerModeLabel": "Tryb streamera\/prezentacji", + "archiveConversation": "Zarchiwizuj tę rozmowę", "profileOnionLabel": "Send this address to contacts you want to connect with", "addPeerTab": "Add a contact", "addPeer": "Add Contact", @@ -28,7 +44,7 @@ "dontSavePeerHistory": "Delete History", "unblockBtn": "Unblock Contact", "blockUnknownLabel": "Block Unknown Contacts", - "blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings", + "blockUnknownConnectionsEnabledDescription": "Połączenia od nieznanych kontaktów są blokowane. Można to zmienić w Ustawieniach", "networkStatusConnecting": "Connecting to network and contacts...", "showMessageButton": "Show Message", "blockedMessageMessage": "This message is from a profile you have blocked.", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 9e593a6b..5cac141c 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,22 @@ { "@@locale": "pt", - "@@last_modified": "2021-10-15T20:45:29+02:00", + "@@last_modified": "2021-11-02T19:44:05+01:00", + "enterServerPassword": "Enter password to unlock server", + "unlockProfileTip": "Please create or unlock a profile to begin!", + "unlockServerTip": "Please create or unlock a server to begin!", + "addServerTooltip": "Add new server", + "serversManagerTitleShort": "Servers", + "serversManagerTitleLong": "Servers You Host", + "saveServerButton": "Save Server", + "serverAutostartDescription": "Controls if the application will automatically launch the server on start", + "serverAutostartLabel": "Autostart", + "serverEnabledDescription": "Start or stop the server", + "serverEnabled": "Server Enabled", + "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", + "serverDescriptionLabel": "Server Description", + "serverAddress": "Server Address", + "editServerTitle": "Edit Server", + "addServerTitle": "Add Server", "titleManageProfilesShort": "Profiles", "descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses", "descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.", diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index 32030c30..57cb18d8 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -15,7 +15,7 @@ import '../errorHandler.dart'; import '../main.dart'; import '../config.dart'; -/// Global Settings View provides access to modify all the Globally Relevant Settings including Locale, Theme and Experiments. +/// Pane to add or edit a server class AddEditServerView extends StatefulWidget { const AddEditServerView(); @@ -56,7 +56,7 @@ class _AddEditServerViewState extends State { return Scaffold( appBar: AppBar( - title: ctrlrOnion.text.isEmpty ? Text("Add Server") : Text("Edit Server"), //AppLocalizations.of(context)!.cwtchSettingsTitle), + title: ctrlrOnion.text.isEmpty ? Text(AppLocalizations.of(context)!.addServerTitle) : Text(AppLocalizations.of(context)!.editServerTitle), //), ), body: _buildSettingsList(), ); @@ -94,7 +94,7 @@ class _AddEditServerViewState extends State { SizedBox( height: 20, ), - CwtchLabel(label: "Onion"), //AppLocalizations.of(context)!.displayNameLabel), + CwtchLabel(label: AppLocalizations.of(context)!.serverAddress), SizedBox( height: 20, ), @@ -108,7 +108,8 @@ class _AddEditServerViewState extends State { SizedBox( height: 20, ), - CwtchLabel(label: "Description"), //AppLocalizations.of(context)!.displayNameLabel), + CwtchLabel(label: AppLocalizations.of(context)!.serverDescriptionLabel), + Text(AppLocalizations.of(context)!.serverDescriptionDescription), SizedBox( height: 20, ), @@ -127,8 +128,8 @@ class _AddEditServerViewState extends State { Visibility( visible: serverInfoState.onion.isNotEmpty, child: SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.blockUnknownLabel*/ "Enabled", style: TextStyle(color: settings.current().mainTextColor())), - subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), + title: Text(AppLocalizations.of(context)!.serverEnabled, style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.serverEnabledDescription), value: serverInfoState.running, onChanged: (bool value) { serverInfoState.setRunning(value); @@ -146,8 +147,8 @@ class _AddEditServerViewState extends State { // Auto start SwitchListTile( - title: Text(/*AppLocalizations.of(context)!.blockUnknownLabel*/ "Autostart", style: TextStyle(color: settings.current().mainTextColor())), - subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), + title: Text(AppLocalizations.of(context)!.serverAutostartLabel, style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.serverAutostartDescription), value: serverInfoState.autoStart, onChanged: (bool value) { serverInfoState.setAutostart(value); @@ -273,7 +274,7 @@ class _AddEditServerViewState extends State { child: ElevatedButton( onPressed: serverInfoState.onion.isEmpty ? _createPressed : _savePressed, child: Text( - serverInfoState.onion.isEmpty ? "Add Server" : "Save Server",//AppLocalizations.of(context)!.addNewProfileBtn : AppLocalizations.of(context)!.saveProfileBtn, + serverInfoState.onion.isEmpty ? AppLocalizations.of(context)!.addServerTitle : AppLocalizations.of(context)!.saveServerButton, textAlign: TextAlign.center, ), ), diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index ea896a7d..2067f578 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -235,9 +235,9 @@ class _ProfileMgrViewState extends State { ).toList(); if (tiles.isEmpty) { - return const Center( - child: const Text( - "Please create or unlock a profile to begin!", + return Center( + child: Text( + AppLocalizations.of(context)!.unlockProfileTip, textAlign: TextAlign.center, )); } diff --git a/lib/views/serversview.dart b/lib/views/serversview.dart index dbec10cd..a9696df8 100644 --- a/lib/views/serversview.dart +++ b/lib/views/serversview.dart @@ -31,15 +31,15 @@ class _ServersView extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Servers you host"), //AppLocalizations.of(context)!.torNetworkStatus), + title: Text( MediaQuery.of(context).size.width > 600 ? AppLocalizations.of(context)!.serversManagerTitleLong : AppLocalizations.of(context)!.serversManagerTitleShort), actions: getActions(), ), floatingActionButton: FloatingActionButton( onPressed: _pushAddServer, - tooltip: "Add new Server", //AppLocalizations.of(context)!.addNewProfileBtn, + tooltip: AppLocalizations.of(context)!.addServerTooltip, child: Icon( Icons.add, - semanticLabel: "Add new Server", //AppLocalizations.of(context)!.addNewProfileBtn, + semanticLabel: AppLocalizations.of(context)!.addServerTooltip, ), ), body: Consumer( @@ -58,9 +58,9 @@ class _ServersView extends State { ).toList(); if (tiles.isEmpty) { - return const Center( - child: const Text( - "Please create or unlock a server to begin!", + return Center( + child: Text( + AppLocalizations.of(context)!.unlockServerTip, textAlign: TextAlign.center, )); } @@ -101,7 +101,7 @@ class _ServersView extends State { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Text("Enter password to unlock server"),//AppLocalizations.of(context)!.enterProfilePassword), + Text(AppLocalizations.of(context)!.enterServerPassword), SizedBox( height: 20, ), From cae44cbd466ae75063ca0d14078804c0fa03c95e Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 2 Nov 2021 13:47:04 -0700 Subject: [PATCH 09/26] Ensure unique keys for messages --- lib/models/messages/filemessage.dart | 2 +- lib/models/messages/invitemessage.dart | 3 +-- lib/models/messages/quotedmessage.dart | 2 +- lib/models/messages/textmessage.dart | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index fa178dd4..b0dbc421 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -20,7 +20,7 @@ class FileMessage extends Message { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { - String idx = Provider.of(context).isGroup == true && this.metadata.signature != null ? this.metadata.signature! : this.metadata.messageIndex.toString(); + String idx = this.metadata.contactHandle + this.metadata.messageIndex.toString(); dynamic shareObj = jsonDecode(this.content); if (shareObj == null) { return MessageRow(MalformedBubble()); diff --git a/lib/models/messages/invitemessage.dart b/lib/models/messages/invitemessage.dart index 7313207f..df21e313 100644 --- a/lib/models/messages/invitemessage.dart +++ b/lib/models/messages/invitemessage.dart @@ -21,8 +21,7 @@ class InviteMessage extends Message { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { - String idx = Provider.of(context).isGroup == true && this.metadata.signature != null ? this.metadata.signature! : this.metadata.messageIndex.toString(); - + String idx = this.metadata.contactHandle + this.metadata.messageIndex.toString(); String inviteTarget; String inviteNick; String invite = this.content; diff --git a/lib/models/messages/quotedmessage.dart b/lib/models/messages/quotedmessage.dart index 3bb85961..8bc0c905 100644 --- a/lib/models/messages/quotedmessage.dart +++ b/lib/models/messages/quotedmessage.dart @@ -94,7 +94,7 @@ class QuotedMessage extends Message { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { - String idx = Provider.of(context).isGroup == true && this.metadata.signature != null ? this.metadata.signature! : this.metadata.messageIndex.toString(); + String idx = this.metadata.contactHandle + this.metadata.messageIndex.toString(); return MessageRow( QuotedMessageBubble(message["body"], quotedMessage.then((LocallyIndexedMessage? localIndex) { if (localIndex != null) { diff --git a/lib/models/messages/textmessage.dart b/lib/models/messages/textmessage.dart index 4f0d4906..a5782ac4 100644 --- a/lib/models/messages/textmessage.dart +++ b/lib/models/messages/textmessage.dart @@ -32,7 +32,7 @@ class TextMessage extends Message { return ChangeNotifierProvider.value( value: this.metadata, builder: (bcontext, child) { - String idx = this.metadata.messageIndex.toString(); + String idx = this.metadata.contactHandle + this.metadata.messageIndex.toString(); return MessageRow(MessageBubble(this.content), key: Provider.of(bcontext).getMessageKey(idx)); }); } From c5f154a25f77cf064c6ed83828cc8df313aab663 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 2 Nov 2021 13:47:35 -0700 Subject: [PATCH 10/26] Fmt --- lib/cwtch/cwtchNotifier.dart | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 2f952dd0..10e8461b 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -150,26 +150,26 @@ class CwtchNotifier { // 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) { - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages = idx + 1; + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages = idx + 1; - //if not currently open - if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { - profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; - } + //if not currently open + if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; + } - var timestampSent = DateTime.tryParse(data['TimestampSent'])!; - // TODO: There are 2 timestamps associated with a new group message - time sent and time received. - // Sent refers to the time a profile alleges they sent a message - // Received refers to the time we actually saw the message from the server - // These can obviously be very different for legitimate reasons. - // We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for - // order. - // In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline - // 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.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); - notificationManager.notify("New Message From Group!"); + var timestampSent = DateTime.tryParse(data['TimestampSent'])!; + // TODO: There are 2 timestamps associated with a new group message - time sent and time received. + // Sent refers to the time a profile alleges they sent a message + // Received refers to the time we actually saw the message from the server + // These can obviously be very different for legitimate reasons. + // We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for + // order. + // In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline + // 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.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); + notificationManager.notify("New Message From Group!"); } } else { // from me (already displayed - do not update counter) From e9407ae426a7afd1d846a4c0583853a0d7058e7c Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 2 Nov 2021 14:48:52 -0700 Subject: [PATCH 11/26] fixing as per PR --- lib/cwtch/cwtch.dart | 2 ++ lib/views/addeditprofileview.dart | 4 ++-- lib/views/addeditservers.dart | 6 +++--- lib/views/serversview.dart | 1 - lib/widgets/serverrow.dart | 1 - 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 8ee539fa..5e229b47 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -1,5 +1,7 @@ import 'package:flutter/src/services/text_input.dart'; +// To handle profiles that are "unencrypted" (i.e don't require a password to open) we currently create a profile with a defacto, hardcoded password. +// Details: https://docs.openprivacy.ca/cwtch-security-handbook/profile_encryption_and_storage.html const DefaultPassword = "be gay do crime"; abstract class Cwtch { diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index 25e0749b..6b8f1e7c 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -296,12 +296,12 @@ class _AddEditProfileViewState extends State { // Profile Editing if (ctrlrPass.value.text.isEmpty) { // Don't update password, only update name - Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "public.name", ctrlrNick.value.text); + Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "profile.name", ctrlrNick.value.text); Navigator.of(context).pop(); } else { // At this points passwords have been validated to be the same and not empty // Update both password and name, even if name hasn't been changed... - Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "public.name", ctrlrNick.value.text); + Provider.of(context, listen: false).cwtch.SetProfileAttribute(Provider.of(context, listen: false).onion, "profile.name", ctrlrNick.value.text); final updatePasswordEvent = { "EventType": "ChangePassword", "Data": {"Password": ctrlrOldPass.text, "NewPassword": ctrlrPass.text} diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index 57cb18d8..63a547a1 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -138,7 +138,6 @@ class _AddEditServerViewState extends State { } else { Provider.of(context, listen: false).cwtch.StopServer(serverInfoState.onion); } - // ?? serverInfoState.enabled = value; + notify? }, activeTrackColor: settings.theme.defaultButtonActiveColor(), inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), @@ -195,7 +194,8 @@ class _AddEditServerViewState extends State { height: 20, ), Visibility( - visible: usePassword, + // Currently we don't support password change for servers so also gate this on Add server, when ready to support changing password remove the onion.isEmpty check + visible: serverInfoState.onion.isEmpty && usePassword, child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Visibility( visible: serverInfoState.onion.isNotEmpty && serverInfoState.isEncrypted, @@ -323,7 +323,7 @@ class _AddEditServerViewState extends State { if (_formKey.currentState!.validate()) { - // TODO change password + // TODO support change password } Navigator.of(context).pop(); } diff --git a/lib/views/serversview.dart b/lib/views/serversview.dart index a9696df8..84c7e532 100644 --- a/lib/views/serversview.dart +++ b/lib/views/serversview.dart @@ -144,7 +144,6 @@ class _ServersView extends State { providers: [ChangeNotifierProvider( create: (_) => ServerInfoState(onion: "", serverBundle: "", description: "", autoStart: true, running: false, isEncrypted: true), )], - //ChangeNotifierProvider.value(value: Provider.of(context))], child: AddEditServerView(), ); }, diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index 0588df82..4741195d 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -86,7 +86,6 @@ class _ServerRowState extends State { providers: [ChangeNotifierProvider( create: (_) => server, )], - //ChangeNotifierProvider.value(value: Provider.of(context))], child: AddEditServerView(), ); }, From 3e75c4d1066a59b89bb8daaf04995ec9ff9225ab Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 2 Nov 2021 15:10:36 -0700 Subject: [PATCH 12/26] last cleanup --- lib/l10n/intl_de.arb | 5 ++++- lib/l10n/intl_en.arb | 5 ++++- lib/l10n/intl_es.arb | 5 ++++- lib/l10n/intl_fr.arb | 5 ++++- lib/l10n/intl_it.arb | 5 ++++- lib/l10n/intl_pl.arb | 5 ++++- lib/l10n/intl_pt.arb | 5 ++++- lib/views/addeditservers.dart | 2 +- lib/views/contactsview.dart | 12 +++++++++++- lib/views/globalsettingsview.dart | 4 ++-- lib/views/profilemgrview.dart | 2 +- 11 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 34d6c77f..575bbdb3 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,9 @@ { "@@locale": "de", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e44616f2..b6badee4 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,9 @@ { "@@locale": "en", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index f5255737..1892268e 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,9 @@ { "@@locale": "es", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 6c083644..37865907 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,9 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index f0626eb6..97a272ba 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,9 @@ { "@@locale": "it", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index fdfc189a..8905b0ea 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,9 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 5cac141c..7000bec8 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,9 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-02T19:44:05+01:00", + "@@last_modified": "2021-11-02T23:03:43+01:00", + "copyAddress": "Copy Address", + "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", + "settingServers": "Hosting Servers", "enterServerPassword": "Enter password to unlock server", "unlockProfileTip": "Please create or unlock a profile to begin!", "unlockServerTip": "Please create or unlock a server to begin!", diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index 63a547a1..c4477659 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -56,7 +56,7 @@ class _AddEditServerViewState extends State { return Scaffold( appBar: AppBar( - title: ctrlrOnion.text.isEmpty ? Text(AppLocalizations.of(context)!.addServerTitle) : Text(AppLocalizations.of(context)!.editServerTitle), //), + title: ctrlrOnion.text.isEmpty ? Text(AppLocalizations.of(context)!.addServerTitle) : Text(AppLocalizations.of(context)!.editServerTitle), ), body: _buildSettingsList(), ); diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index ae2af434..0ac5ae72 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -5,6 +5,7 @@ import 'package:cwtch/widgets/contactrow.dart'; import 'package:cwtch/widgets/profileimage.dart'; import 'package:cwtch/widgets/textfield.dart'; import 'package:cwtch/widgets/tor_icon.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../main.dart'; import '../settings.dart'; @@ -104,9 +105,18 @@ class _ContactsViewState extends State { actions.add(Tooltip(message: AppLocalizations.of(context)!.blockUnknownConnectionsEnabledDescription, child: Icon(CwtchIcons.block_unknown))); } - // TODO copy ID + // Copy profile onion + actions.add(IconButton( + icon: Icon(CwtchIcons.address_copy_2), + tooltip: AppLocalizations.of(context)!.copyAddress, + onPressed: () { + Clipboard.setData(new ClipboardData(text: Provider.of(context, listen: false).onion)); + })); + + // TODO servers + // Search contacts actions.add(IconButton( // need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset icon: Icon(showSearchBar || Provider.of(context).isFiltered ? Icons.search_off : Icons.search), diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 4525fd88..6f71f73d 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -190,8 +190,8 @@ class _GlobalSettingsViewState extends State { secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()), ), SwitchListTile( - title: Text("Enable Servers", /*AppLocalizations.of(context)!.enableGroups,*/ style: TextStyle(color: settings.current().mainTextColor())), - subtitle: Text("Enable Servers"), //AppLocalizations.of(context)!.descriptionExperimentsGroups), + title: Text(AppLocalizations.of(context)!.settingServers, style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.settingServersDescription), value: settings.isExperimentEnabled(ServerManagementExperiment), onChanged: (bool value) { Provider.of(context, listen: false).clear(); diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index 2067f578..e0a150e1 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -100,7 +100,7 @@ class _ProfileMgrViewState extends State { // Servers if (Provider.of(context).isExperimentEnabled(ServerManagementExperiment)) { - actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: "Servers", onPressed: _pushServers)); + actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers)); } // Global Settings From 60a4775f4d860533e4c351a2882a6a08bdebce47 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 2 Nov 2021 15:13:12 -0700 Subject: [PATCH 13/26] libcwtch bump --- LIBCWTCH-GO.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 09a831fa..71c8f534 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-14-g63e96f3-2021-11-02-04-31 \ No newline at end of file +v1.3.1-17-gbaddf63-2021-11-02-19-11 \ No newline at end of file From 82294eb6afaeb47261f5bec86ec1796825307b64 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 2 Nov 2021 15:19:16 -0700 Subject: [PATCH 14/26] proper tooltips --- lib/widgets/serverrow.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index 4741195d..e20095be 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -58,7 +58,7 @@ class _ServerRowState extends State { // Copy server button IconButton( enableFeedback: true, - tooltip: AppLocalizations.of(context)!.editProfile + " " + server.onion, + tooltip: AppLocalizations.of(context)!.copyAddress, icon: Icon(CwtchIcons.address_copy_2, color: Provider.of(context).current().mainTextColor()), onPressed: () { Clipboard.setData(new ClipboardData(text: server.serverBundle)); @@ -68,7 +68,7 @@ class _ServerRowState extends State { // Edit button IconButton( enableFeedback: true, - tooltip: AppLocalizations.of(context)!.editProfile + " " + server.onion, + tooltip: AppLocalizations.of(context)!.editServerTitle, icon: Icon(Icons.create, color: Provider.of(context).current().mainTextColor()), onPressed: () { _pushEditServer(server); From 3ac8c32afe93d3c946ca10d0cf667ff3c0bb40bf Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 3 Nov 2021 12:44:09 -0700 Subject: [PATCH 15/26] libcwtch bump --- LIBCWTCH-GO.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 71c8f534..f6bca1aa 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-17-gbaddf63-2021-11-02-19-11 \ No newline at end of file +v1.3.1-23-gb47b270-2021-11-03-19-29 \ No newline at end of file From fc59bd332b2766eb4dac51501f2fdda3ad29ad1b Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 3 Nov 2021 12:53:32 -0700 Subject: [PATCH 16/26] restruct mobile from servers experiment and tab --- lib/views/globalsettingsview.dart | 40 +++++++++++++++++-------------- lib/views/profilemgrview.dart | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 6f71f73d..5484263a 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/servers.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -189,24 +190,27 @@ class _GlobalSettingsViewState extends State { inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), secondary: Icon(CwtchIcons.enable_groups, color: settings.current().mainTextColor()), ), - SwitchListTile( - title: Text(AppLocalizations.of(context)!.settingServers, style: TextStyle(color: settings.current().mainTextColor())), - subtitle: Text(AppLocalizations.of(context)!.settingServersDescription), - value: settings.isExperimentEnabled(ServerManagementExperiment), - onChanged: (bool value) { - Provider.of(context, listen: false).clear(); - if (value) { - settings.enableExperiment(ServerManagementExperiment); - } else { - settings.disableExperiment(ServerManagementExperiment); - } - // Save Settings... - saveSettings(context); - }, - activeTrackColor: settings.theme.defaultButtonActiveColor(), - inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), - secondary: Icon(CwtchIcons.dns_24px, color: settings.current().mainTextColor()), - ), + Visibility( + visible: !Platform.isAndroid && !Platform.isIOS, + child: + SwitchListTile( + title: Text(AppLocalizations.of(context)!.settingServers, style: TextStyle(color: settings.current().mainTextColor())), + subtitle: Text(AppLocalizations.of(context)!.settingServersDescription), + value: settings.isExperimentEnabled(ServerManagementExperiment), + onChanged: (bool value) { + Provider.of(context, listen: false).clear(); + if (value) { + settings.enableExperiment(ServerManagementExperiment); + } else { + settings.disableExperiment(ServerManagementExperiment); + } + // Save Settings... + saveSettings(context); + }, + activeTrackColor: settings.theme.defaultButtonActiveColor(), + inactiveTrackColor: settings.theme.defaultButtonDisabledColor(), + secondary: Icon(CwtchIcons.dns_24px, color: settings.current().mainTextColor()), + )), SwitchListTile( title: Text(AppLocalizations.of(context)!.settingFileSharing, style: TextStyle(color: settings.current().mainTextColor())), subtitle: Text(AppLocalizations.of(context)!.descriptionFileSharing), diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index e0a150e1..e33209c0 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -99,7 +99,7 @@ class _ProfileMgrViewState extends State { )); // Servers - if (Provider.of(context).isExperimentEnabled(ServerManagementExperiment)) { + if (Provider.of(context).isExperimentEnabled(ServerManagementExperiment) && !Platform.isAndroid && !Platform.isIOS) { actions.add(IconButton(icon: Icon(CwtchIcons.dns_black_24dp), tooltip: AppLocalizations.of(context)!.serversManagerTitleShort, onPressed: _pushServers)); } From 87f7c2c29b95eb370f0eee82fc73f09b95c2138e Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 3 Nov 2021 18:56:53 -0700 Subject: [PATCH 17/26] server delete functionality and minor cleanups --- LIBCWTCH-GO.version | 2 +- lib/cwtch/cwtchNotifier.dart | 6 ++ lib/errorHandler.dart | 38 +++++++++++++ lib/l10n/intl_de.arb | 7 ++- lib/l10n/intl_en.arb | 7 ++- lib/l10n/intl_es.arb | 7 ++- lib/l10n/intl_fr.arb | 45 ++++++++------- lib/l10n/intl_it.arb | 7 ++- lib/l10n/intl_pl.arb | 7 ++- lib/l10n/intl_pt.arb | 7 ++- lib/models/servers.dart | 5 ++ lib/views/addeditservers.dart | 102 ++++++++++++++++++++++++++++------ lib/views/profilemgrview.dart | 1 + lib/widgets/profilerow.dart | 2 + lib/widgets/serverrow.dart | 3 + 15 files changed, 202 insertions(+), 44 deletions(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index f6bca1aa..3fe724f0 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-23-gb47b270-2021-11-03-19-29 \ No newline at end of file +v1.3.1-25-g4adb501-2021-11-04-04-20 \ No newline at end of file diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 44fdc8e0..9c69ca7d 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -104,6 +104,12 @@ class CwtchNotifier { // todo standarize error.handleUpdate("deleteprofile.success"); break; + case "ServerDeleted": + error.handleUpdate("deletedserver." + data["Status"]); + if (data["Status"] == "success") { + serverListState.delete(data["Identity"]); + } + break; case "DeleteContact": profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(data["RemotePeer"]); break; diff --git a/lib/errorHandler.dart b/lib/errorHandler.dart index 4feada1c..a54094c7 100644 --- a/lib/errorHandler.dart +++ b/lib/errorHandler.dart @@ -21,6 +21,27 @@ class ErrorHandler extends ChangeNotifier { bool deleteProfileError = false; bool deleteProfileSuccess = false; + static const String deletedServerErrorPrefix = "deletedserver"; + bool deletedServerError = false; + bool deletedServerSuccess = false; + + reset() { + invalidImportStringError = false; + contactAlreadyExistsError = false; + explicitAddContactSuccess = false; + + importBundleError = false; + importBundleSuccess = false; + + deleteProfileError = false; + deleteProfileSuccess = false; + + deletedServerError = false; + deletedServerSuccess = false; + + notifyListeners(); + } + /// Called by the event bus. handleUpdate(String error) { var parts = error.split("."); @@ -37,6 +58,8 @@ class ErrorHandler extends ChangeNotifier { case deleteProfileErrorPrefix: handleDeleteProfileError(errorType); break; + case deletedServerErrorPrefix: + handleDeletedServerError(errorType); } notifyListeners(); @@ -91,4 +114,19 @@ class ErrorHandler extends ChangeNotifier { break; } } + + handleDeletedServerError(String errorType) { + // reset + deletedServerError = false; + deletedServerSuccess = false; + + switch (errorType) { + case successErrorType: + deletedServerSuccess = true; + break; + default: + deletedServerError = true; + break; + } + } } diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 575bbdb3..aa6b06eb 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,11 @@ { "@@locale": "de", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b6badee4..088c5b45 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,11 @@ { "@@locale": "en", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 1892268e..bcc6aaeb 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,11 @@ { "@@locale": "es", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 37865907..351be299 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,25 +1,30 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-02T23:03:43+01:00", - "copyAddress": "Copy Address", - "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", - "settingServers": "Hosting Servers", - "enterServerPassword": "Enter password to unlock server", - "unlockProfileTip": "Please create or unlock a profile to begin!", - "unlockServerTip": "Please create or unlock a server to begin!", - "addServerTooltip": "Add new server", - "serversManagerTitleShort": "Servers", - "serversManagerTitleLong": "Servers You Host", - "saveServerButton": "Save Server", - "serverAutostartDescription": "Controls if the application will automatically launch the server on start", - "serverAutostartLabel": "Autostart", - "serverEnabledDescription": "Start or stop the server", - "serverEnabled": "Server Enabled", - "serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared", - "serverDescriptionLabel": "Server Description", - "serverAddress": "Server Address", - "editServerTitle": "Edit Server", - "addServerTitle": "Add Server", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", + "copyAddress": "Copier l'adresse", + "settingServersDescription": "L'expérience des serveurs d'hébergement permet d'héberger et de gérer les serveurs Cwtch.", + "settingServers": "Serveurs d'hébergement", + "enterServerPassword": "Entrez le mot de passe pour déverrouiller le serveur", + "unlockProfileTip": "Veuillez créer ou déverrouiller un profil pour commencer !", + "unlockServerTip": "Veuillez créer ou déverrouiller un serveur pour commencer !", + "addServerTooltip": "Ajouter un nouveau serveur", + "serversManagerTitleShort": "Serveurs", + "serversManagerTitleLong": "Serveurs que vous hébergez", + "saveServerButton": "Enregistrer le serveur", + "serverAutostartDescription": "Contrôle si l'application lance automatiquement le serveur au démarrage.", + "serverAutostartLabel": "Démarrage automatique", + "serverEnabledDescription": "Démarrer ou arrêter le serveur", + "serverEnabled": "Serveur activé", + "serverDescriptionDescription": "Votre description du serveur est à des fins de gestion personnelle uniquement, elle ne sera jamais partagée.", + "serverDescriptionLabel": "Description du serveur", + "serverAddress": "Adresse du serveur", + "editServerTitle": "Modifier le serveur", + "addServerTitle": "Ajouter un serveur", "titleManageProfilesShort": "Profils", "descriptionStreamerMode": "Si elle est activée, cette option donne un rendu visuel plus privé à l'application pour la diffusion en direct ou la présentation, par exemple, en masquant profil et adresses de contacts.", "descriptionFileSharing": "L'expérience de partage de fichiers vous permet d'envoyer et de recevoir des fichiers à partir de contacts et de groupes Cwtch. Notez que si vous partagez un fichier avec un groupe, les membres de ce groupe se connecteront avec vous directement via Cwtch pour le télécharger.", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 97a272ba..87e767bf 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,11 @@ { "@@locale": "it", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 8905b0ea..542c0348 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,11 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 7000bec8..90eef948 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,11 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-02T23:03:43+01:00", + "@@last_modified": "2021-11-04T02:39:11+01:00", + "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", + "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", + "deleteServerConfirmBtn": "Really delete server", + "deleteServerSuccess": "Successfully deleted server", + "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", "copyAddress": "Copy Address", "settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers", "settingServers": "Hosting Servers", diff --git a/lib/models/servers.dart b/lib/models/servers.dart index 2cafbe60..a2277e20 100644 --- a/lib/models/servers.dart +++ b/lib/models/servers.dart @@ -34,6 +34,11 @@ class ServerListState extends ChangeNotifier { notifyListeners(); } + void delete(String onion) { + _servers.removeWhere((element) => element.onion == onion); + notifyListeners(); + } + List get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier } diff --git a/lib/views/addeditservers.dart b/lib/views/addeditservers.dart index c4477659..c551ea4d 100644 --- a/lib/views/addeditservers.dart +++ b/lib/views/addeditservers.dart @@ -164,6 +164,7 @@ class _AddEditServerViewState extends State { // ***** Password ***** + // use password toggle Visibility( visible: serverInfoState.onion.isEmpty, child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -186,20 +187,19 @@ class _AddEditServerViewState extends State { Padding( padding: EdgeInsets.symmetric(horizontal: 24), child: Text( - usePassword ? AppLocalizations.of(context)!.encryptedProfileDescription : AppLocalizations.of(context)!.plainProfileDescription, + usePassword ? AppLocalizations.of(context)!.encryptedServerDescription : AppLocalizations.of(context)!.plainServerDescription, textAlign: TextAlign.center, - )) + )), + SizedBox( + height: 20, + ), ])), - SizedBox( - height: 20, - ), + + + // current password Visibility( - // Currently we don't support password change for servers so also gate this on Add server, when ready to support changing password remove the onion.isEmpty check - visible: serverInfoState.onion.isEmpty && usePassword, - child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Visibility( - visible: serverInfoState.onion.isNotEmpty && serverInfoState.isEncrypted, - child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + visible: serverInfoState.onion.isNotEmpty && serverInfoState.isEncrypted, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel), SizedBox( height: 20, @@ -215,8 +215,8 @@ class _AddEditServerViewState extends State { usePassword) { return AppLocalizations.of(context)!.passwordErrorEmpty; } - if (Provider.of(context).deleteProfileError == true) { - return AppLocalizations.of(context)!.enterCurrentPasswordForDelete; + if (Provider.of(context).deletedServerError == true) { + return AppLocalizations.of(context)!.enterCurrentPasswordForDeleteServer; } return null; }, @@ -225,6 +225,12 @@ class _AddEditServerViewState extends State { height: 20, ), ])), + + // new passwords 1 & 2 + Visibility( + // Currently we don't support password change for servers so also gate this on Add server, when ready to support changing password remove the onion.isEmpty check + visible: serverInfoState.onion.isEmpty && usePassword, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ CwtchLabel(label: AppLocalizations.of(context)!.newPassword), SizedBox( height: 20, @@ -263,6 +269,7 @@ class _AddEditServerViewState extends State { }), ]), ), + SizedBox( height: 20, ), @@ -281,13 +288,25 @@ class _AddEditServerViewState extends State { ), ], ), + Visibility( + visible: serverInfoState.onion.isNotEmpty, + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ + SizedBox( + height: 20, + ), + Tooltip( + message: AppLocalizations.of(context)!.enterCurrentPasswordForDeleteServer, + child: ElevatedButton.icon( + onPressed: () { + showAlertDialog(context); + }, + icon: Icon(Icons.delete_forever), + label: Text(AppLocalizations.of(context)!.deleteBtn), + )) + ])) // ***** END Password ***** - - - - ])))))); }); }); @@ -327,4 +346,53 @@ class _AddEditServerViewState extends State { } Navigator.of(context).pop(); } + + showAlertDialog(BuildContext context) { + // set up the buttons + Widget cancelButton = ElevatedButton( + child: Text(AppLocalizations.of(context)!.cancel), + onPressed: () { + Navigator.of(context).pop(); // dismiss dialog + }, + ); + Widget continueButton = ElevatedButton( + child: Text(AppLocalizations.of(context)!.deleteServerConfirmBtn), + onPressed: () { + var onion = Provider + .of(context, listen: false) + .onion; + Provider + .of(context, listen: false) + .cwtch + .DeleteServer(onion, Provider.of(context, listen: false).isEncrypted ? ctrlrOldPass.value.text : DefaultPassword); + Future.delayed( + const Duration(milliseconds: 500), + () { + if (globalErrorHandler.deletedServerSuccess) { + final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.deleteServerSuccess + ":" + onion)); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + Navigator.of(context).popUntil((route) => route.settings.name == "servers"); // dismiss dialog + } else { + Navigator.of(context).pop(); + } + }, + ); + }); + // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: Text(AppLocalizations.of(context)!.deleteServerConfirmBtn), + actions: [ + cancelButton, + continueButton, + ], + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } } \ No newline at end of file diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index e33209c0..de7f65b5 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -129,6 +129,7 @@ class _ProfileMgrViewState extends State { void _pushServers() { Navigator.of(context).push(MaterialPageRoute( + settings: RouteSettings(name: "servers"), builder: (BuildContext context) { return MultiProvider( providers: [Provider.value(value: Provider.of(context))], diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index d2df0f66..39bce219 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -7,6 +7,7 @@ import 'package:cwtch/widgets/profileimage.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../errorHandler.dart'; import '../main.dart'; import '../model.dart'; import '../settings.dart'; @@ -101,6 +102,7 @@ class _ProfileRowState extends State { } void _pushEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) { + Provider.of(context).reset(); Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { return MultiProvider( diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index e20095be..8da87c65 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -8,6 +8,7 @@ import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../cwtch_icons_icons.dart'; +import '../errorHandler.dart'; import '../model.dart'; import '../settings.dart'; @@ -80,7 +81,9 @@ class _ServerRowState extends State { } void _pushEditServer(ServerInfoState server ) { + Provider.of(context).reset(); Navigator.of(context).push(MaterialPageRoute( + settings: RouteSettings(name: "serveraddedit"), builder: (BuildContext context) { return MultiProvider( providers: [ChangeNotifierProvider( From fb18a0e3b8dfdc98a92017b3bcbc92ef51324d7c Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 3 Nov 2021 21:41:58 -0700 Subject: [PATCH 18/26] fix serverliststate add to handle readds (happens on saving global settings) --- lib/models/servers.dart | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/models/servers.dart b/lib/models/servers.dart index a2277e20..bf9710dd 100644 --- a/lib/models/servers.dart +++ b/lib/models/servers.dart @@ -19,8 +19,18 @@ class ServerListState extends ChangeNotifier { } void add(String onion, String serverBundle, bool running, String description, bool autoStart, bool isEncrypted) { - print("servers.add desc:$description autostart: $autoStart"); - _servers.add(ServerInfoState(onion: onion, serverBundle: serverBundle, running: running, description: description, autoStart: autoStart, isEncrypted: isEncrypted)); + var sis = ServerInfoState(onion: onion, serverBundle: serverBundle, running: running, description: description, autoStart: autoStart, isEncrypted: isEncrypted); + int idx = _servers.indexWhere((element) => element.onion == onion); + if (idx >= 0) { + _servers[idx] = sis; + } else { + _servers.add(ServerInfoState(onion: onion, + serverBundle: serverBundle, + running: running, + description: description, + autoStart: autoStart, + isEncrypted: isEncrypted)); + } notifyListeners(); } From 6331a6da95150a927f53e759c3c4eb33c68fc3e5 Mon Sep 17 00:00:00 2001 From: erinn Date: Thu, 4 Nov 2021 15:02:26 -0700 Subject: [PATCH 19/26] l10n updates for file resumption --- lib/l10n/intl_de.arb | 5 ++++- lib/l10n/intl_en.arb | 5 ++++- lib/l10n/intl_es.arb | 5 ++++- lib/l10n/intl_fr.arb | 15 +++++++++------ lib/l10n/intl_it.arb | 5 ++++- lib/l10n/intl_pl.arb | 5 ++++- lib/l10n/intl_pt.arb | 5 ++++- 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index aa6b06eb..f8bc1fca 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,9 @@ { "@@locale": "de", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 088c5b45..d9aab850 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,9 @@ { "@@locale": "en", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index bcc6aaeb..b1408582 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,9 @@ { "@@locale": "es", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 351be299..825c0eca 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,11 +1,14 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-04T02:39:11+01:00", - "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", - "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", - "deleteServerConfirmBtn": "Really delete server", - "deleteServerSuccess": "Successfully deleted server", - "enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", + "plainServerDescription": "Nous vous recommandons de protéger vos serveurs Cwtch par un mot de passe. Si vous ne définissez pas de mot de passe sur ce serveur, toute personne ayant accès à cet appareil peut être en mesure d'accéder aux informations concernant ce serveur, y compris les clés cryptographiques sensibles.", + "encryptedServerDescription": "Le chiffrement d’un serveur avec un mot de passe le protège des autres personnes qui peuvent également utiliser cet appareil. Les serveurs cryptés ne peuvent pas être déchiffrés, affichés ou accessibles tant que le mot de passe correct n’est pas entré pour les déverrouiller.", + "deleteServerConfirmBtn": "Supprimer vraiment le serveur", + "deleteServerSuccess": "Le serveur a été supprimé avec succès", + "enterCurrentPasswordForDeleteServer": "Veuillez saisir le mot de passe actuel pour supprimer ce serveur", "copyAddress": "Copier l'adresse", "settingServersDescription": "L'expérience des serveurs d'hébergement permet d'héberger et de gérer les serveurs Cwtch.", "settingServers": "Serveurs d'hébergement", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 87e767bf..97dda2c2 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,9 @@ { "@@locale": "it", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 542c0348..d16fc53f 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,9 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 90eef948..a2190708 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,9 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-04T02:39:11+01:00", + "@@last_modified": "2021-11-04T22:56:21+01:00", + "fileCheckingStatus": "Checking download status", + "fileInterrupted": "Interrupted", + "fileSavedTo": "Saved to", "plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.", "encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.", "deleteServerConfirmBtn": "Really delete server", From e55d9301e407106c2b590a451044ee9099d67d67 Mon Sep 17 00:00:00 2001 From: erinn Date: Thu, 4 Nov 2021 15:31:50 -0700 Subject: [PATCH 20/26] file resumption support --- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 44 +++++++++++-------- lib/cwtch/cwtch.dart | 2 + lib/cwtch/cwtchNotifier.dart | 7 ++- lib/cwtch/ffi.dart | 16 +++++++ lib/cwtch/gomobile.dart | 6 +++ lib/model.dart | 34 ++++++++++++-- lib/models/messages/filemessage.dart | 12 +++++ lib/widgets/filebubble.dart | 25 ++++++++--- 8 files changed, 119 insertions(+), 27 deletions(-) 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 f4ef3152..471a2ede 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -112,24 +112,26 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : if (dlID == null) { dlID = 0; } - val channelId = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createDownloadNotificationChannel(fileKey, fileKey) - } else { - // If earlier version channel ID is not used - // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) - "" - }; - val newNotification = NotificationCompat.Builder(applicationContext, channelId) - .setOngoing(true) - .setContentTitle("Downloading")//todo: translate - .setContentText(title) - .setSmallIcon(android.R.drawable.stat_sys_download) - .setProgress(progressMax, progress, false) - .setSound(null) - //.setSilent(true) - .build(); - notificationManager.notify(dlID, newNotification); + if (progress >= 0) { + val channelId = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createDownloadNotificationChannel(fileKey, fileKey) + } else { + // If earlier version channel ID is not used + // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) + "" + }; + val newNotification = NotificationCompat.Builder(applicationContext, channelId) + .setOngoing(true) + .setContentTitle("Downloading")//todo: translate + .setContentText(title) + .setSmallIcon(android.R.drawable.stat_sys_download) + .setProgress(progressMax, progress, false) + .setSound(null) + //.setSilent(true) + .build(); + notificationManager.notify(dlID, newNotification); + } } catch (e: Exception) { Log.i("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace()); } @@ -241,6 +243,12 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : val fileKey = (a.get("fileKey") as? String) ?: "" Cwtch.checkDownloadStatus(profile, fileKey) } + "VerifyOrResumeDownload" -> { + val profile = (a.get("ProfileOnion") as? String) ?: "" + val handle = (a.get("handle") as? String) ?: "" + val fileKey = (a.get("fileKey") as? String) ?: "" + Cwtch.verifyOrResumeDownload(profile, handle, fileKey) + } "SendProfileEvent" -> { val onion = (a.get("onion") as? String) ?: "" val jsonEvent = (a.get("jsonEvent") as? String) ?: "" diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 0df9c0b0..2a8b03e7 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -48,6 +48,8 @@ abstract class Cwtch { void CreateDownloadableFile(String profile, String handle, String filenameSuggestion, String filekey); // ignore: non_constant_identifier_names void CheckDownloadStatus(String profile, String fileKey); + // ignore: non_constant_identifier_names + void VerifyOrResumeDownload(String profile, String handle, String filekey); // ignore: non_constant_identifier_names void ArchiveConversation(String profile, String handle); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 2f952dd0..6012385a 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -340,7 +340,12 @@ class CwtchNotifier { profileCN.getProfile(data["ProfileOnion"])?.downloadMarkManifest(data["FileKey"]); break; case "FileDownloadProgressUpdate": - profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], int.parse(data["Progress"])); + var progress = int.parse(data["Progress"]); + profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], progress, int.parse(data["FileSizeInChunks"])); + // progress == -1 is a "download was interrupted" message and should contain a path + if (progress < 0) { + profileCN.getProfile(data["ProfileOnion"])?.downloadSetPath(data["FileKey"], data["FilePath"]); + } break; case "FileDownloaded": profileCN.getProfile(data["ProfileOnion"])?.downloadMarkFinished(data["FileKey"], data["FilePath"]); diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 6b7cb552..1ae11669 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -410,6 +410,22 @@ class CwtchFfi implements Cwtch { malloc.free(u2); } + @override + // ignore: non_constant_identifier_names + void VerifyOrResumeDownload(String profileOnion, String contactHandle, String filekey) { + var fn = library.lookup>("c_VerifyOrResumeDownload"); + // ignore: non_constant_identifier_names + final VerifyOrResumeDownload = fn.asFunction(); + final u1 = profileOnion.toNativeUtf8(); + final u2 = contactHandle.toNativeUtf8(); + final u3 = filekey.toNativeUtf8(); + VerifyOrResumeDownload(u1, u1.length, u2, u2.length, u3, u3.length); + malloc.free(u1); + malloc.free(u2); + malloc.free(u3); + } + + @override // ignore: non_constant_identifier_names void ResetTor() { diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index bf73813e..c57d1cdb 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -154,6 +154,12 @@ class CwtchGomobile implements Cwtch { cwtchPlatform.invokeMethod("CheckDownloadStatus", {"ProfileOnion": profileOnion, "fileKey": fileKey}); } + @override + // ignore: non_constant_identifier_names + void VerifyOrResumeDownload(String profileOnion, String contactHandle, String filekey) { + cwtchPlatform.invokeMethod("VerifyOrResumeDownload", {"ProfileOnion": profileOnion, "handle": contactHandle, "filekey": filekey}); + } + @override // ignore: non_constant_identifier_names void ResetTor() { diff --git a/lib/model.dart b/lib/model.dart index f036be0f..94be845d 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -362,11 +362,21 @@ class ProfileInfoState extends ChangeNotifier { this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); } - void downloadUpdate(String fileKey, int progress) { + void downloadUpdate(String fileKey, int progress, int numChunks) { if (!downloadActive(fileKey)) { - print("error: received progress for unknown download " + fileKey); + if (progress < 0) { + this._downloads[fileKey] = FileDownloadProgress(numChunks, DateTime.now()); + this._downloads[fileKey]!.interrupted = true; + notifyListeners(); + } else { + print("error: received progress for unknown download " + fileKey); + } } else { + if (this._downloads[fileKey]!.interrupted) { + this._downloads[fileKey]!.interrupted = false; + } this._downloads[fileKey]!.chunksDownloaded = progress; + this._downloads[fileKey]!.chunksTotal = numChunks; notifyListeners(); } } @@ -394,7 +404,7 @@ class ProfileInfoState extends ChangeNotifier { } bool downloadActive(String fileKey) { - return this._downloads.containsKey(fileKey); + return this._downloads.containsKey(fileKey) && !this._downloads[fileKey]!.interrupted; } bool downloadGotManifest(String fileKey) { @@ -405,10 +415,27 @@ class ProfileInfoState extends ChangeNotifier { return this._downloads.containsKey(fileKey) && this._downloads[fileKey]!.complete; } + bool downloadInterrupted(String fileKey) { + return this._downloads.containsKey(fileKey) && this._downloads[fileKey]!.interrupted; + } + + void downloadMarkResumed(String fileKey) { + if (this._downloads.containsKey(fileKey)) { + this._downloads[fileKey]!.interrupted = false; + } + } + double downloadProgress(String fileKey) { return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.progress() : 0.0; } + // used for loading interrupted download info; use downloadMarkFinished for successful downloads + void downloadSetPath(String fileKey, String path) { + if (this._downloads.containsKey(fileKey)) { + this._downloads[fileKey]!.downloadedTo = path; + } + } + String? downloadFinalPath(String fileKey) { return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.downloadedTo : null; } @@ -431,6 +458,7 @@ class FileDownloadProgress { int chunksTotal = 1; bool complete = false; bool gotManifest = false; + bool interrupted = false; String? downloadedTo; DateTime? timeStart; DateTime? timeEnd; diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index fa178dd4..43da9899 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -12,6 +12,7 @@ import '../../model.dart'; class FileMessage extends Message { final MessageMetadata metadata; final String content; + final RegExp nonHex = RegExp(r'[^a-f0-9]'); FileMessage(this.metadata, this.content); @@ -30,6 +31,10 @@ class FileMessage extends Message { String nonce = shareObj['n'] as String; int fileSize = shareObj['s'] as int; + if (!validHash(rootHash, nonce)) { + return MessageRow(MalformedBubble()); + } + return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize), key: Provider.of(bcontext).getMessageKey(idx)); }); } @@ -47,6 +52,9 @@ class FileMessage extends Message { String rootHash = shareObj['h'] as String; String nonce = shareObj['n'] as String; int fileSize = shareObj['s'] as int; + if (!validHash(rootHash, nonce)) { + return MessageRow(MalformedBubble()); + } return FileBubble( nameSuggestion, rootHash, @@ -61,4 +69,8 @@ class FileMessage extends Message { MessageMetadata getMetadata() { return this.metadata; } + + bool validHash(String hash, String nonce) { + return hash.length == 128 && nonce.length == 48 && !hash.contains(nonHex) && !nonce.contains(nonHex); + } } diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index 496e0924..a0052919 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -33,6 +33,11 @@ class FileBubble extends StatefulWidget { } class FileBubbleState extends State { + @override + void initState() { + super.initState(); + } + @override Widget build(BuildContext context) { var fromMe = Provider.of(context).senderHandle == Provider.of(context).onion; @@ -71,7 +76,7 @@ class FileBubbleState extends State { } else if (Provider.of(context).downloadComplete(widget.fileKey())) { // in this case, whatever marked download.complete would have also set the path var path = Provider.of(context).downloadFinalPath(widget.fileKey())!; - wdgDecorations = Text('Saved to: ' + path + '\u202F'); + wdgDecorations = Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F'); } else if (Provider.of(context).downloadActive(widget.fileKey())) { if (!Provider.of(context).downloadGotManifest(widget.fileKey())) { wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'); @@ -84,12 +89,15 @@ class FileBubbleState extends State { } else if (flagStarted) { // in this case, the download was done in a previous application launch, // so we probably have to request an info lookup - var path = Provider.of(context).downloadFinalPath(widget.fileKey()); - if (path == null) { - wdgDecorations = Text('Checking download status...' + '\u202F'); + if (!Provider.of(context).downloadInterrupted(widget.fileKey()) ) { + wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F'); Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, widget.fileKey()); } else { - wdgDecorations = Text('Saved to: ' + path + '\u202F'); + var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; + wdgDecorations = Column( + crossAxisAlignment: CrossAxisAlignment.start, + children:[Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),ElevatedButton(onPressed: _btnResume, child: Text('Verify/resume'))] + ); } } else { wdgDecorations = Center( @@ -167,6 +175,13 @@ class FileBubbleState extends State { } } + void _btnResume() async { + var profileOnion = Provider.of(context, listen: false).onion; + var handle = Provider.of(context, listen: false).senderHandle; + Provider.of(context, listen: false).downloadMarkResumed(widget.fileKey()); + Provider.of(context, listen: false).cwtch.VerifyOrResumeDownload(profileOnion, handle, widget.fileKey()); + } + // Construct an file chrome for the sender Widget senderFileChrome(String chrome, String fileName, String rootHash, int fileSize) { return ListTile( From 10838ba67a3455b950c2c9e79fecf7254fed0c2c Mon Sep 17 00:00:00 2001 From: erinn Date: Thu, 4 Nov 2021 15:44:31 -0700 Subject: [PATCH 21/26] l10n updates for file resumption 2 --- lib/l10n/intl_de.arb | 3 ++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_es.arb | 3 ++- lib/l10n/intl_fr.arb | 3 ++- lib/l10n/intl_it.arb | 3 ++- lib/l10n/intl_pl.arb | 3 ++- lib/l10n/intl_pt.arb | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index f8bc1fca..b3faa019 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,7 @@ { "@@locale": "de", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d9aab850..7fb454e1 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,7 @@ { "@@locale": "en", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index b1408582..9ec6ad47 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,7 @@ { "@@locale": "es", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 825c0eca..6ad148e0 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,7 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 97dda2c2..25727fc8 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,7 @@ { "@@locale": "it", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index d16fc53f..deaffbb6 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,7 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index a2190708..ed0b132d 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,7 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-04T22:56:21+01:00", + "@@last_modified": "2021-11-04T23:42:54+01:00", + "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", "fileSavedTo": "Saved to", From 626aa386d0f5ffb0c53a9dcabb9284bb78172d8d Mon Sep 17 00:00:00 2001 From: erinn Date: Thu, 4 Nov 2021 16:31:01 -0700 Subject: [PATCH 22/26] update lcg version --- LIBCWTCH-GO.version | 2 +- lib/widgets/filebubble.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 3fe724f0..ccabd97d 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-25-g4adb501-2021-11-04-04-20 \ No newline at end of file +v1.3.1-31-g706be7f-2021-11-04-22-49 \ No newline at end of file diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index a0052919..33fa503e 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -96,7 +96,7 @@ class FileBubbleState extends State { var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; wdgDecorations = Column( crossAxisAlignment: CrossAxisAlignment.start, - children:[Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),ElevatedButton(onPressed: _btnResume, child: Text('Verify/resume'))] + children:[Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton))] ); } } else { From 659b8d5bf142a5e5d075516c9e3a969336b75f28 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 4 Nov 2021 18:10:47 -0700 Subject: [PATCH 23/26] macos drone build --- .drone.yml | 84 +++++++++++++++++++++++++++++++++++++- LIBCWTCH-GO-MACOS.version | 1 + README.md | 7 +++- fetch-libcwtch-go-macos.sh | 6 +++ fetch-libcwtch-go.sh | 2 - fetch-tor-macos.sh | 7 ++++ macos/package-release.sh | 3 +- pubspec.yaml | 2 +- 8 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 LIBCWTCH-GO-MACOS.version create mode 100755 fetch-libcwtch-go-macos.sh create mode 100755 fetch-tor-macos.sh diff --git a/.drone.yml b/.drone.yml index e8dd2ffd..0f9b5ce7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -125,7 +125,6 @@ steps: - find . -type f -exec sha256sum {} \; > ./../sha256s.txt - mv ./../sha256s.txt . - cd .. - # TODO: do deployment once files actaully compile - scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/ - name: notify-email @@ -276,3 +275,86 @@ trigger: event: - push - pull_request + +--- +kind: pipeline +type: exec +name: macos + +platform: + os: darwin + arch: amd64 + +clone: + disable: true + +steps: + - name: clone + environment: + buildbot_key_b64: + from_secret: buildbot_key_b64 + commands: + - mkdir ~/.ssh + - echo $buildbot_key_b64 > ~/.ssh/id_rsa.b64 + - base64 -d ~/.ssh/id_rsa.b64 > ~/.ssh/id_rsa + - chmod 400 ~/.ssh/id_rsa + # force by pass of ssh host key check, less secure + - ssh-keyscan -H git.openprivacy.ca >> ~/.ssh/known_hosts + - git init + - git config core.sshCommand 'ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa' + - git remote add origin gogs@git.openprivacy.ca:$DRONE_REPO.git + - git pull origin trunk + - git fetch --tags + - git checkout $DRONE_COMMIT + # use Drone ssh var instead of hardcode to allow forks to build (gogs@git.openprivacy.ca:cwtch.im/cwtch-ui.git) + #- git clone gogs@git.openprivacy.ca:$DRONE_REPO.git . + #- git checkout $DRONE_COMMIT + + - name: fetch + commands: + - ./fetch-tor-macos.sh + - echo `git describe --tags --abbrev=1` > VERSION + - echo `date +%G-%m-%d-%H-%M` > BUILDDATE + - export PATH=$PATH:/Users/Dan/development/flutter/bin + - flutter pub get + - mkdir deploy + - ./fetch-libcwtch-go-macos.sh + - gem install --user-install cocoapods + + - name: build-macos + commands: + - export PATH=$PATH:/Users/Dan/development/flutter/bin + - export GEM_HOME=$HOME/.gem + - export PATH=$GEM_HOME/ruby/2.6.0/bin:$PATH + - flutter config --enable-macos-desktop + - flutter build macos --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE` + - export PATH=$PATH:/usr/local/bin #create-dmg + - macos/package-release.sh + - mkdir -p deploy + - mv Cwtch.dmg deploy + + - name: deploy-buildfiles + environment: + BUILDFILES_KEY: + from_secret: buildfiles_key + when: + event: push + status: [ success ] + commands: + - echo $BUILDFILES_KEY > ~/id_rsab64 + - base64 -d ~/id_rsab64 > ~/id_rsa + - chmod 400 ~/id_rsa + - export DIR=flwtch-macos-`cat VERSION`-`cat BUILDDATE` + - mv deploy $DIR + - cd $DIR + - find . -type f -exec shasum -a 512 {} \; > ./../sha512s.txt + - mv ./../sha512s.txt . + - cd .. + - scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/ + +trigger: + #repo: cwtch.im/cwtch-ui # allow forks to build? + branch: trunk + event: + - push + - pull_request \ No newline at end of file diff --git a/LIBCWTCH-GO-MACOS.version b/LIBCWTCH-GO-MACOS.version new file mode 100644 index 00000000..14a54896 --- /dev/null +++ b/LIBCWTCH-GO-MACOS.version @@ -0,0 +1 @@ +2021-11-04-19-59-v1.3.1-35-g81705ce \ No newline at end of file diff --git a/README.md b/README.md index da93bdd1..29280794 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ To build a release version and load normal profiles, use `build-release.sh X` in - run `flutter config --enable-linux-desktop` if you've never done so before - optional: launch cwtch-ui directly by running `flutter run -d linux` - to build cwtch-ui, run `flutter build linux` +- optional: launch cwtch-ui build with `env LD_LIBRARY_PATH=linux ./build/linux/x64/release/bundle/cwtch` - to package the build, run `linux/package-release.sh` ### Building on Windows (for Windows) @@ -60,9 +61,11 @@ To build a release version and load normal profiles, use `build-release.sh X` in ### Building on MacOS -- Navigate to https://git.openprivacy.ca/cwtch.im/libcwtch-go/releases and download the latest libCwtch.dylib into this folder -- Download and install Tor Browser (it's currently the only way to get tor for macos) +- Cocaopods is required, you may need to `gem install cocaopods -v 1.9.3` +- copy `libCwtch.dylib` into the root folder, or run `fetch-libcwtch-go-macos.sh` to download it +- run `fetch-tor-macos.sh` to fetch Tor or Download and install Tor Browser and `cp -r /Applications/Tor\ Browser.app/Contents/MacOS/Tor ./macos/` - `flutter build macos` +- optional: launch cwtch-ui build with `./build/linux/x64/release/bundle/cwtch` - `./macos/package-release.sh` results in a Cwtch.dmg that has libCwtch.dylib and tor in it as well and can be installed into Applications diff --git a/fetch-libcwtch-go-macos.sh b/fetch-libcwtch-go-macos.sh new file mode 100755 index 00000000..8fc870a1 --- /dev/null +++ b/fetch-libcwtch-go-macos.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +VERSION=`cat LIBCWTCH-GO-MACOS.version` +echo $VERSION + +curl https://build.openprivacy.ca/files/libCwtch-go-macos-$VERSION/libCwtch.dylib --output libCwtch.dylib diff --git a/fetch-libcwtch-go.sh b/fetch-libcwtch-go.sh index 364e9ec3..fe331d0b 100755 --- a/fetch-libcwtch-go.sh +++ b/fetch-libcwtch-go.sh @@ -5,5 +5,3 @@ echo $VERSION wget https://build.openprivacy.ca/files/libCwtch-go-$VERSION/cwtch.aar -O android/cwtch/cwtch.aar wget https://build.openprivacy.ca/files/libCwtch-go-$VERSION/libCwtch.so -O linux/libCwtch.so - -# wget https://build.openprivacy.ca/files/libCwtch-go-$VERSION/libCwtch.dll -O windows/libCwtch.dll diff --git a/fetch-tor-macos.sh b/fetch-tor-macos.sh new file mode 100755 index 00000000..0d3e2ac7 --- /dev/null +++ b/fetch-tor-macos.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +cd macos +curl https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-macos-0.4.6.7.tar.gz --output tor.tar.gz +tar -xzf tor.tar.gz +chmod a+x Tor/tor.real +cd .. diff --git a/macos/package-release.sh b/macos/package-release.sh index 2fb65cf1..e3c09936 100755 --- a/macos/package-release.sh +++ b/macos/package-release.sh @@ -3,13 +3,14 @@ # Run from SRCROOT cp libCwtch.dylib build/macos/Build/Products/Release/Cwtch.app/Contents/Frameworks/ -cp -r /Applications/Tor\ Browser.app/Contents/MacOS/Tor build/macos/Build/Products/Release/Cwtch.app/Contents/MacOS/ +cp -r macos/Tor build/macos/Build/Products/Release/Cwtch.app/Contents/MacOS/ rm Cwtch.dmg rm -r macos_dmg mkdir macos_dmg cp -r "build/macos/Build/Products/Release/Cwtch.app" macos_dmg/ +# https://github.com/create-dmg/create-dmg create-dmg \ --volname "Cwtch" \ --volicon "macos/cwtch.icns" \ diff --git a/pubspec.yaml b/pubspec.yaml index 366d60a6..533b66f5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.3.0+21 +version: 1.4.0+22 environment: sdk: ">=2.12.0 <3.0.0" From 0d4c67923d79c2f94bb5b5d5836a4c7dd54e21d4 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 4 Nov 2021 22:34:06 -0700 Subject: [PATCH 24/26] Upgrade libCwtch to v1.4.0 --- LIBCWTCH-GO-MACOS.version | 2 +- LIBCWTCH-GO.version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LIBCWTCH-GO-MACOS.version b/LIBCWTCH-GO-MACOS.version index 14a54896..66faa096 100644 --- a/LIBCWTCH-GO-MACOS.version +++ b/LIBCWTCH-GO-MACOS.version @@ -1 +1 @@ -2021-11-04-19-59-v1.3.1-35-g81705ce \ No newline at end of file +2021-11-05-01-22-v1.4.0 \ No newline at end of file diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index ccabd97d..7d0e34d1 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.3.1-31-g706be7f-2021-11-04-22-49 \ No newline at end of file +2021-11-05-05-23-v1.4.0 \ No newline at end of file From 44b77f0a90251332394fd38a8fd16f3d013b00f3 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 5 Nov 2021 13:45:50 -0700 Subject: [PATCH 25/26] libcwtch go bump (fix create server on mac), copy keys text fix --- LIBCWTCH-GO-MACOS.version | 2 +- LIBCWTCH-GO.version | 2 +- lib/l10n/intl_de.arb | 3 ++- lib/l10n/intl_en.arb | 3 ++- lib/l10n/intl_es.arb | 3 ++- lib/l10n/intl_fr.arb | 11 ++++++----- lib/l10n/intl_it.arb | 3 ++- lib/l10n/intl_pl.arb | 3 ++- lib/l10n/intl_pt.arb | 3 ++- lib/widgets/serverrow.dart | 2 +- 10 files changed, 21 insertions(+), 14 deletions(-) diff --git a/LIBCWTCH-GO-MACOS.version b/LIBCWTCH-GO-MACOS.version index 66faa096..1a2c6b98 100644 --- a/LIBCWTCH-GO-MACOS.version +++ b/LIBCWTCH-GO-MACOS.version @@ -1 +1 @@ -2021-11-05-01-22-v1.4.0 \ No newline at end of file +2021-11-05-16-35-v1.4.0-4-g86c5a51 \ No newline at end of file diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 7d0e34d1..aeff4d62 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -2021-11-05-05-23-v1.4.0 \ No newline at end of file +2021-11-05-20-35-v1.4.0-4-g86c5a51 \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index b3faa019..8d43326e 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,7 @@ { "@@locale": "de", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 7fb454e1..8db70b2e 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,7 @@ { "@@locale": "en", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 9ec6ad47..413a12c6 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,7 @@ { "@@locale": "es", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 6ad148e0..85154efb 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,10 +1,11 @@ { "@@locale": "fr", - "@@last_modified": "2021-11-04T23:42:54+01:00", - "verfiyResumeButton": "Verify\/resume", - "fileCheckingStatus": "Checking download status", - "fileInterrupted": "Interrupted", - "fileSavedTo": "Saved to", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", + "verfiyResumeButton": "Vérifier\/reprendre", + "fileCheckingStatus": "Vérification de l'état du téléchargement", + "fileInterrupted": "Interrompu", + "fileSavedTo": "Enregistré dans", "plainServerDescription": "Nous vous recommandons de protéger vos serveurs Cwtch par un mot de passe. Si vous ne définissez pas de mot de passe sur ce serveur, toute personne ayant accès à cet appareil peut être en mesure d'accéder aux informations concernant ce serveur, y compris les clés cryptographiques sensibles.", "encryptedServerDescription": "Le chiffrement d’un serveur avec un mot de passe le protège des autres personnes qui peuvent également utiliser cet appareil. Les serveurs cryptés ne peuvent pas être déchiffrés, affichés ou accessibles tant que le mot de passe correct n’est pas entré pour les déverrouiller.", "deleteServerConfirmBtn": "Supprimer vraiment le serveur", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 25727fc8..2ad4b024 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,7 @@ { "@@locale": "it", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index deaffbb6..e79afa90 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,7 @@ { "@@locale": "pl", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index ed0b132d..e17409f6 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,7 @@ { "@@locale": "pt", - "@@last_modified": "2021-11-04T23:42:54+01:00", + "@@last_modified": "2021-11-05T21:38:20+01:00", + "copyServerKeys": "Copy keys", "verfiyResumeButton": "Verify\/resume", "fileCheckingStatus": "Checking download status", "fileInterrupted": "Interrupted", diff --git a/lib/widgets/serverrow.dart b/lib/widgets/serverrow.dart index 8da87c65..574d7c7e 100644 --- a/lib/widgets/serverrow.dart +++ b/lib/widgets/serverrow.dart @@ -59,7 +59,7 @@ class _ServerRowState extends State { // Copy server button IconButton( enableFeedback: true, - tooltip: AppLocalizations.of(context)!.copyAddress, + tooltip: AppLocalizations.of(context)!.copyServerKeys, icon: Icon(CwtchIcons.address_copy_2, color: Provider.of(context).current().mainTextColor()), onPressed: () { Clipboard.setData(new ClipboardData(text: server.serverBundle)); From d3da68272b09f1f80db9831bfb65ab5506c2082f Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 5 Nov 2021 17:23:45 -0700 Subject: [PATCH 26/26] libcwtch go bump (fix join on add serverer); swap deploy folder name parts date/ver --- .drone.yml | 6 +++--- LIBCWTCH-GO-MACOS.version | 2 +- LIBCWTCH-GO.version | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0f9b5ce7..70b5c84e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -117,7 +117,7 @@ steps: - echo $BUILDFILES_KEY > ~/id_rsab64 - base64 -d ~/id_rsab64 > ~/id_rsa - chmod 400 ~/id_rsa - - export DIR=flwtch-`cat VERSION`-`cat BUILDDATE` + - export DIR=flwtch-`cat BUILDDATE`-`cat VERSION` - mv deploy $DIR - cp -r coverage/html $DIR/coverage-tests - cp -r test/failures $DIR/test-failures || true @@ -237,7 +237,7 @@ steps: - $Env:zipsha = $Env:zip + '.sha512' - $Env:msix = 'cwtch-install-' + $Env:version + '.msix' - $Env:msixsha = $Env:msix + '.sha512' - - $Env:buildname = 'flwtch-win-' + $Env:version + '-' + $Env:builddate + - $Env:buildname = 'flwtch-win-' + $Env:builddate + '-' + $Env:version - $Env:builddir = $Env:buildname - echo $Env:pfx > codesign.pfx.b64 - certutil -decode codesign.pfx.b64 codesign.pfx @@ -344,7 +344,7 @@ steps: - echo $BUILDFILES_KEY > ~/id_rsab64 - base64 -d ~/id_rsab64 > ~/id_rsa - chmod 400 ~/id_rsa - - export DIR=flwtch-macos-`cat VERSION`-`cat BUILDDATE` + - export DIR=flwtch-macos-`cat BUILDDATE`-`cat VERSION` - mv deploy $DIR - cd $DIR - find . -type f -exec shasum -a 512 {} \; > ./../sha512s.txt diff --git a/LIBCWTCH-GO-MACOS.version b/LIBCWTCH-GO-MACOS.version index 1a2c6b98..c7494de8 100644 --- a/LIBCWTCH-GO-MACOS.version +++ b/LIBCWTCH-GO-MACOS.version @@ -1 +1 @@ -2021-11-05-16-35-v1.4.0-4-g86c5a51 \ No newline at end of file +2021-11-05-20-13-v1.4.1 \ No newline at end of file diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index aeff4d62..4260781a 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -2021-11-05-20-35-v1.4.0-4-g86c5a51 \ No newline at end of file +2021-11-06-00-13-v1.4.1 \ No newline at end of file