diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index 128ddda6..49c25403 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -121,7 +121,7 @@ abstract class Cwtch { Future Shutdown(); // non-ffi - String defaultDownloadPath(); + String? defaultDownloadPath(); bool isL10nInit(); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 66abce25..b2d4dea6 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -309,6 +309,14 @@ class CwtchNotifier { case "UpdateServerInfo": profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]); break; + case "TokenManagerInfo": + List 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": String invite = data["GroupInvite"].toString(); if (invite.startsWith("torv3")) { diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 0ebcb460..408a1c86 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -762,9 +762,13 @@ class CwtchFfi implements Cwtch { } @override - String defaultDownloadPath() { + String? defaultDownloadPath() { Map 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 diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 16fd0f99..e1b650ea 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -294,7 +294,7 @@ class CwtchGomobile implements Cwtch { } @override - String defaultDownloadPath() { + String? defaultDownloadPath() { return this.androidHomeDirectoryStr; } diff --git a/lib/l10n/custom_material_delegate.dart b/lib/l10n/custom_material_delegate.dart index cd91a907..d5470c54 100644 --- a/lib/l10n/custom_material_delegate.dart +++ b/lib/l10n/custom_material_delegate.dart @@ -618,4 +618,8 @@ class MaterialLocalizationLu extends MaterialLocalizations { // TODO: implement timeOfDayFormat throw UnimplementedError(); } + + @override + // TODO: implement menuBarMenuLabel + String get menuBarMenuLabel => throw UnimplementedError(); } diff --git a/lib/l10n/intl_cy.arb b/lib/l10n/intl_cy.arb index b1baa8a7..408e763a 100644 --- a/lib/l10n/intl_cy.arb +++ b/lib/l10n/intl_cy.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Eidaleg \/ Italiana", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_da.arb b/lib/l10n/intl_da.arb index bcdfbbde..b5616229 100644 --- a/lib/l10n/intl_da.arb +++ b/lib/l10n/intl_da.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italiensk \/ Italiano", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index 2506749b..feae21c1 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1,8 +1,11 @@ { "@@locale": "de", - "@@last_modified": "2022-07-30T00:18:33+02:00", - "localeTr": "Türkisch \/ Türk", + "@@last_modified": "2022-09-06T20:59:50+02:00", + "acquiredTicketsFromServer": "Antispam Challenge Complete", + "acquiringTicketsFromServer": "Performing Antispam Challenge", "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", "manageSharedFiles": "Freigegebene Dateien verwalten", "tooltipPinConversation": "Konversation oben in \"Konversationen\" anheften", diff --git a/lib/l10n/intl_el.arb b/lib/l10n/intl_el.arb index 7edab56f..53408aa8 100644 --- a/lib/l10n/intl_el.arb +++ b/lib/l10n/intl_el.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italian \/ Italiano", "localeCy": "Ουαλικά \/ Cymraeg", diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ad766127..c1a8ec16 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,6 +1,9 @@ { "@@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", "localeTr": "Turkish \/ Türk", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 31989200..f339e028 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italiano \/ Italiano", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 20a2420a..c4fb46c5 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1,8 +1,11 @@ { "@@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", - "localeIt": "Italien \/ Italiano", "tooltipPinConversation": "Épingler la conversation en haut de «Conversations»", "tooltipUnpinConversation": "Détacher la conversation du haut de «Conversations»", "viewReplies": "Voir les réponses à ce message", diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 8eacd0e6..cf46d65d 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italiano \/ Italiano", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_lb.arb b/lib/l10n/intl_lb.arb index 31c870e4..c19b3f00 100644 --- a/lib/l10n/intl_lb.arb +++ b/lib/l10n/intl_lb.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italienesch", "localeEn": "Englesch \/ English", diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index 534500f9..a7d1fc6d 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italiensk", "localeEn": "Engelsk \/ English", diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 26d251e5..9121bcb9 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Włoski \/ Italiano", "localeEn": "Angielski \/ English", diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index 9a7824d7..d0273e4e 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italian \/ Italiano", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_ro.arb b/lib/l10n/intl_ro.arb index 5de297d2..fc1aa6de 100644 --- a/lib/l10n/intl_ro.arb +++ b/lib/l10n/intl_ro.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Italiană", "localeEn": "Engleză \/ English", diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 097720e3..baf424ad 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -1,6 +1,9 @@ { "@@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", "localeIt": "Итальянский \/ Italiano", "tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"", diff --git a/lib/l10n/intl_tr.arb b/lib/l10n/intl_tr.arb index 7314735e..e13e0ef0 100644 --- a/lib/l10n/intl_tr.arb +++ b/lib/l10n/intl_tr.arb @@ -1,12 +1,59 @@ { "@@locale": "tr", - "@@last_modified": "2022-07-30T00:18:33+02:00", - "localeTr": "Türk \/ Türk", - "localeIt": "Italian \/ Italiano", + "@@last_modified": "2022-09-06T20:59:50+02:00", + "acquiredTicketsFromServer": "Antispam Challenge Complete", + "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ı açı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", "createGroupTitle": "Grup Oluştur", "serverLabel": "Sunucu", - "defaultGroupName": "Awesome Group", "createGroupBtn": "Oluştur", "profileOnionLabel": "Bu adresi bağlantı kurmak istediğiniz insanlara gönderin", "addPeerTab": "Kişi ekle", @@ -46,15 +93,12 @@ "dmTooltip": "DM için tıklayın", "couldNotSendMsgError": "Mesaj gönderilemedi", "acknowledgedLabel": "Onaylandı", - "pendingLabel": "Bekliyor", "peerBlockedMessage": "Kişi engelli", "peerOfflineMessage": "Kişi çevrimdışı, mesajlar şu anda iletilemiyor", "copyBtn": "Kopyala", "newGroupBtn": "Yeni grup oluştur", - "acceptGroupInviteLabel": "Daveti kabul etmek istiyor musunuz", "acceptGroupBtn": "Kabul Et", "rejectGroupBtn": "Reddet", - "chatBtn": "Sohbet", "listsBtn": "Listeler", "bulletinsBtn": "Bültenler", "puzzleGameBtn": "Yapboz", @@ -69,12 +113,9 @@ "addProfileTitle": "Yeni profil ekle", "editProfileTitle": "Profili Düzenle", "profileName": "Kullanıcı adı", - "defaultProfileName": "Alice", "newProfile": "Yeni Profil", "editProfile": "Profili Düzenle", - "radioNoPassword": "Şifrelenmemiş (parola yok)", "noPasswordWarning": "Bu hesapta parola kullanılmaması yerel olarak depolanan verilerin şifrelenmeyeceği anlamına gelir", - "yourDisplayName": "Kullanıcı Adınız", "currentPasswordLabel": "Mevcut Parola", "password2Label": "Parolayı yeniden gir", "passwordErrorEmpty": "Parola boş bırakılamaz", @@ -97,7 +138,6 @@ "zoomLabel": "Arayüz yakınlaştırma (aslen metin ve buton boyutlarını etkiler)", "blockUnknownLabel": "Tanınmayan Kişileri Engelle", "settingLanguage": "Dil", - "localeDe": "German \/ Deutsch", "settingInterfaceZoom": "Yakınlaştırma seviyesi", "largeTextLabel": "Büyük", "settingTheme": "Açık Tema Kullan", @@ -112,7 +152,6 @@ "loadingTor": "Tor yükleniyor...", "viewGroupMembershipTooltip": "Grup Üyeliğini Görüntüle", "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...", "networkStatusOnline": "Çevrimiçi", "newConnectionPaneTitle": "Yeni Bağlantı", @@ -155,7 +194,6 @@ "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.", "torNetworkStatus": "Tor ağ durumu", - "debugLog": "Konsol hata ayıklama kayıtlarını aç", "profileDeleteSuccess": "Profil başarıyla silindi", "malformedMessage": "Hatalı biçimlendirilmiş mesaj", "shutdownCwtchTooltip": "Cwtch'u Kapat", @@ -175,7 +213,6 @@ "tooltipRejectContactRequest": "Bağlantı isteğini reddet", "tooltipReplyToThisMessage": "Bu mesajı yanıtla", "tooltipRemoveThisQuotedMessage": "Alıntılanan mesajı kaldır.", - "localePl": "Polish \/ Polski", "settingUIColumnPortrait": "UI Sütunları Portre Modu", "settingUIColumnLandscape": "UI Sütunları Yatay Modu", "settingUIColumnSingle": "Tek", @@ -243,7 +280,6 @@ "fieldDescriptionLabel": "Açıklama", "manageKnownServersButton": "Bilinen Sunucuları Yönet", "displayNameTooltip": "Lütfen bir ad girin", - "serverMetricsLabel": "Sunucu Bilgileri", "manageKnownServersLong": "Bilinen Sunucuları Yönet", "manageKnownServersShort": "Sunucular", "serverTotalMessagesLabel": "Toplam Mesaj", @@ -258,7 +294,6 @@ "msgFileTooBig": "Dosya boyutu 10 GB'ı geçemez", "msgConfirmSend": "Göndermek istediğinize emin misiniz", "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.", "torSettingsEnabledAdvanced": "Gelişmiş Tor Konfigürasyonunu Etkinleştir", "torSettingsUseCustomTorServiceConfiguration": "Özel Tor Hizmeti Konfigürasyonunu (torrc) Kullan", @@ -269,18 +304,11 @@ "torSettingsCustomControlPort": "Özel Kontrol Portu", "torSettingsErrorSettingPort": "Port Numarası 1 ile 65535 arasında olmalıdır", "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.", - "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", "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", - "notificationPolicyMute": "Sessiz", - "tooltipSelectACustomProfileImage": "Profil Resmi Seçin", - "notificationPolicyOptIn": "Mümkünse Al", "notificationPolicyDefaultAll": "Tümü Varsayılan", - "conversationNotificationPolicyOptIn": "Mümkünse Al", "conversationNotificationPolicyDefault": "Varsayılan", "conversationNotificationPolicyNever": "Asla", "notificationPolicySettingLabel": "Bildirim İlkeleri", @@ -296,25 +324,19 @@ "settingGroupBehaviour": "Davranış", "settingsGroupAppearance": "Görünüş", "settingsGroupExperiments": "Deneyler", - "notificationContentSimpleEvent": "Yalın Olay", "newMessageNotificationSimple": "Yeni Mesaj", - "localeDa": "Danish \/ Dansk", "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", - "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", "failedToImportProfile": "Profil İçe Aktarılırken Hata Oluştu", "successfullyImportedProfile": "Profil Başarıyla İçe Aktarıldı: %profile", "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ç", "clickableLinksCopy": "URL'yi kopyala", "formattingExperiment": "Mesaj Biçimlendirme", "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", "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", "okButton": "OK", "tooltipBoldText": "Kalın", @@ -331,24 +353,5 @@ "headingReplies": "Yanıtlar", "messageNoReplies": "Bu mesaja yanıt gelmemiş.", "fileDownloadUnavailable": "Bu dosya indirmeye uygun görünmüyor. Gönderen dosyanın indirilmesini engellemiş olabilir.", - "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" + "replyingTo": "%1 hesabına yanıt veriliyor" } \ No newline at end of file diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 502d0e86..42e6ed40 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -56,6 +56,7 @@ class ContactInfoState extends ChangeNotifier { late bool _archived; late bool _pinned; + int _antispamTickets = 0; String? _acnCircuit; String? _messageDraft; @@ -100,8 +101,17 @@ class ContactInfoState extends ChangeNotifier { String? get acnCircuit => this._acnCircuit; + String? get messageDraft => this._messageDraft; + set antispamTickets(int antispamTickets) { + this._antispamTickets = antispamTickets; + notifyListeners(); + } + + int get antispamTickets => this._antispamTickets; + + set acnCircuit(String? acnCircuit) { this._acnCircuit = acnCircuit; notifyListeners(); diff --git a/lib/themes/opaque.dart b/lib/themes/opaque.dart index 095ab1d3..a5bc6f93 100644 --- a/lib/themes/opaque.dart +++ b/lib/themes/opaque.dart @@ -174,7 +174,6 @@ ThemeData mkThemeData(Settings opaque) { ? opaque.current().defaultButtonDisabledColor : null), enableFeedback: true, - splashFactory: InkRipple.splashFactory, padding: MaterialStateProperty.all(EdgeInsets.all(20)), shape: MaterialStateProperty.all(RoundedRectangleBorder( borderRadius: BorderRadius.circular(6.0), diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index b7a681d2..593e28ed 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -40,7 +40,6 @@ class _AddContactViewState extends State { String lastContactValue = ""; bool failedImport = false; - @override Widget build(BuildContext context) { // if we haven't picked a server yet, pick the first one in the list... @@ -162,24 +161,19 @@ class _AddContactViewState extends State { onChanged: (String importBundle) async { if (lastContactValue != importBundle) { lastContactValue = importBundle; - var profileOnion = Provider - .of(bcontext, listen: false) - .onion; - Provider - .of(bcontext, listen: false) - .cwtch - .ImportBundle(profileOnion, importBundle.replaceFirst("cwtch:", "")).then((result) { - if (result == "importBundle.success") { - failedImport = false; - if (AppLocalizations.of(bcontext) != null) { - final snackBar = SnackBar(content: Text(AppLocalizations.of(bcontext)!.successfullAddedContact + importBundle)); - ScaffoldMessenger.of(bcontext).showSnackBar(snackBar); - Navigator.popUntil(bcontext, (route) => route.settings.name == "conversations"); - } - } else { - failedImport = true; - } - }); + var profileOnion = Provider.of(bcontext, listen: false).onion; + Provider.of(bcontext, listen: false).cwtch.ImportBundle(profileOnion, importBundle.replaceFirst("cwtch:", "")).then((result) { + if (result == "importBundle.success") { + failedImport = false; + if (AppLocalizations.of(bcontext) != null) { + final snackBar = SnackBar(content: Text(AppLocalizations.of(bcontext)!.successfullAddedContact + importBundle)); + ScaffoldMessenger.of(bcontext).showSnackBar(snackBar); + Navigator.popUntil(bcontext, (route) => route.settings.name == "conversations"); + } + } else { + failedImport = true; + } + }); } }, hintText: '', diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index f1c27139..5324564c 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -31,6 +31,9 @@ class ContactsView extends StatefulWidget { // selectConversation can be called from anywhere to set the active conversation void selectConversation(BuildContext context, int handle) { + if (handle == Provider.of(context, listen: false).selectedConversation) { + return; + } // requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts var unread = Provider.of(context, listen: false).contactList.getContact(handle)!.unreadMessages; var previouslySelected = Provider.of(context, listen: false).selectedConversation; @@ -122,8 +125,7 @@ class _ContactsViewState extends State { }), ) ]), - title: RepaintBoundary( - child: Row(children: [ + title: Row(children: [ ProfileImage( imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) ? Provider.of(context).imagePath @@ -141,7 +143,7 @@ class _ContactsViewState extends State { Expanded( child: Text("%1 » %2".replaceAll("%1", Provider.of(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts), overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of(context).current().mainTextColor))), - ])), + ]), actions: getActions(context), ), floatingActionButton: FloatingActionButton( @@ -215,7 +217,7 @@ class _ContactsViewState extends State { ChangeNotifierProvider.value(value: contact), ChangeNotifierProvider.value(value: Provider.of(context).serverList), ], - builder: (context, child) => RepaintBoundary(child: ContactRow()), + builder: (context, child) => ContactRow(), ); }); @@ -240,7 +242,7 @@ class _ContactsViewState extends State { }, ); - return RepaintBoundary(child: contactList); + return contactList; } void _pushAddContact(bool newGroup) { diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 8b79755c..6a0f353f 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -393,7 +393,12 @@ class _GlobalSettingsViewState extends State { value: settings.isExperimentEnabled(FileSharingExperiment), onChanged: (bool value) { if (value) { - settings.enableExperiment(FileSharingExperiment); + if (checkDownloadDirectory(context, settings)) { + settings.enableExperiment(FileSharingExperiment); + } else { + settings.disableExperiment(FileSharingExperiment); + settings.disableExperiment(ImagePreviewsExperiment); + } } else { settings.disableExperiment(FileSharingExperiment); settings.disableExperiment(ImagePreviewsExperiment); @@ -413,8 +418,11 @@ class _GlobalSettingsViewState extends State { value: settings.isExperimentEnabled(ImagePreviewsExperiment), onChanged: (bool value) { if (value) { - settings.enableExperiment(ImagePreviewsExperiment); - settings.downloadPath = Provider.of(context, listen: false).cwtch.defaultDownloadPath(); + if (checkDownloadDirectory(context, settings)) { + settings.enableExperiment(ImagePreviewsExperiment); + } else { + settings.disableExperiment(ImagePreviewsExperiment); + } } else { settings.disableExperiment(ImagePreviewsExperiment); } @@ -539,6 +547,30 @@ class _GlobalSettingsViewState extends State { } } +bool checkDownloadDirectory(context, settings) { + bool showError = false; + if (settings.downloadPath != "") { + } else { + // check if the default download path exists + var path = Provider.of(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 String constructVersionString(PackageInfo pinfo) { if (pinfo == null) { diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 92d18915..8ec9b0e4 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -169,14 +169,36 @@ class _MessageViewState extends State { : null, title: Row(children: [ ProfileImage( - imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) - ? Provider.of(context).imagePath - : Provider.of(context).defaultImagePath, - diameter: 42, - border: Provider.of(context).current().portraitOnlineBorderColor, - badgeTextColor: Colors.red, - badgeColor: Colors.red, - ), + imagePath: Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment) + ? Provider.of(context).imagePath + : Provider.of(context).defaultImagePath, + diameter: 42, + border: Provider.of(context).current().portraitOnlineBorderColor, + badgeTextColor: Colors.red, + badgeColor: Provider.of(context).theme.portraitContactBadgeColor, + badgeIcon: Provider.of(context).isGroup + ? (Tooltip( + message: Provider.of(context).isOnline() ? Provider.of(context).antispamTickets == 0 + ? AppLocalizations.of(context)!.acquiringTicketsFromServer + : AppLocalizations.of(context)!.acquiredTicketsFromServer + : AppLocalizations.of(context)!.serverConnectivityDisconnected, + child: Provider.of(context).isOnline() ? Provider.of(context).antispamTickets == 0 + ? Icon( + Icons.schedule_send, + size: 10.0, + semanticLabel: AppLocalizations.of(context)!.acquiringTicketsFromServer, + color: Provider.of(context).theme.portraitContactBadgeTextColor, + ) + : Icon( + Icons.send, + color: Provider.of(context).theme.portraitContactBadgeTextColor, + size: 10.0, + ) : Icon( + CwtchIcons.onion_off, + color: Provider.of(context).theme.portraitContactBadgeTextColor, + size: 10.0, + ))) + : null), SizedBox( width: 10, ), @@ -329,6 +351,12 @@ class _MessageViewState extends State { } void _sendMessageHandler(dynamic messageJson) { + if (Provider.of(context, listen: false).antispamTickets == 0) { + final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.acquiringTicketsFromServer)); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + return; + } + var profileOnion = Provider.of(context, listen: false).profileOnion; var identifier = Provider.of(context, listen: false).identifier; var profile = Provider.of(context, listen: false); @@ -561,8 +589,14 @@ class _MessageViewState extends State { suffixIcon: ElevatedButton( key: Key("btnSend"), 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(context).theme.defaultButtonTextColor), - onPressed: isOffline ? null : _sendMessage, + child: Tooltip( + message: isOffline + ? AppLocalizations.of(context)!.serverNotSynced + : Provider.of(context, listen: false).antispamTickets == 0 + ? AppLocalizations.of(context)!.acquiringTicketsFromServer + : AppLocalizations.of(context)!.sendMessage, + child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of(context).theme.defaultButtonTextColor)), + onPressed: isOffline || Provider.of(context, listen: false).antispamTickets == 0 ? null : _sendMessage, ))), ))); diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index 56ef0183..f7885820 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -340,7 +340,7 @@ class _ProfileMgrViewState extends State { (ProfileInfoState profile) { return ChangeNotifierProvider.value( value: profile, - builder: (context, child) => RepaintBoundary(child: ProfileRow()), + builder: (context, child) => ProfileRow(), ); }, ); diff --git a/lib/views/profileserversview.dart b/lib/views/profileserversview.dart index 2c4e8aab..e897696e 100644 --- a/lib/views/profileserversview.dart +++ b/lib/views/profileserversview.dart @@ -50,7 +50,7 @@ class _ProfileServersView extends State { (RemoteServerInfoState server) { return ChangeNotifierProvider.value( value: server, - builder: (context, child) => RepaintBoundary(child: RemoteServerRow()), + builder: (context, child) => RemoteServerRow(), ); }, ); diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index 1fe8f3c0..9cc978c4 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -37,12 +37,11 @@ class _ContactRowState extends State { )); } - return Card( - clipBehavior: Clip.antiAlias, - color: Provider.of(context).selectedConversation == contact.identifier ? Provider.of(context).theme.backgroundHilightElementColor : null, - borderOnForeground: true, - margin: EdgeInsets.all(0.0), - child: InkWell( + return InkWell( + enableFeedback: true, + splashFactory: InkSplash.splashFactory, + child: Ink( + 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 @@ -144,16 +143,20 @@ class _ContactRowState extends State { Provider.of(context, listen: false).resort(); }, )) - ]), - onTap: () { - selectConversation(context, contact.identifier); - }, - onHover: (onHover) { - setState(() { - isHover = onHover; - }); - }, - )); + ])), + onTap: () { + setState(() { + selectConversation(context, contact.identifier); + }); + }, + onHover: (hover) { + if (isHover != hover) { + setState(() { + isHover = hover; + }); + } + }, + ); } void _btnApprove() { diff --git a/lib/widgets/profileimage.dart b/lib/widgets/profileimage.dart index 4fae95f4..cf21f531 100644 --- a/lib/widgets/profileimage.dart +++ b/lib/widgets/profileimage.dart @@ -17,7 +17,8 @@ class ProfileImage extends StatefulWidget { required this.badgeTextColor, this.maskOut = false, this.tooltip = "", - this.badgeEdit = false}); + this.badgeEdit = false, + this.badgeIcon = null}); final double diameter; final String imagePath; final Color border; @@ -27,6 +28,7 @@ class ProfileImage extends StatefulWidget { final bool maskOut; final bool badgeEdit; final String tooltip; + final Widget? badgeIcon; @override _ProfileImageState createState() => _ProfileImageState(); @@ -81,7 +83,7 @@ class _ProfileImageState extends State { padding: const EdgeInsets.all(2.0), //border size child: ClipOval(clipBehavior: Clip.antiAlias, child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image))))), Visibility( - visible: widget.badgeEdit || widget.badgeCount > 0, + visible: widget.badgeIcon != null || widget.badgeEdit || widget.badgeCount > 0, child: Positioned( bottom: 0.0, right: 0.0, @@ -93,7 +95,7 @@ class _ProfileImageState extends State { Icons.edit, 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))), ), )), ]));