Merge remote-tracking branch 'origin/HEAD' into less_memory_test_branch

This commit is contained in:
Vitor Pamplona
2023-03-09 15:42:22 -05:00
24 changed files with 1207 additions and 216 deletions

View File

@@ -12,8 +12,8 @@ android {
applicationId "com.vitorpamplona.amethyst"
minSdk 26
targetSdk 33
versionCode 89
versionName "0.24.0"
versionCode 90
versionName "0.24.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@@ -9,6 +9,7 @@ import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
import com.vitorpamplona.amethyst.service.model.Contact
import com.vitorpamplona.amethyst.service.model.ContactListEvent
import com.vitorpamplona.amethyst.service.model.DeletionEvent
import com.vitorpamplona.amethyst.service.model.IdentityClaim
import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
import com.vitorpamplona.amethyst.service.model.MetadataEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
@@ -106,11 +107,11 @@ class Account(
}
}
fun sendNewUserMetadata(toString: String) {
fun sendNewUserMetadata(toString: String, identities: List<IdentityClaim>) {
if (!isWriteable()) return
loggedIn.privKey?.let {
val event = MetadataEvent.create(toString, loggedIn.privKey!!)
val event = MetadataEvent.create(toString, identities, loggedIn.privKey!!)
Client.send(event)
LocalCache.consume(event)
}

View File

@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.service.model
import android.util.Log
import com.google.gson.Gson
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.HexKey
import com.vitorpamplona.amethyst.model.toHexKey
import nostr.postr.Utils
@@ -14,6 +15,127 @@ data class ContactMetaData(
val nip05: String?
)
abstract class IdentityClaim(
var identity: String,
var proof: String
) {
abstract fun toProofUrl(): String
abstract fun toIcon(): Int
abstract fun toDescriptor(): Int
abstract fun platform(): String
fun platformIdentity() = "${platform()}:$identity"
companion object {
fun create(platformIdentity: String, proof: String): IdentityClaim? {
val platformIdentity = platformIdentity.split(':')
val platform = platformIdentity[0]
val identity = platformIdentity[1]
return when (platform.lowercase()) {
GitHubIdentity.platform -> GitHubIdentity(identity, proof)
TwitterIdentity.platform -> TwitterIdentity(identity, proof)
TelegramIdentity.platform -> TelegramIdentity(identity, proof)
MastodonIdentity.platform -> MastodonIdentity(identity, proof)
else -> throw IllegalArgumentException("Platform $platform not supported")
}
}
}
}
class GitHubIdentity(
identity: String,
proof: String
) : IdentityClaim(identity, proof) {
override fun toProofUrl() = "https://gist.github.com/$identity/$proof"
override fun platform() = platform
override fun toIcon() = R.drawable.github
override fun toDescriptor() = R.string.github
companion object {
val platform = "github"
fun parseProofUrl(proofUrl: String): GitHubIdentity? {
return try {
if (proofUrl.isBlank()) return null
val path = proofUrl.removePrefix("https://gist.github.com/").split("?")[0].split("/")
GitHubIdentity(path[0], path[1])
} catch (e: Exception) {
null
}
}
}
}
class TwitterIdentity(
identity: String,
proof: String
) : IdentityClaim(identity, proof) {
override fun toProofUrl() = "https://twitter.com/$identity/status/$proof"
override fun platform() = platform
override fun toIcon() = R.drawable.twitter
override fun toDescriptor() = R.string.twitter
companion object {
val platform = "twitter"
fun parseProofUrl(proofUrl: String): TwitterIdentity? {
return try {
if (proofUrl.isBlank()) return null
val path = proofUrl.removePrefix("https://twitter.com/").split("?")[0].split("/")
TwitterIdentity(path[0], path[2])
} catch (e: Exception) {
null
}
}
}
}
class TelegramIdentity(
identity: String,
proof: String
) : IdentityClaim(identity, proof) {
override fun toProofUrl() = "https://t.me/$proof"
override fun platform() = platform
override fun toIcon() = R.drawable.telegram
override fun toDescriptor() = R.string.telegram
companion object {
val platform = "telegram"
}
}
class MastodonIdentity(
identity: String,
proof: String
) : IdentityClaim(identity, proof) {
override fun toProofUrl() = "https://$identity/$proof"
override fun platform() = platform
override fun toIcon() = R.drawable.mastodon
override fun toDescriptor() = R.string.mastodon
companion object {
val platform = "mastodon"
fun parseProofUrl(proofUrl: String): MastodonIdentity? {
return try {
if (proofUrl.isBlank()) return null
val path = proofUrl.removePrefix("https://").split("?")[0].split("/")
return MastodonIdentity(path[0], path[1])
} catch (e: Exception) {
null
}
}
}
}
class MetadataEvent(
id: HexKey,
pubKey: HexKey,
@@ -29,18 +151,31 @@ class MetadataEvent(
null
}
fun identityClaims() = tags.filter { it.firstOrNull() == "i" }.mapNotNull {
try {
IdentityClaim.create(it.get(1), it.get(2))
} catch (e: Exception) {
Log.e("MetadataEvent", "Can't parse identity [${it.joinToString { "," }}]", e)
null
}
}
companion object {
const val kind = 0
val gson = Gson()
fun create(contactMetaData: ContactMetaData, privateKey: ByteArray, createdAt: Long = Date().time / 1000): MetadataEvent {
return create(gson.toJson(contactMetaData), privateKey, createdAt = createdAt)
fun create(contactMetaData: ContactMetaData, identities: List<IdentityClaim>, privateKey: ByteArray, createdAt: Long = Date().time / 1000): MetadataEvent {
return create(gson.toJson(contactMetaData), identities, privateKey, createdAt = createdAt)
}
fun create(contactMetaData: String, privateKey: ByteArray, createdAt: Long = Date().time / 1000): MetadataEvent {
fun create(contactMetaData: String, identities: List<IdentityClaim>, privateKey: ByteArray, createdAt: Long = Date().time / 1000): MetadataEvent {
val content = contactMetaData
val pubKey = Utils.pubkeyCreate(privateKey).toHexKey()
val tags = listOf<List<String>>()
val tags = mutableListOf<List<String>>()
identities?.forEach {
tags.add(listOf("i", it.platformIdentity(), it.proof))
}
val id = generateId(pubKey, createdAt, kind, tags, content)
val sig = Utils.sign(id, privateKey)
return MetadataEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())

View File

@@ -7,7 +7,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
@@ -15,7 +17,6 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
@@ -26,7 +27,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
val postViewModel: NewUserMetadataViewModel = viewModel()
@@ -66,158 +66,207 @@ fun NewUserMetadataView(onClose: () -> Unit, account: Account) {
)
}
Spacer(modifier = Modifier.height(10.dp))
Column(
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState())
) {
Row(
modifier = Modifier.fillMaxWidth(1f),
verticalAlignment = Alignment.CenterVertically
) {
OutlinedTextField(
label = { Text(text = stringResource(R.string.display_name)) },
modifier = Modifier.weight(1f),
value = postViewModel.displayName.value,
onValueChange = { postViewModel.displayName.value = it },
placeholder = {
Text(
text = stringResource(R.string.my_display_name),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences
),
singleLine = true
)
Text("@", Modifier.padding(5.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.username)) },
modifier = Modifier.weight(1f),
value = postViewModel.userName.value,
onValueChange = { postViewModel.userName.value = it },
placeholder = {
Text(
text = stringResource(R.string.my_username),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
}
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(1f), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
label = { Text(text = stringResource(R.string.display_name)) },
modifier = Modifier.weight(1f),
value = postViewModel.displayName.value,
onValueChange = { postViewModel.displayName.value = it },
label = { Text(text = stringResource(R.string.about_me)) },
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
value = postViewModel.about.value,
onValueChange = { postViewModel.about.value = it },
placeholder = {
Text(
text = stringResource(R.string.my_display_name),
text = stringResource(id = R.string.about_me),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences
),
singleLine = true
maxLines = 10
)
Text("@", Modifier.padding(5.dp))
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.username)) },
modifier = Modifier.weight(1f),
value = postViewModel.userName.value,
onValueChange = { postViewModel.userName.value = it },
label = { Text(text = stringResource(R.string.avatar_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.picture.value,
onValueChange = { postViewModel.picture.value = it },
placeholder = {
Text(
text = stringResource(R.string.my_username),
text = "https://mywebsite.com/me.jpg",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.banner_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.banner.value,
onValueChange = { postViewModel.banner.value = it },
placeholder = {
Text(
text = "https://mywebsite.com/mybanner.jpg",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.website_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.website.value,
onValueChange = { postViewModel.website.value = it },
placeholder = {
Text(
text = "https://mywebsite.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.nip_05)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.nip05.value,
onValueChange = { postViewModel.nip05.value = it },
placeholder = {
Text(
text = "_@mywebsite.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.ln_address)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.lnAddress.value,
onValueChange = { postViewModel.lnAddress.value = it },
placeholder = {
Text(
text = "me@mylightiningnode.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.ln_url_outdated)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.lnURL.value,
onValueChange = { postViewModel.lnURL.value = it },
placeholder = {
Text(
text = stringResource(R.string.lnurl),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.twitter)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.twitter.value,
onValueChange = { postViewModel.twitter.value = it },
placeholder = {
Text(
text = stringResource(R.string.twitter_proof_url_template),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.mastodon)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.mastodon.value,
onValueChange = { postViewModel.mastodon.value = it },
placeholder = {
Text(
text = stringResource(R.string.mastodon_proof_url_template),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.github)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.github.value,
onValueChange = { postViewModel.github.value = it },
placeholder = {
Text(
text = stringResource(R.string.github_proof_url_template),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
)
}
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.about_me)) },
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
value = postViewModel.about.value,
onValueChange = { postViewModel.about.value = it },
placeholder = {
Text(
text = stringResource(id = R.string.about_me),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences
),
maxLines = 10
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.avatar_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.picture.value,
onValueChange = { postViewModel.picture.value = it },
placeholder = {
Text(
text = "https://mywebsite.com/me.jpg",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.banner_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.banner.value,
onValueChange = { postViewModel.banner.value = it },
placeholder = {
Text(
text = "https://mywebsite.com/mybanner.jpg",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.website_url)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.website.value,
onValueChange = { postViewModel.website.value = it },
placeholder = {
Text(
text = "https://mywebsite.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.nip_05)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.nip05.value,
onValueChange = { postViewModel.nip05.value = it },
placeholder = {
Text(
text = "_@mywebsite.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.ln_address)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.lnAddress.value,
onValueChange = { postViewModel.lnAddress.value = it },
placeholder = {
Text(
text = "me@mylightiningnode.com",
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringResource(R.string.ln_url_outdated)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.lnURL.value,
onValueChange = { postViewModel.lnURL.value = it },
placeholder = {
Text(
text = stringResource(R.string.lnurl),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
},
singleLine = true
)
}
}
}

View File

@@ -5,6 +5,9 @@ import androidx.lifecycle.ViewModel
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.model.GitHubIdentity
import com.vitorpamplona.amethyst.service.model.MastodonIdentity
import com.vitorpamplona.amethyst.service.model.TwitterIdentity
import java.io.ByteArrayInputStream
import java.io.StringWriter
@@ -23,6 +26,10 @@ class NewUserMetadataViewModel : ViewModel() {
val lnAddress = mutableStateOf("")
val lnURL = mutableStateOf("")
val twitter = mutableStateOf("")
val github = mutableStateOf("")
val mastodon = mutableStateOf("")
fun load(account: Account) {
this.account = account
@@ -36,6 +43,19 @@ class NewUserMetadataViewModel : ViewModel() {
nip05.value = it.info?.nip05 ?: ""
lnAddress.value = it.info?.lud16 ?: ""
lnURL.value = it.info?.lud06 ?: ""
twitter.value = ""
github.value = ""
mastodon.value = ""
// TODO: Validate Telegram input, somehow.
it.info?.latestMetadata?.identityClaims()?.forEach {
when (it) {
is TwitterIdentity -> twitter.value = it.toProofUrl()
is GitHubIdentity -> github.value = it.toProofUrl()
is MastodonIdentity -> mastodon.value = it.toProofUrl()
}
}
}
}
@@ -61,10 +81,34 @@ class NewUserMetadataViewModel : ViewModel() {
currentJson.put("lud16", lnAddress.value.trim())
currentJson.put("lud06", lnURL.value.trim())
var claims = latest?.identityClaims() ?: emptyList()
if (twitter.value.isBlank()) {
// delete twitter
claims = claims.filter { it !is TwitterIdentity }
}
if (github.value.isBlank()) {
// delete github
claims = claims.filter { it !is GitHubIdentity }
}
if (mastodon.value.isBlank()) {
// delete mastodon
claims = claims.filter { it !is MastodonIdentity }
}
// Updates while keeping other identities intact
val newClaims = listOfNotNull(
TwitterIdentity.parseProofUrl(twitter.value),
GitHubIdentity.parseProofUrl(github.value),
MastodonIdentity.parseProofUrl(mastodon.value)
) + claims.filter { it !is TwitterIdentity && it !is GitHubIdentity && it !is MastodonIdentity }
val writer = StringWriter()
ObjectMapper().writeValue(writer, currentJson)
account.sendNewUserMetadata(writer.buffer.toString())
account.sendNewUserMetadata(writer.buffer.toString(), newClaims)
clear()
}
@@ -79,5 +123,8 @@ class NewUserMetadataViewModel : ViewModel() {
nip05.value = ""
lnAddress.value = ""
lnURL.value = ""
twitter.value = ""
github.value = ""
mastodon.value = ""
}
}

View File

@@ -1,11 +1,14 @@
package com.vitorpamplona.amethyst.ui.components
import androidx.compose.foundation.background
import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Icon
@@ -17,6 +20,7 @@ import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@@ -24,18 +28,27 @@ import com.vitorpamplona.amethyst.R
@Composable
fun SelectTextDialog(text: String, onDismiss: () -> Unit) {
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val maxHeight =
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
screenHeight * 0.6f
} else {
screenHeight * 0.9f
}
Dialog(
onDismissRequest = onDismiss
) {
Card {
Column {
Column(
modifier = Modifier.heightIn(24.dp, maxHeight)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
IconButton(
onClick = onDismiss,
modifier = Modifier.background(MaterialTheme.colors.background)
onClick = onDismiss
) {
Icon(
imageVector = Icons.Default.ArrowBack,
@@ -46,9 +59,13 @@ fun SelectTextDialog(text: String, onDismiss: () -> Unit) {
Text(text = stringResource(R.string.select_text_dialog_top))
}
Divider()
Row(modifier = Modifier.padding(16.dp)) {
SelectionContainer {
Text(text)
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
) {
Row(modifier = Modifier.padding(16.dp)) {
SelectionContainer {
Text(text)
}
}
}
}

View File

@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui.note
import android.content.Intent
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
@@ -25,11 +26,13 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow
@@ -753,7 +756,8 @@ fun UserPicture(
@Composable
fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, accountViewModel: AccountViewModel) {
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current.applicationContext
val appContext = LocalContext.current.applicationContext
val actContext = LocalContext.current
DropdownMenu(
expanded = popupExpanded,
@@ -769,6 +773,33 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
}
Divider()
}
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(accountViewModel.decrypt(note) ?: "")); onDismiss() }) {
Text(stringResource(R.string.copy_text))
}
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString("@${note.author?.pubkeyNpub()}" ?: "")); onDismiss() }) {
Text(stringResource(R.string.copy_user_pubkey))
}
DropdownMenuItem(onClick = { clipboardManager.setText(AnnotatedString(note.idNote())); onDismiss() }) {
Text(stringResource(R.string.copy_note_id))
}
DropdownMenuItem(onClick = {
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(
Intent.EXTRA_TEXT,
externalLinkForNote(note)
)
putExtra(Intent.EXTRA_TITLE, actContext.getString(R.string.quick_action_share_browser_link))
}
val shareIntent = Intent.createChooser(sendIntent, appContext.getString(R.string.quick_action_share))
ContextCompat.startActivity(actContext, shareIntent, null)
onDismiss()
}) {
Text(stringResource(R.string.quick_action_share))
}
Divider()
DropdownMenuItem(onClick = { accountViewModel.broadcast(note); onDismiss() }) {
Text(stringResource(R.string.broadcast))
}
@@ -784,7 +815,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
note.author?.let {
accountViewModel.hide(
it,
context
appContext
)
}; onDismiss()
}) {
@@ -793,35 +824,35 @@ fun NoteDropDownMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit,
Divider()
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.SPAM)
note.author?.let { accountViewModel.hide(it, context) }
note.author?.let { accountViewModel.hide(it, appContext) }
onDismiss()
}) {
Text(stringResource(R.string.report_spam_scam))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.PROFANITY)
note.author?.let { accountViewModel.hide(it, context) }
note.author?.let { accountViewModel.hide(it, appContext) }
onDismiss()
}) {
Text(stringResource(R.string.report_hateful_speech))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.IMPERSONATION)
note.author?.let { accountViewModel.hide(it, context) }
note.author?.let { accountViewModel.hide(it, appContext) }
onDismiss()
}) {
Text(stringResource(R.string.report_impersonation))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.NUDITY)
note.author?.let { accountViewModel.hide(it, context) }
note.author?.let { accountViewModel.hide(it, appContext) }
onDismiss()
}) {
Text(stringResource(R.string.report_nudity_porn))
}
DropdownMenuItem(onClick = {
accountViewModel.report(note, ReportEvent.ReportType.ILLEGAL)
note.author?.let { accountViewModel.hide(it, context) }
note.author?.let { accountViewModel.hide(it, appContext) }
onDismiss()
}) {
Text(stringResource(R.string.report_illegal_behaviour))

View File

@@ -47,6 +47,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup
@@ -81,7 +82,7 @@ fun VerticalDivider(color: Color) =
@Composable
fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, accountViewModel: AccountViewModel) {
val context = LocalContext.current
val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.2f)
val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.1f)
val cardShape = RoundedCornerShape(5.dp)
val clipboardManager = LocalClipboardManager.current
val scope = rememberCoroutineScope()
@@ -186,7 +187,6 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni
ContextCompat.startActivity(context, shareIntent, null)
onDismiss()
}
VerticalDivider(primaryLight)
}
}
}
@@ -237,7 +237,7 @@ fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Uni
fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) {
Column(
modifier = Modifier
.size(64.dp)
.size(70.dp)
.clickable { onClick() },
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
@@ -245,9 +245,9 @@ fun NoteQuickActionItem(icon: ImageVector, label: String, onClick: () -> Unit) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colors.onPrimary
modifier = Modifier.size(24.dp).padding(bottom = 5.dp),
tint = Color.White
)
Text(text = label, fontSize = 12.sp)
Text(text = label, fontSize = 12.sp, color = Color.White, textAlign = TextAlign.Center)
}
}

View File

@@ -9,6 +9,7 @@ import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.EditNote
import androidx.compose.material.icons.filled.Link
import androidx.compose.material.icons.filled.MoreVert
@@ -58,6 +59,7 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent
import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent
import com.vitorpamplona.amethyst.service.model.IdentityClaim
import com.vitorpamplona.amethyst.service.model.ReportEvent
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
import com.vitorpamplona.amethyst.ui.components.AsyncImageProxy
@@ -349,20 +351,21 @@ private fun ProfileHeader(
) {
MessageButton(baseUser, navController)
NPubCopyButton(baseUser)
// No need for this button anymore
// NPubCopyButton(baseUser)
if (accountUser == baseUser) {
EditButton(account)
} else {
if (account.isHidden(baseUser)) {
ShowUserButton {
account.showUser(baseUser.pubkeyHex)
}
} else if (accountUser.isFollowing(baseUser)) {
UnfollowButton { coroutineScope.launch(Dispatchers.IO) { account.unfollow(baseUser) } }
} else {
FollowButton { coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } }
}
if (account.isHidden(baseUser)) {
ShowUserButton {
account.showUser(baseUser.pubkeyHex)
}
} else if (accountUser.isFollowing(baseUser)) {
UnfollowButton { coroutineScope.launch(Dispatchers.IO) { account.unfollow(baseUser) } }
} else {
FollowButton { coroutineScope.launch(Dispatchers.IO) { account.follow(baseUser) } }
}
}
}
@@ -388,6 +391,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, navController:
val userBadge = userBadgeState?.user ?: return
val uri = LocalUriHandler.current
val clipboardManager = LocalClipboardManager.current
Row(verticalAlignment = Alignment.Bottom) {
user.bestDisplayName()?.let {
@@ -408,6 +412,44 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, navController:
}
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = user.pubkeyDisplayHex(),
modifier = Modifier.padding(top = 1.dp, bottom = 1.dp),
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
IconButton(
modifier = Modifier.size(30.dp).padding(start = 5.dp),
onClick = { clipboardManager.setText(AnnotatedString(user.pubkeyNpub())); }
) {
Icon(
imageVector = Icons.Default.ContentCopy,
null,
modifier = Modifier.padding(end = 5.dp).size(15.dp),
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
}
}
userBadge.acceptedBadges?.let { note ->
(note.event as? BadgeProfilesEvent)?.let { event ->
FlowRow(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
event.badgeAwardEvents().forEach { badgeAwardEvent ->
val baseNote = LocalCache.notes[badgeAwardEvent]
if (baseNote != null) {
val badgeAwardState by baseNote.live().metadata.observeAsState()
val baseBadgeDefinition = badgeAwardState?.note?.replyTo?.firstOrNull()
if (baseBadgeDefinition != null) {
BadgeThumb(baseBadgeDefinition, navController, 50.dp)
}
}
}
}
}
}
DisplayNip05ProfileStatus(user)
val website = user.info?.website
@@ -461,20 +503,25 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, navController:
}
}
userBadge.acceptedBadges?.let { note ->
(note.event as? BadgeProfilesEvent)?.let { event ->
FlowRow(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 5.dp)) {
event.badgeAwardEvents().forEach { badgeAwardEvent ->
val baseNote = LocalCache.notes[badgeAwardEvent]
if (baseNote != null) {
val badgeAwardState by baseNote.live().metadata.observeAsState()
val baseBadgeDefinition = badgeAwardState?.note?.replyTo?.firstOrNull()
val identities = user.info?.latestMetadata?.identityClaims()
if (!identities.isNullOrEmpty()) {
identities.forEach { identity: IdentityClaim ->
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
tint = Color.Unspecified,
painter = painterResource(id = identity.toIcon()),
contentDescription = stringResource(identity.toDescriptor()),
modifier = Modifier.size(16.dp)
)
if (baseBadgeDefinition != null) {
BadgeThumb(baseBadgeDefinition, navController, 50.dp)
}
}
}
ClickableText(
text = AnnotatedString(identity.identity),
onClick = { runCatching { uri.openUri(identity.toProofUrl()) } },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary),
modifier = Modifier
.padding(top = 1.dp, bottom = 1.dp, start = 5.dp)
.weight(1f)
)
}
}
}

View File

@@ -0,0 +1,4 @@
<vector android:height="128dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="128dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#3A6CAC" android:fillType="evenOdd" android:pathData="M296.13,354.17c49.88,-5.89 102.94,-24.03 102.94,-110.19c0,-24.49 -8.62,-44.45 -22.67,-59.87c2.27,-5.89 9.52,-28.11 -2.73,-58.95c0,0 -18.14,-5.9 -60.76,22.67c-18.14,-4.98 -38.09,-8.16 -56.68,-8.16c-19.05,0 -39.01,3.18 -56.7,8.16c-43.08,-28.57 -61.22,-22.67 -61.22,-22.67c-12.24,30.83 -4.98,53.06 -2.72,58.95c-14.06,15.42 -22.68,35.38 -22.68,59.87c0,86.16 53.06,104.3 102.94,110.19c-6.34,5.45 -12.24,15.87 -14.51,30.39c-12.7,5.44 -45.81,15.87 -65.76,-18.59c0,0 -11.8,-21.31 -34.01,-22.67c0,0 -22.22,-0.45 -1.81,13.59c0,0 14.96,6.81 24.94,32.65c0,0 13.6,43.09 76.18,29.48v38.54c0,5.91 -4.53,12.7 -15.86,10.89C96.14,438.98 32.2,354.63 32.2,255.77c0,-123.81 100.22,-224.02 224.03,-224.02c123.35,0 224.02,100.22 223.57,224.02c0,98.86 -63.95,182.75 -152.83,212.69c-11.34,2.27 -15.87,-4.53 -15.87,-10.89V395.45C311.1,374.58 304.29,360.98 296.13,354.17L296.13,354.17zM512,256.23C512,114.73 397.26,0 256.23,0C114.73,0 0,114.73 0,256.23C0,397.26 114.73,512 256.23,512C397.26,512 512,397.26 512,256.23L512,256.23z"/>
</vector>

View File

@@ -0,0 +1,14 @@
<vector android:height="128dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="128dp"
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:pathData="M317,381q-124,28 -123,-39 69,15 149,2 67,-13 72,-80 3,-101 -3,-116 -19,-49 -72,-58 -98,-10 -162,0 -56,10 -75,58 -12,31 -3,147 3,32 9,53 13,46 70,69 83,23 138,-9">
<aapt:attr name="android:fillColor">
<gradient android:endX="418" android:endY="440"
android:startX="91" android:startY="80" android:type="linear">
<item android:color="#FF6364FF" android:offset="0"/>
<item android:color="#FF563ACC" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:fillColor="#fff" android:pathData="M360,293h-36v-93q-1,-26 -29,-23 -20,3 -20,34v47h-36v-47q0,-31 -20,-34 -30,-3 -30,28v88h-36v-91q1,-51 44,-60 33,-5 51,21l9,15 9,-15q16,-26 51,-21 43,9 43,60"/>
</vector>

View File

@@ -0,0 +1,6 @@
<vector android:height="128dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="128dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#c8daea" android:pathData="M199,404c-11,0 -10,-4 -13,-14l-32,-105 245,-144"/>
<path android:fillColor="#a9c9dd" android:pathData="M199,404c7,0 11,-4 16,-8l45,-43 -56,-34"/>
<path android:fillColor="#37aee2" android:pathData="M204,319l135,99c14,9 26,4 30,-14l55,-258c5,-22 -9,-32 -24,-25L79,245c-21,8 -21,21 -4,26l83,26 190,-121c9,-5 17,-3 11,4"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="128dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="128dp">
<path
android:pathData="M437,152a72,72 0,0 1,-40 12a72,72 0,0 0,32 -40a72,72 0,0 1,-45 17a72,72 0,0 0,-122 65a200,200 0,0 1,-145 -74a72,72 0,0 0,22 94a72,72 0,0 1,-32 -7a72,72 0,0 0,56 69a72,72 0,0 1,-32 1a72,72 0,0 0,67 50a200,200 0,0 1,-105 29a200,200 0,0 0,309 -179a200,200 0,0 0,35 -37"
android:fillColor="#1da1f2"/>
</vector>

View File

@@ -1,7 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name_release" translatable="false">Amethyst</string>
<string name="app_name_debug" translatable="false">Amethyst Debug</string>
<string name="mark_all_known_as_read">Marcar los conocidos como leídos</string>
<string name="mark_all_new_as_read">Marcar los nuevos como leídos</string>
<string name="mark_all_as_read">Marcar todos como leídos</string>
<string name="secret_key_copied_to_clipboard">Clave secreta (nsec) copiada al portapapeles</string>
<string name="copy_my_secret_key">Copiar mi clave secreta</string>
<string name="biometric_authentication_failed">Autenticación fallida</string>
<string name="biometric_error">Error</string>
<string name="badge_created_by">Credo por %1$s</string>
<string name="badge_award_image_for">Imagen del Badge para %1$s</string>
<string name="new_badge_award_notif">Has recibido un nuevo Badge</string>
<string name="award_granted_to">Badge otorgado a</string>
<string name="copied_note_text_to_clipboard">Texto de nota copiado al portapapeles</string>
<string name="copied_user_id_to_clipboard">\@npub del autor copiado al portapapeles</string>
<string name="copied_note_id_to_clipboard">ID de nota copiada (@note1) al portapapeles</string>
<string name="select_text_dialog_top">Seleccionar texto</string>
<string name="point_to_the_qr_code">Apunta al código QR</string>
<string name="show_qr">Mostrar el QR</string>
<string name="profile_image">Imagen de Perfil</string>
@@ -80,6 +93,7 @@
<string name="private_message_feed">Fuente de mensajes privados</string>
<string name="public_chat_feed">Fuente de chat público</string>
<string name="global_feed">Fuente global</string>
<string name="search_feed">Fuente de búsqueda</string>
<string name="add_a_relay">Anadir transmisor</string>
<string name="display_name">Nombre para mostrar</string>
<string name="my_display_name">Mi nombre para mostrar</string>
@@ -155,15 +169,12 @@
<string name="public_chat">Chat público</string>
<string name="posts_received">publicaciones recibidas</string>
<string name="remove">Eliminar</string>
<string name="sats" translatable="false">sats</string>
<string name="auto">Auto</string>
<string name="translated_from">traducido de</string>
<string name="to">a</string>
<string name="show_in">Mostrar en</string>
<string name="first">primero</string>
<string name="always_translate_to">"Traducir siempre a "</string>
<string name="nip_05" translatable="false">NIP-05</string>
<string name="lnurl" translatable="false">LNURL...</string>
<string name="never">nunca</string>
<string name="now">ahora</string>
<string name="h">h</string>
@@ -174,4 +185,16 @@
<string name="report_hateful_speech">Denuncia discurso de odio</string>
<string name="report_nudity_porn">Reportar desnudos / Porno</string>
<string name="others">otros</string>
<string name="quick_action_select">Selecciona</string>
<string name="quick_action_share_browser_link">Compartir enlace del navegador</string>
<string name="quick_action_share">Compartir</string>
<string name="quick_action_copy_user_id">ID del autor</string>
<string name="quick_action_copy_note_id">ID de la nota</string>
<string name="quick_action_copy_text">Copiar texto</string>
<string name="quick_action_delete">Eliminar</string>
<string name="quick_action_unfollow">Dejar de seguir</string>
<string name="quick_action_follow">Seguir</string>
<string name="quick_action_request_deletion_alert_title">Solicitar eliminación</string>
<string name="quick_action_request_deletion_alert_body">Amethyst solicitará que se elimine su nota de los relays a los que está conectado actualmente. No hay garantía de que su nota se elimine permanentemente de esos relays, o de otros relays donde pueda almacenarse.</string>
<string name="backup_keys">Claves de respaldo</string>
</resources>

View File

@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name_release" translatable="false">Amethyst</string>
<string name="app_name_debug" translatable="false">Amethyst Debug</string>
<string name="point_to_the_qr_code">Pointer vers le QR code</string>
<string name="show_qr">Montrer le QR code</string>
<string name="profile_image">Image de profil</string>
@@ -80,6 +78,7 @@
<string name="private_message_feed">Flux de messages privés</string>
<string name="public_chat_feed">Flux de chat public</string>
<string name="global_feed">Flux mondial</string>
<string name="search_feed">Flux de recherche</string>
<string name="add_a_relay">Ajouter un relais</string>
<string name="display_name">Nom visible du public</string>
<string name="my_display_name">Mon nom visible du public</string>
@@ -155,15 +154,12 @@
<string name="public_chat">Chat public</string>
<string name="posts_received">messages reçus</string>
<string name="remove">Retirer</string>
<string name="sats" translatable="false">sats</string>
<string name="auto">Auto</string>
<string name="translated_from">traduit de</string>
<string name="to">vers</string>
<string name="show_in">Montre en</string>
<string name="first">en premier</string>
<string name="always_translate_to">"Toujours traduire en "</string>
<string name="nip_05" translatable="false">NIP-05</string>
<string name="lnurl" translatable="false">LNURL...</string>
<string name="never">jamais</string>
<string name="now">maintenant</string>
<string name="h">h</string>
@@ -182,4 +178,5 @@
\n- **Conservez** une sauvegarde sécurisée de votre clé secrète pour la récupération de compte. Nous vous recommandons d\'utiliser un gestionnaire de mots de passe.
</string>
<string name="secret_key_copied_to_clipboard">Clé secrète (nsec) copiée dans le presse-papiers</string>
<string name="backup_keys">Clés de sauvegarde</string>
</resources>

View File

@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name_release" translatable="false">Amethyst</string>
<string name="app_name_debug" translatable="false">Amethyst Debug</string>
<string name="point_to_the_qr_code">Mutass a QR kódra</string>
<string name="show_qr">QR kód megjelenítése</string>
<string name="profile_image">Profil kép</string>
@@ -156,15 +154,12 @@
<string name="public_chat">Publikus Chat</string>
<string name="posts_received">beérkezett hozzászólások</string>
<string name="remove">Eltávolítás</string>
<string name="sats" translatable="false">sats</string>
<string name="auto">Automatikus</string>
<string name="translated_from">fordítás erről</string>
<string name="to">erre</string>
<string name="show_in">Mutasd ebben</string>
<string name="first">első</string>
<string name="always_translate_to">Mindig fordítsa le</string>
<string name="nip_05" translatable="false">NIP-05</string>
<string name="lnurl" translatable="false">LNURL...</string>
<string name="never">soha</string>
<string name="now">most</string>
<string name="h">ó</string>

View File

@@ -78,6 +78,7 @@
<string name="private_message_feed">Feed de mensagens privadas</string>
<string name="public_chat_feed">Feed Chat Público</string>
<string name="global_feed">Feed Global</string>
<string name="search_feed">Fonte de pesquisa</string>
<string name="add_a_relay">Adicionar um Relay</string>
<string name="display_name">Nome de Exibição</string>
<string name="my_display_name">Meu nome de exibição</string>

View File

@@ -1,6 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name_release" translatable="false">Amethyst</string>
<string name="app_name_debug" translatable="false">Amethyst Debug</string>
<string name="point_to_the_qr_code">Наведите на QR код</string>
<string name="show_qr">Показать QR</string>
<string name="profile_image">Фото профиля</string>
@@ -160,8 +158,6 @@
<string name="show_in">Показать сперва на</string>
<string name="first" />
<string name="always_translate_to">Всегда переводить на</string>
<string name="nip_05" translatable="false">NIP-05</string>
<string name="lnurl" translatable="false">LNURL...</string>
<string name="never">никогда</string>
<string name="now">сейчас</string>
<string name="h">ч</string>

View File

@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name_release">Amethyst</string>
<string name="app_name_debug">Amethyst Debug</string>
<string name="point_to_the_qr_code">QR Code\'a yönlendir</string>
<string name="show_qr">QR Göster</string>
<string name="profile_image">Profil Resmi</string>
@@ -154,15 +152,12 @@
<string name="public_chat">Herkese Açık Chat</string>
<string name="posts_received">alınan gönderiler</string>
<string name="remove">Kaldır</string>
<string name="sats">satlar</string>
<string name="auto">Otomatik</string>
<string name="translated_from">şundan çevirildi</string>
<string name="to">şuna</string>
<string name="show_in">Şunda göster</string>
<string name="first">ilk</string>
<string name="always_translate_to">Her zaman şuna çevir</string>
<string name="nip_05">NIP-05</string>
<string name="lnurl">LNURL</string>
<string name="never">asla</string>
<string name="now">şimdi</string>
<string name="h">h</string>

View File

@@ -1,6 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name_release" translatable="false">Amethyst</string>
<string name="app_name_debug" translatable="false">Amethyst Debug</string>
<string name="point_to_the_qr_code">Наведіть на QR код</string>
<string name="show_qr">Показати QR</string>
<string name="profile_image">Фото профілю</string>
@@ -160,8 +158,6 @@
<string name="show_in">Показати спершу на</string>
<string name="first" />
<string name="always_translate_to">Завжди перекладати на</string>
<string name="nip_05" translatable="false">NIP-05</string>
<string name="lnurl" translatable="false">LNURL...</string>
<string name="never">ніколи</string>
<string name="now">зараз</string>
<string name="h">год</string>

View File

@@ -0,0 +1,206 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="point_to_the_qr_code">對準二維碼</string>
<string name="show_qr">顯示二維碼</string>
<string name="profile_image">頭像圖片</string>
<string name="scan_qr">掃描二維碼</string>
<string name="show_anyway">一直顯示</string>
<string name="post_was_flagged_as_inappropriate_by">帖文被標記為不當</string>
<string name="post_not_found">未找到帖子</string>
<string name="channel_image">頻道圖片</string>
<string name="referenced_event_not_found">未找到相關事件</string>
<string name="could_not_decrypt_the_message">無法解密消息</string>
<string name="group_picture">羣聊圖片</string>
<string name="explicit_content">明確內容</string>
<string name="spam">垃圾郵件</string>
<string name="impersonation">冒充</string>
<string name="illegal_behavior">非法行為</string>
<string name="unknown">未知</string>
<string name="relay_icon">中繼器圖標</string>
<string name="unknown_author">未知作者</string>
<string name="copy_text">複製文本</string>
<string name="copy_user_pubkey">複製作者@npub</string>
<string name="copy_note_id">複製筆記ID</string>
<string name="broadcast">廣播</string>
<string name="block_hide_user"><![CDATA[阻止並隱藏用户]]></string>
<string name="report_spam_scam">舉報垃圾郵件/詐騙</string>
<string name="report_impersonation">舉報冒充</string>
<string name="report_explicit_content">舉報明確內容</string>
<string name="report_illegal_behaviour">舉報非法行為</string>
<string name="login_with_a_private_key_to_be_able_to_reply">使用私鑰登錄以便回覆</string>
<string name="login_with_a_private_key_to_be_able_to_boost_posts">使用私鑰登錄以便提升帖子</string>
<string name="login_with_a_private_key_to_like_posts">使用私鑰登錄以便點贊帖子</string>
<string name="no_zap_amount_setup_long_press_to_change">沒有設置Zap金額。長按以更改</string>
<string name="login_with_a_private_key_to_be_able_to_send_zaps">使用私鑰登錄以便發送Zaps</string>
<string name="zaps">Zaps</string>
<string name="view_count">瀏覽次數</string>
<string name="boost">提升</string>
<string name="boosted">已提升</string>
<string name="quote">引用</string>
<string name="new_amount_in_sats">新的聰金額</string>
<string name="add">添加</string>
<string name="replying_to">"回覆 "</string>
<string name="and">" 和 "</string>
<string name="in_channel">" 在頻道 "</string>
<string name="profile_banner">個人檔案橫幅</string>
<string name="following">" 關注"</string>
<string name="followers">" 粉絲"</string>
<string name="profile">個人檔案</string>
<string name="security_filters">安全過濾器</string>
<string name="log_out">登出</string>
<string name="show_more">顯示更多</string>
<string name="lightning_invoice">閃電網絡發票</string>
<string name="pay">付款</string>
<string name="lightning_tips">閃電小費</string>
<string name="note_to_receiver">聰給收款方的留言</string>
<string name="thank_you_so_much">非常感謝!</string>
<string name="amount_in_sats">聰數量</string>
<string name="send_sats">發送聰</string>
<string name="never_translate_from">"不再翻譯"</string>
<string name="error_parsing_preview_for">"解析 %1$s 預覽出錯:%2$s"</string>
<string name="preview_card_image_for">"預覽 %1$s 卡片圖片"</string>
<string name="new_channel">新頻道</string>
<string name="channel_name">頻道名</string>
<string name="my_awesome_group">我的精彩羣聊</string>
<string name="picture_url">圖片鏈接</string>
<string name="description">描述</string>
<string name="about_us">"關於我們.. "</string>
<string name="what_s_on_your_mind">你在想什麼?</string>
<string name="post">發佈</string>
<string name="save">保存</string>
<string name="create">創建</string>
<string name="cancel">取消</string>
<string name="failed_to_upload_the_image">上傳圖片失敗</string>
<string name="relay_address">中繼器地址</string>
<string name="posts">帖文</string>
<string name="errors">錯誤</string>
<string name="home_feed">主頁</string>
<string name="private_message_feed">私信</string>
<string name="public_chat_feed">公開聊天</string>
<string name="global_feed">全球</string>
<string name="search_feed">搜索</string>
<string name="add_a_relay">添加中繼器</string>
<string name="display_name">顯示名稱</string>
<string name="my_display_name">我的顯示名稱</string>
<string name="username">用户名</string>
<string name="my_username">我的用户名</string>
<string name="about_me">關於我</string>
<string name="avatar_url">頭像鏈接</string>
<string name="banner_url">橫幅鏈接</string>
<string name="website_url">網站鏈接</string>
<string name="ln_address">LN地址</string>
<string name="ln_url_outdated">LN鏈接過期</string>
<string name="image_saved_to_the_gallery">圖片已保存到相冊</string>
<string name="failed_to_save_the_image">保存圖片失敗</string>
<string name="upload_image">上傳圖片</string>
<string name="uploading">上傳中…</string>
<string name="user_does_not_have_a_lightning_address_setup_to_receive_sats">用户尚未設置閃電地址以接收聰</string>
<string name="reply_here">"回覆這裏.. "</string>
<string name="copies_the_note_id_to_the_clipboard_for_sharing">複製筆記ID到剪貼板供分享</string>
<string name="copy_channel_id_note_to_the_clipboard">複製頻道ID筆記到剪貼板</string>
<string name="edits_the_channel_metadata">編輯頻道元數據</string>
<string name="join">加入</string>
<string name="known">私聊</string>
<string name="new_requests">新請求</string>
<string name="blocked_users">已隱藏用户</string>
<string name="new_threads">新動態</string>
<string name="conversations">會話</string>
<string name="notes">動態</string>
<string name="replies">回覆</string>
<string name="follows">關注</string>
<string name="reports">舉報</string>
<string name="more_options">更多選項</string>
<string name="relays">中繼</string>
<string name="website">網站</string>
<string name="lightning_address">閃電地址</string>
<string name="copies_the_nsec_id_your_password_to_the_clipboard_for_backup">複製 Nsec ID您的私人密鑰到剪貼板以備備份</string>
<string name="copy_private_key_to_the_clipboard">複製私鑰到剪貼板</string>
<string name="copies_the_public_key_to_the_clipboard_for_sharing">複製公鑰到剪貼板以供共享</string>
<string name="copy_public_key_npub_to_the_clipboard">複製公鑰NPub到剪貼板</string>
<string name="send_a_direct_message">發送直接消息</string>
<string name="edits_the_user_s_metadata">編輯用户元數據</string>
<string name="follow">關注</string>
<string name="unblock">不隱藏</string>
<string name="copy_user_id">複製用户ID</string>
<string name="unblock_user">隱藏該用户</string>
<string name="npub_hex_username">npubhex用户名 </string>
<string name="clear">清除</string>
<string name="app_logo">應用標誌</string>
<string name="nsec_npub_hex_private_key">nsec/ npub/ hex 私鑰</string>
<string name="show_password">顯示密碼</string>
<string name="hide_password">隱藏密碼</string>
<string name="invalid_key">無效密鑰</string>
<string name="i_accept_the">我接受</string>
<string name="terms_of_use">使用條款</string>
<string name="acceptance_of_terms_is_required">需要接受條款</string>
<string name="key_is_required">需要密鑰</string>
<string name="login">登錄</string>
<string name="generate_a_new_key">生成新密鑰</string>
<string name="loading_feed">正在加載</string>
<string name="error_loading_replies">加載回覆出錯:</string>
<string name="try_again">重試</string>
<string name="feed_is_empty">信息流為空。</string>
<string name="refresh">刷新</string>
<string name="created">創建</string>
<string name="with_description_of">描述為</string>
<string name="and_picture">並上傳圖片</string>
<string name="changed_chat_name_to">將聊天名稱修改為</string>
<string name="description_to">描述更改為</string>
<string name="and_picture_to">圖片更改為</string>
<string name="leave">離開</string>
<string name="unfollow">取關</string>
<string name="channel_created">頻道已創建</string>
<string name="channel_information_changed_to">頻道信息已更改為</string>
<string name="public_chat">公共聊天</string>
<string name="posts_received">收到文章</string>
<string name="remove">移除</string>
<string name="auto">自動</string>
<string name="translated_from"></string>
<string name="to">更改為</string>
<string name="show_in">先顯示</string>
<string name="first">在前</string>
<string name="always_translate_to">總是翻譯為</string>
<string name="never">從不</string>
<string name="now">現在</string>
<string name="h">小時</string>
<string name="m"></string>
<string name="d"></string>
<string name="nudity">裸體</string>
<string name="profanity_hateful_speech">不文雅的言辭 / 敵意言論</string>
<string name="report_hateful_speech">舉報敵意言論</string>
<string name="report_nudity_porn">舉報裸體 / 情色內容</string>
<string name="others">其他</string>
<string name="mark_all_known_as_read">將所有已知內容標記為已讀</string>
<string name="mark_all_new_as_read">將所有新內容標記為已讀</string>
<string name="mark_all_as_read">將所有內容標記為已讀</string>
<string name="account_backup_tips_md" tools:ignore="Typos">
## 備份與安全提示
\n\n您的帳户由一個私人密鑰保護。 密鑰是以**nsec1**開頭的長隨機字符串。任何擁有您的私人密鑰的人都可以使用您的身份發佈內容。
\n\n- **不要**將您的私人密鑰添加到您不信任的任何網站或軟件,亦不要在網上公開。
\n- Amethyst 開發人員**永遠不會**要求您提供私人密鑰。
\n- **請**保留您的私人密鑰的安全備份,以備帳户恢復。 我們建議使用密碼管理器。
</string>
<string name="secret_key_copied_to_clipboard">私人密鑰nsec已複製到剪貼板</string>
<string name="copy_my_secret_key">複製我的私人密鑰</string>
<string name="biometric_authentication_failed">身份驗證失敗</string>
<string name="biometric_error">錯誤</string>
<string name="badge_created_by">"由 %1$s 創建"</string>
<string name="badge_award_image_for">"頒發給 %1$s 的徽章圖片"</string>
<string name="new_badge_award_notif">您收到了新的徽章獎勵</string>
<string name="award_granted_to">徽章獎勵授予</string>
<string name="copied_note_text_to_clipboard">文本已複製到剪貼板</string>
<string name="copied_user_id_to_clipboard" tools:ignore="Typos">複製作者的 @npub 到剪貼板</string>
<string name="copied_note_id_to_clipboard" tools:ignore="Typos">複製文章ID (@note1) 到剪貼板</string>
<string name="select_text_dialog_top">選擇文本</string>
<string name="quick_action_select">選擇</string>
<string name="quick_action_share_browser_link">分享瀏覽網址</string>
<string name="quick_action_share">分享</string>
<string name="quick_action_copy_user_id">@提及</string>
<string name="quick_action_copy_note_id">引用</string>
<string name="quick_action_copy_text">複製</string>
<string name="quick_action_delete">刪除</string>
<string name="quick_action_unfollow">取關</string>
<string name="quick_action_follow">關注</string>
<string name="quick_action_request_deletion_alert_title">請求刪貼</string>
<string name="quick_action_request_deletion_alert_body">Amethyst 將請求您的記錄從當前連接的中繼器中刪除。不能保證您發佈的筆記將被永久從那些中繼器或其他存儲筆記的中繼器中刪除。</string>
</resources>

View File

@@ -0,0 +1,206 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="point_to_the_qr_code">對準 QR</string>
<string name="show_qr">顯示 QR</string>
<string name="profile_image">頭像圖片</string>
<string name="scan_qr">掃描 QR</string>
<string name="show_anyway">一直顯示</string>
<string name="post_was_flagged_as_inappropriate_by">貼文被標記為不當</string>
<string name="post_not_found">未找到貼文</string>
<string name="channel_image">頻道圖片</string>
<string name="referenced_event_not_found">未找到相關事件</string>
<string name="could_not_decrypt_the_message">無法解密消息</string>
<string name="group_picture">群組圖片</string>
<string name="explicit_content">露骨內容</string>
<string name="spam">垃圾郵件</string>
<string name="impersonation">冒充</string>
<string name="illegal_behavior">非法行為</string>
<string name="unknown">未知</string>
<string name="relay_icon">中繼器圖標</string>
<string name="unknown_author">未知作者</string>
<string name="copy_text">複製文本</string>
<string name="copy_user_pubkey">複製作者@npub</string>
<string name="copy_note_id">複製筆記ID</string>
<string name="broadcast">廣播</string>
<string name="block_hide_user"><![CDATA[阻止並隱藏用户]]></string>
<string name="report_spam_scam">舉報垃圾郵件/詐騙</string>
<string name="report_impersonation">舉報冒充</string>
<string name="report_explicit_content">舉報露骨內容</string>
<string name="report_illegal_behaviour">舉報非法行為</string>
<string name="login_with_a_private_key_to_be_able_to_reply">使用私鑰登錄以便回覆</string>
<string name="login_with_a_private_key_to_be_able_to_boost_posts">使用私鑰登錄以便提升貼文</string>
<string name="login_with_a_private_key_to_like_posts">使用私鑰登錄以便點贊貼文</string>
<string name="no_zap_amount_setup_long_press_to_change">沒有設置Zap金額。長按以更改</string>
<string name="login_with_a_private_key_to_be_able_to_send_zaps">使用私鑰登錄以便發送Zaps</string>
<string name="zaps">Zaps</string>
<string name="view_count">瀏覽次數</string>
<string name="boost">提升</string>
<string name="boosted">已提升</string>
<string name="quote">引用</string>
<string name="new_amount_in_sats">新的金額 (sats)</string>
<string name="add">添加</string>
<string name="replying_to">"回覆 "</string>
<string name="and">" 和 "</string>
<string name="in_channel">" 在頻道 "</string>
<string name="profile_banner">個人檔案橫幅</string>
<string name="following">" 關注"</string>
<string name="followers">" 粉絲"</string>
<string name="profile">個人檔案</string>
<string name="security_filters">安全過濾器</string>
<string name="log_out">登出</string>
<string name="show_more">顯示更多</string>
<string name="lightning_invoice">閃電網絡發票</string>
<string name="pay">付款</string>
<string name="lightning_tips">閃電小費</string>
<string name="note_to_receiver">寫給收款方的留言</string>
<string name="thank_you_so_much">非常感謝!</string>
<string name="amount_in_sats">數量 (sats)</string>
<string name="send_sats">發送 sats</string>
<string name="never_translate_from">"不再翻譯"</string>
<string name="error_parsing_preview_for">"解析 %1$s 預覽出錯:%2$s"</string>
<string name="preview_card_image_for">"預覽 %1$s 卡片圖片"</string>
<string name="new_channel">新頻道</string>
<string name="channel_name">頻道名</string>
<string name="my_awesome_group">我的精彩羣聊</string>
<string name="picture_url">圖片鏈接</string>
<string name="description">描述</string>
<string name="about_us">"關於我們.. "</string>
<string name="what_s_on_your_mind">你在想什麼?</string>
<string name="post">發佈</string>
<string name="save">保存</string>
<string name="create">創建</string>
<string name="cancel">取消</string>
<string name="failed_to_upload_the_image">上傳圖片失敗</string>
<string name="relay_address">中繼器地址</string>
<string name="posts">貼文</string>
<string name="errors">錯誤</string>
<string name="home_feed">主頁</string>
<string name="private_message_feed">私信</string>
<string name="public_chat_feed">公開聊天</string>
<string name="global_feed">全球</string>
<string name="search_feed">搜索</string>
<string name="add_a_relay">添加中繼器</string>
<string name="display_name">顯示名稱</string>
<string name="my_display_name">我的顯示名稱</string>
<string name="username">用户名</string>
<string name="my_username">我的用户名</string>
<string name="about_me">關於我</string>
<string name="avatar_url">頭像鏈接</string>
<string name="banner_url">橫幅鏈接</string>
<string name="website_url">網站鏈接</string>
<string name="ln_address">LN 地址</string>
<string name="ln_url_outdated">LN 鏈接(過期)</string>
<string name="image_saved_to_the_gallery">圖片已保存到相冊</string>
<string name="failed_to_save_the_image">保存圖片失敗</string>
<string name="upload_image">上傳圖片</string>
<string name="uploading">上傳中…</string>
<string name="user_does_not_have_a_lightning_address_setup_to_receive_sats">用户尚未設置閃電地址以接收 sats</string>
<string name="reply_here">"回覆這裡.."</string>
<string name="copies_the_note_id_to_the_clipboard_for_sharing">複製筆記ID到剪貼板供分享</string>
<string name="copy_channel_id_note_to_the_clipboard">複製頻道ID筆記到剪貼板</string>
<string name="edits_the_channel_metadata">編輯頻道元數據</string>
<string name="join">加入</string>
<string name="known">已准</string>
<string name="new_requests">新請求</string>
<string name="blocked_users">已隱藏用户</string>
<string name="new_threads">新動態</string>
<string name="conversations">會話</string>
<string name="notes">動態</string>
<string name="replies">回覆</string>
<string name="follows">關注</string>
<string name="reports">舉報</string>
<string name="more_options">更多選項</string>
<string name="relays">中繼</string>
<string name="website">網站</string>
<string name="lightning_address">閃電地址</string>
<string name="copies_the_nsec_id_your_password_to_the_clipboard_for_backup">複製 NSec (私人密鑰)到剪貼板以備備份</string>
<string name="copy_private_key_to_the_clipboard">複製私鑰到剪貼板</string>
<string name="copies_the_public_key_to_the_clipboard_for_sharing">複製公鑰到剪貼板以供共享</string>
<string name="copy_public_key_npub_to_the_clipboard">複製公鑰NPub到剪貼板</string>
<string name="send_a_direct_message">發送直接消息</string>
<string name="edits_the_user_s_metadata">編輯用户元數據</string>
<string name="follow">關注</string>
<string name="unblock">不隱藏</string>
<string name="copy_user_id">複製用户ID</string>
<string name="unblock_user">隱藏該用户</string>
<string name="npub_hex_username">npubhex用户名 </string>
<string name="clear">清除</string>
<string name="app_logo">應用標誌</string>
<string name="nsec_npub_hex_private_key">nsec/ npub/ hex 私鑰</string>
<string name="show_password">顯示密碼</string>
<string name="hide_password">隱藏密碼</string>
<string name="invalid_key">無效密鑰</string>
<string name="i_accept_the">我接受</string>
<string name="terms_of_use">使用條款</string>
<string name="acceptance_of_terms_is_required">需要接受條款</string>
<string name="key_is_required">需要密鑰</string>
<string name="login">登錄</string>
<string name="generate_a_new_key">生成新密鑰</string>
<string name="loading_feed">正在加載</string>
<string name="error_loading_replies">加載回覆出錯:</string>
<string name="try_again">重試</string>
<string name="feed_is_empty">信息流為空。</string>
<string name="refresh">刷新</string>
<string name="created">創建</string>
<string name="with_description_of">描述為</string>
<string name="and_picture">並上傳圖片</string>
<string name="changed_chat_name_to">將聊天名稱修改為</string>
<string name="description_to">描述更改為</string>
<string name="and_picture_to">圖片更改為</string>
<string name="leave">離開</string>
<string name="unfollow">取關</string>
<string name="channel_created">頻道已創建</string>
<string name="channel_information_changed_to">頻道信息已更改為</string>
<string name="public_chat">公共聊天</string>
<string name="posts_received">收到文章</string>
<string name="remove">移除</string>
<string name="auto">自動</string>
<string name="translated_from"></string>
<string name="to">更改為</string>
<string name="show_in">先顯示</string>
<string name="first">在前</string>
<string name="always_translate_to">總是翻譯為</string>
<string name="never">從不</string>
<string name="now">現在</string>
<string name="h">小時</string>
<string name="m"></string>
<string name="d"></string>
<string name="nudity">裸體</string>
<string name="profanity_hateful_speech">不文雅的言辭 / 敵意言論</string>
<string name="report_hateful_speech">舉報敵意言論</string>
<string name="report_nudity_porn">舉報裸體 / 情色內容</string>
<string name="others">其他</string>
<string name="mark_all_known_as_read">將所有已知內容標記為已讀</string>
<string name="mark_all_new_as_read">將所有新內容標記為已讀</string>
<string name="mark_all_as_read">將所有內容標記為已讀</string>
<string name="account_backup_tips_md" tools:ignore="Typos">
## 備份與安全提示
\n\n您的帳户由一個私人密鑰保護。 密鑰是以**nsec1**開頭的長隨機字符串。任何擁有您的私人密鑰的人都可以使用您的身份發佈內容。
\n\n- **不要**將您的私人密鑰添加到您不信任的任何網站或軟件,亦不要在網上公開。
\n- Amethyst 開發人員**永遠不會**要求您提供私人密鑰。
\n- **請**保留您的私人密鑰的安全備份,以備帳户恢復。 我們建議使用密碼管理器。
</string>
<string name="secret_key_copied_to_clipboard">私人密鑰nsec已複製到剪貼板</string>
<string name="copy_my_secret_key">複製我的私人密鑰</string>
<string name="biometric_authentication_failed">身份驗證失敗</string>
<string name="biometric_error">錯誤</string>
<string name="badge_created_by">"由 %1$s 創建"</string>
<string name="badge_award_image_for">"頒發給 %1$s 的徽章圖片"</string>
<string name="new_badge_award_notif">您收到了新的徽章獎勵</string>
<string name="award_granted_to">徽章獎勵授予</string>
<string name="copied_note_text_to_clipboard">文本已複製到剪貼板</string>
<string name="copied_user_id_to_clipboard" tools:ignore="Typos">複製作者的 @npub 到剪貼板</string>
<string name="copied_note_id_to_clipboard" tools:ignore="Typos">複製文章ID (@note1) 到剪貼板</string>
<string name="select_text_dialog_top">選擇文本</string>
<string name="quick_action_select">選擇</string>
<string name="quick_action_share_browser_link">分享瀏覽網址</string>
<string name="quick_action_share">分享</string>
<string name="quick_action_copy_user_id">作者 ID</string>
<string name="quick_action_copy_note_id">貼文 ID</string>
<string name="quick_action_copy_text">複製</string>
<string name="quick_action_delete">刪除</string>
<string name="quick_action_unfollow">取關</string>
<string name="quick_action_follow">關注</string>
<string name="quick_action_request_deletion_alert_title">請求刪貼</string>
<string name="quick_action_request_deletion_alert_body">Amethyst 將請求您的記錄從當前連接的中繼器中刪除。不能保證您發佈的筆記將被永久從那些中繼器或其他存儲筆記的中繼器中刪除。</string>
</resources>

View File

@@ -0,0 +1,205 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="point_to_the_qr_code">对准二维码</string>
<string name="show_qr">显示二维码</string>
<string name="profile_image">头像图片</string>
<string name="scan_qr">扫描二维码</string>
<string name="show_anyway">一直显示</string>
<string name="post_was_flagged_as_inappropriate_by">帖文被标记为不当</string>
<string name="post_not_found">未找到帖子</string>
<string name="channel_image">频道图片</string>
<string name="referenced_event_not_found">未找到相关事件</string>
<string name="could_not_decrypt_the_message">无法解密消息</string>
<string name="group_picture">群聊图片</string>
<string name="explicit_content">明确内容</string>
<string name="spam">垃圾邮件</string>
<string name="impersonation">冒充</string>
<string name="illegal_behavior">非法行为</string>
<string name="unknown">未知</string>
<string name="relay_icon">中继器图标</string>
<string name="unknown_author">未知作者</string>
<string name="copy_text">复制文本</string>
<string name="copy_user_pubkey">复制作者@npub</string>
<string name="copy_note_id">复制笔记ID</string>
<string name="broadcast">广播</string>
<string name="block_hide_user"><![CDATA[阻止并隐藏用户]]></string>
<string name="report_spam_scam">举报垃圾邮件/诈骗</string>
<string name="report_impersonation">举报冒充</string>
<string name="report_explicit_content">举报明确内容</string>
<string name="report_illegal_behaviour">举报非法行为</string>
<string name="login_with_a_private_key_to_be_able_to_reply">使用私钥登录以便回复</string>
<string name="login_with_a_private_key_to_be_able_to_boost_posts">使用私钥登录以便提升帖子</string>
<string name="login_with_a_private_key_to_like_posts">使用私钥登录以便点赞帖子</string>
<string name="no_zap_amount_setup_long_press_to_change">没有设置Zap金额。长按以更改</string>
<string name="login_with_a_private_key_to_be_able_to_send_zaps">使用私钥登录以便发送Zaps</string>
<string name="zaps">Zaps</string>
<string name="view_count">浏览次数</string>
<string name="boost">提升</string>
<string name="boosted">已提升</string>
<string name="quote">引用</string>
<string name="new_amount_in_sats">新的聪金额</string>
<string name="add">添加</string>
<string name="replying_to">"回复 "</string>
<string name="and">" 和 "</string>
<string name="in_channel">" 在频道 "</string>
<string name="profile_banner">个人档案横幅</string>
<string name="following">" 关注"</string>
<string name="followers">" 粉丝"</string>
<string name="profile">个人档案</string>
<string name="security_filters">安全过滤器</string>
<string name="log_out">登出</string>
<string name="show_more">显示更多</string>
<string name="lightning_invoice">闪电网络发票</string>
<string name="pay">付款</string>
<string name="lightning_tips">闪电小费</string>
<string name="note_to_receiver">聪给收款方的留言</string>
<string name="thank_you_so_much">非常感谢!</string>
<string name="amount_in_sats">聪数量</string>
<string name="send_sats">发送聪</string>
<string name="never_translate_from">"不再翻译"</string>
<string name="error_parsing_preview_for">"解析 %1$s 预览出错:%2$s"</string>
<string name="preview_card_image_for">"预览 %1$s 卡片图片"</string>
<string name="new_channel">新频道</string>
<string name="channel_name">频道名</string>
<string name="my_awesome_group">我的精彩群聊</string>
<string name="picture_url">图片链接</string>
<string name="description">描述</string>
<string name="about_us">"关于我们.. "</string>
<string name="what_s_on_your_mind">你在想什么?</string>
<string name="post">发布</string>
<string name="save">保存</string>
<string name="create">创建</string>
<string name="cancel">取消</string>
<string name="failed_to_upload_the_image">上传图片失败</string>
<string name="relay_address">中继器地址</string>
<string name="posts">帖文</string>
<string name="errors">错误</string>
<string name="home_feed">主页</string>
<string name="private_message_feed">私信</string>
<string name="public_chat_feed">公开聊天</string>
<string name="global_feed">全球</string>
<string name="search_feed">搜索</string>
<string name="add_a_relay">添加中继器</string>
<string name="display_name">显示名称</string>
<string name="my_display_name">我的显示名称</string>
<string name="username">用户名</string>
<string name="my_username">我的用户名</string>
<string name="about_me">关于我</string>
<string name="avatar_url">头像链接</string>
<string name="banner_url">横幅链接</string>
<string name="website_url">网站链接</string>
<string name="ln_address">LN地址</string>
<string name="ln_url_outdated">LN链接过期</string>
<string name="image_saved_to_the_gallery">图片已保存到相册</string>
<string name="failed_to_save_the_image">保存图片失败</string>
<string name="upload_image">上传图片</string>
<string name="uploading">上传中…</string>
<string name="user_does_not_have_a_lightning_address_setup_to_receive_sats">用户尚未设置闪电地址以接收聪</string>
<string name="reply_here">"回复这里.. "</string>
<string name="copies_the_note_id_to_the_clipboard_for_sharing">复制笔记ID到剪贴板供分享</string>
<string name="copy_channel_id_note_to_the_clipboard">复制频道ID笔记到剪贴板</string>
<string name="edits_the_channel_metadata">编辑频道元数据</string>
<string name="join">加入</string>
<string name="known">私聊</string>
<string name="new_requests">新请求</string>
<string name="blocked_users">已隐藏用户</string>
<string name="new_threads">新动态</string>
<string name="conversations">会话</string>
<string name="notes">动态</string>
<string name="replies">回复</string>
<string name="follows">关注</string>
<string name="reports">举报</string>
<string name="more_options">更多选项</string>
<string name="relays">中继</string>
<string name="website">网站</string>
<string name="lightning_address">闪电地址</string>
<string name="copies_the_nsec_id_your_password_to_the_clipboard_for_backup">复制 Nsec ID您的私人密钥到剪贴板以备备份</string>
<string name="copy_private_key_to_the_clipboard">复制私钥到剪贴板</string>
<string name="copies_the_public_key_to_the_clipboard_for_sharing">复制公钥到剪贴板以供共享</string>
<string name="copy_public_key_npub_to_the_clipboard">复制公钥NPub到剪贴板</string>
<string name="send_a_direct_message">发送直接消息</string>
<string name="edits_the_user_s_metadata">编辑用户元数据</string>
<string name="follow">关注</string>
<string name="unblock">不隐藏</string>
<string name="copy_user_id">复制用户ID</string>
<string name="unblock_user">隐藏该用户</string>
<string name="npub_hex_username">npubhex用户名 </string>
<string name="clear">清除</string>
<string name="app_logo">应用标志</string>
<string name="nsec_npub_hex_private_key">nsec/ npub/ hex 私钥</string>
<string name="show_password">显示密码</string>
<string name="hide_password">隐藏密码</string>
<string name="invalid_key">无效密钥</string>
<string name="i_accept_the">我接受</string>
<string name="terms_of_use">使用条款</string>
<string name="acceptance_of_terms_is_required">需要接受条款</string>
<string name="key_is_required">需要密钥</string>
<string name="login">登录</string>
<string name="generate_a_new_key">生成新密钥</string>
<string name="loading_feed">正在加载</string>
<string name="error_loading_replies">加载回复出错:</string>
<string name="try_again">重试</string>
<string name="feed_is_empty">信息流为空。</string>
<string name="refresh">刷新</string>
<string name="created">创建</string>
<string name="with_description_of">描述为</string>
<string name="and_picture">并上传图片</string>
<string name="changed_chat_name_to">将聊天名称修改为</string>
<string name="description_to">描述更改为</string>
<string name="and_picture_to">图片更改为</string>
<string name="leave">离开</string>
<string name="unfollow">取关</string>
<string name="channel_created">频道已创建</string>
<string name="channel_information_changed_to">频道信息已更改为</string>
<string name="public_chat">公共聊天</string>
<string name="posts_received">收到文章</string>
<string name="remove">移除</string>
<string name="auto">自动</string>
<string name="translated_from"></string>
<string name="to">更改为</string>
<string name="show_in">先显示</string>
<string name="first">在前</string>
<string name="always_translate_to">总是翻译为</string>
<string name="never">从不</string>
<string name="now">现在</string>
<string name="h">小时</string>
<string name="m"></string>
<string name="d"></string>
<string name="nudity">裸体</string>
<string name="profanity_hateful_speech">不文雅的言辞 / 敌意言论</string>
<string name="report_hateful_speech">举报敌意言论</string>
<string name="report_nudity_porn">举报裸体 / 情色内容</string>
<string name="others">其他</string>
<string name="mark_all_known_as_read">将所有已知内容标记为已读</string>
<string name="mark_all_new_as_read">将所有新内容标记为已读</string>
<string name="mark_all_as_read">将所有内容标记为已读</string>
<string name="account_backup_tips_md" tools:ignore="Typos">
## 备份与安全提示
\n\n您的帐户由一个私人密钥保护。 密钥是以**nsec1**开头的长随机字符串。任何拥有您的私人密钥的人都可以使用您的身份发布内容。
\n\n- **不要**将您的私人密钥添加到您不信任的任何网站或软件,亦不要在网上公开。
\n- Amethyst 开发人员**永远不会**要求您提供私人密钥。
\n- **请**保留您的私人密钥的安全备份,以备帐户恢复。 我们建议使用密码管理器。
</string>
<string name="secret_key_copied_to_clipboard">私人密钥nsec已复制到剪贴板</string>
<string name="copy_my_secret_key">复制我的私人密钥</string>
<string name="biometric_authentication_failed">身份验证失败</string>
<string name="biometric_error">错误</string>
<string name="badge_created_by">"由 %1$s 创建"</string>
<string name="badge_award_image_for">"颁发给 %1$s 的徽章图片"</string>
<string name="new_badge_award_notif">您收到了新的徽章奖励</string>
<string name="award_granted_to">徽章奖励授予</string>
<string name="copied_note_text_to_clipboard">文本已复制到剪贴板</string>
<string name="copied_user_id_to_clipboard" tools:ignore="Typos">复制作者的 @npub 到剪贴板</string>
<string name="copied_note_id_to_clipboard" tools:ignore="Typos">复制文章ID (@note1) 到剪贴板</string>
<string name="select_text_dialog_top">选择文本</string>
<string name="quick_action_select">选择</string>
<string name="quick_action_share_browser_link">分享浏览网址</string>
<string name="quick_action_share">分享</string>
<string name="quick_action_copy_user_id">@提及</string>
<string name="quick_action_copy_note_id">引用</string>
<string name="quick_action_copy_text">复制</string>
<string name="quick_action_delete">删除</string>
<string name="quick_action_unfollow">取关</string>
<string name="quick_action_follow">关注</string>
<string name="quick_action_request_deletion_alert_title">请求删贴</string>
<string name="quick_action_request_deletion_alert_body">Amethyst 将请求您的记录从当前连接的中继器中删除。不能保证您发布的笔记将被永久从那些中继器或其他存储笔记的中继器中删除。</string>
</resources>

View File

@@ -20,7 +20,7 @@
<string name="relay_icon">Relay Icon</string>
<string name="unknown_author">Unknown Author</string>
<string name="copy_text">Copy Text</string>
<string name="copy_user_pubkey">Copy Author @npub</string>
<string name="copy_user_pubkey">Copy Author ID</string>
<string name="copy_note_id">Copy Note ID</string>
<string name="broadcast">Broadcast</string>
<string name="block_hide_user"><![CDATA[Block & Hide User]]></string>
@@ -200,12 +200,25 @@
<string name="quick_action_select">Select</string>
<string name="quick_action_share_browser_link">Share Browser Link</string>
<string name="quick_action_share">Share</string>
<string name="quick_action_copy_user_id">Copy authors ID</string>
<string name="quick_action_copy_note_id">Copy note ID</string>
<string name="quick_action_copy_text">Copy</string>
<string name="quick_action_copy_user_id">Author ID</string>
<string name="quick_action_copy_note_id">Note ID</string>
<string name="quick_action_copy_text">Copy Text</string>
<string name="quick_action_delete">Delete</string>
<string name="quick_action_unfollow">Unfollow</string>
<string name="quick_action_follow">Follow</string>
<string name="quick_action_request_deletion_alert_title">Request Deletion</string>
<string name="quick_action_request_deletion_alert_body">Amethyst will request that your note be deleted from the relays you are currently connected to. There is no guarantee that your note will be permanently deleted from those relays, or from other relays where it may be stored.</string>
<string name="github" translatable="false">Github Gist w/ Proof</string>
<string name="telegram" translatable="false">Telegram</string>
<string name="mastodon" translatable="false">Mastodon Post ID w/ Proof</string>
<string name="twitter" translatable="false">Twitter Status w/ Proof</string>
<string name="github_proof_url_template" translatable="false">https://gist.github.com/&lt;user&gt;/&lt;gist&gt;</string>
<string name="telegram_proof_url_template" translatable="false">https://t.me/&lt;proof post&gt;</string>
<string name="mastodon_proof_url_template" translatable="false">https://&lt;server&gt;/&lt;user&gt;/&lt;proof post&gt;</string>
<string name="twitter_proof_url_template" translatable="false">https://twitter.com/&lt;user&gt;/status/&lt;proof post&gt;</string>
</resources>