From a9cbbf66bdd8a6430e9d689984170814ece9b2e8 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 28 Jun 2024 10:40:55 -0400 Subject: [PATCH 1/7] Adds uploading error messages for common HTTP status codes. --- .../amethyst/service/HttpStatusMessages.kt | 63 +++++++++++++++++++ .../amethyst/service/Nip96Uploader.kt | 49 ++++++++++----- .../amethyst/ui/actions/EditPostViewModel.kt | 41 ++++++++---- .../amethyst/ui/actions/NewMediaModel.kt | 1 + .../amethyst/ui/actions/NewPostViewModel.kt | 1 + .../ui/actions/NewUserMetadataViewModel.kt | 1 + amethyst/src/main/res/values/strings.xml | 33 ++++++++++ 7 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/service/HttpStatusMessages.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/HttpStatusMessages.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/HttpStatusMessages.kt new file mode 100644 index 000000000..009038d54 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/HttpStatusMessages.kt @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.service + +import com.vitorpamplona.amethyst.R + +class HttpStatusMessages { + companion object { + fun resourceIdFor(statusCode: Int = 0): Int? = + when (statusCode) { + 400 -> R.string.http_status_400 + 401 -> R.string.http_status_401 + 402 -> R.string.http_status_402 + 403 -> R.string.http_status_403 + 404 -> R.string.http_status_404 + 405 -> R.string.http_status_405 + 406 -> R.string.http_status_406 + 407 -> R.string.http_status_407 + 408 -> R.string.http_status_408 + 409 -> R.string.http_status_409 + 410 -> R.string.http_status_410 + 411 -> R.string.http_status_411 + 412 -> R.string.http_status_412 + 413 -> R.string.http_status_413 + 414 -> R.string.http_status_414 + 415 -> R.string.http_status_415 + 416 -> R.string.http_status_416 + 417 -> R.string.http_status_417 + 426 -> R.string.http_status_426 + + 500 -> R.string.http_status_500 + 501 -> R.string.http_status_501 + 502 -> R.string.http_status_502 + 503 -> R.string.http_status_503 + 504 -> R.string.http_status_504 + 505 -> R.string.http_status_505 + 506 -> R.string.http_status_506 + 507 -> R.string.http_status_507 + 508 -> R.string.http_status_508 + 511 -> R.string.http_status_511 + + else -> null + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Nip96Uploader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Nip96Uploader.kt index 7d069d0f1..a2b915363 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Nip96Uploader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Nip96Uploader.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.service import android.content.ContentResolver +import android.content.Context import android.net.Uri import android.provider.OpenableColumns import android.webkit.MimeTypeMap @@ -29,7 +30,9 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.vitorpamplona.amethyst.BuildConfig +import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.ammolite.service.HttpClientManager import kotlinx.coroutines.delay import kotlinx.coroutines.suspendCancellableCoroutine @@ -48,7 +51,9 @@ val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') fun randomChars() = List(16) { charPool.random() }.joinToString("") -class Nip96Uploader(val account: Account?) { +class Nip96Uploader( + val account: Account?, +) { suspend fun uploadImage( uri: Uri, contentType: String?, @@ -58,6 +63,7 @@ class Nip96Uploader(val account: Account?) { server: Nip96MediaServers.ServerName, contentResolver: ContentResolver, onProgress: (percentage: Float) -> Unit, + context: Context, ): PartialEvent { val serverInfo = Nip96Retriever() @@ -74,6 +80,7 @@ class Nip96Uploader(val account: Account?) { serverInfo, contentResolver, onProgress, + context, ) } @@ -86,6 +93,7 @@ class Nip96Uploader(val account: Account?) { server: Nip96Retriever.ServerInfo, contentResolver: ContentResolver, onProgress: (percentage: Float) -> Unit, + context: Context, ): PartialEvent { checkNotInMainThread() @@ -111,6 +119,7 @@ class Nip96Uploader(val account: Account?) { sensitiveContent, server, onProgress, + context, ) } @@ -122,6 +131,7 @@ class Nip96Uploader(val account: Account?) { sensitiveContent: String?, server: Nip96Retriever.ServerInfo, onProgress: (percentage: Float) -> Unit, + context: Context, ): PartialEvent { checkNotInMainThread() @@ -130,11 +140,11 @@ class Nip96Uploader(val account: Account?) { contentType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } ?: "" val client = HttpClientManager.getHttpClient() - val requestBody: RequestBody val requestBuilder = Request.Builder() - requestBody = - MultipartBody.Builder() + val requestBody: RequestBody = + MultipartBody + .Builder() .setType(MultipartBody.FORM) .addFormDataPart("expiration", "") .addFormDataPart("size", length.toString()) @@ -142,8 +152,7 @@ class Nip96Uploader(val account: Account?) { alt?.let { body.addFormDataPart("alt", it) } sensitiveContent?.let { body.addFormDataPart("content-warning", it) } contentType?.let { body.addFormDataPart("content_type", it) } - } - .addFormDataPart( + }.addFormDataPart( "file", "$fileName.$extension", object : RequestBody() { @@ -155,8 +164,7 @@ class Nip96Uploader(val account: Account?) { inputStream.source().use(sink::writeAll) } }, - ) - .build() + ).build() nip98Header(server.apiUrl)?.let { requestBuilder.addHeader("Authorization", it) } @@ -178,11 +186,16 @@ class Nip96Uploader(val account: Account?) { } else if (result.status == "success" && result.nip94Event != null) { return result.nip94Event } else { - throw RuntimeException("Failed to upload with message: ${result.message}") + throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, result.message)) } } } else { - throw RuntimeException("Error Uploading image: ${response.code}") + val explanation = HttpStatusMessages.resourceIdFor(response.code) + if (explanation != null) { + throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, stringRes(context, explanation))) + } else { + throw RuntimeException(stringRes(context, R.string.failed_to_upload_with_message, response.code)) + } } } } @@ -191,6 +204,7 @@ class Nip96Uploader(val account: Account?) { hash: String, contentType: String?, server: Nip96Retriever.ServerInfo, + context: Context, ): Boolean { val extension = contentType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(it) } ?: "" @@ -216,7 +230,12 @@ class Nip96Uploader(val account: Account?) { return result.status == "success" } } else { - throw RuntimeException("Error Uploading image: ${response.code}") + val explanation = HttpStatusMessages.resourceIdFor(response.code) + if (explanation != null) { + throw RuntimeException(stringRes(context, R.string.failed_to_delete_with_message, stringRes(context, explanation))) + } else { + throw RuntimeException(stringRes(context, R.string.failed_to_delete_with_message, response.code)) + } } } } @@ -233,7 +252,8 @@ class Nip96Uploader(val account: Account?) { onProgress((currentResult.percentage ?: 100) / 100f) val request: Request = - Request.Builder() + Request + .Builder() .header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}") .url(result.processingUrl) .build() @@ -257,13 +277,12 @@ class Nip96Uploader(val account: Account?) { } } - suspend fun nip98Header(url: String): String? { - return withTimeoutOrNull(5000) { + suspend fun nip98Header(url: String): String? = + withTimeoutOrNull(5000) { suspendCancellableCoroutine { continuation -> nip98Header(url, "POST") { authorizationToken -> continuation.resume(authorizationToken) } } } - } fun nip98Header( url: String, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt index a951d4a7a..53aa0a0e8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt @@ -53,7 +53,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch @Stable -open class EditPostViewModel() : ViewModel() { +open class EditPostViewModel : ViewModel() { var accountViewModel: AccountViewModel? = null var account: Account? = null @@ -185,6 +185,7 @@ open class EditPostViewModel() : ViewModel() { server = server.server, contentResolver = contentResolver, onProgress = {}, + context = context, ) createNIP94Record( @@ -235,13 +236,12 @@ open class EditPostViewModel() : ViewModel() { NostrSearchEventOrUserDataSource.clear() } - open fun findUrlInMessage(): String? { - return message.text.split('\n').firstNotNullOfOrNull { paragraph -> + open fun findUrlInMessage(): String? = + message.text.split('\n').firstNotNullOfOrNull { paragraph -> paragraph.split(' ').firstOrNull { word: String -> RichTextParser.isValidURL(word) || RichTextParser.isUrlWithoutScheme(word) } } - } open fun updateMessage(it: TextFieldValue) { message = it @@ -249,14 +249,18 @@ open class EditPostViewModel() : ViewModel() { if (it.selection.collapsed) { val lastWord = - it.text.substring(0, it.selection.end).substringAfterLast("\n").substringAfterLast(" ") + it.text + .substring(0, it.selection.end) + .substringAfterLast("\n") + .substringAfterLast(" ") userSuggestionAnchor = it.selection userSuggestionsMainMessage = UserSuggestionAnchor.MAIN_MESSAGE if (lastWord.startsWith("@") && lastWord.length > 2) { NostrSearchEventOrUserDataSource.search(lastWord.removePrefix("@")) viewModelScope.launch(Dispatchers.IO) { userSuggestions = - LocalCache.findUsersStartingWith(lastWord.removePrefix("@")) + LocalCache + .findUsersStartingWith(lastWord.removePrefix("@")) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .reversed() } @@ -271,7 +275,10 @@ open class EditPostViewModel() : ViewModel() { userSuggestionAnchor?.let { if (userSuggestionsMainMessage == UserSuggestionAnchor.MAIN_MESSAGE) { val lastWord = - message.text.substring(0, it.end).substringAfterLast("\n").substringAfterLast(" ") + message.text + .substring(0, it.end) + .substringAfterLast("\n") + .substringAfterLast(" ") val lastWordStart = it.end - lastWord.length val wordToInsert = "@${item.pubkeyNpub()}" @@ -288,12 +295,11 @@ open class EditPostViewModel() : ViewModel() { } } - fun canPost(): Boolean { - return message.text.isNotBlank() && + fun canPost(): Boolean = + message.text.isNotBlank() && !isUploadingImage && !wantsInvoice && contentToAddUrl == null - } suspend fun createNIP94Record( uploadingResult: Nip96Uploader.PartialEvent, @@ -304,11 +310,20 @@ open class EditPostViewModel() : ViewModel() { // Images don't seem to be ready immediately after upload val imageUrl = uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1) val remoteMimeType = - uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "m" }?.get(1)?.ifBlank { null } + uploadingResult.tags + ?.firstOrNull { it.size > 1 && it[0] == "m" } + ?.get(1) + ?.ifBlank { null } val originalHash = - uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "ox" }?.get(1)?.ifBlank { null } + uploadingResult.tags + ?.firstOrNull { it.size > 1 && it[0] == "ox" } + ?.get(1) + ?.ifBlank { null } val dim = - uploadingResult.tags?.firstOrNull { it.size > 1 && it[0] == "dim" }?.get(1)?.ifBlank { null } + uploadingResult.tags + ?.firstOrNull { it.size > 1 && it[0] == "dim" } + ?.get(1) + ?.ifBlank { null } val magnet = uploadingResult.tags ?.firstOrNull { it.size > 1 && it[0] == "magnet" } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt index c9d3ccf79..d3cdcfa45 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaModel.kt @@ -139,6 +139,7 @@ open class NewMediaModel : ViewModel() { onProgress = { percent: Float -> uploadingPercentage.value = 0.2f + (0.2f * percent) }, + context = context, ) createNIP94Record( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index 4dd0e23a5..55dc95a68 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -801,6 +801,7 @@ open class NewPostViewModel : ViewModel() { server = server.server, contentResolver = contentResolver, onProgress = {}, + context = context, ) createNIP94Record( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataViewModel.kt index 02dc193f1..93fac1474 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataViewModel.kt @@ -184,6 +184,7 @@ class NewUserMetadataViewModel : ViewModel() { server = account.defaultFileServer, contentResolver = contentResolver, onProgress = {}, + context = context, ) val url = result.tags?.firstOrNull { it.size > 1 && it[0] == "url" }?.get(1) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 6d8f93237..0a6fd89e1 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -760,6 +760,8 @@ Server did not provide a URL after uploading Could not download uploaded media from the server Could not prepare local file to upload: %1$s + Failed to upload: %1$s + Failed to delete: %1$s Edit draft @@ -895,4 +897,35 @@ Requesting Job from DVM Payment request sent, waiting for confirmation from your wallet Waiting for DVM to confirm payment or send results + + Bad Request - The server can’t or won’t process the request. + Unauthorized - The user doesn’t have valid authentication credentials + Payment Required - The server requires payment to complete the request + Forbidden - The user doesn’t have access rights to make the request + Not Found - The server can’t find the requested address + Method Not Allowed - The server supports the request method, but not the target resource + Not Acceptable - The server doesn’t find any content that satisfies the request. + Proxy Authentication Required - The user doesn’t have valid authentication credentials + Request Timeout - The server timed out waiting for somebody else + Conflict - The server can’t fulfill the request because there’s a conflict with the resource + Gone - The content requested has been permanently deleted from the server and will not be reinstated + Length Required - The server rejects the request because it requires a defined + Precondition Failed - The request\'s preconditions in the header fields that the server fails to meet + Payload Too Large - The request is larger than the server’s defined limits, and the server refuses to process it + URI Too Long - The url requested by the client is too long for the server to process. + Unsupported Media Type - The request uses a media format the server does not support + Range Not Satisfiable - The server can’t fulfill the value indicated in the request’s Range header field. + Expectation Failed - The server can’t meet the requirements indicated by the Expect request header field + Upgrade Required - The server refuses to process the request using the current protocol unless the client upgrades to a different protocol. + + Internal Server Error - The server has encountered an unexpected error and cannot complete the request + Not Implemented - The server can’t fulfill the request or doesn’t recognize the request method + Bad Gateway - The server acts as a gateway and got an invalid response from the host + Service Unavailable - This often occurs when a server is overloaded or down for maintenance + Gateway Timeout - The server was acting as a gateway or proxy and timed out, waiting for a response + HTTP Version Not Supported - The server doesn’t support the HTTP version in the request + Variant Also Negotiates - The server has an internal configuration error + Insufficient Storage - The server doesn’t have enough storage to process the request successfully + Loop Detected - The server detects an infinite loop while processing the request + Network Authentication Required - The client must be authenticated to access the network From a57ba2550ce2bae308cba1a4e50d862d495de22c Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Fri, 28 Jun 2024 14:43:45 +0000 Subject: [PATCH 2/7] New Crowdin translations by GitHub Action --- amethyst/src/main/res/values-zh-rCN/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/amethyst/src/main/res/values-zh-rCN/strings.xml b/amethyst/src/main/res/values-zh-rCN/strings.xml index a83417094..0c0e685f1 100644 --- a/amethyst/src/main/res/values-zh-rCN/strings.xml +++ b/amethyst/src/main/res/values-zh-rCN/strings.xml @@ -103,6 +103,7 @@ 添加中继器 显示名称 我的显示名称 + Nostr 太酷了 欢迎! 用户名 我的用户名 @@ -386,6 +387,7 @@ 当帖子有你的关注的报告时警告 新回应符号 未选择回应类型。长按可更改 + Zapraiser 为这个帖子添加目标聪金额。支持的客户端可以将此显示为奖励捐赠的进度条 目标聪金额 Zapraiser 位于 %1$s。距离目标%2$s聪 @@ -671,6 +673,7 @@ 比特币发票 取消比特币发票 取消出售物品 + Zapraiser 取消 Zapraiser 地点 移除地点 From 347b82ef7f5161294862d1480201688e67cd0a81 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 28 Jun 2024 13:28:05 -0400 Subject: [PATCH 3/7] Fixes when the logged in accounts only include the current account. --- .../vitorpamplona/amethyst/service/NostrAccountDataSource.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt index 0d91a22f8..4df69e1e4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt @@ -118,7 +118,8 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") { ) fun createOtherAccountsBaseFilter(): TypedFilter? { - if (otherAccounts.isEmpty()) return null + val otherAuthors = otherAccounts.filter { it != account.userProfile().pubkeyHex } + if (otherAuthors.isEmpty()) return null return TypedFilter( types = EVENT_FINDER_TYPES, filter = @@ -134,7 +135,7 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") { MuteListEvent.KIND, PeopleListEvent.KIND, ), - authors = otherAccounts.filter { it != account.userProfile().pubkeyHex }, + authors = otherAuthors, limit = 100, ), ) From f0044afeabed4bf7f06a368577a731deb7aa8a26 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 28 Jun 2024 14:06:08 -0400 Subject: [PATCH 4/7] Fixes bug on string resources. --- .../EventNotificationConsumer.kt | 25 ++++++++++++++++--- .../amethyst/ui/StringResourceCache.kt | 4 +-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt index 4ff65b474..7580bdc2e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt @@ -205,38 +205,54 @@ class EventNotificationConsumer( event: LnZapEvent, acc: Account, ) { + Log.d("EventNotificationConsumer", "Notify Start ${event.toNostrUri()}") val noteZapEvent = LocalCache.getNoteIfExists(event.id) ?: return + Log.d("EventNotificationConsumer", "Notify Not Notified Yet") + // old event being re-broadcast if (event.createdAt < TimeUtils.fifteenMinutesAgo()) return + Log.d("EventNotificationConsumer", "Notify Not an old event") + val noteZapRequest = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) } ?: return val noteZapped = event.zappedPost().firstOrNull()?.let { LocalCache.checkGetOrCreateNote(it) } ?: return + Log.d("EventNotificationConsumer", "Notify ZapRequest $noteZapRequest zapped $noteZapped") + if ((event.amount ?: BigDecimal.ZERO) < BigDecimal.TEN) return + Log.d("EventNotificationConsumer", "Notify Amount Bigger than 10") + if (event.isTaggedUser(acc.userProfile().pubkeyHex)) { val amount = showAmount(event.amount) + + Log.d("EventNotificationConsumer", "Notify Amount $amount") + (noteZapRequest.event as? LnZapRequestEvent)?.let { event -> acc.decryptZapContentAuthor(noteZapRequest) { + Log.d("EventNotificationConsumer", "Notify Decrypted if Private Zap ${event.id}") + val author = LocalCache.getOrCreateUser(it.pubKey) val senderInfo = Pair(author, it.content.ifBlank { null }) acc.decryptContent(noteZapped) { + Log.d("EventNotificationConsumer", "Notify Decrypted if Private Note") + val zappedContent = it.split("\n").get(0) val user = senderInfo.first.toBestDisplayName() - var title = - stringRes(applicationContext, R.string.app_notification_zaps_channel_message, amount) + var title = stringRes(applicationContext, R.string.app_notification_zaps_channel_message, amount) senderInfo.second?.ifBlank { null }?.let { title += " ($it)" } + var content = stringRes( applicationContext, R.string.app_notification_zaps_channel_message_from, user, ) - zappedContent?.let { + zappedContent.let { content += " " + stringRes( @@ -247,6 +263,9 @@ class EventNotificationConsumer( } val userPicture = senderInfo?.first?.profilePicture() val noteUri = "nostr:Notifications" + + Log.d("EventNotificationConsumer", "Notify ${event.id} $content $title $noteUri") + notificationManager() .sendZapNotification( event.id, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt index 2caf1d52b..c193a35aa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/StringResourceCache.kt @@ -73,7 +73,7 @@ fun stringRes( return String .format( res.configuration.locales.get(0), - resourceCache.get(id) ?: res.getString(id), + resourceCache.get(id) ?: res.getString(id).also { resourceCache.put(id, it) }, *args, - ).also { resourceCache.put(id, it) } + ) } From 7c7f704173afa330444146440b3a6332df22ee9d Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 28 Jun 2024 16:25:44 -0400 Subject: [PATCH 5/7] v0.88.5 --- amethyst/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amethyst/build.gradle b/amethyst/build.gradle index b39f077c5..869259684 100644 --- a/amethyst/build.gradle +++ b/amethyst/build.gradle @@ -12,8 +12,8 @@ android { applicationId "com.vitorpamplona.amethyst" minSdk 26 targetSdk 34 - versionCode 381 - versionName "0.88.4" + versionCode 382 + versionName "0.88.5" buildConfigField "String", "RELEASE_NOTES_ID", "\"2a34cbadd03212c8162e1ff896ba12641821088a2ec8d5e40d54aa80c0510800\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 6eb7b4599083dcf8c7f29b9c9aa49948c4abd5d5 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Fri, 28 Jun 2024 20:27:40 +0000 Subject: [PATCH 6/7] New Crowdin translations by GitHub Action --- .../src/main/res/values-pl-rPL/strings.xml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/amethyst/src/main/res/values-pl-rPL/strings.xml b/amethyst/src/main/res/values-pl-rPL/strings.xml index 95481cddd..3a4ad34ef 100644 --- a/amethyst/src/main/res/values-pl-rPL/strings.xml +++ b/amethyst/src/main/res/values-pl-rPL/strings.xml @@ -14,6 +14,7 @@ Nie można odszyfrować wiadomości Zdjęcie grupy Niedozwolona zawartość + Spam Liczba spamu z tego transmitera Podszywanie się Antyspołeczne zachowanie @@ -199,6 +200,8 @@ nigdy teraz godz. + m + d Nagość Wulgaryzmy / Mowa nienawiści Zgłoś nienawistną mowę @@ -301,6 +304,7 @@ Minimalny Zap Maksymalny Zap Konsensus + (0–100)% Zamknij po dni Nie można głosować @@ -383,6 +387,7 @@ Ostrzegaj, gdy posty zostały zgłoszone przez osoby które obserwujesz Nowy Symbol Odzewu Brak wstępnie wybranych typów reakcji dla tego użytkownika. Przytrzymaj przycisk serce, aby zmienić + Zapraiser Dodaje docelową liczbę satów do podniesienia dla tego wpisu. W zależności od aplikacji może być pokazywany to jako pasek postępu, aby zachęcić do darowizn Docelowa kwota w Satach Zapraiser przy: %1$s. %2$s satach do celu @@ -414,6 +419,7 @@ Minimalny poW Autoryzacja Płatność + Cashu Token Wykup Wyślij do Zap Wallet Otwórz w Portfelu Cashu @@ -507,6 +513,7 @@ Głosy są oceniane na podstawie ilości zapperów. Możesz ustawić minimalną kwotę, aby uniknąć spamerów i maksymalną kwotę, aby uniknąć przejęcia ankiety przez dużych zapperów. Użyj tej samej kwoty w obu polach, aby upewnić się, że każdy głos ma taką samą wartość. Pozostaw to pole puste, aby zaakceptować dowolną kwotę. Nie można wysłać zapa Wyślij wiadomość użytkownikowi + OK Nieudane połączenie z %1$s: %2$s Nie udało się skompilować adresu URL NIP-11 dla %1$s: %2$s Nieudane połączenie z %1$s: %2$s @@ -527,6 +534,7 @@ Brakująca konfiguracja LN Użytkownik %1$s nie ma skonfigurowanego adresu LN, aby odbierać saty Procentowo + 25 Podziel zapsy z Przesyłanie zapasów do Nie znaleziono portfeli Lightning @@ -593,9 +601,11 @@ Cześć, czy to jest nadal dostępne? Sprzedaj przedmiot Tytuł + iPhone 13 Stan Kategoria Cena (w Satach) + 1000 Lokalizacja Miasto, Województwo, Kraj Nowość @@ -614,6 +624,7 @@ Książki Zwierzęta domowe Sporty + Fitness Sztuka Rzemiosło AGD @@ -628,6 +639,8 @@ Serwer nie podał adresu URL po załadowaniu Nie można pobrać przesłanych plików z serwera Nie można przygotować pliku lokalnego do przesłania: %1$s + Nie udało się przesłać: %1$s + Nie udało się usunąć: %1$s Edytuj wersję roboczą Zaloguj się przy użyciu QR kodu Ścieżka @@ -647,6 +660,7 @@ Odpowiedz Zrepostuj lub Zacytuj Lubię + Zap Zmień odzew Zdjęcie profilowe %1$s Transmiter %1$s @@ -658,6 +672,7 @@ Faktura Bitcoin Anuluj fakturę Bitcoin Anuluj sprzedaż przedmiotu + Zapraiser Anuluj Zapraiser Lokalizacja Usuń lokalizację @@ -710,6 +725,7 @@ Repozytorium Git: %1$s Strona internetowa: Klonuj: + OTS: %1$s Potwierdzenie znacznika czasu Istnieje dowód na to, że ten post został podpisany przed %1$s. Dowód został opatrzony pieczęcią w łańcuchu bloków Bitcoin w tym dniu i czasie. Edytuj wpis @@ -737,4 +753,27 @@ Zapytanie o pracę z DVM Prośba o płatność została wysłana, oczekiwanie na potwierdzenie z Twojego portfela Oczekiwanie na potwierdzenie płatności lub wysłanie wyników DVM + Błędne żądanie-Serwer nie może lub nie chce przetworzyć żądania. + Nieautoryzowany - użytkownik nie ma prawidłowych danych uwierzytelniających + Wymagana płatność - serwer wymaga płatności, aby wypełnić żądanie + Zabronione - użytkownik nie ma praw dostępu, aby złożyć żądanie + Nie znaleziono - serwer nie może odnaleźć żądanego adresu + Metoda niedozwolona - serwer obsługuje metodę żądania, ale nie docelowy zasób + Niedopuszczalne - Serwer nie znalazł żadnej zawartości spełniającej żądanie. + Wymagane uwierzytelnianie proxy - użytkownik nie ma prawidłowych danych uwierzytelniania + Limit czasu żądania - serwer przekroczył limit czasu, oczekując na kogoś innego + Konflikt - serwer nie może spełnić żądania, ponieważ istnieje konflikt z zasobem + Przepadło - Żądana zawartość została trwale usunięta z serwera i nie zostanie przywrócona + Wymagana długość - serwer odrzuca żądanie, ponieważ wymaga zdefiniowanej + URI Zbyt długi - URL klienta jest zbyt długi, aby serwer mógł go przetworzyć. + Nieobsługiwany format plików - żądanie używa formatu multimediów, którego serwer nie obsługuje + Wymagana aktualizacja - serwer odmawia przetwarzania żądania przy użyciu bieżącego protokołu, chyba że klient uaktualni go do innego protokołu. + Wewnętrzny błąd serwera - serwer napotkał nieoczekiwany błąd i nie może zakończyć żądania + Nie zaimplementowano - serwer nie może spełnić żądania lub nie rozpoznaje metody żądania + Usługa niedostępna - Często zdarza się, gdy serwer jest przeciążony lub wyłączony w celu konserwacji + Wersja HTTP nie jest obsługiwana - serwer nie obsługuje wersji HTTP w zapytaniu + Wariant również Negocjacje - serwer ma błąd konfiguracji wewnętrznej + Niewystarczająca pojemność - serwer nie ma wystarczającej pojemność, aby pomyślnie przetworzyć żądanie + Wykryta pętla - serwer wykrywa nieskończoną pętlę podczas przetwarzania żądania + Wymagane uwierzytelnienie sieciowe - Klient musi być uwierzytelniony aby uzyskać dostęp do sieci From 1fafb6a503f564b383f09439e2921cff722ace54 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 28 Jun 2024 16:58:45 -0400 Subject: [PATCH 7/7] updates create release script to match new directories --- .github/workflows/create-release.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 24e124faf..a1579b7de 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -99,7 +99,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/play/release/app-play-universal-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-universal-release-unsigned-signed.apk asset_name: amethyst-googleplay-universal-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -110,7 +110,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/play/release/app-play-x86-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-x86-release-unsigned-signed.apk asset_name: amethyst-googleplay-x86-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -121,7 +121,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/play/release/app-play-x86_64-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-x86_64-release-unsigned-signed.apk asset_name: amethyst-googleplay-x86_64-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -132,7 +132,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/play/release/app-play-arm64-v8a-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-arm64-v8a-release-unsigned-signed.apk asset_name: amethyst-googleplay-arm64-v8a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -143,7 +143,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/play/release/app-play-armeabi-v7a-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-armeabi-v7a-release-unsigned-signed.apk asset_name: amethyst-googleplay-armeabi-v7a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -155,7 +155,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-universal-release-unsigned-signed.apk asset_name: amethyst-fdroid-universal-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -166,7 +166,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-x86-release-unsigned-signed.apk asset_name: amethyst-fdroid-x86-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -177,7 +177,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-x86_64-release-unsigned-signed.apk asset_name: amethyst-fdroid-x86_64-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -188,7 +188,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/fdroid/release/app-fdroid-arm64-v8a-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-arm64-v8a-release-unsigned-signed.apk asset_name: amethyst-fdroid-arm64-v8a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -199,7 +199,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/apk/fdroid/release/app-fdroid-armeabi-v7a-release-unsigned-signed.apk + asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-armeabi-v7a-release-unsigned-signed.apk asset_name: amethyst-fdroid-armeabi-v7a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -213,7 +213,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/bundle/playRelease/app-play-release.aab + asset_path: amethyst/build/outputs/bundle/playRelease/amethyst-play-release.aab asset_name: amethyst-googleplay-${{ github.ref_name }}.aab asset_content_type: application/zip @@ -225,6 +225,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: amethyst/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab + asset_path: amethyst/build/outputs/bundle/fdroidRelease/amethyst-fdroid-release.aab asset_name: amethyst-fdroid-${{ github.ref_name }}.aab asset_content_type: application/zip