kopia lustrzana https://github.com/vitorpamplona/amethyst
Porównaj commity
33 Commity
b24b37e1d4
...
fa84f88d6c
Autor | SHA1 | Data |
---|---|---|
Tony Giorgio | fa84f88d6c | |
Vitor Pamplona | 3be246c9cc | |
Crowdin Bot | b28546b172 | |
Vitor Pamplona | 0fccfd7f80 | |
Vitor Pamplona | 3f35b57571 | |
Vitor Pamplona | 32b9b6c37a | |
Vitor Pamplona | 2342da114d | |
Vitor Pamplona | ef0d77f8eb | |
Vitor Pamplona | 5559b69bdb | |
Vitor Pamplona | eda25b4cfe | |
Vitor Pamplona | 9ce14e08fd | |
Vitor Pamplona | b046fd91cb | |
Vitor Pamplona | 8c9800664f | |
Vitor Pamplona | b90a57220d | |
Vitor Pamplona | d16b0f58bb | |
Vitor Pamplona | a538b66db3 | |
Vitor Pamplona | f04631b0dd | |
Vitor Pamplona | 6e31cff99c | |
Vitor Pamplona | 1553640c18 | |
Vitor Pamplona | 68b8f9c82a | |
Crowdin Bot | f6cce42028 | |
Vitor Pamplona | 0cbddad9c0 | |
Vitor Pamplona | b14154e2b5 | |
greenart7c3 | c4250ccd35 | |
greenart7c3 | 31516964c8 | |
Vitor Pamplona | 4722b2a617 | |
Vitor Pamplona | eca5b47ab0 | |
Vitor Pamplona | d38b57025c | |
Vitor Pamplona | fa7aa3cf24 | |
Vitor Pamplona | d8e9b4773b | |
Vitor Pamplona | f9a7b13ba1 | |
Vitor Pamplona | ecbf0e404d | |
Tony Giorgio | 08f1b43908 |
|
@ -84,7 +84,7 @@ jobs:
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: Release ${{ github.ref }}
|
release_name: Release ${{ github.ref }}
|
||||||
|
@ -96,7 +96,7 @@ jobs:
|
||||||
id: upload-release-asset-play-universal-apk
|
id: upload-release-asset-play-universal-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/play/release/app-play-universal-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/play/release/app-play-universal-release-unsigned-signed.apk
|
||||||
|
@ -107,7 +107,7 @@ jobs:
|
||||||
id: upload-release-asset-play-x86-apk
|
id: upload-release-asset-play-x86-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/play/release/app-play-x86-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/play/release/app-play-x86-release-unsigned-signed.apk
|
||||||
|
@ -118,7 +118,7 @@ jobs:
|
||||||
id: upload-release-asset-play-x86-64-apk
|
id: upload-release-asset-play-x86-64-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/play/release/app-play-x86_64-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/play/release/app-play-x86_64-release-unsigned-signed.apk
|
||||||
|
@ -152,7 +152,7 @@ jobs:
|
||||||
id: upload-release-asset-fdroid-universal-apk
|
id: upload-release-asset-fdroid-universal-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-universal-release-unsigned-signed.apk
|
||||||
|
@ -163,7 +163,7 @@ jobs:
|
||||||
id: upload-release-asset-fdroid-x86-apk
|
id: upload-release-asset-fdroid-x86-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86-release-unsigned-signed.apk
|
||||||
|
@ -174,7 +174,7 @@ jobs:
|
||||||
id: upload-release-asset-fdroid-x86-64-apk
|
id: upload-release-asset-fdroid-x86-64-apk
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk
|
asset_path: app/build/outputs/apk/fdroid/release/app-fdroid-x86_64-release-unsigned-signed.apk
|
||||||
|
@ -210,7 +210,7 @@ jobs:
|
||||||
id: upload-release-asset-play-aab
|
id: upload-release-asset-play-aab
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/bundle/playRelease/app-play-release.aab
|
asset_path: app/build/outputs/bundle/playRelease/app-play-release.aab
|
||||||
|
@ -222,7 +222,7 @@ jobs:
|
||||||
id: upload-release-asset-fdroid-aab
|
id: upload-release-asset-fdroid-aab
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab
|
asset_path: app/build/outputs/bundle/fdroidRelease/app-fdroid-release.aab
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.9.22" />
|
<option name="version" value="1.9.23" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -12,8 +12,8 @@ android {
|
||||||
applicationId "com.vitorpamplona.amethyst"
|
applicationId "com.vitorpamplona.amethyst"
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 365
|
versionCode 368
|
||||||
versionName "0.86.2"
|
versionName "0.86.5"
|
||||||
buildConfigField "String", "RELEASE_NOTES_ID", "\"a704a11334ed4fe6fc6ee6f8856f6f005da33644770616f1437f8b2b488b52b1\""
|
buildConfigField "String", "RELEASE_NOTES_ID", "\"a704a11334ed4fe6fc6ee6f8856f6f005da33644770616f1437f8b2b488b52b1\""
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
@ -143,7 +143,7 @@ android {
|
||||||
|
|
||||||
composeOptions {
|
composeOptions {
|
||||||
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
|
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
|
||||||
kotlinCompilerExtensionVersion "1.5.8"
|
kotlinCompilerExtensionVersion "1.5.11"
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
resources {
|
||||||
|
@ -151,7 +151,6 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lint {
|
lint {
|
||||||
disable 'MissingTranslation'
|
disable 'MissingTranslation'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1607,7 +1607,7 @@ object LocalCache {
|
||||||
refreshObservers(note)
|
refreshObservers(note)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun consume(
|
fun consume(
|
||||||
event: SealedGossipEvent,
|
event: SealedGossipEvent,
|
||||||
relay: Relay?,
|
relay: Relay?,
|
||||||
) {
|
) {
|
||||||
|
@ -2114,7 +2114,7 @@ object LocalCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun consume(
|
fun consume(
|
||||||
event: DraftEvent,
|
event: DraftEvent,
|
||||||
relay: Relay?,
|
relay: Relay?,
|
||||||
) {
|
) {
|
||||||
|
@ -2330,7 +2330,14 @@ object LocalCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasConsumed(notificationEvent: Event): Boolean {
|
fun hasConsumed(notificationEvent: Event): Boolean {
|
||||||
return notes.containsKey(notificationEvent.id)
|
return if (notificationEvent is AddressableEvent) {
|
||||||
|
val note = addressables.get(notificationEvent.addressTag())
|
||||||
|
val noteEvent = note?.event
|
||||||
|
noteEvent != null && notificationEvent.createdAt <= noteEvent.createdAt()
|
||||||
|
} else {
|
||||||
|
val note = notes.get(notificationEvent.id)
|
||||||
|
note?.event != null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ object HttpClientManager {
|
||||||
var proxyChangeListeners = ArrayList<() -> Unit>()
|
var proxyChangeListeners = ArrayList<() -> Unit>()
|
||||||
private var defaultTimeout = DEFAULT_TIMEOUT_ON_WIFI
|
private var defaultTimeout = DEFAULT_TIMEOUT_ON_WIFI
|
||||||
private var defaultHttpClient: OkHttpClient? = null
|
private var defaultHttpClient: OkHttpClient? = null
|
||||||
|
private var defaultHttpClientWithoutProxy: OkHttpClient? = null
|
||||||
|
|
||||||
// fires off every time value of the property changes
|
// fires off every time value of the property changes
|
||||||
private var internalProxy: Proxy? by
|
private var internalProxy: Proxy? by
|
||||||
|
@ -58,6 +59,10 @@ object HttpClientManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getDefaultProxy(): Proxy? {
|
||||||
|
return this.internalProxy
|
||||||
|
}
|
||||||
|
|
||||||
fun setDefaultTimeout(timeout: Duration) {
|
fun setDefaultTimeout(timeout: Duration) {
|
||||||
Log.d("HttpClient", "Changing timeout to: $timeout")
|
Log.d("HttpClient", "Changing timeout to: $timeout")
|
||||||
if (this.defaultTimeout.seconds != timeout.seconds) {
|
if (this.defaultTimeout.seconds != timeout.seconds) {
|
||||||
|
@ -98,11 +103,18 @@ object HttpClientManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHttpClient(): OkHttpClient {
|
fun getHttpClient(useProxy: Boolean = true): OkHttpClient {
|
||||||
if (this.defaultHttpClient == null) {
|
return if (useProxy) {
|
||||||
this.defaultHttpClient = buildHttpClient(internalProxy, defaultTimeout)
|
if (this.defaultHttpClient == null) {
|
||||||
|
this.defaultHttpClient = buildHttpClient(internalProxy, defaultTimeout)
|
||||||
|
}
|
||||||
|
defaultHttpClient!!
|
||||||
|
} else {
|
||||||
|
if (this.defaultHttpClientWithoutProxy == null) {
|
||||||
|
this.defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
||||||
|
}
|
||||||
|
defaultHttpClientWithoutProxy!!
|
||||||
}
|
}
|
||||||
return defaultHttpClient!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initProxy(
|
fun initProxy(
|
||||||
|
|
|
@ -121,8 +121,9 @@ class Nip11Retriever {
|
||||||
try {
|
try {
|
||||||
val request: Request =
|
val request: Request =
|
||||||
Request.Builder().header("Accept", "application/nostr+json").url(url).build()
|
Request.Builder().header("Accept", "application/nostr+json").url(url).build()
|
||||||
|
val isLocalHost = dirtyUrl.startsWith("ws://127.0.0.1") || dirtyUrl.startsWith("ws://localhost")
|
||||||
|
|
||||||
HttpClientManager.getHttpClient()
|
HttpClientManager.getHttpClient(useProxy = !isLocalHost)
|
||||||
.newCall(request)
|
.newCall(request)
|
||||||
.enqueue(
|
.enqueue(
|
||||||
object : Callback {
|
object : Callback {
|
||||||
|
|
|
@ -268,42 +268,69 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
|
||||||
if (!event.isDeleted()) {
|
if (!event.isDeleted()) {
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
val note = LocalCache.getAddressableNoteIfExists(event.addressTag())
|
||||||
if (note != null && relay.brief in note.relays) return
|
val noteEvent = note?.event
|
||||||
|
if (noteEvent != null) {
|
||||||
|
if (event.createdAt > noteEvent.createdAt() || relay.brief !in note.relays) {
|
||||||
|
LocalCache.consume(event, relay)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// decrypts
|
||||||
|
event.cachedDraft(account.signer) {}
|
||||||
|
|
||||||
// decrypts
|
LocalCache.justConsume(event, relay)
|
||||||
event.cachedDraft(account.signer) {}
|
}
|
||||||
|
|
||||||
LocalCache.justConsume(event, relay)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is GiftWrapEvent -> {
|
is GiftWrapEvent -> {
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
if (note != null && relay.brief in note.relays) return
|
val noteEvent = note?.event as? GiftWrapEvent
|
||||||
|
if (noteEvent != null) {
|
||||||
event.cachedGift(account.signer) { this.consume(it, relay) }
|
if (relay.brief !in note.relays) {
|
||||||
|
LocalCache.justConsume(noteEvent, relay)
|
||||||
|
noteEvent.cachedGift(account.signer) {
|
||||||
|
this.consume(it, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new event
|
||||||
|
event.cachedGift(account.signer) { this.consume(it, relay) }
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is SealedGossipEvent -> {
|
is SealedGossipEvent -> {
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
if (note != null && relay.brief in note.relays) return
|
val noteEvent = note?.event as? SealedGossipEvent
|
||||||
|
if (noteEvent != null) {
|
||||||
event.cachedGossip(account.signer) { LocalCache.justConsume(it, relay) }
|
if (relay.brief !in note.relays) {
|
||||||
|
// adds the relay to seal and inner chat
|
||||||
|
LocalCache.consume(noteEvent, relay)
|
||||||
|
noteEvent.cachedGossip(account.signer) {
|
||||||
|
LocalCache.justConsume(it, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new event
|
||||||
|
event.cachedGossip(account.signer) { LocalCache.justConsume(it, relay) }
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is LnZapEvent -> {
|
is LnZapEvent -> {
|
||||||
// Avoid decrypting over and over again if the event already exist.
|
// Avoid decrypting over and over again if the event already exist.
|
||||||
|
|
||||||
val note = LocalCache.getNoteIfExists(event.id)
|
val note = LocalCache.getNoteIfExists(event.id)
|
||||||
if (note != null && relay.brief in note.relays) return
|
if (note?.event == null) {
|
||||||
|
event.zapRequest?.let {
|
||||||
event.zapRequest?.let {
|
if (it.isPrivateZap()) {
|
||||||
if (it.isPrivateZap()) {
|
it.decryptPrivateZap(account.signer) {}
|
||||||
it.decryptPrivateZap(account.signer) {}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalCache.justConsume(event, relay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.service
|
||||||
import com.vitorpamplona.amethyst.model.Channel
|
import com.vitorpamplona.amethyst.model.Channel
|
||||||
import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
|
import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
|
||||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
||||||
import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
|
import com.vitorpamplona.amethyst.service.relays.EVENT_FINDER_TYPES
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||||
|
@ -63,7 +63,7 @@ object NostrSingleChannelDataSource : NostrDataSource("SingleChannelFeed") {
|
||||||
|
|
||||||
// downloads linked events to this event.
|
// downloads linked events to this event.
|
||||||
return TypedFilter(
|
return TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(ChannelCreateEvent.KIND),
|
kinds = listOf(ChannelCreateEvent.KIND),
|
||||||
|
@ -86,7 +86,7 @@ object NostrSingleChannelDataSource : NostrDataSource("SingleChannelFeed") {
|
||||||
return directEventsToLoad.map {
|
return directEventsToLoad.map {
|
||||||
it.address().let { aTag ->
|
it.address().let { aTag ->
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(aTag.kind),
|
kinds = listOf(aTag.kind),
|
||||||
|
|
|
@ -23,8 +23,8 @@ package com.vitorpamplona.amethyst.service
|
||||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
||||||
|
import com.vitorpamplona.amethyst.service.relays.EVENT_FINDER_TYPES
|
||||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||||
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
|
||||||
|
@ -60,7 +60,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
return groupByEOSEPresence(addressesToWatch).map {
|
return groupByEOSEPresence(addressesToWatch).map {
|
||||||
listOf(
|
listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds =
|
kinds =
|
||||||
|
@ -82,7 +82,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds =
|
kinds =
|
||||||
|
@ -110,7 +110,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
it.address()?.let { aTag ->
|
it.address()?.let { aTag ->
|
||||||
if (aTag.kind < 25000 && aTag.dTag.isBlank()) {
|
if (aTag.kind < 25000 && aTag.dTag.isBlank()) {
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(aTag.kind),
|
kinds = listOf(aTag.kind),
|
||||||
|
@ -120,7 +120,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(aTag.kind),
|
kinds = listOf(aTag.kind),
|
||||||
|
@ -142,7 +142,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
return groupByEOSEPresence(eventsToWatch).map {
|
return groupByEOSEPresence(eventsToWatch).map {
|
||||||
listOf(
|
listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds =
|
kinds =
|
||||||
|
@ -165,7 +165,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds =
|
kinds =
|
||||||
|
@ -190,9 +190,10 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
return groupByEOSEPresence(eventsToWatch).map {
|
return groupByEOSEPresence(eventsToWatch).map {
|
||||||
listOf(
|
listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
|
kinds = listOf(TextNoteEvent.KIND),
|
||||||
tags = mapOf("q" to it.map { it.idHex }),
|
tags = mapOf("q" to it.map { it.idHex }),
|
||||||
since = findMinimumEOSEs(it),
|
since = findMinimumEOSEs(it),
|
||||||
// Max amount of "replies" to download on a specific event.
|
// Max amount of "replies" to download on a specific event.
|
||||||
|
@ -221,7 +222,7 @@ object NostrSingleEventDataSource : NostrDataSource("SingleEventFeed") {
|
||||||
// downloads linked events to this event.
|
// downloads linked events to this event.
|
||||||
return listOf(
|
return listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
ids = interestedEvents.toList(),
|
ids = interestedEvents.toList(),
|
||||||
|
|
|
@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.service
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
|
import com.vitorpamplona.amethyst.service.relays.COMMON_FEED_TYPES
|
||||||
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
import com.vitorpamplona.amethyst.service.relays.EOSETime
|
||||||
|
import com.vitorpamplona.amethyst.service.relays.EVENT_FINDER_TYPES
|
||||||
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
import com.vitorpamplona.amethyst.service.relays.JsonFilter
|
||||||
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
import com.vitorpamplona.amethyst.service.relays.TypedFilter
|
||||||
import com.vitorpamplona.quartz.events.MetadataEvent
|
import com.vitorpamplona.quartz.events.MetadataEvent
|
||||||
|
@ -64,7 +65,7 @@ object NostrSingleUserDataSource : NostrDataSource("SingleUserFeed") {
|
||||||
val minEOSEs = findMinimumEOSEsForUsers(group)
|
val minEOSEs = findMinimumEOSEsForUsers(group)
|
||||||
listOf(
|
listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(MetadataEvent.KIND, StatusEvent.KIND),
|
kinds = listOf(MetadataEvent.KIND, StatusEvent.KIND),
|
||||||
|
@ -73,7 +74,7 @@ object NostrSingleUserDataSource : NostrDataSource("SingleUserFeed") {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
types = COMMON_FEED_TYPES,
|
types = EVENT_FINDER_TYPES,
|
||||||
filter =
|
filter =
|
||||||
JsonFilter(
|
JsonFilter(
|
||||||
kinds = listOf(ReportEvent.KIND),
|
kinds = listOf(ReportEvent.KIND),
|
||||||
|
|
|
@ -28,7 +28,6 @@ import com.vitorpamplona.amethyst.service.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
|
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
|
||||||
import com.vitorpamplona.quartz.encoders.Lud06
|
import com.vitorpamplona.quartz.encoders.Lud06
|
||||||
import com.vitorpamplona.quartz.encoders.toLnUrl
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
@ -151,20 +150,6 @@ class LightningAddressResolver() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lnAddressToLnUrl(
|
|
||||||
lnaddress: String,
|
|
||||||
onSuccess: (String) -> Unit,
|
|
||||||
onError: (String, String) -> Unit,
|
|
||||||
context: Context,
|
|
||||||
) {
|
|
||||||
fetchLightningAddressJson(
|
|
||||||
lnaddress,
|
|
||||||
onSuccess = { onSuccess(it.toByteArray().toLnUrl()) },
|
|
||||||
onError = onError,
|
|
||||||
context = context,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lnAddressInvoice(
|
fun lnAddressInvoice(
|
||||||
lnaddress: String,
|
lnaddress: String,
|
||||||
milliSats: Long,
|
milliSats: Long,
|
||||||
|
@ -190,7 +175,8 @@ class LightningAddressResolver() {
|
||||||
onError(
|
onError(
|
||||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup,
|
R.string.error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup_with_user,
|
||||||
|
lnaddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
null
|
null
|
||||||
|
@ -202,7 +188,8 @@ class LightningAddressResolver() {
|
||||||
onError(
|
onError(
|
||||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.callback_url_not_found_in_the_user_s_lightning_address_server_configuration,
|
R.string.callback_url_not_found_in_the_user_s_lightning_address_server_configuration_with_user,
|
||||||
|
lnaddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -227,7 +214,8 @@ class LightningAddressResolver() {
|
||||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string
|
R.string
|
||||||
.error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup,
|
.error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup_with_user,
|
||||||
|
lnaddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
null
|
null
|
||||||
|
@ -268,7 +256,8 @@ class LightningAddressResolver() {
|
||||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string
|
R.string
|
||||||
.unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error,
|
.unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error_with_user,
|
||||||
|
lnaddress,
|
||||||
reason,
|
reason,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -279,7 +268,8 @@ class LightningAddressResolver() {
|
||||||
context.getString(R.string.error_unable_to_fetch_invoice),
|
context.getString(R.string.error_unable_to_fetch_invoice),
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string
|
R.string
|
||||||
.unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json,
|
.unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json_with_user,
|
||||||
|
lnaddress,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.service.notifications
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
@ -45,6 +46,7 @@ import java.math.BigDecimal
|
||||||
|
|
||||||
class EventNotificationConsumer(private val applicationContext: Context) {
|
class EventNotificationConsumer(private val applicationContext: Context) {
|
||||||
suspend fun consume(event: GiftWrapEvent) {
|
suspend fun consume(event: GiftWrapEvent) {
|
||||||
|
Log.d("EventNotificationConsumer", "New Notification Arrived")
|
||||||
if (!LocalCache.justVerify(event)) return
|
if (!LocalCache.justVerify(event)) return
|
||||||
if (!notificationManager().areNotificationsEnabled()) return
|
if (!notificationManager().areNotificationsEnabled()) return
|
||||||
|
|
||||||
|
@ -64,14 +66,23 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||||
account: Account,
|
account: Account,
|
||||||
) {
|
) {
|
||||||
pushWrappedEvent.cachedGift(account.signer) { notificationEvent ->
|
pushWrappedEvent.cachedGift(account.signer) { notificationEvent ->
|
||||||
if (!LocalCache.hasConsumed(notificationEvent) && LocalCache.justVerify(notificationEvent)) {
|
val consumed = LocalCache.hasConsumed(notificationEvent)
|
||||||
|
val verified = LocalCache.justVerify(notificationEvent)
|
||||||
|
Log.d("EventNotificationConsumer", "New Notification Arrived for ${account.userProfile().toBestDisplayName()} consumed= $consumed && verified= $verified")
|
||||||
|
if (!consumed && verified) {
|
||||||
|
Log.d("EventNotificationConsumer", "New Notification was verified")
|
||||||
unwrapAndConsume(notificationEvent, account) { innerEvent ->
|
unwrapAndConsume(notificationEvent, account) { innerEvent ->
|
||||||
if (!LocalCache.hasConsumed(innerEvent)) {
|
|
||||||
|
Log.d("EventNotificationConsumer", "Unwrapped consume $consumed ${innerEvent.javaClass.simpleName}")
|
||||||
|
if (!consumed) {
|
||||||
if (innerEvent is PrivateDmEvent) {
|
if (innerEvent is PrivateDmEvent) {
|
||||||
|
Log.d("EventNotificationConsumer", "New Nip-04 DM to Notify")
|
||||||
notify(innerEvent, account)
|
notify(innerEvent, account)
|
||||||
} else if (innerEvent is LnZapEvent) {
|
} else if (innerEvent is LnZapEvent) {
|
||||||
|
Log.d("EventNotificationConsumer", "New Zap to Notify")
|
||||||
notify(innerEvent, account)
|
notify(innerEvent, account)
|
||||||
} else if (innerEvent is ChatMessageEvent) {
|
} else if (innerEvent is ChatMessageEvent) {
|
||||||
|
Log.d("EventNotificationConsumer", "New ChatMessage to Notify")
|
||||||
notify(innerEvent, account)
|
notify(innerEvent, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +97,7 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||||
onReady: (Event) -> Unit,
|
onReady: (Event) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (!LocalCache.justVerify(event)) return
|
if (!LocalCache.justVerify(event)) return
|
||||||
|
if (LocalCache.hasConsumed(event)) return
|
||||||
|
|
||||||
when (event) {
|
when (event) {
|
||||||
is GiftWrapEvent -> {
|
is GiftWrapEvent -> {
|
||||||
|
@ -93,9 +105,11 @@ class EventNotificationConsumer(private val applicationContext: Context) {
|
||||||
}
|
}
|
||||||
is SealedGossipEvent -> {
|
is SealedGossipEvent -> {
|
||||||
event.cachedGossip(account.signer) {
|
event.cachedGossip(account.signer) {
|
||||||
// this is not verifiable
|
if (!LocalCache.hasConsumed(it)) {
|
||||||
LocalCache.justConsume(it, null)
|
// this is not verifiable
|
||||||
onReady(it)
|
LocalCache.justConsume(it, null)
|
||||||
|
onReady(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -50,6 +50,9 @@ enum class FeedType {
|
||||||
val COMMON_FEED_TYPES =
|
val COMMON_FEED_TYPES =
|
||||||
setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS, FeedType.PRIVATE_DMS, FeedType.GLOBAL)
|
setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS, FeedType.PRIVATE_DMS, FeedType.GLOBAL)
|
||||||
|
|
||||||
|
val EVENT_FINDER_TYPES =
|
||||||
|
setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS, FeedType.GLOBAL)
|
||||||
|
|
||||||
class Relay(
|
class Relay(
|
||||||
val url: String,
|
val url: String,
|
||||||
val read: Boolean = true,
|
val read: Boolean = true,
|
||||||
|
@ -63,7 +66,12 @@ class Relay(
|
||||||
const val RECONNECTING_IN_SECONDS = 60 * 3
|
const val RECONNECTING_IN_SECONDS = 60 * 3
|
||||||
}
|
}
|
||||||
|
|
||||||
private val httpClient = HttpClientManager.getHttpClient()
|
private val httpClient =
|
||||||
|
if (url.startsWith("ws://127.0.0.1") || url.startsWith("ws://localhost")) {
|
||||||
|
HttpClientManager.getHttpClient(false)
|
||||||
|
} else {
|
||||||
|
HttpClientManager.getHttpClient()
|
||||||
|
}
|
||||||
|
|
||||||
private var listeners = setOf<Listener>()
|
private var listeners = setOf<Listener>()
|
||||||
private var socket: WebSocket? = null
|
private var socket: WebSocket? = null
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.actions
|
package com.vitorpamplona.amethyst.ui.actions
|
||||||
|
|
||||||
|
import com.vitorpamplona.amethyst.service.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
|
@ -36,7 +37,12 @@ class ImageDownloader {
|
||||||
try {
|
try {
|
||||||
HttpURLConnection.setFollowRedirects(true)
|
HttpURLConnection.setFollowRedirects(true)
|
||||||
var url = URL(imageUrl)
|
var url = URL(imageUrl)
|
||||||
var huc = url.openConnection() as HttpURLConnection
|
var huc =
|
||||||
|
if (HttpClientManager.getDefaultProxy() != null) {
|
||||||
|
url.openConnection(HttpClientManager.getDefaultProxy()) as HttpURLConnection
|
||||||
|
} else {
|
||||||
|
url.openConnection() as HttpURLConnection
|
||||||
|
}
|
||||||
huc.instanceFollowRedirects = true
|
huc.instanceFollowRedirects = true
|
||||||
var responseCode = huc.responseCode
|
var responseCode = huc.responseCode
|
||||||
|
|
||||||
|
@ -45,7 +51,12 @@ class ImageDownloader {
|
||||||
|
|
||||||
// open the new connnection again
|
// open the new connnection again
|
||||||
url = URL(newUrl)
|
url = URL(newUrl)
|
||||||
huc = url.openConnection() as HttpURLConnection
|
huc =
|
||||||
|
if (HttpClientManager.getDefaultProxy() != null) {
|
||||||
|
url.openConnection(HttpClientManager.getDefaultProxy()) as HttpURLConnection
|
||||||
|
} else {
|
||||||
|
url.openConnection() as HttpURLConnection
|
||||||
|
}
|
||||||
responseCode = huc.responseCode
|
responseCode = huc.responseCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import net.engawapg.lib.zoomable.rememberZoomState
|
import net.engawapg.lib.zoomable.rememberZoomState
|
||||||
import net.engawapg.lib.zoomable.zoomable
|
import net.engawapg.lib.zoomable.zoomable
|
||||||
|
|
||||||
|
@ -542,7 +543,10 @@ fun ShowHash(
|
||||||
|
|
||||||
if (content.hash != null) {
|
if (content.hash != null) {
|
||||||
LaunchedEffect(key1 = content.url) {
|
LaunchedEffect(key1 = content.url) {
|
||||||
val newVerifiedHash = verifyHash(content)
|
val newVerifiedHash =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
verifyHash(content)
|
||||||
|
}
|
||||||
if (newVerifiedHash != verifiedHash) {
|
if (newVerifiedHash != verifiedHash) {
|
||||||
verifiedHash = newVerifiedHash
|
verifiedHash = newVerifiedHash
|
||||||
}
|
}
|
||||||
|
|
|
@ -942,7 +942,7 @@ fun ZapReaction(
|
||||||
var wantsToZap by remember { mutableStateOf(false) }
|
var wantsToZap by remember { mutableStateOf(false) }
|
||||||
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
|
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
|
||||||
var wantsToSetCustomZap by remember { mutableStateOf(false) }
|
var wantsToSetCustomZap by remember { mutableStateOf(false) }
|
||||||
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
|
var showErrorMessageDialog by remember { mutableStateOf<List<String>>(emptyList()) }
|
||||||
var wantsToPay by
|
var wantsToPay by
|
||||||
remember(baseNote) {
|
remember(baseNote) {
|
||||||
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
|
mutableStateOf<ImmutableList<ZapPaymentHandler.Payable>>(
|
||||||
|
@ -972,7 +972,7 @@ fun ZapReaction(
|
||||||
onError = { _, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = showErrorMessageDialog + message
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPayViaIntent = { wantsToPay = it },
|
onPayViaIntent = { wantsToPay = it },
|
||||||
|
@ -998,7 +998,7 @@ fun ZapReaction(
|
||||||
onError = { _, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = showErrorMessageDialog + message
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
||||||
|
@ -1006,19 +1006,20 @@ fun ZapReaction(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showErrorMessageDialog != null) {
|
if (showErrorMessageDialog.isNotEmpty()) {
|
||||||
|
val msg = showErrorMessageDialog.joinToString("\n")
|
||||||
ErrorMessageDialog(
|
ErrorMessageDialog(
|
||||||
title = stringResource(id = R.string.error_dialog_zap_error),
|
title = stringResource(id = R.string.error_dialog_zap_error),
|
||||||
textContent = showErrorMessageDialog ?: "",
|
textContent = msg,
|
||||||
onClickStartMessage = {
|
onClickStartMessage = {
|
||||||
baseNote.author?.let {
|
baseNote.author?.let {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
val route = routeToMessage(it, showErrorMessageDialog, accountViewModel)
|
val route = routeToMessage(it, msg, accountViewModel)
|
||||||
nav(route)
|
nav(route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDismiss = { showErrorMessageDialog = null },
|
onDismiss = { showErrorMessageDialog = emptyList() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1038,7 +1039,7 @@ fun ZapReaction(
|
||||||
wantsToPay = persistentListOf()
|
wantsToPay = persistentListOf()
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = it
|
showErrorMessageDialog = showErrorMessageDialog + it
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1050,7 +1051,7 @@ fun ZapReaction(
|
||||||
onError = { _, message ->
|
onError = { _, message ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
zappingProgress = 0f
|
zappingProgress = 0f
|
||||||
showErrorMessageDialog = message
|
showErrorMessageDialog = showErrorMessageDialog + message
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
onProgress = { scope.launch(Dispatchers.Main) { zappingProgress = it } },
|
||||||
|
|
|
@ -424,18 +424,17 @@ fun UpdateZapAmountDialog(
|
||||||
Modifier.weight(1f),
|
Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
|
|
||||||
/* TODO: Find a way to open this in the PWA
|
IconButton(onClick = {
|
||||||
IconButton(onClick = {
|
onClose()
|
||||||
onClose()
|
runCatching { uri.openUri("https://app.mutinywallet.com/settings/connections?name=Amethyst") }
|
||||||
runCatching { uri.openUri("https://app.mutinywallet.com/settings/connections?callbackUri=nostr+walletconnect&name=Amethyst") }
|
}) {
|
||||||
}) {
|
Icon(
|
||||||
Icon(
|
painter = painterResource(R.mipmap.mutiny),
|
||||||
painter = painterResource(R.mipmap.mutiny),
|
null,
|
||||||
null,
|
modifier = Modifier.size(24.dp),
|
||||||
modifier = Modifier.size(24.dp),
|
tint = Color.Unspecified,
|
||||||
tint = Color.Unspecified
|
)
|
||||||
)
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
|
@ -30,8 +30,10 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Done
|
import androidx.compose.material.icons.outlined.Done
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
@ -361,7 +363,7 @@ fun PayViaIntentDialog(
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
Surface {
|
Surface {
|
||||||
Column(modifier = Modifier.padding(10.dp)) {
|
Column(modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState())) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
|
|
@ -1,2 +1,158 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools"></resources>
|
<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="your_profile_image">आपका चित्र</string>
|
||||||
|
<string name="scan_qr">क्यूआर क्रमचित्र का जाँच करें</string>
|
||||||
|
<string name="show_anyway">फिर भी दिखाएँ</string>
|
||||||
|
<string name="post_was_hidden">इस लेख को छिपाया गया क्योंकि इसमें आपके आच्छाद्य उपयोगकर्ता अथवा शब्द उल्लेखित हैं</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="other">अन्य</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="timestamp_it">समयांकन करें</string>
|
||||||
|
<string name="timestamp_pending">समयांकन : शेष पुष्टिकरण</string>
|
||||||
|
<string name="timestamp_pending_short">समयांकन : शेष</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>
|
||||||
|
<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">ज्साप संख्या स्थापित नही किया गया। दीर्घतः दबाएँ परिवर्तित करने के लिए</string>
|
||||||
|
<string name="login_with_a_private_key_to_be_able_to_send_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="login_with_a_private_key_to_be_able_to_hide_word">आप ख्याप्य कुंचिका का प्रयोग कर रहे हैं जिससे केवल पढ सकते हैं। शब्द अथवा वाक्य छिपाने के लिए गुप्त कुंचिका के साथ प्रवेशांकन करें</string>
|
||||||
|
<string name="login_with_a_private_key_to_be_able_to_show_word">आप ख्याप्य कुंचिका का प्रयोग कर रहे हैं जिससे केवल पढ सकते हैं। शब्द अथवा वाक्य दिखाने के लिए गुप्त कुंचिका के साथ प्रवेशांकन करें</string>
|
||||||
|
<string name="zaps">ज्साप</string>
|
||||||
|
<string name="view_count">दृष्ट संख्या</string>
|
||||||
|
<string name="boost">उद्धृत करें</string>
|
||||||
|
<string name="boosted">उद्धृत किया गया</string>
|
||||||
|
<string name="edited">सम्पादित</string>
|
||||||
|
<string name="edited_number">सम्पादन #%1$s</string>
|
||||||
|
<string name="original">मूल</string>
|
||||||
|
<string name="quote">वचन</string>
|
||||||
|
<string name="fork">पथभेद</string>
|
||||||
|
<string name="propose_an_edit">सम्पादन सुझाव दें</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="payment_successful">भुगतान सफल</string>
|
||||||
|
<string name="error_parsing_error_message">अपक्रम संदेश परखने में अपक्रम</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="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="bytes">अष्टक</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="my_awesome_name">उष्ट्रपक्षी मक्बढिया</string>
|
||||||
|
<string name="welcome">स्वागतम उष्ट्रपक्षी</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">लै॰ने॰ पता</string>
|
||||||
|
<string name="ln_url_outdated">लै॰ने॰ पता (पुराना)</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">नोस्ट्र में बाँटने के लिए टीका विभेदक का प्रतिलिपि करता है योज्यफलक में</string>
|
||||||
|
<string name="copy_channel_id_note_to_the_clipboard">प्रणाली विभेदक (टीका) का प्रतिलिपि करें योज्यफलक में</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="follow_back">प्रति अनुसरण करें</string>
|
||||||
|
<string name="unblock">अवरोध हटाएँ</string>
|
||||||
|
<string name="copy_user_id">उपयोगकर्ता विभेदक का प्रतिलिपि करें</string>
|
||||||
|
<string name="unblock_user">उपयोगकर्ता अवरोध हटाएँ</string>
|
||||||
|
<string name="npub_hex_username">"npub, उपयोगकर्ता नाम, लेख"</string>
|
||||||
|
<string name="clear">मार्जित करें</string>
|
||||||
|
<string name="app_logo">क्रमक चिह्न</string>
|
||||||
|
<string name="nsec_npub_hex_private_key">nsec.. अथवा npub..</string>
|
||||||
|
<string name="ncryptsec_password">पारणशब्द कुंचिका को खोलने के लिए</string>
|
||||||
|
<string name="show_password">पारणशब्द दिखाएँ</string>
|
||||||
|
<string name="hide_password">पारणशब्द छिपाएँ</string>
|
||||||
|
<string name="invalid_key">अमान्य कुंचिका</string>
|
||||||
|
<string name="invalid_key_with_message">अमान्य कुंचिका : %1$s</string>
|
||||||
|
<string name="i_accept_the">"मैं स्वीकार कर्ता हूँ "</string>
|
||||||
|
</resources>
|
||||||
|
|
|
@ -659,11 +659,22 @@
|
||||||
<string name="could_not_resolve_check_if_you_are_connected_if_the_server_is_up_and_if_the_lightning_address_is_correct_exception">Could not resolve %1$s. Check if you are connected, if the server is up and if the lightning address %2$s is correct.\n\nException was: %3$s</string>
|
<string name="could_not_resolve_check_if_you_are_connected_if_the_server_is_up_and_if_the_lightning_address_is_correct_exception">Could not resolve %1$s. Check if you are connected, if the server is up and if the lightning address %2$s is correct.\n\nException was: %3$s</string>
|
||||||
<string name="could_not_fetch_invoice_from">Could not fetch invoice from %1$s</string>
|
<string name="could_not_fetch_invoice_from">Could not fetch invoice from %1$s</string>
|
||||||
<string name="error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup">Error Parsing JSON from Lightning Address. Check the user\'s lightning setup</string>
|
<string name="error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup">Error Parsing JSON from Lightning Address. Check the user\'s lightning setup</string>
|
||||||
|
<string name="error_parsing_json_from_lightning_address_check_the_user_s_lightning_setup_with_user">Error Parsing JSON from %1$s. Check the user\'s lightning setup</string>
|
||||||
|
|
||||||
<string name="callback_url_not_found_in_the_user_s_lightning_address_server_configuration">Callback URL not found in the User\'s lightning address server configuration</string>
|
<string name="callback_url_not_found_in_the_user_s_lightning_address_server_configuration">Callback URL not found in the User\'s lightning address server configuration</string>
|
||||||
|
<string name="callback_url_not_found_in_the_user_s_lightning_address_server_configuration_with_user">Callback URL not found from %1$s\'s response</string>
|
||||||
|
|
||||||
<string name="error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup">Error Parsing JSON from Lightning Address\'s invoice fetch. Check the user\'s lightning setup</string>
|
<string name="error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup">Error Parsing JSON from Lightning Address\'s invoice fetch. Check the user\'s lightning setup</string>
|
||||||
|
<string name="error_parsing_json_from_lightning_address_s_invoice_fetch_check_the_user_s_lightning_setup_with_user">Error Parsing JSON from %1$s\'s invoice fetch. Check the user\'s lightning setup</string>
|
||||||
|
|
||||||
<string name="incorrect_invoice_amount_sats_from_it_should_have_been">Incorrect invoice amount (%1$s sats) from %2$s. It should have been %3$s</string>
|
<string name="incorrect_invoice_amount_sats_from_it_should_have_been">Incorrect invoice amount (%1$s sats) from %2$s. It should have been %3$s</string>
|
||||||
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error">Unable to create a lightning invoice before sending the zap. The receiver\'s lightning wallet sent the following error: %1$s</string>
|
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error">Unable to create a lightning invoice before sending the zap. The receiver\'s lightning wallet sent the following error: %1$s</string>
|
||||||
|
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_the_receiver_s_lightning_wallet_sent_the_following_error_with_user">Unable to create a lightning invoice. Message from %1$s: %2$s</string>
|
||||||
|
|
||||||
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json">Unable to create a lightning invoice before sending the zap. Element pr not found in the resulting JSON.</string>
|
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json">Unable to create a lightning invoice before sending the zap. Element pr not found in the resulting JSON.</string>
|
||||||
|
<string name="unable_to_create_a_lightning_invoice_before_sending_the_zap_element_pr_not_found_in_the_resulting_json_with_user">Unable to create a lightning invoice from %1$s: Element pr not found in the resulting JSON.</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="read_only_user">Read-only user</string>
|
<string name="read_only_user">Read-only user</string>
|
||||||
<string name="no_reactions_setup">No reactions setup</string>
|
<string name="no_reactions_setup">No reactions setup</string>
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ android {
|
||||||
}
|
}
|
||||||
composeOptions {
|
composeOptions {
|
||||||
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
|
// Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin
|
||||||
kotlinCompilerExtensionVersion "1.5.8"
|
kotlinCompilerExtensionVersion "1.5.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[versions]
|
[versions]
|
||||||
accompanistAdaptive = "0.34.0"
|
accompanistAdaptive = "0.34.0"
|
||||||
activityCompose = "1.8.2"
|
activityCompose = "1.8.2"
|
||||||
agp = "8.3.1"
|
agp = "8.3.2"
|
||||||
androidKotlinGeohash = "1.0"
|
androidKotlinGeohash = "1.0"
|
||||||
androidLifecycle = "2.7.0"
|
androidLifecycle = "2.7.0"
|
||||||
androidxJunit = "1.2.0-alpha03"
|
androidxJunit = "1.2.0-alpha03"
|
||||||
|
@ -12,7 +12,7 @@ benchmarkJunit4 = "1.2.3"
|
||||||
biometricKtx = "1.2.0-alpha05"
|
biometricKtx = "1.2.0-alpha05"
|
||||||
blurhash = "1.0.0"
|
blurhash = "1.0.0"
|
||||||
coil = "2.6.0"
|
coil = "2.6.0"
|
||||||
composeBom = "2024.03.00"
|
composeBom = "2024.04.00"
|
||||||
coreKtx = "1.12.0"
|
coreKtx = "1.12.0"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.5.1"
|
||||||
firebaseBom = "32.8.0"
|
firebaseBom = "32.8.0"
|
||||||
|
@ -21,7 +21,7 @@ gms = "4.4.1"
|
||||||
jacksonModuleKotlin = "2.17.0"
|
jacksonModuleKotlin = "2.17.0"
|
||||||
jna = "5.14.0"
|
jna = "5.14.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
kotlin = "1.9.22"
|
kotlin = "1.9.23"
|
||||||
kotlinxCollectionsImmutable = "0.3.7"
|
kotlinxCollectionsImmutable = "0.3.7"
|
||||||
languageId = "17.0.5"
|
languageId = "17.0.5"
|
||||||
lazysodiumAndroid = "5.1.0"
|
lazysodiumAndroid = "5.1.0"
|
||||||
|
|
|
@ -97,9 +97,9 @@ class Result(
|
||||||
|
|
||||||
class ExternalSignerLauncher(
|
class ExternalSignerLauncher(
|
||||||
private val npub: String,
|
private val npub: String,
|
||||||
val signerPackageName: String = "com.greenart7c3.nostrsigner",
|
val signerPackageName: String,
|
||||||
) {
|
) {
|
||||||
private val contentCache = LruCache<String, (String) -> Unit>(20)
|
private val contentCache = LruCache<String, (String) -> Unit>(50)
|
||||||
|
|
||||||
private var signerAppLauncher: ((Intent) -> Unit)? = null
|
private var signerAppLauncher: ((Intent) -> Unit)? = null
|
||||||
private var contentResolver: (() -> ContentResolver)? = null
|
private var contentResolver: (() -> ContentResolver)? = null
|
||||||
|
@ -125,20 +125,20 @@ class ExternalSignerLauncher(
|
||||||
val localResults: Array<Result> = Result.fromJsonArray(results)
|
val localResults: Array<Result> = Result.fromJsonArray(results)
|
||||||
localResults.forEach {
|
localResults.forEach {
|
||||||
val signature = it.signature ?: ""
|
val signature = it.signature ?: ""
|
||||||
val packageName = it.`package` ?: ""
|
val packageName = it.`package`?.let { "-$it" } ?: ""
|
||||||
val id = it.id ?: ""
|
val id = it.id ?: ""
|
||||||
if (id.isNotBlank()) {
|
if (id.isNotBlank()) {
|
||||||
val result = if (packageName.isNotBlank()) "$signature-$packageName" else signature
|
val result = if (packageName.isNotBlank()) "$signature$packageName" else signature
|
||||||
val contentCache = contentCache.get(id)
|
val contentCache = contentCache.get(id)
|
||||||
contentCache?.invoke(result)
|
contentCache?.invoke(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val signature = data.getStringExtra("signature") ?: ""
|
val signature = data.getStringExtra("signature") ?: ""
|
||||||
val packageName = data.getStringExtra("package") ?: ""
|
val packageName = data.getStringExtra("package")?.let { "-$it" } ?: ""
|
||||||
val id = data.getStringExtra("id") ?: ""
|
val id = data.getStringExtra("id") ?: ""
|
||||||
if (id.isNotBlank()) {
|
if (id.isNotBlank()) {
|
||||||
val result = if (packageName.isNotBlank()) "$signature-$packageName" else signature
|
val result = if (packageName.isNotBlank()) "$signature$packageName" else signature
|
||||||
val contentCache = contentCache.get(id)
|
val contentCache = contentCache.get(id)
|
||||||
contentCache?.invoke(result)
|
contentCache?.invoke(result)
|
||||||
}
|
}
|
||||||
|
@ -246,13 +246,11 @@ class ExternalSignerLauncher(
|
||||||
columnName: String = "signature",
|
columnName: String = "signature",
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val result =
|
getDataFromResolver(
|
||||||
getDataFromResolver(
|
SignerType.SIGN_EVENT,
|
||||||
SignerType.SIGN_EVENT,
|
arrayOf(event.toJson(), event.pubKey()),
|
||||||
arrayOf(event.toJson(), event.pubKey()),
|
columnName,
|
||||||
columnName,
|
).fold(
|
||||||
)
|
|
||||||
result.fold(
|
|
||||||
onFailure = { },
|
onFailure = { },
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
|
@ -286,7 +284,7 @@ class ExternalSignerLauncher(
|
||||||
): kotlin.Result<String?> {
|
): kotlin.Result<String?> {
|
||||||
val localData =
|
val localData =
|
||||||
if (signerType !== SignerType.GET_PUBLIC_KEY) {
|
if (signerType !== SignerType.GET_PUBLIC_KEY) {
|
||||||
data.toList().plus(npub).toTypedArray()
|
arrayOf(*data, npub)
|
||||||
} else {
|
} else {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
@ -326,15 +324,24 @@ class ExternalSignerLauncher(
|
||||||
return kotlin.Result.success(null)
|
return kotlin.Result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hashCodeFields(
|
||||||
|
str1: String,
|
||||||
|
str2: String,
|
||||||
|
onReady: (String) -> Unit,
|
||||||
|
): Int {
|
||||||
|
var result = str1.hashCode()
|
||||||
|
result = 31 * result + str2.hashCode()
|
||||||
|
result = 31 * result + onReady.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
fun decrypt(
|
fun decrypt(
|
||||||
encryptedContent: String,
|
encryptedContent: String,
|
||||||
pubKey: HexKey,
|
pubKey: HexKey,
|
||||||
signerType: SignerType = SignerType.NIP04_DECRYPT,
|
signerType: SignerType = SignerType.NIP04_DECRYPT,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val id = (encryptedContent + pubKey + onReady.toString()).hashCode().toString()
|
getDataFromResolver(signerType, arrayOf(encryptedContent, pubKey)).fold(
|
||||||
val result = getDataFromResolver(signerType, arrayOf(encryptedContent, pubKey))
|
|
||||||
result.fold(
|
|
||||||
onFailure = { },
|
onFailure = { },
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
|
@ -342,7 +349,7 @@ class ExternalSignerLauncher(
|
||||||
encryptedContent,
|
encryptedContent,
|
||||||
signerType,
|
signerType,
|
||||||
pubKey,
|
pubKey,
|
||||||
id,
|
hashCodeFields(encryptedContent, pubKey, onReady).toString(),
|
||||||
onReady,
|
onReady,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -358,9 +365,7 @@ class ExternalSignerLauncher(
|
||||||
signerType: SignerType = SignerType.NIP04_ENCRYPT,
|
signerType: SignerType = SignerType.NIP04_ENCRYPT,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val id = (decryptedContent + pubKey + onReady.toString()).hashCode().toString()
|
getDataFromResolver(signerType, arrayOf(decryptedContent, pubKey)).fold(
|
||||||
val result = getDataFromResolver(signerType, arrayOf(decryptedContent, pubKey))
|
|
||||||
result.fold(
|
|
||||||
onFailure = { },
|
onFailure = { },
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
|
@ -368,7 +373,7 @@ class ExternalSignerLauncher(
|
||||||
decryptedContent,
|
decryptedContent,
|
||||||
signerType,
|
signerType,
|
||||||
pubKey,
|
pubKey,
|
||||||
id,
|
hashCodeFields(decryptedContent, pubKey, onReady).toString(),
|
||||||
onReady,
|
onReady,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -382,9 +387,7 @@ class ExternalSignerLauncher(
|
||||||
event: LnZapRequestEvent,
|
event: LnZapRequestEvent,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val result =
|
getDataFromResolver(SignerType.DECRYPT_ZAP_EVENT, arrayOf(event.toJson(), event.pubKey)).fold(
|
||||||
getDataFromResolver(SignerType.DECRYPT_ZAP_EVENT, arrayOf(event.toJson(), event.pubKey))
|
|
||||||
result.fold(
|
|
||||||
onFailure = { },
|
onFailure = { },
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
package com.vitorpamplona.quartz.signers
|
package com.vitorpamplona.quartz.signers
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.goterl.lazysodium.BuildConfig
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.encoders.toNpub
|
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventFactory
|
import com.vitorpamplona.quartz.events.EventFactory
|
||||||
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
||||||
|
@ -32,7 +31,7 @@ import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||||
|
|
||||||
class NostrSignerExternal(
|
class NostrSignerExternal(
|
||||||
pubKey: HexKey,
|
pubKey: HexKey,
|
||||||
val launcher: ExternalSignerLauncher = ExternalSignerLauncher(pubKey.hexToByteArray().toNpub()),
|
val launcher: ExternalSignerLauncher,
|
||||||
) : NostrSigner(pubKey) {
|
) : NostrSigner(pubKey) {
|
||||||
override fun <T : Event> sign(
|
override fun <T : Event> sign(
|
||||||
createdAt: Long,
|
createdAt: Long,
|
||||||
|
@ -78,7 +77,7 @@ class NostrSignerExternal(
|
||||||
event.kind,
|
event.kind,
|
||||||
event.tags,
|
event.tags,
|
||||||
event.content,
|
event.content,
|
||||||
signature,
|
signature.split("-")[0],
|
||||||
) as? T?
|
) as? T?
|
||||||
)
|
)
|
||||||
?.let { onReady(it) }
|
?.let { onReady(it) }
|
||||||
|
@ -91,9 +90,11 @@ class NostrSignerExternal(
|
||||||
toPublicKey: HexKey,
|
toPublicKey: HexKey,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Log.d("NostrExternalSigner", "Encrypt NIP04 Event: $decryptedContent")
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d("NostrExternalSigner", "Encrypt NIP04 Event: $decryptedContent")
|
||||||
|
}
|
||||||
|
|
||||||
return launcher.encrypt(
|
launcher.encrypt(
|
||||||
decryptedContent,
|
decryptedContent,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
SignerType.NIP04_ENCRYPT,
|
SignerType.NIP04_ENCRYPT,
|
||||||
|
@ -106,9 +107,11 @@ class NostrSignerExternal(
|
||||||
fromPublicKey: HexKey,
|
fromPublicKey: HexKey,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Log.d("NostrExternalSigner", "Decrypt NIP04 Event: $encryptedContent")
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d("NostrExternalSigner", "Decrypt NIP04 Event: $encryptedContent")
|
||||||
|
}
|
||||||
|
|
||||||
return launcher.decrypt(
|
launcher.decrypt(
|
||||||
encryptedContent,
|
encryptedContent,
|
||||||
fromPublicKey,
|
fromPublicKey,
|
||||||
SignerType.NIP04_DECRYPT,
|
SignerType.NIP04_DECRYPT,
|
||||||
|
@ -121,9 +124,11 @@ class NostrSignerExternal(
|
||||||
toPublicKey: HexKey,
|
toPublicKey: HexKey,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Log.d("NostrExternalSigner", "Encrypt NIP44 Event: $decryptedContent")
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d("NostrExternalSigner", "Encrypt NIP44 Event: $decryptedContent")
|
||||||
|
}
|
||||||
|
|
||||||
return launcher.encrypt(
|
launcher.encrypt(
|
||||||
decryptedContent,
|
decryptedContent,
|
||||||
toPublicKey,
|
toPublicKey,
|
||||||
SignerType.NIP44_ENCRYPT,
|
SignerType.NIP44_ENCRYPT,
|
||||||
|
@ -136,9 +141,11 @@ class NostrSignerExternal(
|
||||||
fromPublicKey: HexKey,
|
fromPublicKey: HexKey,
|
||||||
onReady: (String) -> Unit,
|
onReady: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Log.d("NostrExternalSigner", "Decrypt NIP44 Event: $encryptedContent")
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d("NostrExternalSigner", "Decrypt NIP44 Event: $encryptedContent")
|
||||||
|
}
|
||||||
|
|
||||||
return launcher.decrypt(
|
launcher.decrypt(
|
||||||
encryptedContent,
|
encryptedContent,
|
||||||
fromPublicKey,
|
fromPublicKey,
|
||||||
SignerType.NIP44_DECRYPT,
|
SignerType.NIP44_DECRYPT,
|
||||||
|
@ -150,7 +157,7 @@ class NostrSignerExternal(
|
||||||
event: LnZapRequestEvent,
|
event: LnZapRequestEvent,
|
||||||
onReady: (LnZapPrivateEvent) -> Unit,
|
onReady: (LnZapPrivateEvent) -> Unit,
|
||||||
) {
|
) {
|
||||||
return launcher.decryptZapEvent(event) { jsonEvent ->
|
launcher.decryptZapEvent(event) { jsonEvent ->
|
||||||
try {
|
try {
|
||||||
(Event.fromJson(jsonEvent) as? LnZapPrivateEvent)?.let { onReady(it) }
|
(Event.fromJson(jsonEvent) as? LnZapPrivateEvent)?.let { onReady(it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
Ładowanie…
Reference in New Issue