Expose antispam status in UI
continuous-integration/drone/pr Build was killed Details

This commit is contained in:
Sarah Jamie Lewis 2022-09-09 12:23:08 -07:00
parent 76c925d874
commit 37e18d03a1
30 changed files with 265 additions and 128 deletions

View File

@ -121,7 +121,7 @@ abstract class Cwtch {
Future<void> Shutdown(); Future<void> Shutdown();
// non-ffi // non-ffi
String defaultDownloadPath(); String? defaultDownloadPath();
bool isL10nInit(); bool isL10nInit();

View File

@ -309,6 +309,14 @@ class CwtchNotifier {
case "UpdateServerInfo": case "UpdateServerInfo":
profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]); profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]);
break; break;
case "TokenManagerInfo":
List<dynamic> associatedGroups = jsonDecode(data["Data"]);
int count = int.parse(data["ServerTokenCount"]);
associatedGroups.forEach((identifier) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(int.parse(identifier.toString()))!.antispamTickets = count;
});
EnvironmentConfig.debugLog("update server token count for ${associatedGroups}, $count");
break;
case "NewGroup": case "NewGroup":
String invite = data["GroupInvite"].toString(); String invite = data["GroupInvite"].toString();
if (invite.startsWith("torv3")) { if (invite.startsWith("torv3")) {

View File

@ -762,9 +762,13 @@ class CwtchFfi implements Cwtch {
} }
@override @override
String defaultDownloadPath() { String? defaultDownloadPath() {
Map<String, String> envVars = Platform.environment; Map<String, String> envVars = Platform.environment;
return path.join(envVars[Platform.isWindows ? 'UserProfile' : 'HOME']!, "Downloads"); String nominalPath = path.join(envVars[Platform.isWindows ? 'UserProfile' : 'HOME']!, "Downloads");
if (Directory(nominalPath).existsSync() == false) {
return null;
}
return nominalPath;
} }
@override @override

View File

@ -294,7 +294,7 @@ class CwtchGomobile implements Cwtch {
} }
@override @override
String defaultDownloadPath() { String? defaultDownloadPath() {
return this.androidHomeDirectoryStr; return this.androidHomeDirectoryStr;
} }

View File

