Merge branch 'main' into amber
This commit is contained in:
+2
-2
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.vitorpamplona.amethyst"
|
||||
minSdk 26
|
||||
targetSdk 34
|
||||
versionCode 289
|
||||
versionName "0.75.10"
|
||||
versionCode 291
|
||||
versionName "0.75.12"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -1194,13 +1194,7 @@ object LocalCache {
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
notes.remove(it.idHex)
|
||||
// Doesn't need to clean up the replies and mentions.. Too small to matter.
|
||||
|
||||
// Counts the replies
|
||||
it.replyTo?.forEach { parent ->
|
||||
parent.removeReply(it)
|
||||
}
|
||||
removeFromCache(it)
|
||||
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
@@ -1219,12 +1213,7 @@ object LocalCache {
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
notes.remove(it.idHex)
|
||||
|
||||
// Counts the replies
|
||||
it.replyTo?.forEach { parent ->
|
||||
parent.removeReply(it)
|
||||
}
|
||||
removeFromCache(it)
|
||||
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
@@ -1251,17 +1240,12 @@ object LocalCache {
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
notes.remove(it.idHex)
|
||||
|
||||
val newerVersion = addressables[(it.event as? AddressableEvent)?.address()?.toTag()]
|
||||
if (newerVersion != null) {
|
||||
it.moveAllReferencesTo(newerVersion)
|
||||
}
|
||||
|
||||
it.replyTo?.forEach { masterNote ->
|
||||
removeLinkFromParentNote(it)
|
||||
}
|
||||
|
||||
removeFromCache(it)
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
@@ -1290,11 +1274,7 @@ object LocalCache {
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
notes.remove(it.idHex)
|
||||
|
||||
removeLinkFromParentNote(it)
|
||||
removeAuthorLinkTo(it)
|
||||
|
||||
removeFromCache(it)
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
@@ -1311,73 +1291,39 @@ object LocalCache {
|
||||
}
|
||||
}
|
||||
|
||||
fun removeLinkFromParentNote(it: Note) {
|
||||
it.replyTo?.forEach { masterNote ->
|
||||
masterNote.removeReply(it)
|
||||
masterNote.removeBoost(it)
|
||||
masterNote.removeReaction(it)
|
||||
masterNote.removeZap(it)
|
||||
masterNote.removeReport(it)
|
||||
private fun removeFromCache(note: Note) {
|
||||
note.replyTo?.forEach { masterNote ->
|
||||
masterNote.removeReply(note)
|
||||
masterNote.removeBoost(note)
|
||||
masterNote.removeReaction(note)
|
||||
masterNote.removeZap(note)
|
||||
masterNote.removeReport(note)
|
||||
masterNote.clearEOSE() // allows reloading of these events if needed
|
||||
}
|
||||
}
|
||||
|
||||
fun pruneExpiredEvents() {
|
||||
checkNotInMainThread()
|
||||
|
||||
val toBeRemoved = notes.filter {
|
||||
it.value.event?.isExpired() == true
|
||||
}.values
|
||||
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
notes.remove(it.idHex)
|
||||
|
||||
removeLinkFromParentNote(it)
|
||||
removeAuthorLinkTo(it)
|
||||
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
removeChildrenOf(childrenToBeRemoved)
|
||||
|
||||
if (toBeRemoved.size > 1) {
|
||||
println("PRUNE: ${toBeRemoved.size} thread replies removed.")
|
||||
}
|
||||
}
|
||||
|
||||
fun pruneHiddenMessages(account: Account) {
|
||||
checkNotInMainThread()
|
||||
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
val toBeRemoved = account.hiddenUsers.map { userHex ->
|
||||
(
|
||||
notes.values.filter {
|
||||
it.event?.pubKey() == userHex
|
||||
} + addressables.values.filter {
|
||||
it.event?.pubKey() == userHex
|
||||
}
|
||||
).toSet()
|
||||
}.flatten()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
// Counts the replies
|
||||
it.replyTo?.forEach { masterNote ->
|
||||
removeLinkFromParentNote(it)
|
||||
if (note.event is LnZapEvent) {
|
||||
(note.event as LnZapEvent).zappedAuthor().mapNotNull {
|
||||
val author = getUserIfExists(it)
|
||||
author?.removeZap(note)
|
||||
author?.clearEOSE()
|
||||
}
|
||||
}
|
||||
if (note.event is LnZapRequestEvent) {
|
||||
(note.event as LnZapRequestEvent).zappedAuthor().mapNotNull {
|
||||
val author = getUserIfExists(it)
|
||||
author?.removeZap(note)
|
||||
author?.clearEOSE()
|
||||
}
|
||||
}
|
||||
if (note.event is ReportEvent) {
|
||||
(note.event as ReportEvent).reportedAuthor().mapNotNull {
|
||||
val author = getUserIfExists(it.key)
|
||||
author?.removeReport(note)
|
||||
author?.clearEOSE()
|
||||
}
|
||||
|
||||
notes.remove(it.idHex)
|
||||
|
||||
removeAuthorLinkTo(it)
|
||||
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
removeChildrenOf(childrenToBeRemoved)
|
||||
|
||||
println("PRUNE: ${toBeRemoved.size} messages removed because they were Hidden")
|
||||
notes.remove(note.idHex)
|
||||
}
|
||||
|
||||
fun removeAuthorLinkTo(note: Note) {
|
||||
@@ -1406,11 +1352,56 @@ object LocalCache {
|
||||
|
||||
fun removeChildrenOf(nextToBeRemoved: List<Note>) {
|
||||
nextToBeRemoved.forEach { note ->
|
||||
removeAuthorLinkTo(note)
|
||||
notes.remove(note.idHex)
|
||||
removeFromCache(note)
|
||||
}
|
||||
}
|
||||
|
||||
fun pruneExpiredEvents() {
|
||||
checkNotInMainThread()
|
||||
|
||||
val toBeRemoved = notes.filter {
|
||||
it.value.event?.isExpired() == true
|
||||
}.values
|
||||
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
removeFromCache(it)
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
removeChildrenOf(childrenToBeRemoved)
|
||||
|
||||
if (toBeRemoved.size > 1) {
|
||||
println("PRUNE: ${toBeRemoved.size} thread replies removed.")
|
||||
}
|
||||
}
|
||||
|
||||
fun pruneHiddenMessages(account: Account) {
|
||||
checkNotInMainThread()
|
||||
|
||||
val childrenToBeRemoved = mutableListOf<Note>()
|
||||
|
||||
val toBeRemoved = account.hiddenUsers.map { userHex ->
|
||||
(
|
||||
notes.values.filter {
|
||||
it.event?.pubKey() == userHex
|
||||
} + addressables.values.filter {
|
||||
it.event?.pubKey() == userHex
|
||||
}
|
||||
).toSet()
|
||||
}.flatten()
|
||||
|
||||
toBeRemoved.forEach {
|
||||
removeFromCache(it)
|
||||
childrenToBeRemoved.addAll(it.removeAllChildNotes())
|
||||
}
|
||||
|
||||
removeChildrenOf(childrenToBeRemoved)
|
||||
|
||||
println("PRUNE: ${toBeRemoved.size} messages removed because they were Hidden")
|
||||
}
|
||||
|
||||
fun pruneContactLists(loggedIn: Set<HexKey>) {
|
||||
checkNotInMainThread()
|
||||
|
||||
@@ -1503,9 +1494,10 @@ object LocalCache {
|
||||
is LiveActivitiesChatMessageEvent -> consume(event, relay)
|
||||
is LnZapEvent -> {
|
||||
event.zapRequest?.let {
|
||||
// must have a valid request
|
||||
verifyAndConsume(it, relay)
|
||||
consume(event)
|
||||
}
|
||||
consume(event)
|
||||
}
|
||||
is LnZapRequestEvent -> consume(event)
|
||||
is LnZapPaymentRequestEvent -> consume(event)
|
||||
|
||||
@@ -136,26 +136,60 @@ open class Note(val idHex: String) {
|
||||
.format(DateTimeFormatter.ofPattern("uuuu-MM-dd-HH:mm:ss"))
|
||||
}
|
||||
|
||||
data class LevelSignature(val signature: String, val createdAt: Long?, val author: User?)
|
||||
|
||||
/**
|
||||
* This method caches signatures during each execution to avoid recalculation in longer threads
|
||||
*/
|
||||
fun replyLevelSignature(
|
||||
eventsToConsider: Set<Note>,
|
||||
cachedSignatures: MutableMap<Note, String>
|
||||
): String {
|
||||
cachedSignatures: MutableMap<Note, LevelSignature>,
|
||||
account: User,
|
||||
accountFollowingSet: Set<String>,
|
||||
now: Long
|
||||
): LevelSignature {
|
||||
val replyTo = replyTo
|
||||
if (event is RepostEvent || event is GenericRepostEvent || replyTo == null || replyTo.isEmpty()) {
|
||||
return "/" + formattedDateTime(createdAt() ?: 0) + ";"
|
||||
return LevelSignature(
|
||||
signature = "/" + formattedDateTime(createdAt() ?: 0) + ";",
|
||||
createdAt = createdAt(),
|
||||
author = author
|
||||
)
|
||||
}
|
||||
|
||||
val mySignature = (
|
||||
val parent = (
|
||||
replyTo
|
||||
.filter { it in eventsToConsider } // This forces the signature to be based on a branch, avoiding two roots
|
||||
.map {
|
||||
cachedSignatures[it] ?: it.replyLevelSignature(eventsToConsider, cachedSignatures).apply { cachedSignatures.put(it, this) }
|
||||
cachedSignatures[it] ?: it.replyLevelSignature(
|
||||
eventsToConsider,
|
||||
cachedSignatures,
|
||||
account,
|
||||
accountFollowingSet,
|
||||
now
|
||||
).apply { cachedSignatures.put(it, this) }
|
||||
}
|
||||
.maxByOrNull { it.length }?.removeSuffix(";") ?: ""
|
||||
) + "/" + formattedDateTime(createdAt() ?: 0) + ";"
|
||||
.maxByOrNull { it.signature.length }
|
||||
)
|
||||
|
||||
val parentSignature = parent?.signature?.removeSuffix(";") ?: ""
|
||||
|
||||
val threadOrder = if (parent?.author == author && createdAt() != null) {
|
||||
// author of the thread first, in **ascending** order
|
||||
"9" + formattedDateTime((parent?.createdAt ?: 0) + (now - (createdAt() ?: 0)))
|
||||
} else if (author?.pubkeyHex == account.pubkeyHex) {
|
||||
"8" + formattedDateTime(createdAt() ?: 0) // my replies
|
||||
} else if (author?.pubkeyHex in accountFollowingSet) {
|
||||
"7" + formattedDateTime(createdAt() ?: 0) // my follows replies.
|
||||
} else {
|
||||
"0" + formattedDateTime(createdAt() ?: 0) // everyone else.
|
||||
}
|
||||
|
||||
val mySignature = LevelSignature(
|
||||
signature = parentSignature + "/" + threadOrder + ";",
|
||||
createdAt = createdAt(),
|
||||
author = author
|
||||
)
|
||||
|
||||
cachedSignatures[this] = mySignature
|
||||
return mySignature
|
||||
|
||||
@@ -47,13 +47,13 @@ class Nip05Verifier() {
|
||||
if (it.isSuccessful) {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError("Could not resolve $nip05. Error: ${it.code}. Check if the server up and if the address $nip05 is correct")
|
||||
onError("Could not resolve $nip05. Error: ${it.code}. Check if the server is up and if the address $nip05 is correct")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call, e: java.io.IOException) {
|
||||
onError("Could not resolve $url. Check if the server up and if the address $nip05 is correct")
|
||||
onError("Could not resolve $url. Check if the server is up and if the address $nip05 is correct")
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class NostrDataSource(val debugName: String) {
|
||||
// Log.e("ERROR", "Relay ${relay.url}: ${error.message}")
|
||||
}
|
||||
|
||||
override fun onRelayStateChange(type: Relay.Type, relay: Relay, channel: String?) {
|
||||
override fun onRelayStateChange(type: Relay.Type, relay: Relay, subscriptionId: String?) {
|
||||
// Log.d("RELAY", "Relay ${relay.url} ${when (type) {
|
||||
// Relay.Type.CONNECT -> "connected."
|
||||
// Relay.Type.DISCONNECT -> "disconnected."
|
||||
@@ -57,9 +57,9 @@ abstract class NostrDataSource(val debugName: String) {
|
||||
// Relay.Type.EOSE -> "sent all events it had stored."
|
||||
// }}")
|
||||
|
||||
if (type == Relay.Type.EOSE && channel != null) {
|
||||
if (type == Relay.Type.EOSE && subscriptionId != null && subscriptionId in subscriptions.keys) {
|
||||
// updates a per subscripton since date
|
||||
subscriptions[channel]?.updateEOSE(TimeUtils.now(), relay.url)
|
||||
subscriptions[subscriptionId]?.updateEOSE(TimeUtils.now(), relay.url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ object NostrHomeDataSource : NostrDataSource("HomeFeed") {
|
||||
}
|
||||
|
||||
fun createFollowAccountsFilter(): TypedFilter {
|
||||
val follows = account.selectedUsersFollowList(account.defaultHomeFollowList) ?: emptySet()
|
||||
val followSet = follows.plus(account.userProfile().pubkeyHex).toList()
|
||||
val follows = account.selectedUsersFollowList(account.defaultHomeFollowList)
|
||||
val followSet = follows?.plus(account.userProfile().pubkeyHex)?.toList()?.ifEmpty { null }
|
||||
|
||||
return TypedFilter(
|
||||
types = setOf(FeedType.FOLLOWS),
|
||||
|
||||
+2
-2
@@ -57,12 +57,12 @@ class LightningAddressResolver() {
|
||||
if (it.isSuccessful) {
|
||||
onSuccess(it.body.string())
|
||||
} else {
|
||||
onError("The receiver's lightning service at $url is not available. It was calculated from the lightning address \"${lnaddress}\". Error: ${it.code}. Check if the server up and if the lightning address is correct")
|
||||
onError("The receiver's lightning service at $url is not available. It was calculated from the lightning address \"${lnaddress}\". Error: ${it.code}. Check if the server is up and if the lightning address is correct")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
onError("Could not resolve $url. Check if the server up and if the lightning address $lnaddress is correct")
|
||||
onError("Could not resolve $url. Check if the server is up and if the lightning address $lnaddress is correct")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -20,6 +20,7 @@ import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.events.SealedGossipEvent
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
|
||||
@@ -129,15 +130,17 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||
private fun notify(event: LnZapEvent) {
|
||||
val noteZapEvent = LocalCache.notes[event.id] ?: return
|
||||
|
||||
val noteZapRequest = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) }
|
||||
val noteZapRequest = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) } ?: return
|
||||
val noteZapped = event.zappedPost().firstOrNull()?.let { LocalCache.checkGetOrCreateNote(it) }
|
||||
|
||||
if ((event.amount ?: BigDecimal.ZERO) < BigDecimal.TEN) return
|
||||
|
||||
LocalPreferences.allSavedAccounts().forEach {
|
||||
val acc = LocalPreferences.loadFromEncryptedStorage(it.npub)
|
||||
|
||||
if (acc != null && acc.userProfile().pubkeyHex == event.zappedAuthor().firstOrNull()) {
|
||||
val amount = showAmount(event.amount)
|
||||
val senderInfo = (noteZapRequest?.event as? LnZapRequestEvent)?.let {
|
||||
val senderInfo = (noteZapRequest.event as? LnZapRequestEvent)?.let {
|
||||
val decryptedContent = acc.decryptZapContentAuthor(noteZapRequest)
|
||||
if (decryptedContent != null) {
|
||||
val author = LocalCache.getOrCreateUser(decryptedContent.pubKey)
|
||||
|
||||
@@ -704,6 +704,7 @@ fun ControlWhenPlayerIsActive(
|
||||
|
||||
controller.addListener(listener)
|
||||
onDispose {
|
||||
view.keepScreenOn = false
|
||||
controller.removeListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
package com.vitorpamplona.amethyst.ui.dal
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.ThreadAssembler
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
@Immutable
|
||||
class ThreadFeedFilter(val noteId: String) : FeedFilter<Note>() {
|
||||
class ThreadFeedFilter(val account: Account, val noteId: String) : FeedFilter<Note>() {
|
||||
|
||||
override fun feedKey(): String {
|
||||
return noteId
|
||||
}
|
||||
|
||||
override fun feed(): List<Note> {
|
||||
val cachedSignatures: MutableMap<Note, String> = mutableMapOf()
|
||||
val eventsToWatch = ThreadAssembler().findThreadFor(noteId) ?: emptySet()
|
||||
val cachedSignatures: MutableMap<Note, Note.LevelSignature> = mutableMapOf()
|
||||
val followingSet = account.selectedUsersFollowList(KIND3_FOLLOWS) ?: emptySet()
|
||||
val eventsToWatch = ThreadAssembler().findThreadFor(noteId)
|
||||
val now = TimeUtils.now()
|
||||
|
||||
// Currently orders by date of each event, descending, at each level of the reply stack
|
||||
val order = compareByDescending<Note> { it.replyLevelSignature(eventsToWatch, cachedSignatures) }
|
||||
val order = compareByDescending<Note> {
|
||||
it.replyLevelSignature(eventsToWatch, cachedSignatures, account.userProfile(), followingSet, now).signature
|
||||
}
|
||||
|
||||
return eventsToWatch.sortedWith(order)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ fun DrawerContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 25.dp)
|
||||
.padding(top = 100.dp),
|
||||
.padding(top = 70.dp),
|
||||
scaffoldState,
|
||||
accountViewModel,
|
||||
nav
|
||||
@@ -162,7 +162,7 @@ fun ProfileContent(
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(150.dp)
|
||||
.height(120.dp)
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
@@ -171,7 +171,7 @@ fun ProfileContent(
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(150.dp)
|
||||
.height(120.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -220,7 +220,6 @@ fun ProfileContent(
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(top = 15.dp)
|
||||
.clickable(
|
||||
onClick = {
|
||||
nav(route)
|
||||
|
||||
@@ -62,57 +62,63 @@ fun BlankNote(modifier: Modifier = Modifier, showDivider: Boolean = false, idHex
|
||||
@Composable
|
||||
fun HiddenNote(
|
||||
reports: ImmutableSet<Note>,
|
||||
isHiddenAuthor: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
isQuote: Boolean = false,
|
||||
nav: (String) -> Unit,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Row(modifier = Modifier.padding(horizontal = if (!isQuote) 12.dp else 6.dp)) {
|
||||
Column(modifier = Modifier.padding(start = if (!isQuote) 10.dp else 5.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
start = 20.dp,
|
||||
end = 20.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(30.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.post_was_flagged_as_inappropriate_by),
|
||||
color = Color.Gray
|
||||
Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(
|
||||
modifier = Modifier.padding(start = if (!isQuote) 30.dp else 25.dp, end = 20.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(30.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.post_was_flagged_as_inappropriate_by),
|
||||
color = Color.Gray
|
||||
)
|
||||
FlowRow(modifier = Modifier.padding(top = 10.dp)) {
|
||||
if (isHiddenAuthor) {
|
||||
UserPicture(
|
||||
user = accountViewModel.userProfile(),
|
||||
size = Size35dp,
|
||||
nav = nav,
|
||||
accountViewModel = accountViewModel
|
||||
)
|
||||
}
|
||||
reports.forEach {
|
||||
NoteAuthorPicture(
|
||||
baseNote = it,
|
||||
size = Size35dp,
|
||||
nav = nav,
|
||||
accountViewModel = accountViewModel
|
||||
)
|
||||
FlowRow(modifier = Modifier.padding(top = 10.dp)) {
|
||||
reports.forEach {
|
||||
NoteAuthorPicture(
|
||||
baseNote = it,
|
||||
nav = nav,
|
||||
accountViewModel = accountViewModel,
|
||||
size = Size35dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
onClick = onClick,
|
||||
shape = ButtonBorder,
|
||||
colors = ButtonDefaults
|
||||
.buttonColors(
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
),
|
||||
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
|
||||
) {
|
||||
Text(text = stringResource(R.string.show_anyway), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Divider(
|
||||
thickness = 0.25.dp
|
||||
)
|
||||
Button(
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
onClick = onClick,
|
||||
shape = ButtonBorder,
|
||||
colors = ButtonDefaults
|
||||
.buttonColors(
|
||||
backgroundColor = MaterialTheme.colors.primary
|
||||
),
|
||||
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
|
||||
) {
|
||||
Text(text = stringResource(R.string.show_anyway), color = Color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Divider(
|
||||
thickness = 0.25.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE
|
||||
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_PLANNED
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -135,13 +134,9 @@ fun CheckHiddenChannelCardCompose(
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
if (showHidden) {
|
||||
var state by remember {
|
||||
val state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,19 +180,14 @@ fun LoadedChannelCardCompose(
|
||||
) {
|
||||
var state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
WatchForReports(note, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports ->
|
||||
if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) {
|
||||
val newState = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports)
|
||||
WatchForReports(note, accountViewModel) { newState ->
|
||||
if (state != newState) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
state = newState
|
||||
}
|
||||
@@ -219,7 +209,7 @@ fun LoadedChannelCardCompose(
|
||||
|
||||
@Composable
|
||||
fun RenderChannelCardReportState(
|
||||
state: NoteComposeReportState,
|
||||
state: AccountViewModel.NoteComposeReportState,
|
||||
note: Note,
|
||||
routeForLastRead: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -233,6 +223,7 @@ fun RenderChannelCardReportState(
|
||||
if (showHiddenNote) {
|
||||
HiddenNote(
|
||||
state.relevantReports,
|
||||
state.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
modifier,
|
||||
false,
|
||||
|
||||
@@ -72,8 +72,6 @@ import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||
import com.vitorpamplona.quartz.events.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.events.toImmutableListOfLists
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -155,17 +153,13 @@ fun LoadedChatMessageCompose(
|
||||
) {
|
||||
var state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
WatchForReports(baseNote, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports ->
|
||||
if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) {
|
||||
state = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports.toImmutableSet())
|
||||
WatchForReports(baseNote, accountViewModel) { newState ->
|
||||
if (state != newState) {
|
||||
state = newState
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +175,7 @@ fun LoadedChatMessageCompose(
|
||||
if (it) {
|
||||
HiddenNote(
|
||||
state.relevantReports,
|
||||
state.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
Modifier,
|
||||
innerQuote,
|
||||
|
||||
@@ -42,7 +42,6 @@ import androidx.compose.material.Text
|
||||
import androidx.compose.material.darkColors
|
||||
import androidx.compose.material.lightColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.SideEffect
|
||||
@@ -204,7 +203,6 @@ import com.vitorpamplona.quartz.events.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.events.UserMetadata
|
||||
import com.vitorpamplona.quartz.events.toImmutableListOfLists
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -285,11 +283,7 @@ fun CheckHiddenNoteCompose(
|
||||
// Ignores reports as well
|
||||
val state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -332,13 +326,6 @@ fun CheckHiddenNoteCompose(
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class NoteComposeReportState(
|
||||
val isAcceptable: Boolean,
|
||||
val canPreview: Boolean,
|
||||
val relevantReports: ImmutableSet<Note>
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun LoadedNoteCompose(
|
||||
note: Note,
|
||||
@@ -355,19 +342,14 @@ fun LoadedNoteCompose(
|
||||
) {
|
||||
var state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
WatchForReports(note, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports ->
|
||||
if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) {
|
||||
val newState = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports)
|
||||
WatchForReports(note, accountViewModel) { newState ->
|
||||
if (state != newState) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
state = newState
|
||||
}
|
||||
@@ -394,7 +376,7 @@ fun LoadedNoteCompose(
|
||||
|
||||
@Composable
|
||||
fun RenderReportState(
|
||||
state: NoteComposeReportState,
|
||||
state: AccountViewModel.NoteComposeReportState,
|
||||
note: Note,
|
||||
routeForLastRead: String? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -413,6 +395,7 @@ fun RenderReportState(
|
||||
if (showHiddenNote) {
|
||||
HiddenNote(
|
||||
state.relevantReports,
|
||||
state.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
modifier,
|
||||
isBoostedNote,
|
||||
@@ -444,7 +427,7 @@ fun RenderReportState(
|
||||
fun WatchForReports(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
onChange: (Boolean, Boolean, ImmutableSet<Note>) -> Unit
|
||||
onChange: (AccountViewModel.NoteComposeReportState) -> Unit
|
||||
) {
|
||||
val userFollowsState by accountViewModel.userFollows.observeAsState()
|
||||
val noteReportsState by note.live().reports.observeAsState()
|
||||
|
||||
@@ -79,9 +79,7 @@ fun NoteAuthorPicture(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: ((User) -> Unit)? = null
|
||||
) {
|
||||
val author by baseNote.live().metadata.map {
|
||||
it.note.author
|
||||
}.distinctUntilChanged().observeAsState(baseNote.author)
|
||||
val author by baseNote.live().authorChanges.observeAsState(baseNote.author)
|
||||
|
||||
Crossfade(targetState = author) {
|
||||
if (it == null) {
|
||||
@@ -118,23 +116,19 @@ fun UserPicture(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val route by remember {
|
||||
val route by remember(user) {
|
||||
derivedStateOf {
|
||||
"User/${user.pubkeyHex}"
|
||||
}
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ClickableUserPicture(
|
||||
baseUser = user,
|
||||
size = size,
|
||||
accountViewModel = accountViewModel,
|
||||
modifier = pictureModifier,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
nav(route)
|
||||
}
|
||||
nav(route)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -280,9 +274,7 @@ fun InnerBaseUserPicture(
|
||||
accountViewModel: AccountViewModel,
|
||||
modifier: Modifier
|
||||
) {
|
||||
val userProfile by baseUser.live().metadata.map {
|
||||
it.user.profilePicture()
|
||||
}.distinctUntilChanged().observeAsState(baseUser.profilePicture())
|
||||
val userProfile by baseUser.live().profilePictureChanges.observeAsState(baseUser.profilePicture())
|
||||
|
||||
PictureAndFollowingMark(
|
||||
userHex = baseUser.pubkeyHex,
|
||||
@@ -624,9 +616,7 @@ fun NoteDropDownMenu(note: Note, popupExpanded: MutableState<Boolean>, accountVi
|
||||
fun WatchBookmarksFollowsAndAccount(note: Note, accountViewModel: AccountViewModel, onNew: (DropDownParams) -> Unit) {
|
||||
val followState by accountViewModel.userProfile().live().follows.observeAsState()
|
||||
val bookmarkState by accountViewModel.userProfile().live().bookmarks.observeAsState()
|
||||
val showSensitiveContent by accountViewModel.accountLiveData.map {
|
||||
it.account.showSensitiveContent
|
||||
}.distinctUntilChanged().observeAsState(accountViewModel.account.showSensitiveContent)
|
||||
val showSensitiveContent by accountViewModel.showSensitiveContentChanges.observeAsState(accountViewModel.account.showSensitiveContent)
|
||||
|
||||
LaunchedEffect(key1 = followState, key2 = bookmarkState, key3 = showSensitiveContent) {
|
||||
launch(Dispatchers.IO) {
|
||||
|
||||
@@ -95,10 +95,10 @@ class NostrDiscoverChatFeedViewModel(val account: Account) : FeedViewModel(Disco
|
||||
}
|
||||
}
|
||||
|
||||
class NostrThreadFeedViewModel(val noteId: String) : FeedViewModel(ThreadFeedFilter(noteId)) {
|
||||
class Factory(val noteId: String) : ViewModelProvider.Factory {
|
||||
class NostrThreadFeedViewModel(account: Account, noteId: String) : FeedViewModel(ThreadFeedFilter(account, noteId)) {
|
||||
class Factory(val account: Account, val noteId: String) : ViewModelProvider.Factory {
|
||||
override fun <NostrThreadFeedViewModel : ViewModel> create(modelClass: Class<NostrThreadFeedViewModel>): NostrThreadFeedViewModel {
|
||||
return NostrThreadFeedViewModel(noteId) as NostrThreadFeedViewModel
|
||||
return NostrThreadFeedViewModel(account, noteId) as NostrThreadFeedViewModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +262,7 @@ fun NoteMaster(
|
||||
|
||||
HiddenNote(
|
||||
reports,
|
||||
note.author?.let { account.isHidden(it) } ?: false,
|
||||
accountViewModel,
|
||||
Modifier,
|
||||
false,
|
||||
|
||||
+30
-8
@@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
@@ -61,22 +62,26 @@ class AccountViewModel(val account: Account) : ViewModel() {
|
||||
val userFollows: LiveData<UserState> = account.userProfile().live().follows.map { it }
|
||||
val userRelays: LiveData<UserState> = account.userProfile().live().relays.map { it }
|
||||
|
||||
val discoveryListLiveData = accountLiveData.map {
|
||||
val discoveryListLiveData = account.live.map {
|
||||
it.account.defaultDiscoveryFollowList
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val homeListLiveData = accountLiveData.map {
|
||||
val homeListLiveData = account.live.map {
|
||||
it.account.defaultHomeFollowList
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val notificationListLiveData = accountLiveData.map {
|
||||
val notificationListLiveData = account.live.map {
|
||||
it.account.defaultNotificationFollowList
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val storiesListLiveData = accountLiveData.map {
|
||||
val storiesListLiveData = account.live.map {
|
||||
it.account.defaultStoriesFollowList
|
||||
}.distinctUntilChanged()
|
||||
|
||||
val showSensitiveContentChanges = account.live.map {
|
||||
it.account.showSensitiveContent
|
||||
}.distinctUntilChanged()
|
||||
|
||||
fun updateAutomaticallyStartPlayback(
|
||||
automaticallyStartPlayback: ConnectivityType
|
||||
) {
|
||||
@@ -435,14 +440,24 @@ class AccountViewModel(val account: Account) : ViewModel() {
|
||||
return account.defaultZapType
|
||||
}
|
||||
|
||||
fun isNoteAcceptable(note: Note, onReady: (Boolean, Boolean, ImmutableSet<Note>) -> Unit) {
|
||||
@Immutable
|
||||
data class NoteComposeReportState(
|
||||
val isAcceptable: Boolean = true,
|
||||
val canPreview: Boolean = true,
|
||||
val isHiddenAuthor: Boolean = false,
|
||||
val relevantReports: ImmutableSet<Note> = persistentSetOf()
|
||||
)
|
||||
|
||||
fun isNoteAcceptable(note: Note, onReady: (NoteComposeReportState) -> Unit) {
|
||||
viewModelScope.launch {
|
||||
val isFromLoggedIn = note.author?.pubkeyHex == userProfile().pubkeyHex
|
||||
val isFromLoggedInFollow = note.author?.let { userProfile().isFollowingCached(it) } ?: true
|
||||
|
||||
if (isFromLoggedIn || isFromLoggedInFollow) {
|
||||
// No need to process if from trusted people
|
||||
onReady(true, true, persistentSetOf())
|
||||
onReady(NoteComposeReportState(true, true, false, persistentSetOf()))
|
||||
} else if (note.author?.let { account.isHidden(it) } == true) {
|
||||
onReady(NoteComposeReportState(false, false, true, persistentSetOf()))
|
||||
} else {
|
||||
val newCanPreview = !note.hasAnyReports()
|
||||
|
||||
@@ -450,11 +465,18 @@ class AccountViewModel(val account: Account) : ViewModel() {
|
||||
|
||||
if (newCanPreview && newIsAcceptable) {
|
||||
// No need to process reports if nothing is wrong
|
||||
onReady(true, true, persistentSetOf())
|
||||
onReady(NoteComposeReportState(true, true, false, persistentSetOf()))
|
||||
} else {
|
||||
val newRelevantReports = account.getRelevantReports(note)
|
||||
|
||||
onReady(newIsAcceptable, newCanPreview, newRelevantReports.toImmutableSet())
|
||||
onReady(
|
||||
NoteComposeReportState(
|
||||
newIsAcceptable,
|
||||
newCanPreview,
|
||||
false,
|
||||
newRelevantReports.toImmutableSet()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, nav: (Stri
|
||||
|
||||
val feedViewModel: NostrThreadFeedViewModel = viewModel(
|
||||
key = noteId + "NostrThreadFeedViewModel",
|
||||
factory = NostrThreadFeedViewModel.Factory(noteId)
|
||||
factory = NostrThreadFeedViewModel.Factory(accountViewModel.account, noteId)
|
||||
)
|
||||
|
||||
NostrThreadDataSource.loadThread(noteId)
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@@ -52,7 +53,6 @@ import com.vitorpamplona.amethyst.ui.note.FileStorageHeaderDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.HiddenNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteComposeReportState
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteDropDownMenu
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelay
|
||||
@@ -76,7 +76,6 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -250,19 +249,14 @@ fun LoadedVideoCompose(
|
||||
) {
|
||||
var state by remember {
|
||||
mutableStateOf(
|
||||
NoteComposeReportState(
|
||||
isAcceptable = true,
|
||||
canPreview = true,
|
||||
relevantReports = persistentSetOf()
|
||||
)
|
||||
AccountViewModel.NoteComposeReportState()
|
||||
)
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
WatchForReports(note, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports ->
|
||||
if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) {
|
||||
val newState = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports)
|
||||
WatchForReports(note, accountViewModel) { newState ->
|
||||
if (state != newState) {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
state = newState
|
||||
}
|
||||
@@ -281,7 +275,7 @@ fun LoadedVideoCompose(
|
||||
|
||||
@Composable
|
||||
fun RenderReportState(
|
||||
state: NoteComposeReportState,
|
||||
state: AccountViewModel.NoteComposeReportState,
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
@@ -290,11 +284,12 @@ fun RenderReportState(
|
||||
|
||||
Crossfade(targetState = !state.isAcceptable && !showReportedNote) { showHiddenNote ->
|
||||
if (showHiddenNote) {
|
||||
Column(remember { Modifier.fillMaxSize(1f) }, verticalArrangement = Arrangement.Center) {
|
||||
Column(remember { Modifier.fillMaxSize() }, verticalArrangement = Arrangement.Center) {
|
||||
HiddenNote(
|
||||
state.relevantReports,
|
||||
state.isHiddenAuthor,
|
||||
accountViewModel,
|
||||
Modifier,
|
||||
Modifier.fillMaxWidth(),
|
||||
false,
|
||||
nav,
|
||||
onClick = { showReportedNote = true }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="point_to_the_qr_code">أشر إلى رمز QR</string>
|
||||
<string name="point_to_the_qr_code">أشر إلى رمز (QR Code)</string>
|
||||
<string name="show_qr">أظهر QR</string>
|
||||
<string name="profile_image">صورة الملف الشخصي</string>
|
||||
<string name="scan_qr">مسح QR</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>
|
||||
@@ -12,15 +12,18 @@
|
||||
<string name="could_not_decrypt_the_message">لا يمكن فك تشفير الرسالة</string>
|
||||
<string name="group_picture">صورة المجموعة</string>
|
||||
<string name="explicit_content">محتوى فاضح</string>
|
||||
<string name="spam">رسائل عشوائية</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">نسخ المفتاح العام</string>
|
||||
<string name="copy_note_id">نسخ معرف </string>
|
||||
<string name="broadcast">بث</string>
|
||||
<string name="request_deletion">حذف الطلب</string>
|
||||
<string name="block_report">حظر / تبليغ</string>
|
||||
<string name="block_hide_user"><![CDATA[حظر و إخفاء المستخدم]]></string>
|
||||
<string name="report_spam_scam">الابلاغ عن البريد العشوائي / الاحتيال</string>
|
||||
<string name="report_impersonation">الإبلاغ عن التمثيل</string>
|
||||
@@ -31,6 +34,8 @@
|
||||
<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="login_with_a_private_key_to_be_able_to_follow">قم بتسجيل الدخول باستخدام مفتاح خاص لتتمكن من متابعة المستخدمين</string>
|
||||
<string name="login_with_a_private_key_to_be_able_to_unfollow">قم بتسجيل الدخول بمفتاحك الخاص للتتمكن من إلغاء متابعة المستخدمين </string>
|
||||
<string name="view_count">مشاهدة العد</string>
|
||||
<string name="boost">تعزيز</string>
|
||||
<string name="boosted">معزز</string>
|
||||
@@ -41,6 +46,8 @@
|
||||
<string name="and">" و "</string>
|
||||
<string name="in_channel">"في القناة "</string>
|
||||
<string name="profile_banner">لافتة الملف الشخصي</string>
|
||||
<string name="payment_successful">تم الدفع بنجاح</string>
|
||||
<string name="error_parsing_error_message">خطأ أثناء تحليل رسالة الخطأ</string>
|
||||
<string name="following">"المتابعون"</string>
|
||||
<string name="followers">"متابِعاً"</string>
|
||||
<string name="profile">الملف الشخصي</string>
|
||||
@@ -52,7 +59,7 @@
|
||||
<string name="lightning_tips">Lightning تبرع</string>
|
||||
<string name="note_to_receiver">ملاحظة للمستقبل</string>
|
||||
<string name="thank_you_so_much">شكرا جزيلا!</string>
|
||||
<string name="amount_in_sats">المبلغ في Sats</string>
|
||||
<string name="amount_in_sats">المبلغ ب الSats</string>
|
||||
<string name="send_sats">إرسال Sats</string>
|
||||
<string name="error_parsing_preview_for">"خطأ في تحليل المعاينة لـ %1$s : %2$s"</string>
|
||||
<string name="preview_card_image_for">"معاينة صورة البطاقة لـ %1$s"</string>
|
||||
@@ -68,8 +75,9 @@
|
||||
<string name="create">إنشاء</string>
|
||||
<string name="cancel">إلغاء</string>
|
||||
<string name="failed_to_upload_the_image">فشل تحميل الصورة</string>
|
||||
<string name="relay_address">Relay عنوان</string>
|
||||
<string name="relay_address"> عنوان الخادم (Relay Adress)</string>
|
||||
<string name="posts">المنشورات</string>
|
||||
<string name="bytes">Bytes</string>
|
||||
<string name="errors">الأخطاء</string>
|
||||
<string name="home_feed">المنشورات الرئيسية</string>
|
||||
<string name="private_message_feed"> الرسائل الخاصة</string>
|
||||
@@ -90,7 +98,7 @@
|
||||
<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">لا يمتلك المستخدم إعداد عنوان lighting لاستقبال sats</string>
|
||||
<string name="user_does_not_have_a_lightning_address_setup_to_receive_sats">لا يمتلك المستخدم عنوان (Lightning Address) لاستقبال sats</string>
|
||||
<string name="reply_here">"الرد هنا.. "</string>
|
||||
<string name="copies_the_note_id_to_the_clipboard_for_sharing">نسخ معرف الملاحظة إلى الحافظة للمشاركة</string>
|
||||
<string name="copy_channel_id_note_to_the_clipboard">انسخ معرف القناة (ملاحظة) إلى الحافظة</string>
|
||||
@@ -166,4 +174,190 @@
|
||||
<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="backup_keys">قم بعمل نسخة احتياطية للمفاتيح</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="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">تم نسخ رمز الملاحظة/المنشور (note ID) إلى الحافظة</string>
|
||||
<string name="select_text_dialog_top">تحديد نص</string>
|
||||
<string name="private_conversation_notification">"<استحالة فك تشفير الرسالة الخاصة>
|
||||
\n\n أنت الآن في محادثة خاصة/مشفرة بين %1$s و %2$s ."</string>
|
||||
<string name="account_switch_add_account_dialog_title">إضافة حساب جديد</string>
|
||||
<string name="drawer_accounts">الحسابات</string>
|
||||
<string name="account_switch_select_account">اختر حساب</string>
|
||||
<string name="account_switch_add_account_btn">إضافة حساب جديد</string>
|
||||
<string name="account_switch_active_account">تنشيط الحساب</string>
|
||||
<string name="account_switch_has_private_key">يحتوي على مفتاح خاص</string>
|
||||
<string name="account_switch_pubkey_only">قراءة فقط، لا يوجد مفتاح خاص</string>
|
||||
<string name="back">رجوع</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">رمز/معرف الملاحظة (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 حذف ملاحظتك من الخوادم (relays) الذي أنت متصل به حاليا. لا يوجد ضمان لحذف ملاحظتك بشكل دائم من تلك الخوادم أو من الخوادم الأخرى التي ربما حفظت فيها.</string>
|
||||
<string name="quick_action_block_dialog_btn">حظر </string>
|
||||
<string name="quick_action_delete_dialog_btn">حذف</string>
|
||||
<string name="quick_action_block">حظر</string>
|
||||
<string name="quick_action_report">التبليغ</string>
|
||||
<string name="quick_action_delete_button">حذف</string>
|
||||
<string name="quick_action_dont_show_again_button">لا تظهر مرة أخرى</string>
|
||||
<string name="report_dialog_illegal">تصرف غير قانوني</string>
|
||||
<string name="report_dialog_blocking_a_user">حظر المستخدم سوف يخفي محتواه في التطبيق الخاص بك. ملاحظاتك لا تزال قابلة للعرض علناً، بما في ذلك للأشخاص الذين تقوم بحظرهم. يتم إدراج المستخدمين المحظورين على شاشة مرشحات الأمان (Security Filters screen).</string>
|
||||
<string name="report_dialog_block_hide_user_btn"><![CDATA[حظر و إخفاء المستخدم]]></string>
|
||||
<string name="report_dialog_reminder_public">ستكون جميع التقارير المنشورة مرئية للجمهور.</string>
|
||||
<string name="report_dialog_additional_reason_placeholder">اختياريا تستطيع توفير سياق إضافي حول تقريرك…</string>
|
||||
<string name="report_dialog_additional_reason_label">سياق إضافي</string>
|
||||
<string name="report_dialog_select_reason_label">السبب</string>
|
||||
<string name="report_dialog_select_reason_placeholder">حدد سببا</string>
|
||||
<string name="report_dialog_post_report_btn">تبليغ المنشور (Report)</string>
|
||||
<string name="report_dialog_title">حظر / تبليغ</string>
|
||||
<string name="block_only">حظر </string>
|
||||
<string name="bookmarks">المفضلة</string>
|
||||
<string name="private_bookmarks">المفضلة الخاصة</string>
|
||||
<string name="public_bookmarks">المفضلة العامة</string>
|
||||
<string name="add_to_private_bookmarks">إضافة إلى المفضلات الخاصة</string>
|
||||
<string name="add_to_public_bookmarks">إضافة إلى المفضلات العامة</string>
|
||||
<string name="remove_from_private_bookmarks">إزالة من المفضلات الخاصة</string>
|
||||
<string name="remove_from_public_bookmarks">إزالة من المفضلات العامة</string>
|
||||
<string name="wallet_connect_service">خدمة Wallet Connect </string>
|
||||
<string name="wallet_connect_service_relay">خادم Wallet Connect </string>
|
||||
<string name="wallet_connect_service_secret">كلمة سر خدمة Wallet Connect</string>
|
||||
<string name="wallet_connect_service_show_secret">إظهار كلمة السر</string>
|
||||
<string name="wallet_connect_service_secret_placeholder">المفتاح السري بصيغة nsec أو hex</string>
|
||||
<string name="post_poll">نشر الاستطلاع</string>
|
||||
<string name="poll_heading_required">الحقول المطلوبة:</string>
|
||||
<string name="poll_primary_description">وصف الاستطلاع الأساسي…</string>
|
||||
<string name="poll_option_index">الخيار %s</string>
|
||||
<string name="poll_option_description">وصف خيار الاستطلاع</string>
|
||||
<string name="poll_heading_optional">حقل اختياري</string>
|
||||
<string name="poll_zap_value_min">الحد الأدنى</string>
|
||||
<string name="poll_zap_value_max">الحد الاقصى</string>
|
||||
<string name="poll_consensus_threshold_percent">(0–100)%</string>
|
||||
<string name="poll_closing_time">إغلاق بعد</string>
|
||||
<string name="poll_closing_time_days">أيام</string>
|
||||
<string name="poll_is_closed">تم إغلاق الاستطلاع لأي تصويت جديد</string>
|
||||
<string name="one_vote_per_user_on_atomic_votes">يسمح بصوت واحد فقط لكل مستخدم في هذا النوع من الاستطلاع</string>
|
||||
<string name="looking_for_event">"البحث عن الحدث %1$s"</string>
|
||||
<string name="custom_zaps_add_a_message">أضف رسالة عامة</string>
|
||||
<string name="custom_zaps_add_a_message_private">أضف رسالة خاصة</string>
|
||||
<string name="custom_zaps_add_a_message_nonzap">أضف رسالة مع الفاتورة</string>
|
||||
<string name="custom_zaps_add_a_message_example">شكرا لك على كل مساهمتك!</string>
|
||||
<string name="lightning_create_and_add_invoice">إنشاء وإضافة</string>
|
||||
<string name="poll_author_no_vote">لا يستطيع مؤلف الاستطلاع التصويت في استطلاعه.</string>
|
||||
<string name="hash_verification_passed">هذا المحتوى هو نفسه منذ النشر</string>
|
||||
<string name="hash_verification_failed">لقد تغير هذا المحتوى. ربما لم يشاهد مؤلفه التغيير أو يوافق عليه بعد.</string>
|
||||
<string name="content_description_add_image">أضف صورة</string>
|
||||
<string name="content_description_add_video">اضف فيديو</string>
|
||||
<string name="content_description_add_document">أضف مستند</string>
|
||||
<string name="add_content">أضف إلى الرسالة</string>
|
||||
<string name="content_description">وصف المحتوى/المحتويات</string>
|
||||
<string name="content_description_example">A blue boat in a white sandy beach at sunset
|
||||
(قارب أزرق في شاطئ رملي أبيض عند غروب الشمس)</string>
|
||||
<string name="zap_type_public">عام</string>
|
||||
<string name="zap_type_public_explainer">يستطيع الجميع رؤية المعاملة والرسالة</string>
|
||||
<string name="zap_type_private">خاص</string>
|
||||
<string name="zap_type_private_explainer">يمكن للمرسل والمستقبل رؤية بعضهما البعض وقراءة الرسالة</string>
|
||||
<string name="zap_type_anonymous">مجهول الهوية</string>
|
||||
<string name="zap_type_anonymous_explainer">المستقبل والجمهور لا يعرفان من الذي قام بإرسال الدفع</string>
|
||||
<string name="file_server">خادم الملفات</string>
|
||||
<string name="upload_server_imgur">imgur.com - موثوق به</string>
|
||||
<string name="upload_server_imgur_explainer">يمكن لـ Imgur تعديل الملف</string>
|
||||
<string name="upload_server_nostrimg">nostrimg.com - (موثوق به)</string>
|
||||
<string name="upload_server_nostrimg_explainer">يمكن لـ NostrImg تعديل الملف</string>
|
||||
<string name="upload_server_nostrbuild">nostr.build- (موثوق به)</string>
|
||||
<string name="upload_server_nostrbuild_explainer">يمكن لـ Nostr.build تعديل الملف</string>
|
||||
<string name="upload_server_nostrfilesdev">nostrfiles.dev - (موثوق به)</string>
|
||||
<string name="upload_server_nostrfilesdev_explainer">يمكن لـ Nostrfiles.dev تعديل الملف</string>
|
||||
<string name="upload_server_nostrcheckme">nostrcheck.me - (موثوق به)</string>
|
||||
<string name="upload_server_nostrcheckme_explainer">يمكن لـ nostrcheck.me تعديل الملف</string>
|
||||
<string name="upload_server_imgur_nip94_explainer">تحقق مما إذا قام Imgur بتعديل الملف.
|
||||
NIP جديد: قد لا يراه العملاء الآخرون</string>
|
||||
<string name="app_notification_zaps_channel_message_from">من طرف %1$s</string>
|
||||
<string name="app_notification_zaps_channel_message_for">ل %1$s</string>
|
||||
<string name="reply_notify">تنبيه: </string>
|
||||
<string name="channel_list_join_conversation">الانضمام إلى المحادثة</string>
|
||||
<string name="channel_list_user_or_group_id">معرف المستخدم أو المجموعة</string>
|
||||
<string name="channel_list_user_or_group_id_demo">npub, nevent أو hex</string>
|
||||
<string name="channel_list_create_channel">إنشاء</string>
|
||||
<string name="channel_list_join_channel">إنضمام</string>
|
||||
<string name="today">اليوم</string>
|
||||
<string name="content_warning">تحذير من المحتوى</string>
|
||||
<string name="content_warning_hide_all_sensitive_content">إخفاء المحتوى الحساس دائماً</string>
|
||||
<string name="content_warning_show_all_sensitive_content">إظهار المحتوى الحساس دائماً</string>
|
||||
<string name="content_warning_see_warnings">إظهار تحذيرات المحتوى دائمًا</string>
|
||||
<string name="recommended_apps">التطبيقات الموصى بها : </string>
|
||||
<string name="filter_spam_from_strangers">تصفية الرسائل المزعجة من الغرباء</string>
|
||||
<string name="warn_when_posts_have_reports_from_your_follows">تنبيه عندما تكون منشورتك قد تم التبليغ عنها </string>
|
||||
<string name="new_reaction_symbol">رمز رد فعل جديد</string>
|
||||
<string name="no_reaction_type_setup_long_press_to_change">لم يتم تحديد أي نوع من ردود الفعل. اضغط مطولاً للتغيير</string>
|
||||
<string name="read_from_relay">القراءة من الخادم (Relay)</string>
|
||||
<string name="write_to_relay">الكتابة للخادم</string>
|
||||
<string name="an_error_occurred_trying_to_get_relay_information">حدث خطأ أثناء محاولة الحصول على معلومات الخادم من %1$s</string>
|
||||
<string name="owner">المالك</string>
|
||||
<string name="version">الاصدار</string>
|
||||
<string name="software">البرنامج</string>
|
||||
<string name="contact">جهة الاتصال</string>
|
||||
<string name="supports">الNIPs المدعومة</string>
|
||||
<string name="admission_fees">رسوم القبول</string>
|
||||
<string name="payments_url">رابط الدفع</string>
|
||||
<string name="limitations">الحدود</string>
|
||||
<string name="countries">الدول</string>
|
||||
<string name="languages">اللغات</string>
|
||||
<string name="tags">الوسوم</string>
|
||||
<string name="posting_policy">سياسات النشر</string>
|
||||
<string name="message_length">طول الرسالة</string>
|
||||
<string name="subscriptions">الإشتراكات</string>
|
||||
<string name="filters">الفلاتر</string>
|
||||
<string name="subscription_id_length">طول معرّف الاشتراك</string>
|
||||
<string name="minimum_prefix">الحد الأدنى لل prefix </string>
|
||||
<string name="maximum_event_tags">الحد الأقصى لوسوم الحدث</string>
|
||||
<string name="content_length">طول المحتوى</string>
|
||||
<string name="minimum_pow">الحد الأدنى لل PoW</string>
|
||||
<string name="auth">المصادقة</string>
|
||||
<string name="payment">الدفع</string>
|
||||
<string name="cashu">Cashu Token</string>
|
||||
<string name="geohash_title">عرض الموقع كـ </string>
|
||||
<string name="new_feature_nip24_might_not_be_available_title">ميزة جديدة</string>
|
||||
<string name="new_feature_nip24_activate">تفعيل</string>
|
||||
<string name="messages_create_public_chat">عام</string>
|
||||
<string name="messages_new_message">خاص</string>
|
||||
<string name="messages_new_message_to">إلى</string>
|
||||
<string name="messages_new_message_subject">الموضوع</string>
|
||||
<string name="messages_new_message_subject_caption">موضوع المحادثة</string>
|
||||
<string name="messages_new_message_to_caption">"\@User1, @User2, @User3"</string>
|
||||
<string name="messages_group_descriptor">أعضاء هذه المجموعة</string>
|
||||
<string name="messages_new_subject_message">التوضيح للأعضاء</string>
|
||||
<string name="messages_new_subject_message_placeholder">تغيير اسم الأهداف الجديدة.</string>
|
||||
<string name="language_description">من أجل واجهة التطبيق</string>
|
||||
<string name="theme_description">مظهر غامق أو فاتح أو اوتوماتيكي</string>
|
||||
<string name="automatically_load_images_gifs_description">تحميل الصور و الصور المتحركة تلقائياً</string>
|
||||
<string name="automatically_play_videos_description">تشغيل الفيديوهات و الصور المتحركة تلقائياً</string>
|
||||
<string name="automatically_show_url_preview_description">إظهار معاينات الروابط</string>
|
||||
<string name="load_image_description">متى يتم تحميل الصور</string>
|
||||
<string name="copy_url_to_clipboard">نسخ عنوان الرابط إلى الحافظة</string>
|
||||
<string name="copy_the_note_id_to_the_clipboard">انسخ معرف النص (ID) إلى الحافظة</string>
|
||||
<string name="created_at">أنشئ في</string>
|
||||
<string name="rules">القواعد</string>
|
||||
<string name="status_update">تحديث الحالة</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="could_not_decrypt_the_message">Az üzenet dekódolása sikertelen</string>
|
||||
<string name="group_picture">Csoport Kép</string>
|
||||
<string name="explicit_content">Szókimondó tartalom</string>
|
||||
<string name="spam">Spam</string>
|
||||
<string name="impersonation">Megszemélyesítés</string>
|
||||
<string name="illegal_behavior">Illegális viselkedés</string>
|
||||
<string name="unknown">Ismeretlen</string>
|
||||
@@ -22,6 +23,7 @@
|
||||
<string name="copy_note_id">A bejegyzés azonosítójának másolása</string>
|
||||
<string name="broadcast">Közvetít</string>
|
||||
<string name="request_deletion">Törlés Kérése</string>
|
||||
<string name="block_report">Tiltás és Jelentés</string>
|
||||
<string name="block_hide_user"><![CDATA[Tiltás és Felhasználó elrejtése]]></string>
|
||||
<string name="report_spam_scam">Spam / Csalás bejelentés</string>
|
||||
<string name="report_impersonation">Megszemélyesítés bejelentés</string>
|
||||
@@ -32,6 +34,8 @@
|
||||
<string name="login_with_a_private_key_to_like_posts">Jelentkezzen be privát kulccsal a bejegyzések kedveléséhez</string>
|
||||
<string name="no_zap_amount_setup_long_press_to_change">Nincs Zap összeg beállítva. Nyomja meg hosszan a változtatáshoz</string>
|
||||
<string name="login_with_a_private_key_to_be_able_to_send_zaps">Jelentkezzen be privát kulccsal, hogy Zaps-t küldhessen</string>
|
||||
<string name="login_with_a_private_key_to_be_able_to_follow">Jelentkezz be a privát kulccsal, hogy követni tudd</string>
|
||||
<string name="login_with_a_private_key_to_be_able_to_unfollow">Jelentkezz be a privát kulccsal, hogy őt kikövesd</string>
|
||||
<string name="zaps">Zap-ek</string>
|
||||
<string name="view_count">Megtekintések száma</string>
|
||||
<string name="boost">Megosztás</string>
|
||||
@@ -43,6 +47,8 @@
|
||||
<string name="and">" és "</string>
|
||||
<string name="in_channel">"csatornában "</string>
|
||||
<string name="profile_banner">Profil Banner</string>
|
||||
<string name="payment_successful">Sikeres fizetés</string>
|
||||
<string name="error_parsing_error_message">Hiba a hibaüzenet elemzésekor</string>
|
||||
<string name="following">" Követek"</string>
|
||||
<string name="followers">" Követők"</string>
|
||||
<string name="profile">Profil</string>
|
||||
@@ -161,6 +167,7 @@
|
||||
<string name="translations_show_in_lang_first">Először %1$s nyelven</string>
|
||||
<string name="translations_always_translate_to_lang">Mindig fordítsa le %1$s-ra</string>
|
||||
<string name="translations_never_translate_from_lang">Soha ne fordíts %1$s-ról</string>
|
||||
<string name="nip_05">Nostr Cím</string>
|
||||
<string name="never">soha</string>
|
||||
<string name="now">most</string>
|
||||
<string name="h">ó</string>
|
||||
@@ -257,7 +264,10 @@
|
||||
<string name="poll_option_index">Szavazás %s megoszlása</string>
|
||||
<string name="poll_option_description">Szavazás opcióinak leírása</string>
|
||||
<string name="poll_heading_optional">Kiegészítő mezők:</string>
|
||||
<string name="poll_zap_value_min">Minimum Zap</string>
|
||||
<string name="poll_zap_value_max">Maximum Zap</string>
|
||||
<string name="poll_consensus_threshold">Konszenzus</string>
|
||||
<string name="poll_consensus_threshold_percent">(0–100)%</string>
|
||||
<string name="poll_closing_time">Szavazás lezárása</string>
|
||||
<string name="poll_closing_time_days">napok</string>
|
||||
<string name="poll_is_closed">A szavazásra nem lehet már új szavazatot leadni</string>
|
||||
@@ -294,10 +304,22 @@
|
||||
<string name="upload_server_imgur_explainer">Feltöltés az Imgur-ba. Az Imgur a képet/videót bármikor megváltoztathatja</string>
|
||||
<string name="upload_server_nostrimg">nostrimg.com - kevésbé biztonságos</string>
|
||||
<string name="upload_server_nostrimg_explainer">Feltöltés a NostrImg-be. A NostrImg a képet bármikor megváltoztathatja</string>
|
||||
<string name="upload_server_nostrbuild">nostr.build - megbízható</string>
|
||||
<string name="upload_server_nostrbuild_explainer">A Nostr.build a fájlt módosíthatja</string>
|
||||
<string name="upload_server_nostrfilesdev">nostrfiles.dev - megbízható</string>
|
||||
<string name="upload_server_nostrfilesdev_explainer">A Nostrfiles.dev a fájlt módosíthatja</string>
|
||||
<string name="upload_server_nostrcheckme">nostrcheck.me - megbízható</string>
|
||||
<string name="upload_server_nostrcheckme_explainer">a Nostrcheck.me a fájlt módosíthatja</string>
|
||||
<string name="upload_server_imgur_nip94">Ellenőrizhető Imgur (NIP-94)</string>
|
||||
<string name="upload_server_imgur_nip94_explainer">Megvéd attól, hogy az Imgur a képet/videót a közzététel után megváltoztassa</string>
|
||||
<string name="upload_server_nostrimg_nip94">Ellenőrizhető NostrImg (NIP-94)</string>
|
||||
<string name="upload_server_nostrimg_nip94_explainer">Megvéd attól, hogy az NostrImg a képet a közzététel után megváltoztassa</string>
|
||||
<string name="upload_server_nostrbuild_nip94">Ellenőrizhető Nostr.build (NIP-94)</string>
|
||||
<string name="upload_server_nostrbuild_nip94_explainer">Ellenőrzi, hogy a Nostr.build a fájlt módosította-e. Új NIP: előfordulhat, hogy más kliensek nem jelenítik meg</string>
|
||||
<string name="upload_server_nostrfilesdev_nip94">Ellenőrizhető Nostrfiles.dev (NIP-94)</string>
|
||||
<string name="upload_server_nostrfilesdev_nip94_explainer">Ellenőrzi, hogy a Nostrfiles.dev a fájlt módosította-e. Új NIP: előfordulhat, hogy más kliensek nem jelenítik meg</string>
|
||||
<string name="upload_server_nostrcheckme_nip94">Ellenőrizhető Nostrcheck.me (NIP-94)</string>
|
||||
<string name="upload_server_nostrcheckme_nip94_explainer">Ellenőrzi, hogy a Nostrcheck.me a fájlt módosította-e. Új NIP: előfordulhat, hogy más kliensek nem jelenítik meg</string>
|
||||
<string name="upload_server_relays_nip95">Saját csomópontjaid (NIP-95)</string>
|
||||
<string name="upload_server_relays_nip95_explainer">A fájlokat a csomópontokra töltik fel és ott tárolják. Rögzített URL-től mentesek (harmadik féltől való függőség). Győződj meg róla, hogy legalább egy NIP-95 csomópont a csomópontlistában szerepel</string>
|
||||
<string name="connect_via_tor_short">Tor/Orbot beállítása</string>
|
||||
@@ -325,6 +347,7 @@
|
||||
<string name="app_notification_dms_channel_description">Értesítést küld, amikor privát üzeneted érkezik</string>
|
||||
<string name="app_notification_zaps_channel_name">Zap-et kaptál</string>
|
||||
<string name="app_notification_zaps_channel_description">Értesítést küld, amikor valaki neked Zap-et küldött</string>
|
||||
<string name="app_notification_zaps_channel_message">%1$s sats</string>
|
||||
<string name="app_notification_zaps_channel_message_from">%1$s -tól</string>
|
||||
<string name="app_notification_zaps_channel_message_for">%1$s -ért</string>
|
||||
<string name="reply_notify">Értesítés: </string>
|
||||
@@ -350,6 +373,7 @@
|
||||
<string name="sats_to_complete">A ZapGyűjtés %1$s-nál. %2$s sats kell a célig</string>
|
||||
<string name="read_from_relay">Olvassás a csomópontból</string>
|
||||
<string name="write_to_relay">Írás a csomópontra</string>
|
||||
<string name="an_error_occurred_trying_to_get_relay_information">Hiba történt a közvetítő információinak lekérése közben a %1$s -ról</string>
|
||||
<string name="owner">Tulajdonos</string>
|
||||
<string name="version">Verzió</string>
|
||||
<string name="software">Szoftver</string>
|
||||
@@ -372,8 +396,12 @@
|
||||
<string name="minimum_pow">Minimális PoW</string>
|
||||
<string name="auth">Azonosítás</string>
|
||||
<string name="payment">Fizetés</string>
|
||||
<string name="cashu">Cashu Token</string>
|
||||
<string name="cashu_redeem">Beváltás</string>
|
||||
<string name="no_lightning_address_set">Nincs Lightning Cím beállítva</string>
|
||||
<string name="copied_token_to_clipboard">Token a vágólapra másolva</string>
|
||||
<string name="live_stream_live_tag">ÉLŐ</string>
|
||||
<string name="live_stream_offline_tag">NINCS ADÁS</string>
|
||||
<string name="live_stream_ended_tag">VÉGE</string>
|
||||
<string name="live_stream_planned_tag">ÜTEMEZETT</string>
|
||||
<string name="live_stream_is_offline">Az élő közvetítés offline</string>
|
||||
@@ -392,6 +420,7 @@
|
||||
<string name="settings">Beállítások</string>
|
||||
<string name="connectivity_type_always">Mindig</string>
|
||||
<string name="connectivity_type_wifi_only">Csak WIFI</string>
|
||||
<string name="connectivity_type_never">Soha</string>
|
||||
<string name="system">Rendszer</string>
|
||||
<string name="light">Világos</string>
|
||||
<string name="dark">Sötét</string>
|
||||
@@ -442,4 +471,7 @@
|
||||
<string name="status_update">Állapotod változtatása</string>
|
||||
<string name="lightning_wallets_not_found">Hiba a hibaüzenet elemzésekor</string>
|
||||
<string name="poll_zap_value_min_max_explainer">A szavazatokat a Zap-ek összegével súlyozzuk. Beállíthatsz egy minimális összeget, hogy a kéretlen leveleket elkerüld, és egy maximális összeget annak elkerülésére, hogy a szavazás feletti irányítást a nagy Zapperek vegyék át. Mindkét mezőben ugyanazt az összeget használd, hogy minden szavazat azonos értéket kapjon. Bármilyen összeg elfogadásához, hagyjd üresen.</string>
|
||||
<string name="error_dialog_zap_error">Zap küldése nem sikerült</string>
|
||||
<string name="error_dialog_talk_to_user">Üzenet a Felhasználónak</string>
|
||||
<string name="error_dialog_button_ok">Ok</string>
|
||||
</resources>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<string name="post_was_flagged_as_inappropriate_by">帖文被标记为不当</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>
|
||||
@@ -31,6 +32,9 @@
|
||||
<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="login_with_a_private_key_to_be_able_to_follow">使用私钥登录以便能够关注</string>
|
||||
<string name="login_with_a_private_key_to_be_able_to_unfollow">使用私钥登录以便能够取消关注</string>
|
||||
<string name="zaps">打闪</string>
|
||||
<string name="view_count">浏览次数</string>
|
||||
<string name="boost">提升</string>
|
||||
<string name="boosted">已提升</string>
|
||||
@@ -41,6 +45,7 @@
|
||||
<string name="and">" 和 "</string>
|
||||
<string name="in_channel">" 在频道 "</string>
|
||||
<string name="profile_banner">个人档案横幅</string>
|
||||
<string name="payment_successful">付款成功</string>
|
||||
<string name="following">" 关注"</string>
|
||||
<string name="followers">" 粉丝"</string>
|
||||
<string name="profile">个人档案</string>
|
||||
@@ -158,6 +163,7 @@
|
||||
<string name="translations_show_in_lang_first">先显示 %1$s 在前</string>
|
||||
<string name="translations_always_translate_to_lang">总是翻译为 %1$s</string>
|
||||
<string name="translations_never_translate_from_lang">不再翻译 %1$s</string>
|
||||
<string name="nip_05">Nostr 地址</string>
|
||||
<string name="never">从不</string>
|
||||
<string name="now">现在</string>
|
||||
<string name="h">时</string>
|
||||
@@ -242,6 +248,73 @@
|
||||
<string name="wallet_connect_service_explainer">使用您的私钥在不离开应用程序的情况下支付 zaps。 任何具有访问 Nostr 私钥的人都可以使用您的钱包余额。要保留您的资金避免损失,如果可能,请使用私人中继器。中继器操作员可以看到您的付款元数据。</string>
|
||||
<string name="wallet_connect_service_pubkey">钱包绑定公钥</string>
|
||||
<string name="wallet_connect_service_relay">钱包绑定中继</string>
|
||||
<string name="wallet_connect_service_show_secret">显示私钥</string>
|
||||
<string name="wallet_connect_service_secret_placeholder">nsec / 十六进制私钥</string>
|
||||
<string name="pledge_amount_in_sats">聪数量</string>
|
||||
<string name="poll_heading_optional">可选字段:</string>
|
||||
<string name="poll_zap_value_min">打闪最低金额</string>
|
||||
<string name="poll_zap_value_max">打闪最高金额</string>
|
||||
<string name="poll_consensus_threshold_percent">(0–100)%</string>
|
||||
<string name="poll_closing_time_days">天</string>
|
||||
<string name="poll_zap_amount">打闪金额</string>
|
||||
<string name="looking_for_event">"“正在查找事件%1$s”"</string>
|
||||
<string name="custom_zaps_add_a_message">添加公开消息</string>
|
||||
<string name="custom_zaps_add_a_message_nonzap">添加发票消息</string>
|
||||
<string name="lightning_create_and_add_invoice">创建并添加</string>
|
||||
<string name="content_description_add_image">添加图片</string>
|
||||
<string name="content_description_add_video">添加视频</string>
|
||||
<string name="content_description_add_document">添加文件</string>
|
||||
<string name="add_content">添加到消息</string>
|
||||
<string name="content_description">内容描述</string>
|
||||
<string name="content_description_example">一艘蓝色船,位于白沙滩的日落点</string>
|
||||
<string name="zap_type">打闪种类</string>
|
||||
<string name="zap_type_public">公开</string>
|
||||
<string name="zap_type_public_explainer">所有人都能看到交易和消息</string>
|
||||
<string name="zap_type_private_explainer">发送方和接收方能互相看到并读取消息</string>
|
||||
<string name="zap_type_anonymous">匿名</string>
|
||||
<string name="zap_type_nonzap">非打闪</string>
|
||||
<string name="do_you_really_want_to_disable_tor_title">断开与你的 Orbot/Tor 连接?</string>
|
||||
<string name="yes">是</string>
|
||||
<string name="no">否</string>
|
||||
<string name="follow_list_global">全球</string>
|
||||
<string name="use_orbot">使用 Orbot</string>
|
||||
<string name="today">今天</string>
|
||||
<string name="recommended_apps">推荐:</string>
|
||||
<string name="version">版本</string>
|
||||
<string name="software">软件</string>
|
||||
<string name="contact">联络</string>
|
||||
<string name="supports">支持的 NIPs</string>
|
||||
<string name="admission_fees">接纳费</string>
|
||||
<string name="payments_url">付款网址</string>
|
||||
<string name="limitations">限制</string>
|
||||
<string name="countries">国家</string>
|
||||
<string name="languages">语言</string>
|
||||
<string name="tags">标签</string>
|
||||
<string name="message_length">消息长度</string>
|
||||
<string name="subscriptions">订阅</string>
|
||||
<string name="filters">筛选器</string>
|
||||
<string name="live_stream_offline_tag">离线</string>
|
||||
<string name="live_stream_ended_tag">已结束</string>
|
||||
<string name="followed_tags">已关注的标签</string>
|
||||
<string name="relay_setup">中继器</string>
|
||||
<string name="discover_live">直播</string>
|
||||
<string name="discover_community">社区</string>
|
||||
<string name="discover_chat">聊天</string>
|
||||
<string name="add_sensitive_content_label">敏感内容</string>
|
||||
<string name="settings">设置</string>
|
||||
<string name="connectivity_type_always">始终</string>
|
||||
<string name="connectivity_type_wifi_only">仅限 Wifi</string>
|
||||
<string name="connectivity_type_never">从不</string>
|
||||
<string name="system">系统</string>
|
||||
<string name="light">浅色</string>
|
||||
<string name="dark">深色</string>
|
||||
<string name="language">语言</string>
|
||||
<string name="theme">主题</string>
|
||||
<string name="load_image">加载图像</string>
|
||||
<string name="nip05_verified">Nostr 地址已验证</string>
|
||||
<string name="nip05_failed">Nostr 地址验证失败</string>
|
||||
<string name="nip05_checking">正在检查 Nostr 地址</string>
|
||||
<string name="default_relays">默认</string>
|
||||
<string name="messages_create_public_chat">公开</string>
|
||||
<string name="created_at">创建于</string>
|
||||
</resources>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<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">打閃</string>
|
||||
<string name="view_count">瀏覽次數</string>
|
||||
<string name="boost">提升</string>
|
||||
<string name="boosted">已提升</string>
|
||||
@@ -42,6 +43,7 @@
|
||||
<string name="and">" 和 "</string>
|
||||
<string name="in_channel">" 在頻道 "</string>
|
||||
<string name="profile_banner">個人檔案橫幅</string>
|
||||
<string name="payment_successful">付款成功</string>
|
||||
<string name="following">" 關注"</string>
|
||||
<string name="followers">" 粉絲"</string>
|
||||
<string name="profile">個人檔案</string>
|
||||
@@ -158,6 +160,7 @@
|
||||
<string name="translations_show_in_lang_first">顯示 %1$s 在前</string>
|
||||
<string name="translations_always_translate_to_lang">總是翻譯為 %1$s</string>
|
||||
<string name="translations_never_translate_from_lang">不再翻譯 %1$s</string>
|
||||
<string name="nip_05">Nostr 地址</string>
|
||||
<string name="never">從不</string>
|
||||
<string name="now">現在</string>
|
||||
<string name="h">小時</string>
|
||||
@@ -171,6 +174,7 @@
|
||||
<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="backup_keys">備份密鑰</string>
|
||||
<string name="account_backup_tips_md" tools:ignore="Typos"> ## 備份與安全提示
|
||||
\n\n您的帳户由一個私人密鑰保護。 密鑰是以**nsec1**開頭的長隨機字符串。任何擁有您的私人密鑰的人都可以使用您的身份發佈內容。
|
||||
\n\n- **不要**將您的私人密鑰添加到您不信任的任何網站或軟件,亦不要在網上公開。
|
||||
@@ -189,6 +193,13 @@
|
||||
<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="account_switch_add_account_dialog_title">添加新帳戶</string>
|
||||
<string name="drawer_accounts">帳戶</string>
|
||||
<string name="account_switch_select_account">選擇帳戶</string>
|
||||
<string name="account_switch_add_account_btn">添加新帳戶</string>
|
||||
<string name="account_switch_active_account">活躍帳戶</string>
|
||||
<string name="account_switch_has_private_key">有私鑰</string>
|
||||
<string name="back">返回</string>
|
||||
<string name="quick_action_select">選擇</string>
|
||||
<string name="quick_action_share_browser_link">分享瀏覽網址</string>
|
||||
<string name="quick_action_share">分享</string>
|
||||
@@ -200,4 +211,55 @@
|
||||
<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>
|
||||
<string name="quick_action_block_dialog_btn">屏蔽</string>
|
||||
<string name="quick_action_delete_dialog_btn">刪除</string>
|
||||
<string name="quick_action_block">屏蔽</string>
|
||||
<string name="quick_action_report">舉報</string>
|
||||
<string name="quick_action_delete_button">刪除</string>
|
||||
<string name="report_dialog_select_reason_label">原因</string>
|
||||
<string name="report_dialog_select_reason_placeholder">選擇原因...</string>
|
||||
<string name="report_dialog_post_report_btn">發佈舉報</string>
|
||||
<string name="report_dialog_title">屏蔽並舉報</string>
|
||||
<string name="block_only">屏蔽</string>
|
||||
<string name="bookmarks">書籤</string>
|
||||
<string name="private_bookmarks">私人書籤</string>
|
||||
<string name="public_bookmarks">公開書籤</string>
|
||||
<string name="add_to_private_bookmarks">添加到私人書籤</string>
|
||||
<string name="add_to_public_bookmarks">添加到公開書籤</string>
|
||||
<string name="remove_from_private_bookmarks">從私人書籤移除</string>
|
||||
<string name="remove_from_public_bookmarks">從公開書籤移除</string>
|
||||
<string name="lightning_create_and_add_invoice">創建並添加</string>
|
||||
<string name="content_description_add_image">添加圖像</string>
|
||||
<string name="content_description_add_video">添加視頻</string>
|
||||
<string name="content_description_add_document">添加文件</string>
|
||||
<string name="add_content">添加到消息</string>
|
||||
<string name="zap_type">打閃種類</string>
|
||||
<string name="zap_type_public">公開</string>
|
||||
<string name="zap_type_anonymous">匿名</string>
|
||||
<string name="zap_type_nonzap">非打閃</string>
|
||||
<string name="connect_via_tor_short">Tor/Orbot 設置</string>
|
||||
<string name="connect_via_tor">通過你的 Orbot 設置連接</string>
|
||||
<string name="do_you_really_want_to_disable_tor_title">斷開與你的 Orbot/Tor 連接?</string>
|
||||
<string name="yes">是</string>
|
||||
<string name="no">否</string>
|
||||
<string name="live_stream_live_tag">直播</string>
|
||||
<string name="live_stream_offline_tag">離線</string>
|
||||
<string name="live_stream_ended_tag">已結束</string>
|
||||
<string name="live_stream_has_ended">直播已結束</string>
|
||||
<string name="followed_tags">以關注的標籤</string>
|
||||
<string name="relay_setup">中繼器</string>
|
||||
<string name="discover_live">直播</string>
|
||||
<string name="discover_community">社群</string>
|
||||
<string name="settings">設置</string>
|
||||
<string name="connectivity_type_always">始終</string>
|
||||
<string name="connectivity_type_never">永不</string>
|
||||
<string name="system">系統</string>
|
||||
<string name="light">淺色</string>
|
||||
<string name="dark">深色</string>
|
||||
<string name="language">語言</string>
|
||||
<string name="theme">主題</string>
|
||||
<string name="automatically_load_images_gifs">圖像預覽</string>
|
||||
<string name="automatically_show_url_preview">鏈接預覽</string>
|
||||
<string name="load_image">加載圖像</string>
|
||||
<string name="default_relays">默認</string>
|
||||
</resources>
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ buildscript {
|
||||
room_version = "2.4.3"
|
||||
accompanist_version = '0.30.1'
|
||||
coil_version = '2.4.0'
|
||||
vico_version = '1.11.0'
|
||||
vico_version = '1.11.1'
|
||||
exoplayer_version = '1.1.1'
|
||||
media3_version = '1.1.1'
|
||||
core_ktx_version = '1.10.1'
|
||||
|
||||
@@ -21,7 +21,7 @@ class LnZapEvent(
|
||||
fromJson(it)
|
||||
} as? LnZapRequestEvent
|
||||
} catch (e: Exception) {
|
||||
Log.e("LnZapEvent", "Failed to Parse Contained Post ${description()}", e)
|
||||
Log.w("LnZapEvent", "Failed to Parse Contained Post ${description()}", e)
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user