From 267b1b09b19e0fe9679a4c2a59eff22aff81a3f3 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 4 Apr 2023 13:58:42 -0700 Subject: [PATCH] Status + Profile Attributes --- .../kotlin/im/cwtch/flwtch/MainActivity.kt | 11 ++ lib/cwtch/cwtch.dart | 3 + lib/cwtch/cwtchNotifier.dart | 47 ++++++- lib/cwtch/ffi.dart | 53 ++++++++ lib/cwtch/gomobile.dart | 18 +++ lib/l10n/intl_cy.arb | 9 +- lib/l10n/intl_da.arb | 9 +- lib/l10n/intl_de.arb | 9 +- lib/l10n/intl_el.arb | 9 +- lib/l10n/intl_en.arb | 9 +- lib/l10n/intl_es.arb | 9 +- lib/l10n/intl_fr.arb | 9 +- lib/l10n/intl_it.arb | 9 +- lib/l10n/intl_ko.arb | 19 ++- lib/l10n/intl_lb.arb | 9 +- lib/l10n/intl_nl.arb | 9 +- lib/l10n/intl_no.arb | 9 +- lib/l10n/intl_pl.arb | 9 +- lib/l10n/intl_pt.arb | 9 +- lib/l10n/intl_pt_BR.arb | 9 +- lib/l10n/intl_ro.arb | 9 +- lib/l10n/intl_ru.arb | 9 +- lib/l10n/intl_sk.arb | 9 +- lib/l10n/intl_tr.arb | 9 +- lib/main.dart | 8 +- lib/models/contact.dart | 63 ++++++++- lib/models/messages/filemessage.dart | 10 +- lib/models/profile.dart | 39 ++++++ lib/themes/opaque.dart | 3 + lib/views/addeditprofileview.dart | 124 +++++++++++++----- lib/views/contactsview.dart | 39 +++++- lib/views/messageview.dart | 19 +-- lib/views/peersettingsview.dart | 22 +++- lib/widgets/contactrow.dart | 14 +- lib/widgets/messagerow.dart | 2 +- lib/widgets/textfield.dart | 1 + 36 files changed, 561 insertions(+), 96 deletions(-) diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt index 0065c815..3b3c4d85 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -483,6 +483,17 @@ class MainActivity: FlutterActivity() { val v: String = call.argument("Val") ?: "" Cwtch.setProfileAttribute(profile, key, v) } + "GetProfileAttribute" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val key: String = call.argument("Key") ?: "" + Data.Builder().putString("result", Cwtch.getProfileAttribute(profile, key)).build() + } + "GetConversationAttribute" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val key: String = call.argument("Key") ?: "" + Data.Builder().putString("result", Cwtch.getConversationAttribute(profile, conversation.toLong(), key)).build() + } "SetConversationAttribute" -> { val profile: String = call.argument("ProfileOnion") ?: "" val conversation: Int = call.argument("conversation") ?: 0 diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 6dbb6f29..394926c5 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -95,9 +95,12 @@ abstract class Cwtch { Future ImportBundle(String profile, String bundle); // ignore: non_constant_identifier_names void SetProfileAttribute(String profile, String key, String val); + String? GetProfileAttribute(String profile, String key); // ignore: non_constant_identifier_names void SetConversationAttribute(String profile, int conversation, String key, String val); // ignore: non_constant_identifier_names + String? GetConversationAttribute(String profile, int identifier, String s); + // ignore: non_constant_identifier_names void SetMessageAttribute(String profile, int conversation, int channel, int message, String key, String val); // ignore: non_constant_identifier_names void LoadServers(String password); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index ca268689..85324657 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -30,14 +30,15 @@ class CwtchNotifier { late NotificationsManager notificationManager; late AppState appState; late ServerListState serverListState; + late FlwtchState flwtchState; String? notificationSimple; String? notificationConversationInfo; SeenMessageCallback? seenMessageCallback; - CwtchNotifier( - ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) { + CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, + ServerListState serverListStateCN, FlwtchState flwtchStateCN) { profileCN = pcn; settings = settingsCN; error = errorCN; @@ -45,6 +46,7 @@ class CwtchNotifier { notificationManager = notificationManagerP; appState = appStateCN; serverListState = serverListStateCN; + flwtchState = flwtchStateCN; } void l10nInit(String notificationSimple, String notificationConversationInfo) { @@ -74,6 +76,19 @@ class CwtchNotifier { // if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta... profileCN.add(data["Identity"], data["name"], data["picture"], data["defaultPicture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["autostart"] == "true", data["tag"] != "v1-defaultPassword"); + + // Update Profile Attributes + profileCN.getProfile(data["Identity"])?.setAttribute(0, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-1")); + profileCN.getProfile(data["Identity"])?.setAttribute(1, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-2")); + profileCN.getProfile(data["Identity"])?.setAttribute(2, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-3")); + profileCN.getProfile(data["Identity"])?.setAvailabilityStatus(flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-status") ?? ""); + profileCN.getProfile(data["Identity"])?.contactList.contacts.forEach((contact) { + contact.setAttribute(0, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-1")); + contact.setAttribute(1, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-2")); + contact.setAttribute(2, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-3")); + contact.setAvailabilityStatus(flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-status") ?? ""); + }); + break; case "ContactCreated": EnvironmentConfig.debugLog("ContactCreated $data"); @@ -291,6 +306,10 @@ class CwtchNotifier { profileCN.getProfile(data["ProfileOnion"])?.downloadSetPathForSender(filekey, data["Data"]); } } + } else if (data["Key"].toString().startsWith("public.profile.profile-attribute")) { + // ignore these events... + } else if (data["Key"].toString().startsWith("public.profile.profile-status")) { + profileCN.getProfile(data["ProfileOnion"])?.setAvailabilityStatus(data["Data"]); } else { EnvironmentConfig.debugLog("unhandled set attribute event: ${data['Key']}"); } @@ -377,6 +396,30 @@ class CwtchNotifier { profileCN.getProfile(data["ProfileOnion"])?.waitForDownloadComplete(contact.identifier, fileKey); } } + } else if (data['Path'] == "profile.profile-attribute-1" || data['Path'] == "profile.profile-attribute-2" || data['Path'] == "profile.profile-attribute-3") { + if (data["Exists"] == "true") { + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); + if (contact != null) { + switch (data['Path']) { + case "profile.profile-attribute-1": + contact.setAttribute(0, data["Data"]); + break; + case "profile.profile-attribute-2": + contact.setAttribute(1, data["Data"]); + break; + case "profile.profile-attribute-3": + contact.setAttribute(2, data["Data"]); + break; + } + } + } + } else if (data['Path'] == "profile.profile-status") { + if (data["Exists"] == "true") { + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); + if (contact != null) { + contact.setAvailabilityStatus(data['Data']); + } + } } else { EnvironmentConfig.debugLog("unhandled ret val event: ${data['Path']}"); } diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 8b601eed..b5ed651c 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -72,6 +72,9 @@ typedef GetJsonBlobFromStrStrIntFn = Pointer Function(Pointer, int, typedef get_json_blob_from_str_int_function = Pointer Function(Pointer, Int32, Int32); typedef GetJsonBlobFromStrIntFn = Pointer Function(Pointer, int, int); +typedef get_json_blob_from_str_str_function = Pointer Function(Pointer, Int32, Pointer, Int32); +typedef GetJsonBlobFromStrStrFn = Pointer Function(Pointer, int, Pointer, int); + typedef get_json_blob_from_str_int_int_str_function = Pointer Function(Pointer, Int32, Int32, Int32, Pointer, Int32); typedef GetJsonBlobFromStrIntIntStrFn = Pointer Function( Pointer, @@ -962,4 +965,54 @@ class CwtchFfi implements Cwtch { } return false; } + + @override + String? GetProfileAttribute(String profile, String key) { + var getProfileAttributeC = library.lookup>("c_GetProfileAttribute"); + // ignore: non_constant_identifier_names + final GetProfileAttribute = getProfileAttributeC.asFunction(); + final utf8profile = profile.toNativeUtf8(); + final utf8key = key.toNativeUtf8(); + Pointer jsonMessageBytes = GetProfileAttribute(utf8profile, utf8profile.length, utf8key, utf8key.length); + String jsonMessage = jsonMessageBytes.toDartString(); + _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); + malloc.free(utf8profile); + malloc.free(utf8key); + + try { + dynamic attributeResult = json.decode(jsonMessage); + if (attributeResult["Exists"]) { + return attributeResult["Value"]; + } + } catch (e) { + EnvironmentConfig.debugLog("error getting profile attribute: $e"); + } + + return null; + } + + @override + String? GetConversationAttribute(String profile, int conversation, String key) { + var getConversationAttributeC = library.lookup>("c_GetConversationAttribute"); + // ignore: non_constant_identifier_names + final GetConversationAttribute = getConversationAttributeC.asFunction(); + final utf8profile = profile.toNativeUtf8(); + final utf8key = key.toNativeUtf8(); + Pointer jsonMessageBytes = GetConversationAttribute(utf8profile, utf8profile.length, conversation, utf8key, utf8key.length); + String jsonMessage = jsonMessageBytes.toDartString(); + _UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes); + malloc.free(utf8profile); + malloc.free(utf8key); + + try { + dynamic attributeResult = json.decode(jsonMessage); + if (attributeResult["Exists"]) { + return attributeResult["Value"]; + } + } catch (e) { + EnvironmentConfig.debugLog("error getting profile attribute: $e"); + } + + return null; + } } diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 78d56b5c..7bbdc12f 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -388,4 +388,22 @@ class CwtchGomobile implements Cwtch { // Blodeuwedd is not currently supported on lower end devices. return false; } + + @override + String? GetProfileAttribute(String profile, String key) { + dynamic attributeResult = cwtchPlatform.invokeMethod("GetProfileAttribute", {"ProfileOnion": profile, "key": key}); + if (attributeResult["Exists"]) { + return attributeResult["Value"]; + } + return null; + } + + @override + String? GetConversationAttribute(String profile, int conversation, String key) { + dynamic attributeResult = cwtchPlatform.invokeMethod("GetProfileAttribute", {"ProfileOnion": profile, "conversation": conversation, "key": key}); + if (attributeResult["Exists"]) { + return attributeResult["Value"]; + } + return null; + } } diff --git a/lib/l10n/intl_cy.arb b/lib/l10n/intl_cy.arb index 998c8fa1..e276526d 100644 --- a/lib/l10n/intl_cy.arb +++ b/lib/l10n/intl_cy.arb @@ -1,6 +1,13 @@ { "@@locale": "cy", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index 0eb5d4a8..3d964a6a 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1,6 +1,13 @@ { "@@locale": "da", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 6b07ef5c..eb2843fd 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,6 +1,13 @@ { "@@locale": "de", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index d7662a9b..342f1f09 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,6 +1,13 @@ { "@@locale": "el", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ed694fe9..af12bda1 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,13 @@ { "@@locale": "en", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusAway": "Away", + "availabilityStatusBusy": "Busy", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddSummarize": "Summarize Conversation", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 54c5c3d2..e81c4531 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,13 @@ { "@@locale": "es", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 25cb0f8a..c8c88e1f 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,6 +1,13 @@ { "@@locale": "fr", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 7dfc262c..8ded0001 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,13 @@ { "@@locale": "it", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index a328e7dc..c4bbe2e2 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -1,14 +1,21 @@ { "@@locale": "ko", - "@@last_modified": "2023-03-27T20:38:31+02:00", - "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", - "blodeuweddProcessing": "Blodeuwedd is processing...", - "blodeuweddTranslate": "Translate Message", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", + "blodeuweddExperimentEnable": "Blodeuwedd (블러드웨드) 어시스턴트", + "blodeuweddDescription": "Blodeuwedd (불러드웨드) 어시스턴트는 로컬에서 호스팅되는 언어 모델을 통한 대화 내용 요약 및 메시지 번역과 같은 Cwtch에 새로운 기능을 추가합니다.", "blodeuweddSummarize": "Summarize Conversation", + "blodeuweddTranslate": "메시지 번역", + "blodeuweddProcessing": "Blodeuwedd 처리 중...", + "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddPath": "The directory where the Blodeuwedd is located on your computer.", "blodeuweddNotSupported": "This version of Cwtch has been compiled without support for the Blodeuwedd Assistant.", - "blodeuweddDescription": "The Blodeuwedd assistant adds new features to Cwtch such as chat transcript summarization and message translation via a locally hosted language model.", - "blodeuweddExperimentEnable": "Blodeuwedd Assistant", "labelACNCircuitInfo": "ACN 회로 정보", "clickableLinksWarning": "이 URL을 열면 Cwtch 외부에서 응용 프로그램이 시작되고 메타데이터가 노출되거나 Cwtch의 보안이 손상될 수 있습니다. 신뢰할 수 있는 사용자의 URL만 엽니다. 계속하시겠습니까?", "thisFeatureRequiresGroupExpermientsToBeEnabled": "이 기능을 사용하려면 설정에서 그룹 실험을 사용하도록 설정해야 합니다.", diff --git a/lib/l10n/intl_lb.arb b/lib/l10n/intl_lb.arb index 7861d0b8..0e5b2af9 100644 --- a/lib/l10n/intl_lb.arb +++ b/lib/l10n/intl_lb.arb @@ -1,6 +1,13 @@ { "@@locale": "lb", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 19ff7488..c63e98ea 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1,6 +1,13 @@ { "@@locale": "nl", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index 346146ed..007e3153 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -1,6 +1,13 @@ { "@@locale": "no", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 339aa25f..66f79eee 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,13 @@ { "@@locale": "pl", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 2bb91d9d..0a3ad93d 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,13 @@ { "@@locale": "pt", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 809899dd..a4452a04 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -1,6 +1,13 @@ { "@@locale": "pt_BR", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index dfc274e6..a0980398 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,6 +1,13 @@ { "@@locale": "ro", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 70375987..3919496f 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,13 @@ { "@@locale": "ru", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_sk.arb b/lib/l10n/intl_sk.arb index 15806e78..931c86ea 100644 --- a/lib/l10n/intl_sk.arb +++ b/lib/l10n/intl_sk.arb @@ -1,6 +1,13 @@ { "@@locale": "sk", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 1f806ed4..29fe687d 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,6 +1,13 @@ { "@@locale": "tr", - "@@last_modified": "2023-03-27T20:38:31+02:00", + "@@last_modified": "2023-04-03T22:39:52+02:00", + "profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ", + "profileInfoHint2": "You can add up to 3 fields.", + "profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.", + "availabilityStatusTooltip": "Set your availability status.", + "availabilityStatusBusy": "Busy", + "availabilityStatusAway": "Away", + "availabilityStatusAvailable": "Available", "blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.", "blodeuweddProcessing": "Blodeuwedd is processing...", "blodeuweddTranslate": "Translate Message", diff --git a/lib/main.dart b/lib/main.dart index 180ffae7..f972a018 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -88,13 +88,15 @@ class FlwtchState extends State with WindowListener { shutdownLinuxMethodChannel.setMethodCallHandler(shutdownDirect); print("initState: creating cwtchnotifier, ffi"); if (Platform.isAndroid) { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList); + var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState, globalServersList, this); cwtch = CwtchGomobile(cwtchNotifier); } else if (Platform.isLinux) { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList); + var cwtchNotifier = + new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); cwtch = CwtchFfi(cwtchNotifier); } else { - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList); + var cwtchNotifier = + new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this); cwtch = CwtchFfi(cwtchNotifier); } print("initState: invoking cwtch.Start()"); diff --git a/lib/models/contact.dart b/lib/models/contact.dart index a6fa8234..4d6c8f30 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -1,6 +1,10 @@ +import 'dart:ffi'; + import 'package:cwtch/main.dart'; import 'package:cwtch/models/message_draft.dart'; import 'package:cwtch/models/profile.dart'; +import 'package:cwtch/themes/opaque.dart'; +import 'package:cwtch/views/contactsview.dart'; import 'package:cwtch/widgets/messagerow.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -101,7 +105,7 @@ class ContactInfoState extends ChangeNotifier { keys = Map>(); } - String get nickname => this._nickname + (this._messageDraft.isEmpty() ? "" : "*"); + String get nickname => this._nickname; String get savePeerHistory => this._savePeerHistory; String? get acnCircuit => this._acnCircuit; @@ -354,4 +358,61 @@ class ContactInfoState extends ChangeNotifier { this.messageCache.updateTranslationEvent(messageID, translation); notifyListeners(); } + + // Contact Attributes. Can be set in Profile Edit View... + List attributes = [null, null, null]; + void setAttribute(int i, String? value) { + this.attributes[i] = value; + notifyListeners(); + } + + ProfileStatusMenu availabilityStatus = ProfileStatusMenu.available; + void setAvailabilityStatus(String status) { + switch (status) { + case "available": + availabilityStatus = ProfileStatusMenu.available; + break; + case "busy": + availabilityStatus = ProfileStatusMenu.busy; + break; + case "away": + availabilityStatus = ProfileStatusMenu.away; + break; + default: + ProfileStatusMenu.available; + } + notifyListeners(); + } + + Color getBorderColor(OpaqueThemeType theme) { + if (this.isBlocked) { + return theme.portraitBlockedBorderColor; + } + if (this.isOnline()) { + switch (this.availabilityStatus) { + case ProfileStatusMenu.available: + return theme.portraitOnlineBorderColor; + case ProfileStatusMenu.away: + return theme.portraitOnlineAwayColor; + case ProfileStatusMenu.busy: + return theme.portraitOnlineBusyColor; + } + } + return theme.portraitOfflineBorderColor; + } + + String augmentedNickname(BuildContext context) { + return this.nickname + (this.availabilityStatus == ProfileStatusMenu.available ? "" : " (" +this.statusString(context) + ")"); + } + + String statusString(BuildContext context) { + switch (this.availabilityStatus) { + case ProfileStatusMenu.available: + return AppLocalizations.of(context)!.availabilityStatusAvailable; + case ProfileStatusMenu.away: + return AppLocalizations.of(context)!.availabilityStatusAway; + case ProfileStatusMenu.busy: + return AppLocalizations.of(context)!.availabilityStatusBusy; + } + } } diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index c60f222e..c425ae0b 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -33,11 +33,11 @@ class FileMessage extends Message { int fileSize = shareObj['s'] as int; String fileKey = rootHash + "." + nonce; - if (metadata.attributes["file-downloaded"] == "true") { - if (!Provider.of(context).downloadKnown(fileKey)) { - Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, fileKey); - } - } + // if (metadata.attributes["file-downloaded"] == "true") { + // if (!Provider.of(context).downloadKnown(fileKey)) { + Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, fileKey); + // } + //} if (!validHash(rootHash, nonce)) { return MessageRow(MalformedBubble(), index); diff --git a/lib/models/profile.dart b/lib/models/profile.dart index 0abe9b5b..0c43d519 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -1,9 +1,12 @@ import 'dart:convert'; +import 'package:cwtch/config.dart'; import 'package:cwtch/models/remoteserver.dart'; import 'package:flutter/widgets.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../themes/opaque.dart'; +import '../views/contactsview.dart'; import 'contact.dart'; import 'contactlist.dart'; import 'filedownloadprogress.dart'; @@ -398,4 +401,40 @@ class ProfileInfoState extends ChangeNotifier { this._downloads.remove(fileKey); notifyListeners(); } + + // Profile Attributes. Can be set in Profile Edit View... + List attributes = [null, null, null]; + void setAttribute(int i, String? value) { + this.attributes[i] = value; + notifyListeners(); + } + + ProfileStatusMenu availabilityStatus = ProfileStatusMenu.available; + void setAvailabilityStatus(String status) { + switch (status) { + case "available": + availabilityStatus = ProfileStatusMenu.available; + break; + case "busy": + availabilityStatus = ProfileStatusMenu.busy; + break; + case "away": + availabilityStatus = ProfileStatusMenu.away; + break; + default: + ProfileStatusMenu.available; + } + notifyListeners(); + } + + Color getBorderColor(OpaqueThemeType theme) { + switch (this.availabilityStatus) { + case ProfileStatusMenu.available: + return theme.portraitOnlineBorderColor; + case ProfileStatusMenu.away: + return theme.portraitOnlineAwayColor; + case ProfileStatusMenu.busy: + return theme.portraitOnlineBusyColor; + } + } } diff --git a/lib/themes/opaque.dart b/lib/themes/opaque.dart index cd3f6934..1bd01d03 100644 --- a/lib/themes/opaque.dart +++ b/lib/themes/opaque.dart @@ -106,6 +106,9 @@ abstract class OpaqueThemeType { get portraitProfileBadgeColor => red; get portraitProfileBadgeTextColor => red; + get portraitOnlineAwayColor => Color(0xFFFFF59D); + get portraitOnlineBusyColor => Color(0xFFEF9A9A); + // dropshaddpow // todo: probably should not be reply icon color in messagerow get dropShadowColor => red; diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index f1041d11..7e9a700b 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -38,6 +38,11 @@ class _AddEditProfileViewState extends State { final ctrlrPass = TextEditingController(text: ""); final ctrlrPass2 = TextEditingController(text: ""); final ctrlrOnion = TextEditingController(text: ""); + + final ctrlrAttribute1 = TextEditingController(text: ""); + final ctrlrAttribute2 = TextEditingController(text: ""); + final ctrlrAttribute3 = TextEditingController(text: ""); + ScrollController controller = ScrollController(); late bool usePassword; late bool deleted; @@ -93,42 +98,89 @@ class _AddEditProfileViewState extends State { child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Visibility( visible: Provider.of(context).onion.isNotEmpty, - child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - MouseRegion( - cursor: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? SystemMouseCursors.click : SystemMouseCursors.basic, - child: GestureDetector( - // don't allow setting of profile images if the image previews experiment is disabled. - onTap: Provider.of(context, listen: false).disableFilePicker || - !Provider.of(context, listen: false).isExperimentEnabled(ImagePreviewsExperiment) - ? null - : () { - filesharing.showFilePicker(context, MaxImageFileSharingSize, (File file) { - var profile = Provider.of(context, listen: false).onion; - // Share this image publicly (conversation handle == -1) - Provider.of(context, listen: false).cwtch.ShareFile(profile, -1, file.path); - // update the image cache locally - Provider.of(context, listen: false).imagePath = file.path; - }, () { - final snackBar = SnackBar( - content: Text(AppLocalizations.of(context)!.msgFileTooBig), - duration: Duration(seconds: 4), - ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - }, () {}); - }, - child: ProfileImage( - imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) - ? Provider.of(context).imagePath - : Provider.of(context).defaultImagePath, - diameter: 120, - tooltip: - Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? AppLocalizations.of(context)!.tooltipSelectACustomProfileImage : "", - maskOut: false, - border: theme.theme.portraitOnlineBorderColor, - badgeTextColor: theme.theme.portraitContactBadgeTextColor, - badgeColor: theme.theme.portraitContactBadgeColor, - badgeEdit: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment)))) - ])), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + MouseRegion( + cursor: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? SystemMouseCursors.click : SystemMouseCursors.basic, + child: GestureDetector( + // don't allow setting of profile images if the image previews experiment is disabled. + onTap: Provider.of(context, listen: false).disableFilePicker || + !Provider.of(context, listen: false).isExperimentEnabled(ImagePreviewsExperiment) + ? null + : () { + filesharing.showFilePicker(context, MaxImageFileSharingSize, (File file) { + var profile = Provider.of(context, listen: false).onion; + // Share this image publicly (conversation handle == -1) + Provider.of(context, listen: false).cwtch.ShareFile(profile, -1, file.path); + // update the image cache locally + Provider.of(context, listen: false).imagePath = file.path; + }, () { + final snackBar = SnackBar( + content: Text(AppLocalizations.of(context)!.msgFileTooBig), + duration: Duration(seconds: 4), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }, () {}); + }, + child: ProfileImage( + imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) + ? Provider.of(context).imagePath + : Provider.of(context).defaultImagePath, + diameter: 120, + tooltip: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) + ? AppLocalizations.of(context)!.tooltipSelectACustomProfileImage + : "", + maskOut: false, + border: theme.theme.portraitOnlineBorderColor, + badgeTextColor: theme.theme.portraitContactBadgeTextColor, + badgeColor: theme.theme.portraitContactBadgeColor, + badgeEdit: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment)))) + ]), + SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: Column( + children: [ + Padding( + padding: EdgeInsets.all(5.0), + child: CwtchTextField( + controller: ctrlrAttribute1, + multiLine: false, + onChanged: (profileAttribute1) { + String onion = Provider.of(context, listen: false).onion; + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-1", profileAttribute1); + Provider.of(context, listen: false).attributes[0] = profileAttribute1; + }, + hintText: Provider.of(context).attributes[0] ?? AppLocalizations.of(context)!.profileInfoHint!)), + Padding( + padding: EdgeInsets.all(5.0), + child: CwtchTextField( + controller: ctrlrAttribute2, + multiLine: false, + onChanged: (profileAttribute2) { + String onion = Provider.of(context, listen: false).onion; + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-2", profileAttribute2); + Provider.of(context, listen: false).attributes[1] = profileAttribute2; + }, + hintText: Provider.of(context).attributes[1] ?? AppLocalizations.of(context)!.profileInfoHint2!)), + Padding( + padding: EdgeInsets.all(5.0), + child: CwtchTextField( + controller: ctrlrAttribute3, + multiLine: false, + onChanged: (profileAttribute3) { + String onion = Provider.of(context, listen: false).onion; + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-attribute-3", profileAttribute3); + Provider.of(context, listen: false).attributes[2] = profileAttribute3; + }, + hintText: Provider.of(context).attributes[2] ?? AppLocalizations.of(context)!.profileInfoHint3!)), + ], + )) + ], + )), Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 20, diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 8a4fc1ec..8a37a76e 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -25,6 +25,8 @@ import 'messageview.dart'; enum ShareMenu { copyCode, qrcode } +enum ProfileStatusMenu { available, away, busy } + class ContactsView extends StatefulWidget { const ContactsView({Key? key}) : super(key: key); @@ -137,12 +139,43 @@ class _ContactsViewState extends State { ? Provider.of(context).imagePath : Provider.of(context).defaultImagePath, diameter: 42, - border: Provider.of(context).isOnline - ? Provider.of(context).current().portraitOnlineBorderColor - : Provider.of(context).current().portraitOfflineBorderColor, + border: Provider.of(context).getBorderColor(Provider.of(context).theme), badgeTextColor: Colors.red, badgeColor: Colors.red, ), + PopupMenuButton( + icon: Icon(Icons.online_prediction), + tooltip: AppLocalizations.of(context)!.availabilityStatusTooltip, + splashRadius: Material.defaultSplashRadius / 2, + onSelected: (ProfileStatusMenu item) { + String onion = Provider.of(context, listen: false).onion; + switch (item) { + case ProfileStatusMenu.available: + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-status", "available"); + break; + case ProfileStatusMenu.away: + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-status", "away"); + break; + case ProfileStatusMenu.busy: + Provider.of(context, listen: false).cwtch.SetProfileAttribute(onion, "profile.profile-status", "busy"); + break; + } + }, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + value: ProfileStatusMenu.available, + child: Text(AppLocalizations.of(context)!.availabilityStatusAvailable!,), + ), + PopupMenuItem( + value: ProfileStatusMenu.away, + child: Text(AppLocalizations.of(context)!.availabilityStatusAway!,), + ), + PopupMenuItem( + value: ProfileStatusMenu.busy, + child: Text(AppLocalizations.of(context)!.availabilityStatusBusy!,), + ), + ], + ), SizedBox( width: 10, ), diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 99cb011f..a66a4561 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -196,7 +196,7 @@ class _MessageViewState extends State { ? Provider.of(context).imagePath : Provider.of(context).defaultImagePath, diameter: 42, - border: Provider.of(context).current().portraitOnlineBorderColor, + border: Provider.of(context).getBorderColor(Provider.of(context).theme), badgeTextColor: Colors.red, badgeColor: Provider.of(context).theme.portraitContactBadgeColor, badgeIcon: Provider.of(context).isGroup @@ -230,14 +230,15 @@ class _MessageViewState extends State { ), Expanded( child: Container( - height: 24, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - child: Text( - Provider.of(context).nickname, - overflow: TextOverflow.clip, - maxLines: 1, - ))) + height: 42, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(), + child: Align( + alignment: Alignment.centerLeft, child: Text( + Provider.of(context).augmentedNickname(context), + overflow: TextOverflow.clip, + maxLines: 1, + )))) ]), actions: appBarButtons, ), diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index 35ec44e2..50a1b619 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -96,8 +96,8 @@ class _PeerSettingsViewState extends State { child: Container( margin: EdgeInsets.all(10), padding: EdgeInsets.all(2), - child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ + Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ ProfileImage( imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? Provider.of(context).imagePath @@ -107,7 +107,23 @@ class _PeerSettingsViewState extends State { border: settings.theme.portraitOnlineBorderColor, badgeTextColor: settings.theme.portraitContactBadgeTextColor, badgeColor: settings.theme.portraitContactBadgeColor, - badgeEdit: false) + badgeEdit: false), + SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: Column(children: [ + Padding( + padding: EdgeInsets.all(1), + child: SelectableText(Provider.of(context, listen: false).attributes[0] ?? ""), + ), + Padding( + padding: EdgeInsets.all(1), + child: SelectableText(Provider.of(context, listen: false).attributes[1] ?? ""), + ), + Padding( + padding: EdgeInsets.all(1), + child: SelectableText(Provider.of(context, listen: false).attributes[2] ?? ""), + ) + ])) ]), Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index 8411214b..01eba222 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -44,20 +44,16 @@ class _ContactRowState extends State { color: Provider.of(context).selectedConversation == contact.identifier ? Provider.of(context).theme.backgroundHilightElementColor : Colors.transparent, child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: const EdgeInsets.all(6.0), //border size - child: ProfileImage( + padding: const EdgeInsets.all(6.0), //border size + child: ProfileImage( badgeCount: contact.unreadMessages, badgeColor: Provider.of(context).theme.portraitContactBadgeColor, badgeTextColor: Provider.of(context).theme.portraitContactBadgeTextColor, diameter: 64.0, imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? contact.imagePath : contact.defaultImagePath, disabled: !contact.isOnline(), - border: contact.isOnline() - ? Provider.of(context).theme.portraitOnlineBorderColor - : contact.isBlocked - ? Provider.of(context).theme.portraitBlockedBorderColor - : Provider.of(context).theme.portraitOfflineBorderColor), - ), + border: contact.getBorderColor(Provider.of(context).theme), + )), Expanded( child: Padding( padding: EdgeInsets.all(10.0), @@ -69,7 +65,7 @@ class _ContactRowState extends State { clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: Text( - contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),// + contact.augmentedNickname(context) + (contact.messageDraft.isEmpty() ? "" : "*"), style: TextStyle( fontSize: Provider.of(context).theme.contactOnionTextSize(), diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 45884a78..8920bdee 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -505,7 +505,7 @@ void modalShowTranslation(BuildContext context, ProfileInfoState profile, Settin var bubble = StaticMessageBubble( profile, settings, - MessageMetadata(profile.onion, Provider.of(context).identifier, 1, DateTime.now(), "blodeuwedd", null, null, null, true, false, false, ""), + MessageMetadata(profile.onion, Provider.of(context, listen: false).identifier, 1, DateTime.now(), "blodeuwedd", null, null, null, true, false, false, ""), Row(children: [ Provider.of(context).translation == "" ? Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ diff --git a/lib/widgets/textfield.dart b/lib/widgets/textfield.dart index 9c77280f..021f4bf8 100644 --- a/lib/widgets/textfield.dart +++ b/lib/widgets/textfield.dart @@ -68,6 +68,7 @@ class _CwtchTextFieldState extends State { decoration: InputDecoration( errorMaxLines: 2, hintText: widget.hintText, + hintStyle: TextStyle(color: (theme.current().mainTextColor as Color).withOpacity(0.5)), floatingLabelBehavior: FloatingLabelBehavior.never, filled: true, focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),