@ -618,4 +618,8 @@ class MaterialLocalizationLu extends MaterialLocalizations {
// TODO: implement timeOfDayFormat // TODO: implement timeOfDayFormat
throw UnimplementedError(); throw UnimplementedError();
} }
@override
// TODO: implement menuBarMenuLabel
String get menuBarMenuLabel => throw UnimplementedError();
} }

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "cy", "@@locale": "cy",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Tyrceg \/ Türk", "localeTr": "Tyrceg \/ Türk",
"localeIt": "Eidaleg \/ Italiana", "localeIt": "Eidaleg \/ Italiana",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "da", "@@locale": "da",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Tyrkisk \/ Türk", "localeTr": "Tyrkisk \/ Türk",
"localeIt": "Italiensk \/ Italiano", "localeIt": "Italiensk \/ Italiano",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,8 +1,11 @@
{ {
"@@locale": "de", "@@locale": "de",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"localeTr": "Türkisch \/ Türk", "acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"localeIt": "Italienisch \/ Italiano", "localeIt": "Italienisch \/ Italiano",
"errorDownloadDirectoryDoesNotExist": "Die Dateifreigabe kann nicht aktiviert werden, da der Download-Ordner nicht festgelegt wurde oder auf einen nicht vorhandenen Ordner festgelegt ist.",
"localeTr": "Türkisch \/ Türk",
"viewReplies": "Antworten auf diese Nachricht anzeigen", "viewReplies": "Antworten auf diese Nachricht anzeigen",
"manageSharedFiles": "Freigegebene Dateien verwalten", "manageSharedFiles": "Freigegebene Dateien verwalten",
"tooltipPinConversation": "Konversation oben in \"Konversationen\" anheften", "tooltipPinConversation": "Konversation oben in \"Konversationen\" anheften",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "el", "@@locale": "el",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Τουρκικά \/ Türk", "localeTr": "Τουρκικά \/ Türk",
"localeIt": "Italian \/ Italiano", "localeIt": "Italian \/ Italiano",
"localeCy": "Ουαλικά \/ Cymraeg", "localeCy": "Ουαλικά \/ Cymraeg",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "en", "@@locale": "en",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeIt": "Italian \/ Italiano", "localeIt": "Italian \/ Italiano",
"localeTr": "Turkish \/ Türk", "localeTr": "Turkish \/ Türk",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "es", "@@locale": "es",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turco \/ Türk", "localeTr": "Turco \/ Türk",
"localeIt": "Italiano \/ Italiano", "localeIt": "Italiano \/ Italiano",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,8 +1,11 @@
{ {
"@@locale": "fr", "@@locale": "fr",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Le partage de fichiers ne peut pas être activé car le dossier de téléchargement n'a pas été défini ou est défini sur un dossier qui n'existe pas.",
"localeIt": "italien \/ italien",
"localeTr": "Turc \/ Türk", "localeTr": "Turc \/ Türk",
"localeIt": "Italien \/ Italiano",
"tooltipPinConversation": "Épingler la conversation en haut de «Conversations»", "tooltipPinConversation": "Épingler la conversation en haut de «Conversations»",
"tooltipUnpinConversation": "Détacher la conversation du haut de «Conversations»", "tooltipUnpinConversation": "Détacher la conversation du haut de «Conversations»",
"viewReplies": "Voir les réponses à ce message", "viewReplies": "Voir les réponses à ce message",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "it", "@@locale": "it",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turco \/ Türk", "localeTr": "Turco \/ Türk",
"localeIt": "Italiano \/ Italiano", "localeIt": "Italiano \/ Italiano",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "lb", "@@locale": "lb",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Tierkesch \/ Türk", "localeTr": "Tierkesch \/ Türk",
"localeIt": "Italienesch", "localeIt": "Italienesch",
"localeEn": "Englesch \/ English", "localeEn": "Englesch \/ English",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "no", "@@locale": "no",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Tyrkisk \/ Türk", "localeTr": "Tyrkisk \/ Türk",
"localeIt": "Italiensk", "localeIt": "Italiensk",
"localeEn": "Engelsk \/ English", "localeEn": "Engelsk \/ English",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "pl", "@@locale": "pl",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turecki \/ Türk", "localeTr": "Turecki \/ Türk",
"localeIt": "Włoski \/ Italiano", "localeIt": "Włoski \/ Italiano",
"localeEn": "Angielski \/ English", "localeEn": "Angielski \/ English",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "pt", "@@locale": "pt",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turco \/ Türk", "localeTr": "Turco \/ Türk",
"localeIt": "Italian \/ Italiano", "localeIt": "Italian \/ Italiano",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "ro", "@@locale": "ro",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turcă \/ Türk", "localeTr": "Turcă \/ Türk",
"localeIt": "Italiană", "localeIt": "Italiană",
"localeEn": "Engleză \/ English", "localeEn": "Engleză \/ English",

View File

@ -1,6 +1,9 @@
{ {
"@@locale": "ru", "@@locale": "ru",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Турецкий \/ Türk", "localeTr": "Турецкий \/ Türk",
"localeIt": "Итальянский \/ Italiano", "localeIt": "Итальянский \/ Italiano",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",

View File

@ -1,12 +1,59 @@
{ {
"@@locale": "tr", "@@locale": "tr",
"@@last_modified": "2022-07-30T00:18:33+02:00", "@@last_modified": "2022-09-06T20:59:50+02:00",
"localeTr": "Türk \/ Türk", "acquiredTicketsFromServer": "Antispam Challenge Complete",
"localeIt": "Italian \/ Italiano", "acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "İndirilenler Klasörü ayarlanmadığı veya mevcut olmayan bir klasöre ayarlandığı için dosya paylaşımı etkinleştirilemiyor.",
"radioNoPassword": "Şifrelenmemiş (Parola yok)",
"msgAddToAccept": "Dosyayı kabul etmek için bu hesabı kişilerinize ekleyin.",
"fileSharingSettingsDownloadFolderDescription": "Dosyalar (örn. görsel önizlemeleri etkinleştirildiğinde görsel dosyaları) otomatik olarak indirildiğinde, dosyaların indirileceği varsayılan bir konum gereklidir.",
"torSettingsEnabledCacheDescription": "İndirilmiş Tor uzlaşmasını Cwtch'un bir sonraki açılışında yeniden kullanmak için önbelleğe alın. Bu Tor'un daha hızlıılmasını sağlar. Devre dışı bırakıldığında Cwtch açılırken önbelleğe alınmış verileri siler.",
"notificationContentSimpleEvent": "Yalın Bildiri",
"exportProfileTooltip": "Bu profili şifrelenmiş bir dosyaya yedekle. Şifrelenmiş dosya başka bir Cwtch uygulamasına aktarılabilir.",
"importProfileTooltip": "Başka Cwtch oluşumunda oluşturulmuş bir profili içeri aktarmak için şifrelenmiş Cwtch yedeği kullanın.",
"clickableLinksWarning": "Bu URL'yi açmak Cwtch dışında bir uygulama başlatacak ve metadatanız teşhir olabilir veya Cwtch'un güvenliği tehlikeye girebilir. Yalnızca güvendiğiniz kişilerden gelen URL'leri açın. Devam etmek istediğinize emin misiniz?",
"settingAndroidPowerExemptionDescription": "Opsiyonel: Android'den Cwtch'u optimize edilmiş güç yönetiminden muaf tutmasını isteyin. Bu, daha fazla pil kullanımı pahasına uygulamanın daha stabil çalışmasını sağlayacaktır.",
"localeTr": "Türkçe \/ Türkçe",
"defaultGroupName": "Muhteşem Grup",
"defaultProfileName": "Alice",
"localeDe": "Almanca \/ Deutsch",
"localePl": "Lehçe \/ Polski",
"localeDa": "Danca \/ Dansk",
"acceptGroupInviteLabel": "Daveti kabul etmek istiyor musunuz",
"pendingLabel": "Beklemede",
"chatBtn": "Sohbet",
"yourDisplayName": "Kullanıcı Adınız",
"localeEn": "İngilizce \/ English",
"localeFr": "Fransızca \/ Français",
"localePt": "Portekizce \/ Portuguesa",
"networkStatusAttemptingTor": "Tor ağına bağlanılıyor",
"localeEs": "İspanyolca \/ Español",
"localeIt": "İtalyanca \/ Italiano",
"debugLog": "Konsol hata ayıklama kaydını başlat",
"localeRU": "Rusça \/ Русский",
"serverMetricsLabel": "Sunucu Bilgileri",
"themeNameCwtch": "Cwtch",
"themeNameWitch": "Cadı",
"themeNameVampire": "Vampir",
"themeNameGhost": "Hayalet",
"themeNamePumpkin": "Balkabağı",
"themeNameMermaid": "Denizkızı",
"themeNameMidnight": "Gece",
"themeNameNeon1": "Neon1",
"themeNameNeon2": "Neon2",
"descriptionACNCircuitInfo": "Anonim iletişim ağının (ACN) bu konuşmaya bağlanmak için kullandığı yol hakkında ayrıntılı bilgi.",
"tooltipSelectACustomProfileImage": "Profil Resmi Seçin",
"notificationPolicyMute": "Sessiz",
"localeRo": "Romence \/ Română",
"localeLb": "Lüksemburgca \/ Lëtzebuergesch",
"localeNo": "Norveççe \/ Norsk",
"localeEl": "Yunanca \/ Ελληνικά",
"localeCy": "Galce \/ Cymraeg",
"notificationPolicyOptIn": "Mümkünse Al",
"conversationNotificationPolicyOptIn": "Mümkünse Al",
"tooltipCode": "Kod \/ Monospace", "tooltipCode": "Kod \/ Monospace",
"createGroupTitle": "Grup Oluştur", "createGroupTitle": "Grup Oluştur",
"serverLabel": "Sunucu", "serverLabel": "Sunucu",
"defaultGroupName": "Awesome Group",
"createGroupBtn": "Oluştur", "createGroupBtn": "Oluştur",
"profileOnionLabel": "Bu adresi bağlantı kurmak istediğiniz insanlara gönderin", "profileOnionLabel": "Bu adresi bağlantı kurmak istediğiniz insanlara gönderin",
"addPeerTab": "Kişi ekle", "addPeerTab": "Kişi ekle",
@ -46,15 +93,12 @@
"dmTooltip": "DM için tıklayın", "dmTooltip": "DM için tıklayın",
"couldNotSendMsgError": "Mesaj gönderilemedi", "couldNotSendMsgError": "Mesaj gönderilemedi",
"acknowledgedLabel": "Onaylandı", "acknowledgedLabel": "Onaylandı",
"pendingLabel": "Bekliyor",
"peerBlockedMessage": "Kişi engelli", "peerBlockedMessage": "Kişi engelli",
"peerOfflineMessage": "Kişi çevrimdışı, mesajlar şu anda iletilemiyor", "peerOfflineMessage": "Kişi çevrimdışı, mesajlar şu anda iletilemiyor",
"copyBtn": "Kopyala", "copyBtn": "Kopyala",
"newGroupBtn": "Yeni grup oluştur", "newGroupBtn": "Yeni grup oluştur",
"acceptGroupInviteLabel": "Daveti kabul etmek istiyor musunuz",
"acceptGroupBtn": "Kabul Et", "acceptGroupBtn": "Kabul Et",
"rejectGroupBtn": "Reddet", "rejectGroupBtn": "Reddet",
"chatBtn": "Sohbet",
"listsBtn": "Listeler", "listsBtn": "Listeler",
"bulletinsBtn": "Bültenler", "bulletinsBtn": "Bültenler",
"puzzleGameBtn": "Yapboz", "puzzleGameBtn": "Yapboz",
@ -69,12 +113,9 @@
"addProfileTitle": "Yeni profil ekle", "addProfileTitle": "Yeni profil ekle",
"editProfileTitle": "Profili Düzenle", "editProfileTitle": "Profili Düzenle",
"profileName": "Kullanıcı adı", "profileName": "Kullanıcı adı",
"defaultProfileName": "Alice",
"newProfile": "Yeni Profil", "newProfile": "Yeni Profil",
"editProfile": "Profili Düzenle", "editProfile": "Profili Düzenle",
"radioNoPassword": "Şifrelenmemiş (parola yok)",
"noPasswordWarning": "Bu hesapta parola kullanılmaması yerel olarak depolanan verilerin şifrelenmeyeceği anlamına gelir", "noPasswordWarning": "Bu hesapta parola kullanılmaması yerel olarak depolanan verilerin şifrelenmeyeceği anlamına gelir",
"yourDisplayName": "Kullanıcı Adınız",
"currentPasswordLabel": "Mevcut Parola", "currentPasswordLabel": "Mevcut Parola",
"password2Label": "Parolayı yeniden gir", "password2Label": "Parolayı yeniden gir",
"passwordErrorEmpty": "Parola boş bırakılamaz", "passwordErrorEmpty": "Parola boş bırakılamaz",
@ -97,7 +138,6 @@
"zoomLabel": "Arayüz yakınlaştırma (aslen metin ve buton boyutlarını etkiler)", "zoomLabel": "Arayüz yakınlaştırma (aslen metin ve buton boyutlarını etkiler)",
"blockUnknownLabel": "Tanınmayan Kişileri Engelle", "blockUnknownLabel": "Tanınmayan Kişileri Engelle",
"settingLanguage": "Dil", "settingLanguage": "Dil",
"localeDe": "German \/ Deutsch",
"settingInterfaceZoom": "Yakınlaştırma seviyesi", "settingInterfaceZoom": "Yakınlaştırma seviyesi",
"largeTextLabel": "Büyük", "largeTextLabel": "Büyük",
"settingTheme": "Açık Tema Kullan", "settingTheme": "Açık Tema Kullan",
@ -112,7 +152,6 @@
"loadingTor": "Tor yükleniyor...", "loadingTor": "Tor yükleniyor...",
"viewGroupMembershipTooltip": "Grup Üyeliğini Görüntüle", "viewGroupMembershipTooltip": "Grup Üyeliğini Görüntüle",
"networkStatusDisconnected": "İnternet bağlantısı kesildi, bağlantınızı kontrol edin", "networkStatusDisconnected": "İnternet bağlantısı kesildi, bağlantınızı kontrol edin",
"networkStatusAttemptingTor": "Tor ağına bağlanıyor",
"networkStatusConnecting": "Ağa ve kişilere bağlanıyor...", "networkStatusConnecting": "Ağa ve kişilere bağlanıyor...",
"networkStatusOnline": "Çevrimiçi", "networkStatusOnline": "Çevrimiçi",
"newConnectionPaneTitle": "Yeni Bağlantı", "newConnectionPaneTitle": "Yeni Bağlantı",
@ -155,7 +194,6 @@
"createProfileToBegin": "Başlamak için bir profil oluşturun veya kilidini açın", "createProfileToBegin": "Başlamak için bir profil oluşturun veya kilidini açın",
"addContactFirst": "Sohbete başlamak için bir kişi ekleyin veya seçin.", "addContactFirst": "Sohbete başlamak için bir kişi ekleyin veya seçin.",
"torNetworkStatus": "Tor ağ durumu", "torNetworkStatus": "Tor ağ durumu",
"debugLog": "Konsol hata ayıklama kayıtlarını aç",
"profileDeleteSuccess": "Profil başarıyla silindi", "profileDeleteSuccess": "Profil başarıyla silindi",
"malformedMessage": "Hatalı biçimlendirilmiş mesaj", "malformedMessage": "Hatalı biçimlendirilmiş mesaj",
"shutdownCwtchTooltip": "Cwtch'u Kapat", "shutdownCwtchTooltip": "Cwtch'u Kapat",
@ -175,7 +213,6 @@
"tooltipRejectContactRequest": "Bağlantı isteğini reddet", "tooltipRejectContactRequest": "Bağlantı isteğini reddet",
"tooltipReplyToThisMessage": "Bu mesajı yanıtla", "tooltipReplyToThisMessage": "Bu mesajı yanıtla",
"tooltipRemoveThisQuotedMessage": "Alıntılanan mesajı kaldır.", "tooltipRemoveThisQuotedMessage": "Alıntılanan mesajı kaldır.",
"localePl": "Polish \/ Polski",
"settingUIColumnPortrait": "UI Sütunları Portre Modu", "settingUIColumnPortrait": "UI Sütunları Portre Modu",
"settingUIColumnLandscape": "UI Sütunları Yatay Modu", "settingUIColumnLandscape": "UI Sütunları Yatay Modu",
"settingUIColumnSingle": "Tek", "settingUIColumnSingle": "Tek",
@ -243,7 +280,6 @@
"fieldDescriptionLabel": "Açıklama", "fieldDescriptionLabel": "Açıklama",
"manageKnownServersButton": "Bilinen Sunucuları Yönet", "manageKnownServersButton": "Bilinen Sunucuları Yönet",
"displayNameTooltip": "Lütfen bir ad girin", "displayNameTooltip": "Lütfen bir ad girin",
"serverMetricsLabel": "Sunucu Bilgileri",
"manageKnownServersLong": "Bilinen Sunucuları Yönet", "manageKnownServersLong": "Bilinen Sunucuları Yönet",
"manageKnownServersShort": "Sunucular", "manageKnownServersShort": "Sunucular",
"serverTotalMessagesLabel": "Toplam Mesaj", "serverTotalMessagesLabel": "Toplam Mesaj",
@ -258,7 +294,6 @@
"msgFileTooBig": "Dosya boyutu 10 GB'ı geçemez", "msgFileTooBig": "Dosya boyutu 10 GB'ı geçemez",
"msgConfirmSend": "Göndermek istediğinize emin misiniz", "msgConfirmSend": "Göndermek istediğinize emin misiniz",
"btnSendFile": "Dosya Gönder", "btnSendFile": "Dosya Gönder",
"msgAddToAccept": "Bu dosyayı kabul etmek için hesabı kişilerinize ekleyin.",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Varsayılan tor konfigürasyonunu geçersiz kıl. Uyarı: Bu tehlikeli olabilir. Yalnızca ne yaptığınızı biliyorsanız açın.", "torSettingsUseCustomTorServiceConfigurastionDescription": "Varsayılan tor konfigürasyonunu geçersiz kıl. Uyarı: Bu tehlikeli olabilir. Yalnızca ne yaptığınızı biliyorsanız açın.",
"torSettingsEnabledAdvanced": "Gelişmiş Tor Konfigürasyonunu Etkinleştir", "torSettingsEnabledAdvanced": "Gelişmiş Tor Konfigürasyonunu Etkinleştir",
"torSettingsUseCustomTorServiceConfiguration": "Özel Tor Hizmeti Konfigürasyonunu (torrc) Kullan", "torSettingsUseCustomTorServiceConfiguration": "Özel Tor Hizmeti Konfigürasyonunu (torrc) Kullan",
@ -269,18 +304,11 @@
"torSettingsCustomControlPort": "Özel Kontrol Portu", "torSettingsCustomControlPort": "Özel Kontrol Portu",
"torSettingsErrorSettingPort": "Port Numarası 1 ile 65535 arasında olmalıdır", "torSettingsErrorSettingPort": "Port Numarası 1 ile 65535 arasında olmalıdır",
"settingImagePreviews": "Görsel Önizlemeleri ve Profil Resimleri", "settingImagePreviews": "Görsel Önizlemeleri ve Profil Resimleri",
"fileSharingSettingsDownloadFolderDescription": "Dosyalar otomatik olarak indirildiğinde (örneğin görsel önizlemeleri etkinleştirildiğinde görsel dosyaları), dosyaların indirileceği varsayılan bir konum gereklidir.",
"fileSharingSettingsDownloadFolderTooltip": "İndirilen dosyalara farklı bir varsayılan klasör seçmek için gözat.", "fileSharingSettingsDownloadFolderTooltip": "İndirilen dosyalara farklı bir varsayılan klasör seçmek için gözat.",
"descriptionACNCircuitInfo": "Anonim iletişim ağının (ACN) bu konuşmaya bağlanmak için kullandığı yol hakkında ayrıntılı bilgi.",
"labelACNCircuitInfo": "ACN Ağ Bilgisi", "labelACNCircuitInfo": "ACN Ağ Bilgisi",
"labelTorNetwork": "Tor Ağı", "labelTorNetwork": "Tor Ağı",
"torSettingsEnabledCacheDescription": "Cwtch'un bir sonraki açılışında yeniden kullanmak için indirilmiş Tor uzlaşmasını önbelleğe alın. Bu, Tor'un daha hızlı başlamasını sağlar. Devre dışı bırakıldığında, Cwtch açılırken önbelleğe alınmış verileri temizler.",
"torSettingsEnableCache": "Tor Uzlaşmasını Önbelleğe Al", "torSettingsEnableCache": "Tor Uzlaşmasını Önbelleğe Al",
"notificationPolicyMute": "Sessiz",
"tooltipSelectACustomProfileImage": "Profil Resmi Seçin",
"notificationPolicyOptIn": "Mümkünse Al",
"notificationPolicyDefaultAll": "Tümü Varsayılan", "notificationPolicyDefaultAll": "Tümü Varsayılan",
"conversationNotificationPolicyOptIn": "Mümkünse Al",
"conversationNotificationPolicyDefault": "Varsayılan", "conversationNotificationPolicyDefault": "Varsayılan",
"conversationNotificationPolicyNever": "Asla", "conversationNotificationPolicyNever": "Asla",
"notificationPolicySettingLabel": "Bildirim İlkeleri", "notificationPolicySettingLabel": "Bildirim İlkeleri",
@ -296,25 +324,19 @@
"settingGroupBehaviour": "Davranış", "settingGroupBehaviour": "Davranış",
"settingsGroupAppearance": "Görünüş", "settingsGroupAppearance": "Görünüş",
"settingsGroupExperiments": "Deneyler", "settingsGroupExperiments": "Deneyler",
"notificationContentSimpleEvent": "Yalın Olay",
"newMessageNotificationSimple": "Yeni Mesaj", "newMessageNotificationSimple": "Yeni Mesaj",
"localeDa": "Danish \/ Dansk",
"exportProfile": "Profili Dışa Aktar", "exportProfile": "Profili Dışa Aktar",
"exportProfileTooltip": "Bu profili şifrelenmiş bir dosyaya yedekleyin. Şifrelenmiş dosya başka bir Cwtch uygulamasına aktarılabilir.",
"importProfile": "Profili İçe Aktar", "importProfile": "Profili İçe Aktar",
"importProfileTooltip": "Başka bir Cwtch oluşumunda oluşturulmuş bir profili içeri aktarmak için şifrelenmiş bir Cwtch yedeği kullanın.",
"clickableLinkError": "URL açılmaya çalışılırken hata oluştu", "clickableLinkError": "URL açılmaya çalışılırken hata oluştu",
"failedToImportProfile": "Profil İçe Aktarılırken Hata Oluştu", "failedToImportProfile": "Profil İçe Aktarılırken Hata Oluştu",
"successfullyImportedProfile": "Profil Başarıyla İçe Aktarıldı: %profile", "successfullyImportedProfile": "Profil Başarıyla İçe Aktarıldı: %profile",
"shuttingDownApp": "Kapanıyor...", "shuttingDownApp": "Kapanıyor...",
"clickableLinksWarning": "Bu URL'yi açmak Cwtch dışında bir uygulama başlatacak ve metadata teşhir edebilir veya Cwtch'un güvenliğini tehlikeye atabilir. Yalnızca güvendiğiniz kişilerden gelen URL'leri açın. Devam etmek istediğinize emin misiniz?",
"clickableLinkOpen": "URL'yi aç", "clickableLinkOpen": "URL'yi aç",
"clickableLinksCopy": "URL'yi kopyala", "clickableLinksCopy": "URL'yi kopyala",
"formattingExperiment": "Mesaj Biçimlendirme", "formattingExperiment": "Mesaj Biçimlendirme",
"messageFormattingDescription": "Görüntülenen mesajlarda zengin metin biçimlendirmesini etkinleştirin, örneğin **kalın** ve *italik*", "messageFormattingDescription": "Görüntülenen mesajlarda zengin metin biçimlendirmesini etkinleştirin, örneğin **kalın** ve *italik*",
"thisFeatureRequiresGroupExpermientsToBeEnabled": "Bu özellik Gruplar Özelliği'nin Ayarlar'dan etkinleştirilmesini gerektirir", "thisFeatureRequiresGroupExpermientsToBeEnabled": "Bu özellik Gruplar Özelliği'nin Ayarlar'dan etkinleştirilmesini gerektirir",
"settingAndroidPowerExemption": "Android Pil Optimizasyonlarını Yoksay", "settingAndroidPowerExemption": "Android Pil Optimizasyonlarını Yoksay",
"settingAndroidPowerExemptionDescription": "Opsiyonel: Android'den Cwtch'u optimize edilmiş güç yönetiminden muaf tutmasını isteyin. Bu daha fazla pil kullanımı pahasına uygulamanın daha stabil çalışmasını sağlayacaktır.",
"settingsAndroidPowerReenablePopup": "Cwtch içinden Pil Optimizasyonu yeniden etkinleştirilemiyor. Lütfen Android \/ Ayarlar \/ Uygulamalar \/ Cwtch \/ Pil sayfasına gidin ve 'Pil Kullanımını Yönet' bölümünün altında 'Optimize edilmiş'e basın", "settingsAndroidPowerReenablePopup": "Cwtch içinden Pil Optimizasyonu yeniden etkinleştirilemiyor. Lütfen Android \/ Ayarlar \/ Uygulamalar \/ Cwtch \/ Pil sayfasına gidin ve 'Pil Kullanımını Yönet' bölümünün altında 'Optimize edilmiş'e basın",
"okButton": "OK", "okButton": "OK",
"tooltipBoldText": "Kalın", "tooltipBoldText": "Kalın",
@ -331,24 +353,5 @@
"headingReplies": "Yanıtlar", "headingReplies": "Yanıtlar",
"messageNoReplies": "Bu mesaja yanıt gelmemiş.", "messageNoReplies": "Bu mesaja yanıt gelmemiş.",
"fileDownloadUnavailable": "Bu dosya indirmeye uygun görünmüyor. Gönderen dosyanın indirilmesini engellemiş olabilir.", "fileDownloadUnavailable": "Bu dosya indirmeye uygun görünmüyor. Gönderen dosyanın indirilmesini engellemiş olabilir.",
"replyingTo": "%1 hesabına yanıt veriliyor", "replyingTo": "%1 hesabına yanıt veriliyor"
"localeCy": "Welsh \/ Cymraeg",
"localeEl": "Greek \/ Ελληνικά",
"localeNo": "Norwegian \/ Norsk",
"localeLb": "Luxembourgish \/ Lëtzebuergesch",
"localeRo": "Romanian \/ Română",
"themeNameNeon2": "Neon2",
"themeNameNeon1": "Neon1",
"themeNameMidnight": "Midnight",
"themeNameMermaid": "Mermaid",
"themeNamePumpkin": "Pumpkin",
"themeNameGhost": "Ghost",
"themeNameVampire": "Vampire",
"themeNameWitch": "Witch",
"themeNameCwtch": "Cwtch",
"localeRU": "Russian \/ Русский",
"localeEs": "Spanish \/ Español",
"localePt": "Portuguese \/ Portuguesa",
"localeFr": "French \/ Français",
"localeEn": "English \/ English"
} }

View File

@ -56,6 +56,7 @@ class ContactInfoState extends ChangeNotifier {
late bool _archived; late bool _archived;
late bool _pinned; late bool _pinned;
int _antispamTickets = 0;
String? _acnCircuit; String? _acnCircuit;
String? _messageDraft; String? _messageDraft;
@ -100,8 +101,17 @@ class ContactInfoState extends ChangeNotifier {
String? get acnCircuit => this._acnCircuit; String? get acnCircuit => this._acnCircuit;
String? get messageDraft => this._messageDraft; String? get messageDraft => this._messageDraft;
set antispamTickets(int antispamTickets) {
this._antispamTickets = antispamTickets;
notifyListeners();
}
int get antispamTickets => this._antispamTickets;
set acnCircuit(String? acnCircuit) { set acnCircuit(String? acnCircuit) {
this._acnCircuit = acnCircuit; this._acnCircuit = acnCircuit;
notifyListeners(); notifyListeners();

View File

@ -174,7 +174,6 @@ ThemeData mkThemeData(Settings opaque) {
? opaque.current().defaultButtonDisabledColor ? opaque.current().defaultButtonDisabledColor
: null), : null),
enableFeedback: true, enableFeedback: true,
splashFactory: InkRipple.splashFactory,
padding: MaterialStateProperty.all(EdgeInsets.all(20)), padding: MaterialStateProperty.all(EdgeInsets.all(20)),
shape: MaterialStateProperty.all(RoundedRectangleBorder( shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6.0), borderRadius: BorderRadius.circular(6.0),

View File

@ -40,7 +40,6 @@ class _AddContactViewState extends State<AddContactView> {
String lastContactValue = ""; String lastContactValue = "";
bool failedImport = false; bool failedImport = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// if we haven't picked a server yet, pick the first one in the list... // if we haven't picked a server yet, pick the first one in the list...
@ -162,24 +161,19 @@ class _AddContactViewState extends State<AddContactView> {
onChanged: (String importBundle) async { onChanged: (String importBundle) async {
if (lastContactValue != importBundle) { if (lastContactValue != importBundle) {
lastContactValue = importBundle; lastContactValue = importBundle;
var profileOnion = Provider var profileOnion = Provider.of<ProfileInfoState>(bcontext, listen: false).onion;
.of<ProfileInfoState>(bcontext, listen: false) Provider.of<FlwtchState>(bcontext, listen: false).cwtch.ImportBundle(profileOnion, importBundle.replaceFirst("cwtch:", "")).then((result) {
.onion; if (result == "importBundle.success") {
Provider failedImport = false;
.of<FlwtchState>(bcontext, listen: false) if (AppLocalizations.of(bcontext) != null) {
.cwtch final snackBar = SnackBar(content: Text(AppLocalizations.of(bcontext)!.successfullAddedContact + importBundle));
.ImportBundle(profileOnion, importBundle.replaceFirst("cwtch:", "")).then((result) { ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
if (result == "importBundle.success") { Navigator.popUntil(bcontext, (route) => route.settings.name == "conversations");
failedImport = false; }
if (AppLocalizations.of(bcontext) != null) { } else {
final snackBar = SnackBar(content: Text(AppLocalizations.of(bcontext)!.successfullAddedContact + importBundle)); failedImport = true;
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar); }
Navigator.popUntil(bcontext, (route) => route.settings.name == "conversations"); });
}
} else {
failedImport = true;
}
});
} }
}, },
hintText: '', hintText: '',

View File

@ -31,6 +31,9 @@ class ContactsView extends StatefulWidget {
// selectConversation can be called from anywhere to set the active conversation // selectConversation can be called from anywhere to set the active conversation
void selectConversation(BuildContext context, int handle) { void selectConversation(BuildContext context, int handle) {
if (handle == Provider.of<AppState>(context, listen: false).selectedConversation) {
return;
}
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts // requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
var unread = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages; var unread = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation; var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation;
@ -122,8 +125,7 @@ class _ContactsViewState extends State<ContactsView> {
}), }),
) )
]), ]),
title: RepaintBoundary( title: Row(children: [
child: Row(children: [
ProfileImage( ProfileImage(
imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment) imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
? Provider.of<ProfileInfoState>(context).imagePath ? Provider.of<ProfileInfoState>(context).imagePath
@ -141,7 +143,7 @@ class _ContactsViewState extends State<ContactsView> {
Expanded( Expanded(
child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts), child: Text("%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts),
overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor))), overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor))),
])), ]),
actions: getActions(context), actions: getActions(context),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@ -215,7 +217,7 @@ class _ContactsViewState extends State<ContactsView> {
ChangeNotifierProvider.value(value: contact), ChangeNotifierProvider.value(value: contact),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).serverList), ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).serverList),
], ],
builder: (context, child) => RepaintBoundary(child: ContactRow()), builder: (context, child) => ContactRow(),
); );
}); });
@ -240,7 +242,7 @@ class _ContactsViewState extends State<ContactsView> {
}, },
); );
return RepaintBoundary(child: contactList); return contactList;
} }
void _pushAddContact(bool newGroup) { void _pushAddContact(bool newGroup) {

View File

@ -393,7 +393,12 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
value: settings.isExperimentEnabled(FileSharingExperiment), value: settings.isExperimentEnabled(FileSharingExperiment),
onChanged: (bool value) { onChanged: (bool value) {
if (value) { if (value) {
settings.enableExperiment(FileSharingExperiment); if (checkDownloadDirectory(context, settings)) {
settings.enableExperiment(FileSharingExperiment);
} else {
settings.disableExperiment(FileSharingExperiment);
settings.disableExperiment(ImagePreviewsExperiment);
}
} else { } else {
settings.disableExperiment(FileSharingExperiment); settings.disableExperiment(FileSharingExperiment);
settings.disableExperiment(ImagePreviewsExperiment); settings.disableExperiment(ImagePreviewsExperiment);
@ -413,8 +418,11 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
value: settings.isExperimentEnabled(ImagePreviewsExperiment), value: settings.isExperimentEnabled(ImagePreviewsExperiment),
onChanged: (bool value) { onChanged: (bool value) {
if (value) { if (value) {
settings.enableExperiment(ImagePreviewsExperiment); if (checkDownloadDirectory(context, settings)) {
settings.downloadPath = Provider.of<FlwtchState>(context, listen: false).cwtch.defaultDownloadPath(); settings.enableExperiment(ImagePreviewsExperiment);
} else {
settings.disableExperiment(ImagePreviewsExperiment);
}
} else { } else {
settings.disableExperiment(ImagePreviewsExperiment); settings.disableExperiment(ImagePreviewsExperiment);
} }
@ -539,6 +547,30 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
} }
} }
bool checkDownloadDirectory(context, settings) {
bool showError = false;
if (settings.downloadPath != "") {
} else {
// check if the default download path exists
var path = Provider.of<FlwtchState>(context, listen: false).cwtch.defaultDownloadPath();
if (path != null) {
settings.downloadPath = path;
} else {
showError = true;
}
}
if (!showError && Directory(settings.downloadPath).existsSync()) {
return true;
} else {
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)!.errorDownloadDirectoryDoesNotExist),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return false;
}
}
/// Construct a version string from Package Info /// Construct a version string from Package Info
String constructVersionString(PackageInfo pinfo) { String constructVersionString(PackageInfo pinfo) {
if (pinfo == null) { if (pinfo == null) {

View File

@ -169,14 +169,36 @@ class _MessageViewState extends State<MessageView> {
: null, : null,
title: Row(children: [ title: Row(children: [
ProfileImage( ProfileImage(
imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment) imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
? Provider.of<ContactInfoState>(context).imagePath ? Provider.of<ContactInfoState>(context).imagePath
: Provider.of<ContactInfoState>(context).defaultImagePath, : Provider.of<ContactInfoState>(context).defaultImagePath,
diameter: 42, diameter: 42,
border: Provider.of<Settings>(context).current().portraitOnlineBorderColor, border: Provider.of<Settings>(context).current().portraitOnlineBorderColor,
badgeTextColor: Colors.red, badgeTextColor: Colors.red,
badgeColor: Colors.red, badgeColor: Provider.of<Settings>(context).theme.portraitContactBadgeColor,
), badgeIcon: Provider.of<ContactInfoState>(context).isGroup
? (Tooltip(
message: Provider.of<ContactInfoState>(context).isOnline() ? Provider.of<ContactInfoState>(context).antispamTickets == 0
? AppLocalizations.of(context)!.acquiringTicketsFromServer
: AppLocalizations.of(context)!.acquiredTicketsFromServer
: AppLocalizations.of(context)!.serverConnectivityDisconnected,
child: Provider.of<ContactInfoState>(context).isOnline() ? Provider.of<ContactInfoState>(context).antispamTickets == 0
? Icon(
Icons.schedule_send,
size: 10.0,
semanticLabel: AppLocalizations.of(context)!.acquiringTicketsFromServer,
color: Provider.of<Settings>(context).theme.portraitContactBadgeTextColor,
)
: Icon(
Icons.send,
color: Provider.of<Settings>(context).theme.portraitContactBadgeTextColor,
size: 10.0,
) : Icon(
CwtchIcons.onion_off,
color: Provider.of<Settings>(context).theme.portraitContactBadgeTextColor,
size: 10.0,
)))
: null),
SizedBox( SizedBox(
width: 10, width: 10,
), ),
@ -329,6 +351,12 @@ class _MessageViewState extends State<MessageView> {
} }
void _sendMessageHandler(dynamic messageJson) { void _sendMessageHandler(dynamic messageJson) {
if (Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0) {
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.acquiringTicketsFromServer));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
return;
}
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion; var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier; var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
var profile = Provider.of<ProfileInfoState>(context, listen: false); var profile = Provider.of<ProfileInfoState>(context, listen: false);
@ -561,8 +589,14 @@ class _MessageViewState extends State<MessageView> {
suffixIcon: ElevatedButton( suffixIcon: ElevatedButton(
key: Key("btnSend"), key: Key("btnSend"),
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))), style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))),
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor), child: Tooltip(
onPressed: isOffline ? null : _sendMessage, message: isOffline
? AppLocalizations.of(context)!.serverNotSynced
: Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0
? AppLocalizations.of(context)!.acquiringTicketsFromServer
: AppLocalizations.of(context)!.sendMessage,
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor)),
onPressed: isOffline || Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0 ? null : _sendMessage,
))), ))),
))); )));

View File

@ -340,7 +340,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
(ProfileInfoState profile) { (ProfileInfoState profile) {
return ChangeNotifierProvider<ProfileInfoState>.value( return ChangeNotifierProvider<ProfileInfoState>.value(
value: profile, value: profile,
builder: (context, child) => RepaintBoundary(child: ProfileRow()), builder: (context, child) => ProfileRow(),
); );
}, },
); );

View File

@ -50,7 +50,7 @@ class _ProfileServersView extends State<ProfileServersView> {
(RemoteServerInfoState server) { (RemoteServerInfoState server) {
return ChangeNotifierProvider<RemoteServerInfoState>.value( return ChangeNotifierProvider<RemoteServerInfoState>.value(
value: server, value: server,
builder: (context, child) => RepaintBoundary(child: RemoteServerRow()), builder: (context, child) => RemoteServerRow(),
); );
}, },
); );

View File

@ -37,12 +37,11 @@ class _ContactRowState extends State<ContactRow> {
)); ));
} }
return Card( return InkWell(
clipBehavior: Clip.antiAlias, enableFeedback: true,
color: Provider.of<AppState>(context).selectedConversation == contact.identifier ? Provider.of<Settings>(context).theme.backgroundHilightElementColor : null, splashFactory: InkSplash.splashFactory,
borderOnForeground: true, child: Ink(
margin: EdgeInsets.all(0.0), color: Provider.of<AppState>(context).selectedConversation == contact.identifier ? Provider.of<Settings>(context).theme.backgroundHilightElementColor : Colors.transparent,
child: InkWell(
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Padding( Padding(
padding: const EdgeInsets.all(6.0), //border size padding: const EdgeInsets.all(6.0), //border size
@ -144,16 +143,20 @@ class _ContactRowState extends State<ContactRow> {
Provider.of<ContactListState>(context, listen: false).resort(); Provider.of<ContactListState>(context, listen: false).resort();
}, },
)) ))
]), ])),
onTap: () { onTap: () {
selectConversation(context, contact.identifier); setState(() {
}, selectConversation(context, contact.identifier);
onHover: (onHover) { });
setState(() { },
isHover = onHover; onHover: (hover) {
}); if (isHover != hover) {
}, setState(() {
)); isHover = hover;
});
}
},
);
} }
void _btnApprove() { void _btnApprove() {

View File

@ -17,7 +17,8 @@ class ProfileImage extends StatefulWidget {
required this.badgeTextColor, required this.badgeTextColor,
this.maskOut = false, this.maskOut = false,
this.tooltip = "", this.tooltip = "",
this.badgeEdit = false}); this.badgeEdit = false,
this.badgeIcon = null});
final double diameter; final double diameter;
final String imagePath; final String imagePath;
final Color border; final Color border;
@ -27,6 +28,7 @@ class ProfileImage extends StatefulWidget {
final bool maskOut; final bool maskOut;
final bool badgeEdit; final bool badgeEdit;
final String tooltip; final String tooltip;
final Widget? badgeIcon;
@override @override
_ProfileImageState createState() => _ProfileImageState(); _ProfileImageState createState() => _ProfileImageState();
@ -81,7 +83,7 @@ class _ProfileImageState extends State<ProfileImage> {
padding: const EdgeInsets.all(2.0), //border size padding: const EdgeInsets.all(2.0), //border size
child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))), child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))),
Visibility( Visibility(
visible: widget.badgeEdit || widget.badgeCount > 0, visible: widget.badgeIcon != null || widget.badgeEdit || widget.badgeCount > 0,
child: Positioned( child: Positioned(
bottom: 0.0, bottom: 0.0,
right: 0.0, right: 0.0,
@ -93,7 +95,7 @@ class _ProfileImageState extends State<ProfileImage> {
Icons.edit, Icons.edit,
color: widget.badgeTextColor, color: widget.badgeTextColor,
) )
: Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0)), : (widget.badgeIcon != null ? widget.badgeIcon : Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0))),
), ),
)), )),
])); ]));