mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-06-30 04:10:41 +02:00
Refactors Ammolite to remove the dependency on OkHttp to prepare for KTor and multiplatform settings.
- This also reduces the Singleton coupling between Client and RelayPool. To migrate, create a NostrClient instance on your Application class and update your code to access that `client` instance.
This commit is contained in:
@ -37,8 +37,10 @@ import coil3.memory.MemoryCache
|
|||||||
import coil3.request.crossfade
|
import coil3.request.crossfade
|
||||||
import com.vitorpamplona.amethyst.service.LocationState
|
import com.vitorpamplona.amethyst.service.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.notifications.PokeyReceiver
|
import com.vitorpamplona.amethyst.service.notifications.PokeyReceiver
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.OkHttpWebSocket
|
||||||
import com.vitorpamplona.amethyst.service.playback.VideoCache
|
import com.vitorpamplona.amethyst.service.playback.VideoCache
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.ammolite.relays.NostrClient
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -54,8 +56,10 @@ import kotlin.time.measureTimedValue
|
|||||||
class Amethyst : Application() {
|
class Amethyst : Application() {
|
||||||
val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
val applicationIOScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
|
||||||
|
val client: NostrClient = NostrClient(OkHttpWebSocket.Builder())
|
||||||
|
|
||||||
// Service Manager is only active when the activity is active.
|
// Service Manager is only active when the activity is active.
|
||||||
val serviceManager = ServiceManager(applicationIOScope)
|
val serviceManager = ServiceManager(client, applicationIOScope)
|
||||||
val locationManager = LocationState(this, applicationIOScope)
|
val locationManager = LocationState(this, applicationIOScope)
|
||||||
|
|
||||||
val pokeyReceiver = PokeyReceiver()
|
val pokeyReceiver = PokeyReceiver()
|
||||||
|
@ -43,11 +43,9 @@ import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
|||||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||||
import com.vitorpamplona.ammolite.relays.Client
|
|
||||||
import com.vitorpamplona.ammolite.relays.RelayPool
|
|
||||||
|
|
||||||
fun debugState(context: Context) {
|
fun debugState(context: Context) {
|
||||||
Client
|
Amethyst.instance.client
|
||||||
.allSubscriptions()
|
.allSubscriptions()
|
||||||
.forEach { Log.d("STATE DUMP", "${it.key} ${it.value.joinToString { it.filter.toDebugJson() }}") }
|
.forEach { Log.d("STATE DUMP", "${it.key} ${it.value.joinToString { it.filter.toDebugJson() }}") }
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ fun debugState(context: Context) {
|
|||||||
Log.d("STATE DUMP", "Memory Class " + memClass + " MB (largeHeap $isLargeHeap)")
|
Log.d("STATE DUMP", "Memory Class " + memClass + " MB (largeHeap $isLargeHeap)")
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays())
|
Log.d("STATE DUMP", "Connected Relays: " + Amethyst.instance.client.connectedRelays())
|
||||||
|
|
||||||
Log.d(
|
Log.d(
|
||||||
"STATE DUMP",
|
"STATE DUMP",
|
||||||
|
@ -50,12 +50,12 @@ import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
|||||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.ots.OkHttpBlockstreamExplorer
|
import com.vitorpamplona.amethyst.service.ots.OkHttpBlockstreamExplorer
|
||||||
import com.vitorpamplona.amethyst.service.ots.OkHttpCalendarBuilder
|
import com.vitorpamplona.amethyst.service.ots.OkHttpCalendarBuilder
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||||
import com.vitorpamplona.ammolite.relays.Client
|
import com.vitorpamplona.ammolite.relays.NostrClient
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.encoders.bechToBytes
|
import com.vitorpamplona.quartz.encoders.bechToBytes
|
||||||
import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull
|
import com.vitorpamplona.quartz.encoders.decodePublicKeyAsHexOrNull
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
@ -73,10 +73,12 @@ import kotlinx.coroutines.runBlocking
|
|||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class ServiceManager(
|
class ServiceManager(
|
||||||
|
val client: NostrClient,
|
||||||
val scope: CoroutineScope,
|
val scope: CoroutineScope,
|
||||||
) {
|
) {
|
||||||
private var isStarted: Boolean =
|
// to not open amber in a loop trying to use auth relays and registering for notifications
|
||||||
false // to not open amber in a loop trying to use auth relays and registering for notifications
|
private var isStarted: Boolean = false
|
||||||
|
|
||||||
private var account: Account? = null
|
private var account: Account? = null
|
||||||
|
|
||||||
private var collectorJob: Job? = null
|
private var collectorJob: Job? = null
|
||||||
@ -156,7 +158,7 @@ class ServiceManager(
|
|||||||
|
|
||||||
if (myAccount != null) {
|
if (myAccount != null) {
|
||||||
val relaySet = myAccount.connectToRelaysWithProxy.value
|
val relaySet = myAccount.connectToRelaysWithProxy.value
|
||||||
Client.reconnect(relaySet)
|
client.reconnect(relaySet)
|
||||||
|
|
||||||
collectorJob?.cancel()
|
collectorJob?.cancel()
|
||||||
collectorJob = null
|
collectorJob = null
|
||||||
@ -165,7 +167,7 @@ class ServiceManager(
|
|||||||
myAccount.connectToRelaysWithProxy.collectLatest {
|
myAccount.connectToRelaysWithProxy.collectLatest {
|
||||||
delay(500)
|
delay(500)
|
||||||
if (isStarted) {
|
if (isStarted) {
|
||||||
Client.reconnect(it, onlyIfChanged = true)
|
client.reconnect(it, onlyIfChanged = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +233,7 @@ class ServiceManager(
|
|||||||
NostrUserProfileDataSource.stopSync()
|
NostrUserProfileDataSource.stopSync()
|
||||||
NostrVideoDataSource.stopSync()
|
NostrVideoDataSource.stopSync()
|
||||||
|
|
||||||
Client.reconnect(null)
|
client.reconnect(null)
|
||||||
isStarted = false
|
isStarted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +34,13 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
|
|||||||
import com.vitorpamplona.amethyst.service.LocationState
|
import com.vitorpamplona.amethyst.service.LocationState
|
||||||
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.uploads.FileHeader
|
import com.vitorpamplona.amethyst.service.uploads.FileHeader
|
||||||
import com.vitorpamplona.amethyst.tryAndWait
|
import com.vitorpamplona.amethyst.tryAndWait
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||||
import com.vitorpamplona.ammolite.relays.Client
|
|
||||||
import com.vitorpamplona.ammolite.relays.Constants
|
import com.vitorpamplona.ammolite.relays.Constants
|
||||||
import com.vitorpamplona.ammolite.relays.FeedType
|
import com.vitorpamplona.ammolite.relays.FeedType
|
||||||
import com.vitorpamplona.ammolite.relays.Relay
|
import com.vitorpamplona.ammolite.relays.Relay
|
||||||
@ -48,7 +48,6 @@ import com.vitorpamplona.ammolite.relays.RelaySetupInfo
|
|||||||
import com.vitorpamplona.ammolite.relays.RelaySetupInfoToConnect
|
import com.vitorpamplona.ammolite.relays.RelaySetupInfoToConnect
|
||||||
import com.vitorpamplona.ammolite.relays.TypedFilter
|
import com.vitorpamplona.ammolite.relays.TypedFilter
|
||||||
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.ATag
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
import com.vitorpamplona.quartz.encoders.Dimension
|
import com.vitorpamplona.quartz.encoders.Dimension
|
||||||
@ -1129,7 +1128,7 @@ class Account(
|
|||||||
otherTags = emptyArray(),
|
otherTags = emptyArray(),
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1146,7 +1145,7 @@ class Account(
|
|||||||
relayUse = relays,
|
relayUse = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1160,7 +1159,7 @@ class Account(
|
|||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
// Keep this local to avoid erasing a good contact list.
|
// Keep this local to avoid erasing a good contact list.
|
||||||
// Client.send(it)
|
// Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1202,7 +1201,7 @@ class Account(
|
|||||||
github = github,
|
github = github,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1278,7 +1277,7 @@ class Account(
|
|||||||
if (emojiUrl != null) {
|
if (emojiUrl != null) {
|
||||||
note.event?.let {
|
note.event?.let {
|
||||||
ReactionEvent.create(emojiUrl, it, signer) {
|
ReactionEvent.create(emojiUrl, it, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1289,7 +1288,7 @@ class Account(
|
|||||||
|
|
||||||
note.event?.let {
|
note.event?.let {
|
||||||
ReactionEvent.create(reaction, it, signer) {
|
ReactionEvent.create(reaction, it, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1395,7 +1394,7 @@ class Account(
|
|||||||
|
|
||||||
LocalCache.consume(event, zappedNote) { it.response(signer) { onResponse(it) } }
|
LocalCache.consume(event, zappedNote) { it.response(signer) { onResponse(it) } }
|
||||||
|
|
||||||
Client.sendSingle(
|
Amethyst.instance.client.sendSingle(
|
||||||
signedEvent = event,
|
signedEvent = event,
|
||||||
relayTemplate =
|
relayTemplate =
|
||||||
RelaySetupInfoToConnect(
|
RelaySetupInfoToConnect(
|
||||||
@ -1448,14 +1447,14 @@ class Account(
|
|||||||
|
|
||||||
note.event?.let {
|
note.event?.let {
|
||||||
ReactionEvent.createWarning(it, signer) {
|
ReactionEvent.createWarning(it, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
note.event?.let {
|
note.event?.let {
|
||||||
ReportEvent.create(it, type, signer, content) {
|
ReportEvent.create(it, type, signer, content) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1473,7 +1472,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReportEvent.create(user.pubkeyHex, type, signer) {
|
ReportEvent.create(user.pubkeyHex, type, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1490,7 +1489,7 @@ class Account(
|
|||||||
// chunks in 200 elements to avoid going over the 65KB limit for events.
|
// chunks in 200 elements to avoid going over the 65KB limit for events.
|
||||||
myNoteVersions.chunked(200).forEach { chunkedList ->
|
myNoteVersions.chunked(200).forEach { chunkedList ->
|
||||||
DeletionEvent.create(chunkedList, signer) { deletionEvent ->
|
DeletionEvent.create(chunkedList, signer) { deletionEvent ->
|
||||||
Client.send(deletionEvent)
|
Amethyst.instance.client.send(deletionEvent)
|
||||||
LocalCache.justConsume(deletionEvent, null)
|
LocalCache.justConsume(deletionEvent, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1549,12 +1548,12 @@ class Account(
|
|||||||
note.event?.let {
|
note.event?.let {
|
||||||
if (it.kind() == 1) {
|
if (it.kind() == 1) {
|
||||||
RepostEvent.create(it, signer) {
|
RepostEvent.create(it, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GenericRepostEvent.create(it, signer) {
|
GenericRepostEvent.create(it, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1565,7 +1564,7 @@ class Account(
|
|||||||
note.event?.let {
|
note.event?.let {
|
||||||
if (it is WrappedEvent && it.host != null) {
|
if (it is WrappedEvent && it.host != null) {
|
||||||
it.host?.let {
|
it.host?.let {
|
||||||
Client.sendFilterAndStopOnFirstResponse(
|
Amethyst.instance.client.sendFilterAndStopOnFirstResponse(
|
||||||
filters =
|
filters =
|
||||||
listOf(
|
listOf(
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
@ -1576,12 +1575,12 @@ class Account(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onResponse = {
|
onResponse = {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1595,7 +1594,7 @@ class Account(
|
|||||||
if (pair.value != newAttestation) {
|
if (pair.value != newAttestation) {
|
||||||
OtsEvent.create(pair.key, newAttestation, signer) {
|
OtsEvent.create(pair.key, newAttestation, signer) {
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
|
|
||||||
settings.pendingAttestations.update {
|
settings.pendingAttestations.update {
|
||||||
it - pair.key
|
it - pair.key
|
||||||
@ -1626,7 +1625,7 @@ class Account(
|
|||||||
|
|
||||||
if (contactList != null) {
|
if (contactList != null) {
|
||||||
ContactListEvent.followUser(contactList, user.pubkeyHex, signer) {
|
ContactListEvent.followUser(contactList, user.pubkeyHex, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1642,7 +1641,7 @@ class Account(
|
|||||||
},
|
},
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1655,7 +1654,7 @@ class Account(
|
|||||||
|
|
||||||
if (contactList != null) {
|
if (contactList != null) {
|
||||||
ContactListEvent.followEvent(contactList, channel.idHex, signer) {
|
ContactListEvent.followEvent(contactList, channel.idHex, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1671,7 +1670,7 @@ class Account(
|
|||||||
},
|
},
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1684,7 +1683,7 @@ class Account(
|
|||||||
|
|
||||||
if (contactList != null) {
|
if (contactList != null) {
|
||||||
ContactListEvent.followAddressableEvent(contactList, community.address, signer) {
|
ContactListEvent.followAddressableEvent(contactList, community.address, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1701,7 +1700,7 @@ class Account(
|
|||||||
relayUse = relays,
|
relayUse = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1718,7 +1717,7 @@ class Account(
|
|||||||
tag,
|
tag,
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1734,7 +1733,7 @@ class Account(
|
|||||||
},
|
},
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1770,7 +1769,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onNewEventCreated(event: Event) {
|
fun onNewEventCreated(event: Event) {
|
||||||
Client.send(event)
|
Amethyst.instance.client.send(event)
|
||||||
LocalCache.justConsume(event, null)
|
LocalCache.justConsume(event, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1888,10 +1887,10 @@ class Account(
|
|||||||
): Note? {
|
): Note? {
|
||||||
if (!isWriteable()) return null
|
if (!isWriteable()) return null
|
||||||
|
|
||||||
Client.send(data, relayList = relayList)
|
Amethyst.instance.client.send(data, relayList = relayList)
|
||||||
LocalCache.consume(data, null)
|
LocalCache.consume(data, null)
|
||||||
|
|
||||||
Client.send(signedEvent, relayList = relayList)
|
Amethyst.instance.client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.consume(signedEvent, null)
|
LocalCache.consume(signedEvent, null)
|
||||||
|
|
||||||
return LocalCache.getNoteIfExists(signedEvent.id)
|
return LocalCache.getNoteIfExists(signedEvent.id)
|
||||||
@ -1912,8 +1911,8 @@ class Account(
|
|||||||
signedEvent: FileStorageHeaderEvent,
|
signedEvent: FileStorageHeaderEvent,
|
||||||
relayList: List<RelaySetupInfo>,
|
relayList: List<RelaySetupInfo>,
|
||||||
) {
|
) {
|
||||||
Client.send(data, relayList = relayList)
|
Amethyst.instance.client.send(data, relayList = relayList)
|
||||||
Client.send(signedEvent, relayList = relayList)
|
Amethyst.instance.client.send(signedEvent, relayList = relayList)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendHeader(
|
fun sendHeader(
|
||||||
@ -1921,7 +1920,7 @@ class Account(
|
|||||||
relayList: List<RelaySetupInfo>,
|
relayList: List<RelaySetupInfo>,
|
||||||
onReady: (Note) -> Unit,
|
onReady: (Note) -> Unit,
|
||||||
) {
|
) {
|
||||||
Client.send(signedEvent, relayList = relayList)
|
Amethyst.instance.client.send(signedEvent, relayList = relayList)
|
||||||
LocalCache.justConsume(signedEvent, null)
|
LocalCache.justConsume(signedEvent, null)
|
||||||
|
|
||||||
LocalCache.getNoteIfExists(signedEvent.id)?.let { onReady(it) }
|
LocalCache.getNoteIfExists(signedEvent.id)?.let { onReady(it) }
|
||||||
@ -2121,13 +2120,13 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
replyTo?.forEach { it.event?.let { Amethyst.instance.client.send(it, relayList = relayList) } }
|
||||||
addresses?.forEach {
|
addresses?.forEach {
|
||||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2184,19 +2183,19 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
// broadcast replied notes
|
// broadcast replied notes
|
||||||
replyingTo?.let {
|
replyingTo?.let {
|
||||||
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
replyTo?.forEach { it.event?.let { Amethyst.instance.client.send(it, relayList = relayList) } }
|
||||||
addresses?.forEach {
|
addresses?.forEach {
|
||||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2250,19 +2249,19 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
// broadcast replied notes
|
// broadcast replied notes
|
||||||
replyingTo?.let {
|
replyingTo?.let {
|
||||||
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
replyTo?.forEach { it.event?.let { Amethyst.instance.client.send(it, relayList = relayList) } }
|
||||||
addresses?.forEach {
|
addresses?.forEach {
|
||||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2275,7 +2274,7 @@ class Account(
|
|||||||
val noteEvent = note.event
|
val noteEvent = note.event
|
||||||
if (noteEvent is DraftEvent) {
|
if (noteEvent is DraftEvent) {
|
||||||
noteEvent.createDeletedEvent(signer) {
|
noteEvent.createDeletedEvent(signer) {
|
||||||
Client.sendPrivately(
|
Amethyst.instance.client.sendPrivately(
|
||||||
it,
|
it,
|
||||||
note.relays.map { it.url }.map {
|
note.relays.map { it.url }.map {
|
||||||
RelaySetupInfoToConnect(
|
RelaySetupInfoToConnect(
|
||||||
@ -2359,11 +2358,11 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
replyingTo.event?.let {
|
replyingTo.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2391,11 +2390,11 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
replyingTo.event?.let {
|
replyingTo.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2466,11 +2465,11 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
replyingTo.event?.let {
|
replyingTo.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2497,7 +2496,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2522,9 +2521,9 @@ class Account(
|
|||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
if (relayList.isNotEmpty()) {
|
if (relayList.isNotEmpty()) {
|
||||||
Client.sendPrivately(it, relayList = relayList)
|
Amethyst.instance.client.sendPrivately(it, relayList = relayList)
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
}
|
}
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
@ -2546,9 +2545,9 @@ class Account(
|
|||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
if (relayList.isNotEmpty()) {
|
if (relayList.isNotEmpty()) {
|
||||||
Client.sendPrivately(it, relayList = relayList)
|
Amethyst.instance.client.sendPrivately(it, relayList = relayList)
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
}
|
}
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
@ -2593,7 +2592,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2634,7 +2633,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2690,19 +2689,19 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
// broadcast replied notes
|
// broadcast replied notes
|
||||||
replyingTo?.let {
|
replyingTo?.let {
|
||||||
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
LocalCache.getNoteIfExists(replyingTo)?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
replyTo?.forEach { it.event?.let { Amethyst.instance.client.send(it, relayList = relayList) } }
|
||||||
addresses?.forEach {
|
addresses?.forEach {
|
||||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2728,7 +2727,7 @@ class Account(
|
|||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2782,14 +2781,14 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
// Rebroadcast replies and tags to the current relay set
|
// Rebroadcast replies and tags to the current relay set
|
||||||
replyTo?.forEach { it.event?.let { Client.send(it, relayList = relayList) } }
|
replyTo?.forEach { it.event?.let { Amethyst.instance.client.send(it, relayList = relayList) } }
|
||||||
addresses?.forEach {
|
addresses?.forEach {
|
||||||
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
LocalCache.getAddressableNoteIfExists(it.toTag())?.event?.let {
|
||||||
Client.send(it, relayList = relayList)
|
Amethyst.instance.client.send(it, relayList = relayList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2835,7 +2834,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2881,7 +2880,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2954,7 +2953,7 @@ class Account(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3020,9 +3019,9 @@ class Account(
|
|||||||
fun sendDraftEvent(draftEvent: DraftEvent) {
|
fun sendDraftEvent(draftEvent: DraftEvent) {
|
||||||
val relayList = getPrivateOutBoxRelayList()
|
val relayList = getPrivateOutBoxRelayList()
|
||||||
if (relayList.isNotEmpty()) {
|
if (relayList.isNotEmpty()) {
|
||||||
Client.sendPrivately(draftEvent, relayList)
|
Amethyst.instance.client.sendPrivately(draftEvent, relayList)
|
||||||
} else {
|
} else {
|
||||||
Client.send(draftEvent)
|
Amethyst.instance.client.send(draftEvent)
|
||||||
}
|
}
|
||||||
LocalCache.justConsume(draftEvent, null)
|
LocalCache.justConsume(draftEvent, null)
|
||||||
}
|
}
|
||||||
@ -3072,12 +3071,12 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (relayList != null) {
|
if (relayList != null) {
|
||||||
Client.sendPrivately(signedEvent = wrap, relayList = relayList)
|
Amethyst.instance.client.sendPrivately(signedEvent = wrap, relayList = relayList)
|
||||||
} else {
|
} else {
|
||||||
Client.send(wrap)
|
Amethyst.instance.client.send(wrap)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Client.send(wrap)
|
Amethyst.instance.client.send(wrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3095,7 +3094,7 @@ class Account(
|
|||||||
picture = picture,
|
picture = picture,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
LocalCache.getChannelIfExists(it.id)?.let { follow(it) }
|
LocalCache.getChannelIfExists(it.id)?.let { follow(it) }
|
||||||
@ -3110,7 +3109,7 @@ class Account(
|
|||||||
val oldEvent = oldStatus.event as? StatusEvent ?: return
|
val oldEvent = oldStatus.event as? StatusEvent ?: return
|
||||||
|
|
||||||
StatusEvent.update(oldEvent, newStatus, signer) {
|
StatusEvent.update(oldEvent, newStatus, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3119,7 +3118,7 @@ class Account(
|
|||||||
if (!isWriteable()) return
|
if (!isWriteable()) return
|
||||||
|
|
||||||
StatusEvent.create(newStatus, "general", expiration = null, signer) {
|
StatusEvent.create(newStatus, "general", expiration = null, signer) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3129,11 +3128,11 @@ class Account(
|
|||||||
val oldEvent = oldStatus.event as? StatusEvent ?: return
|
val oldEvent = oldStatus.event as? StatusEvent ?: return
|
||||||
|
|
||||||
StatusEvent.clear(oldEvent, signer) { event ->
|
StatusEvent.clear(oldEvent, signer) { event ->
|
||||||
Client.send(event)
|
Amethyst.instance.client.send(event)
|
||||||
LocalCache.justConsume(event, null)
|
LocalCache.justConsume(event, null)
|
||||||
|
|
||||||
DeletionEvent.createForVersionOnly(listOf(event), signer) { event2 ->
|
DeletionEvent.createForVersionOnly(listOf(event), signer) { event2 ->
|
||||||
Client.send(event2)
|
Amethyst.instance.client.send(event2)
|
||||||
LocalCache.justConsume(event2, null)
|
LocalCache.justConsume(event2, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3154,7 +3153,7 @@ class Account(
|
|||||||
noteEvent.taggedAddresses().filter { it != emojiListEvent.address() },
|
noteEvent.taggedAddresses().filter { it != emojiListEvent.address() },
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3172,7 +3171,7 @@ class Account(
|
|||||||
listOf(emojiListEvent.address()),
|
listOf(emojiListEvent.address()),
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3187,7 +3186,7 @@ class Account(
|
|||||||
noteEvent.taggedAddresses().plus(emojiListEvent.address()),
|
noteEvent.taggedAddresses().plus(emojiListEvent.address()),
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3218,7 +3217,7 @@ class Account(
|
|||||||
originalHash = originalHash, */
|
originalHash = originalHash, */
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) { event ->
|
) { event ->
|
||||||
Client.send(event)
|
Amethyst.instance.client.send(event)
|
||||||
LocalCache.consume(event, null)
|
LocalCache.consume(event, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3241,7 +3240,7 @@ class Account(
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3251,7 +3250,7 @@ class Account(
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3272,7 +3271,7 @@ class Account(
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3282,17 +3281,20 @@ class Account(
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
signer,
|
signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it)
|
LocalCache.consume(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createAuthEvent(
|
fun sendAuthEvent(
|
||||||
relay: Relay,
|
relay: Relay,
|
||||||
challenge: String,
|
challenge: String,
|
||||||
onReady: (RelayAuthEvent) -> Unit,
|
) {
|
||||||
) = createAuthEvent(relay.url, challenge, onReady = onReady)
|
createAuthEvent(relay.url, challenge) {
|
||||||
|
Amethyst.instance.client.sendIfExists(it, relay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createAuthEvent(
|
fun createAuthEvent(
|
||||||
relayUrl: String,
|
relayUrl: String,
|
||||||
@ -3393,7 +3395,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3402,7 +3404,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3418,7 +3420,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3432,7 +3434,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3448,7 +3450,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3457,7 +3459,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3473,7 +3475,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3487,7 +3489,7 @@ class Account(
|
|||||||
isPrivate = true,
|
isPrivate = true,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.consume(it, null)
|
LocalCache.consume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3517,7 +3519,7 @@ class Account(
|
|||||||
originalChannelIdHex = channel.idHex,
|
originalChannelIdHex = channel.idHex,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
|
|
||||||
follow(channel)
|
follow(channel)
|
||||||
@ -3547,9 +3549,9 @@ class Account(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (relayList != null) {
|
if (relayList != null) {
|
||||||
Client.sendPrivately(it, relayList)
|
Amethyst.instance.client.sendPrivately(it, relayList)
|
||||||
} else {
|
} else {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
}
|
}
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
onReady(it)
|
onReady(it)
|
||||||
@ -3764,7 +3766,7 @@ class Account(
|
|||||||
relays = dmRelays,
|
relays = dmRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3772,7 +3774,7 @@ class Account(
|
|||||||
relays = dmRelays,
|
relays = dmRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3798,7 +3800,7 @@ class Account(
|
|||||||
relays = relays,
|
relays = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3806,7 +3808,7 @@ class Account(
|
|||||||
relays = relays,
|
relays = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3832,7 +3834,7 @@ class Account(
|
|||||||
relays = searchRelays,
|
relays = searchRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3840,7 +3842,7 @@ class Account(
|
|||||||
relays = searchRelays,
|
relays = searchRelays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3866,7 +3868,7 @@ class Account(
|
|||||||
relays = relays,
|
relays = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3874,7 +3876,7 @@ class Account(
|
|||||||
relays = relays,
|
relays = relays,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3922,7 +3924,7 @@ class Account(
|
|||||||
relays = servers,
|
relays = servers,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3930,7 +3932,7 @@ class Account(
|
|||||||
relays = servers,
|
relays = servers,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3947,7 +3949,7 @@ class Account(
|
|||||||
relays = servers,
|
relays = servers,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -3955,7 +3957,7 @@ class Account(
|
|||||||
relays = servers,
|
relays = servers,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it)
|
Amethyst.instance.client.send(it)
|
||||||
LocalCache.justConsume(it, null)
|
LocalCache.justConsume(it, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service
|
package com.vitorpamplona.amethyst.service
|
||||||
|
|
||||||
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.ammolite.relays.NostrDataSource
|
import com.vitorpamplona.ammolite.relays.NostrDataSource
|
||||||
import com.vitorpamplona.ammolite.relays.Relay
|
import com.vitorpamplona.ammolite.relays.Relay
|
||||||
@ -28,7 +29,7 @@ import com.vitorpamplona.quartz.events.Event
|
|||||||
|
|
||||||
abstract class AmethystNostrDataSource(
|
abstract class AmethystNostrDataSource(
|
||||||
debugName: String,
|
debugName: String,
|
||||||
) : NostrDataSource(debugName) {
|
) : NostrDataSource(Amethyst.instance.client, debugName) {
|
||||||
override fun consume(
|
override fun consume(
|
||||||
event: Event,
|
event: Event,
|
||||||
relay: Relay,
|
relay: Relay,
|
||||||
|
@ -27,9 +27,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
import com.vitorpamplona.amethyst.service.lnurl.LightningAddressResolver
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.service
|
|||||||
|
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.service
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.encoders.Nip11RelayInformation
|
import com.vitorpamplona.quartz.encoders.Nip11RelayInformation
|
||||||
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
@ -25,7 +25,6 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.relays.EOSEAccount
|
import com.vitorpamplona.amethyst.service.relays.EOSEAccount
|
||||||
import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
|
import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES
|
||||||
import com.vitorpamplona.ammolite.relays.Client
|
|
||||||
import com.vitorpamplona.ammolite.relays.EVENT_FINDER_TYPES
|
import com.vitorpamplona.ammolite.relays.EVENT_FINDER_TYPES
|
||||||
import com.vitorpamplona.ammolite.relays.Relay
|
import com.vitorpamplona.ammolite.relays.Relay
|
||||||
import com.vitorpamplona.ammolite.relays.TypedFilter
|
import com.vitorpamplona.ammolite.relays.TypedFilter
|
||||||
@ -492,9 +491,7 @@ object NostrAccountDataSource : AmethystNostrDataSource("AccountData") {
|
|||||||
super.auth(relay, challenge)
|
super.auth(relay, challenge)
|
||||||
|
|
||||||
if (this::account.isInitialized) {
|
if (this::account.isInitialized) {
|
||||||
account.createAuthEvent(relay, challenge) {
|
account.sendAuthEvent(relay, challenge)
|
||||||
Client.sendIfExists(it, relay)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ import android.util.Log
|
|||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import okhttp3.EventListener
|
import okhttp3.EventListener
|
||||||
import okhttp3.Protocol
|
import okhttp3.Protocol
|
||||||
|
@ -25,8 +25,8 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
|||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
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 okhttp3.Request
|
import okhttp3.Request
|
||||||
|
@ -27,8 +27,8 @@ import com.vitorpamplona.amethyst.BuildConfig
|
|||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.launchAndWaitAll
|
import com.vitorpamplona.amethyst.launchAndWaitAll
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.tryAndWait
|
import com.vitorpamplona.amethyst.tryAndWait
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.events.RelayAuthEvent
|
import com.vitorpamplona.quartz.events.RelayAuthEvent
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerExternal
|
import com.vitorpamplona.quartz.signers.NostrSignerExternal
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
@ -18,41 +18,20 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.ammolite.service
|
package com.vitorpamplona.amethyst.service.okhttp
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager.setDefaultProxy
|
import com.vitorpamplona.quartz.crypto.nip17.NostrCipher
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
class LoggingInterceptor : Interceptor {
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val request: Request = chain.request()
|
|
||||||
val t1 = System.nanoTime()
|
|
||||||
val port =
|
|
||||||
(
|
|
||||||
chain
|
|
||||||
.connection()
|
|
||||||
?.route()
|
|
||||||
?.proxy
|
|
||||||
?.address() as? InetSocketAddress
|
|
||||||
)?.port
|
|
||||||
val response: Response = chain.proceed(request)
|
|
||||||
val t2 = System.nanoTime()
|
|
||||||
|
|
||||||
Log.d("OkHttpLog", "Req $port ${request.url} in ${(t2 - t1) / 1e6}ms")
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object HttpClientManager {
|
object HttpClientManager {
|
||||||
val rootClient =
|
val rootClient =
|
||||||
OkHttpClient
|
OkHttpClient
|
||||||
@ -74,32 +53,32 @@ object HttpClientManager {
|
|||||||
fun setDefaultProxy(proxy: Proxy?) {
|
fun setDefaultProxy(proxy: Proxy?) {
|
||||||
if (currentProxy != proxy) {
|
if (currentProxy != proxy) {
|
||||||
Log.d("HttpClient", "Changing proxy to: ${proxy != null}")
|
Log.d("HttpClient", "Changing proxy to: ${proxy != null}")
|
||||||
this.currentProxy = proxy
|
currentProxy = proxy
|
||||||
|
|
||||||
// recreates singleton
|
// recreates singleton
|
||||||
this.defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentProxy(): Proxy? = this.currentProxy
|
fun getCurrentProxy(): Proxy? = currentProxy
|
||||||
|
|
||||||
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 (defaultTimeout.seconds != timeout.seconds) {
|
||||||
this.defaultTimeout = timeout
|
defaultTimeout = timeout
|
||||||
|
|
||||||
// recreates singleton
|
// recreates singleton
|
||||||
this.defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
||||||
this.defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDefaultUserAgent(userAgentHeader: String) {
|
fun setDefaultUserAgent(userAgentHeader: String) {
|
||||||
Log.d("HttpClient", "Changing userAgent")
|
Log.d("HttpClient", "Changing userAgent")
|
||||||
if (userAgent != userAgentHeader) {
|
if (userAgent != userAgentHeader) {
|
||||||
this.userAgent = userAgentHeader
|
userAgent = userAgentHeader
|
||||||
this.defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
||||||
this.defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +96,7 @@ object HttpClientManager {
|
|||||||
.writeTimeout(duration)
|
.writeTimeout(duration)
|
||||||
.addInterceptor(DefaultContentTypeInterceptor(userAgent))
|
.addInterceptor(DefaultContentTypeInterceptor(userAgent))
|
||||||
.addNetworkInterceptor(LoggingInterceptor())
|
.addNetworkInterceptor(LoggingInterceptor())
|
||||||
|
.addNetworkInterceptor(EncryptedBlobInterceptor())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +115,48 @@ object HttpClientManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EncryptedBlobInterceptor : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val response = chain.proceed(chain.request())
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val cipher = chain.request().tag(NostrCipher::class)
|
||||||
|
|
||||||
|
println("AABBCC Cipher ${chain.request().tag(NostrCipher::class)}")
|
||||||
|
|
||||||
|
if (cipher != null) {
|
||||||
|
val body = response.peekBody(Long.MAX_VALUE)
|
||||||
|
val decryptedBytes = cipher.decrypt(body.bytes())
|
||||||
|
val newBody = decryptedBytes.toResponseBody(body.contentType())
|
||||||
|
return response.newBuilder().body(newBody).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggingInterceptor : Interceptor {
|
||||||
|
@Throws(IOException::class)
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val request: Request = chain.request()
|
||||||
|
val t1 = System.nanoTime()
|
||||||
|
val port =
|
||||||
|
(
|
||||||
|
chain
|
||||||
|
.connection()
|
||||||
|
?.route()
|
||||||
|
?.proxy
|
||||||
|
?.address() as? InetSocketAddress
|
||||||
|
)?.port
|
||||||
|
val response: Response = chain.proceed(request)
|
||||||
|
val t2 = System.nanoTime()
|
||||||
|
|
||||||
|
Log.d("OkHttpLog", "Req $port ${request.url} in ${(t2 - t1) / 1e6}ms")
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getCurrentProxyPort(useProxy: Boolean): Int? =
|
fun getCurrentProxyPort(useProxy: Boolean): Int? =
|
||||||
if (useProxy) {
|
if (useProxy) {
|
||||||
(currentProxy?.address() as? InetSocketAddress)?.port
|
(currentProxy?.address() as? InetSocketAddress)?.port
|
||||||
@ -144,13 +166,13 @@ object HttpClientManager {
|
|||||||
|
|
||||||
fun getHttpClient(useProxy: Boolean): OkHttpClient =
|
fun getHttpClient(useProxy: Boolean): OkHttpClient =
|
||||||
if (useProxy) {
|
if (useProxy) {
|
||||||
if (this.defaultHttpClient == null) {
|
if (defaultHttpClient == null) {
|
||||||
this.defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
defaultHttpClient = buildHttpClient(currentProxy, defaultTimeout)
|
||||||
}
|
}
|
||||||
defaultHttpClient!!
|
defaultHttpClient!!
|
||||||
} else {
|
} else {
|
||||||
if (this.defaultHttpClientWithoutProxy == null) {
|
if (defaultHttpClientWithoutProxy == null) {
|
||||||
this.defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
defaultHttpClientWithoutProxy = buildHttpClient(null, defaultTimeout)
|
||||||
}
|
}
|
||||||
defaultHttpClientWithoutProxy!!
|
defaultHttpClientWithoutProxy!!
|
||||||
}
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.service.okhttp
|
||||||
|
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebSocket
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebSocketListener
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebsocketBuilder
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
class OkHttpWebSocket(
|
||||||
|
val url: String,
|
||||||
|
val forceProxy: Boolean,
|
||||||
|
val out: WebSocketListener,
|
||||||
|
) : WebSocket {
|
||||||
|
private val listener = OkHttpWebsocketListener()
|
||||||
|
private var socket: okhttp3.WebSocket? = null
|
||||||
|
|
||||||
|
fun buildRequest() = Request.Builder().url(url.trim()).build()
|
||||||
|
|
||||||
|
override fun connect() {
|
||||||
|
socket = HttpClientManager.getHttpClient(forceProxy).newWebSocket(buildRequest(), listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class OkHttpWebsocketListener : okhttp3.WebSocketListener() {
|
||||||
|
override fun onOpen(
|
||||||
|
webSocket: okhttp3.WebSocket,
|
||||||
|
response: Response,
|
||||||
|
) = out.onOpen(
|
||||||
|
response.receivedResponseAtMillis - response.sentRequestAtMillis,
|
||||||
|
response.headers.get("Sec-WebSocket-Extensions")?.contains("permessage-deflate") ?: false,
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onMessage(
|
||||||
|
webSocket: okhttp3.WebSocket,
|
||||||
|
text: String,
|
||||||
|
) = out.onMessage(text)
|
||||||
|
|
||||||
|
override fun onClosing(
|
||||||
|
webSocket: okhttp3.WebSocket,
|
||||||
|
code: Int,
|
||||||
|
reason: String,
|
||||||
|
) = out.onClosing(code, reason)
|
||||||
|
|
||||||
|
override fun onClosed(
|
||||||
|
webSocket: okhttp3.WebSocket,
|
||||||
|
code: Int,
|
||||||
|
reason: String,
|
||||||
|
) = out.onClosed(code, reason)
|
||||||
|
|
||||||
|
override fun onFailure(
|
||||||
|
webSocket: okhttp3.WebSocket,
|
||||||
|
t: Throwable,
|
||||||
|
response: Response?,
|
||||||
|
) = out.onFailure(t, response?.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder : WebsocketBuilder {
|
||||||
|
override fun build(
|
||||||
|
url: String,
|
||||||
|
forceProxy: Boolean,
|
||||||
|
out: WebSocketListener,
|
||||||
|
) = OkHttpWebSocket(url, forceProxy, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
socket?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun send(msg: String): Boolean = socket?.send(msg) ?: false
|
||||||
|
}
|
@ -24,7 +24,7 @@ import android.util.Log
|
|||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.ots.BitcoinExplorer
|
import com.vitorpamplona.quartz.ots.BitcoinExplorer
|
||||||
import com.vitorpamplona.quartz.ots.BlockHeader
|
import com.vitorpamplona.quartz.ots.BlockHeader
|
||||||
import com.vitorpamplona.quartz.ots.exceptions.UrlException
|
import com.vitorpamplona.quartz.ots.exceptions.UrlException
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.service.ots
|
package com.vitorpamplona.amethyst.service.ots
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.ots.ICalendar
|
import com.vitorpamplona.quartz.ots.ICalendar
|
||||||
import com.vitorpamplona.quartz.ots.StreamDeserializationContext
|
import com.vitorpamplona.quartz.ots.StreamDeserializationContext
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.service.ots
|
package com.vitorpamplona.amethyst.service.ots
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.ots.ICalendarAsyncSubmit
|
import com.vitorpamplona.quartz.ots.ICalendarAsyncSubmit
|
||||||
import com.vitorpamplona.quartz.ots.StreamDeserializationContext
|
import com.vitorpamplona.quartz.ots.StreamDeserializationContext
|
||||||
import com.vitorpamplona.quartz.ots.Timestamp
|
import com.vitorpamplona.quartz.ots.Timestamp
|
||||||
|
@ -26,7 +26,7 @@ import androidx.annotation.OptIn
|
|||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.session.MediaSession
|
import androidx.media3.session.MediaSession
|
||||||
import androidx.media3.session.MediaSessionService
|
import androidx.media3.session.MediaSessionService
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
|
|
||||||
class PlaybackService : MediaSessionService() {
|
class PlaybackService : MediaSessionService() {
|
||||||
private var videoViewedPositionCache = VideoViewedPositionCache()
|
private var videoViewedPositionCache = VideoViewedPositionCache()
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.service.previews
|
package com.vitorpamplona.amethyst.service.previews
|
||||||
|
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -31,10 +31,10 @@ import com.vitorpamplona.amethyst.BuildConfig
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
|
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
|
||||||
import com.vitorpamplona.amethyst.service.uploads.nip96.randomChars
|
import com.vitorpamplona.amethyst.service.uploads.nip96.randomChars
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
|
@ -32,9 +32,9 @@ import com.vitorpamplona.amethyst.BuildConfig
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
import com.vitorpamplona.amethyst.service.HttpStatusMessages
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
|
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.encoders.Dimension
|
import com.vitorpamplona.quartz.encoders.Dimension
|
||||||
import com.vitorpamplona.quartz.events.HTTPAuthorizationEvent
|
import com.vitorpamplona.quartz.events.HTTPAuthorizationEvent
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -24,7 +24,7 @@ import android.util.Log
|
|||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -39,6 +39,7 @@ import com.vitorpamplona.amethyst.debugState
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
||||||
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.ui.components.DEFAULT_MUTED_SETTING
|
import com.vitorpamplona.amethyst.ui.components.DEFAULT_MUTED_SETTING
|
||||||
import com.vitorpamplona.amethyst.ui.components.keepPlayingMutex
|
import com.vitorpamplona.amethyst.ui.components.keepPlayingMutex
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||||
@ -46,7 +47,6 @@ import com.vitorpamplona.amethyst.ui.screen.AccountScreen
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
||||||
import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
||||||
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
import com.vitorpamplona.quartz.events.ChannelCreateEvent
|
||||||
|
@ -32,7 +32,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.vitorpamplona.amethyst.BuildConfig
|
import com.vitorpamplona.amethyst.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.Call
|
import okhttp3.Call
|
||||||
import okhttp3.Callback
|
import okhttp3.Callback
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.actions.uploads
|
package com.vitorpamplona.amethyst.ui.actions.uploads
|
||||||
|
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
@ -97,6 +97,7 @@ import com.linc.audiowaveform.infiniteLinearGradient
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
|
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
|
||||||
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
|
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.playback.PlaybackClientController
|
import com.vitorpamplona.amethyst.service.playback.PlaybackClientController
|
||||||
import com.vitorpamplona.amethyst.ui.actions.MediaSaverToDisk
|
import com.vitorpamplona.amethyst.ui.actions.MediaSaverToDisk
|
||||||
import com.vitorpamplona.amethyst.ui.note.DownloadForOfflineIcon
|
import com.vitorpamplona.amethyst.ui.note.DownloadForOfflineIcon
|
||||||
@ -117,7 +118,6 @@ import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.VolumeBottomIconSize
|
import com.vitorpamplona.amethyst.ui.theme.VolumeBottomIconSize
|
||||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.videoGalleryModifier
|
import com.vitorpamplona.amethyst.ui.theme.videoGalleryModifier
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.encoders.Dimension
|
import com.vitorpamplona.quartz.encoders.Dimension
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
@ -111,7 +111,6 @@ import com.vitorpamplona.amethyst.ui.theme.drawerSpacing
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import com.vitorpamplona.amethyst.ui.theme.profileContentHeaderModifier
|
import com.vitorpamplona.amethyst.ui.theme.profileContentHeaderModifier
|
||||||
import com.vitorpamplona.amethyst.ui.tor.ConnectTorDialog
|
import com.vitorpamplona.amethyst.ui.tor.ConnectTorDialog
|
||||||
import com.vitorpamplona.ammolite.relays.RelayPool
|
|
||||||
import com.vitorpamplona.ammolite.relays.RelayPoolStatus
|
import com.vitorpamplona.ammolite.relays.RelayPoolStatus
|
||||||
import com.vitorpamplona.quartz.encoders.ATag
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
@ -563,7 +562,7 @@ fun ListContent(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RelayStatus(accountViewModel: AccountViewModel) {
|
private fun RelayStatus(accountViewModel: AccountViewModel) {
|
||||||
val connectedRelaysText by RelayPool.statusFlow.collectAsStateWithLifecycle(RelayPoolStatus(0, 0))
|
val connectedRelaysText by accountViewModel.relayStatusFlow().collectAsStateWithLifecycle(RelayPoolStatus(0, 0))
|
||||||
|
|
||||||
RenderRelayStatus(connectedRelaysText)
|
RenderRelayStatus(connectedRelaysText)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
|
|||||||
import com.vitorpamplona.amethyst.service.Nip05NostrAddressVerifier
|
import com.vitorpamplona.amethyst.service.Nip05NostrAddressVerifier
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
import com.vitorpamplona.ammolite.relays.Client
|
|
||||||
import com.vitorpamplona.ammolite.relays.Constants
|
import com.vitorpamplona.ammolite.relays.Constants
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
@ -297,11 +296,11 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
delay(2000) // waits for the new user to connect to the new relays.
|
delay(2000) // waits for the new user to connect to the new relays.
|
||||||
accountSettings.backupUserMetadata?.let { Client.send(it) }
|
accountSettings.backupUserMetadata?.let { Amethyst.instance.client.send(it) }
|
||||||
accountSettings.backupContactList?.let { Client.send(it) }
|
accountSettings.backupContactList?.let { Amethyst.instance.client.send(it) }
|
||||||
accountSettings.backupNIP65RelayList?.let { Client.send(it) }
|
accountSettings.backupNIP65RelayList?.let { Amethyst.instance.client.send(it) }
|
||||||
accountSettings.backupDMRelayList?.let { Client.send(it) }
|
accountSettings.backupDMRelayList?.let { Amethyst.instance.client.send(it) }
|
||||||
accountSettings.backupSearchRelayList?.let { Client.send(it) }
|
accountSettings.backupSearchRelayList?.let { Amethyst.instance.client.send(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1626,6 +1626,8 @@ class AccountViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun relayStatusFlow() = Amethyst.instance.client.relayStatusFlow()
|
||||||
|
|
||||||
val draftNoteCache = CachedDraftNotes(this)
|
val draftNoteCache = CachedDraftNotes(this)
|
||||||
|
|
||||||
class CachedDraftNotes(
|
class CachedDraftNotes(
|
||||||
|
@ -51,6 +51,7 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
|
|||||||
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
|
||||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser.Companion.isVideoUrl
|
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser.Companion.isVideoUrl
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||||
import com.vitorpamplona.amethyst.ui.components.DisplayBlurHash
|
import com.vitorpamplona.amethyst.ui.components.DisplayBlurHash
|
||||||
@ -66,7 +67,6 @@ import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.quartz.events.PictureEvent
|
import com.vitorpamplona.quartz.events.PictureEvent
|
||||||
import com.vitorpamplona.quartz.events.ProfileGalleryEntryEvent
|
import com.vitorpamplona.quartz.events.ProfileGalleryEntryEvent
|
||||||
import com.vitorpamplona.quartz.events.VideoEvent
|
import com.vitorpamplona.quartz.events.VideoEvent
|
||||||
|
@ -27,7 +27,7 @@ import android.content.ServiceConnection
|
|||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import org.torproject.jni.TorService
|
import org.torproject.jni.TorService
|
||||||
import org.torproject.jni.TorService.LocalBinder
|
import org.torproject.jni.TorService.LocalBinder
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ dependencies {
|
|||||||
implementation libs.androidx.runtime.runtime
|
implementation libs.androidx.runtime.runtime
|
||||||
|
|
||||||
implementation project(path: ':quartz')
|
implementation project(path: ':quartz')
|
||||||
implementation libs.okhttp
|
|
||||||
|
|
||||||
testImplementation libs.junit
|
testImplementation libs.junit
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ package com.vitorpamplona.ammolite.relays
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebsocketBuilder
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventInterface
|
import com.vitorpamplona.quartz.events.EventInterface
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
@ -37,10 +38,16 @@ import java.util.concurrent.TimeUnit
|
|||||||
* The Nostr Client manages multiple personae the user may switch between. Events are received and
|
* The Nostr Client manages multiple personae the user may switch between. Events are received and
|
||||||
* published through multiple relays. Events are stored with their respective persona.
|
* published through multiple relays. Events are stored with their respective persona.
|
||||||
*/
|
*/
|
||||||
object Client : RelayPool.Listener {
|
class NostrClient(
|
||||||
|
private val websocketBuilder: WebsocketBuilder,
|
||||||
|
) : RelayPool.Listener {
|
||||||
|
private val relayPool: RelayPool = RelayPool()
|
||||||
|
private val subscriptions: MutableSubscriptionManager = MutableSubscriptionManager()
|
||||||
|
|
||||||
private var listeners = setOf<Listener>()
|
private var listeners = setOf<Listener>()
|
||||||
private var relays = emptyArray<Relay>()
|
private var relays = emptyArray<Relay>()
|
||||||
private var subscriptions = mapOf<String, List<TypedFilter>>()
|
|
||||||
|
fun buildRelay(it: RelaySetupInfoToConnect): Relay = Relay(it.url, it.read, it.write, it.forceProxy, it.feedTypes, websocketBuilder, subscriptions)
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun reconnect(
|
fun reconnect(
|
||||||
@ -52,33 +59,33 @@ object Client : RelayPool.Listener {
|
|||||||
|
|
||||||
if (onlyIfChanged) {
|
if (onlyIfChanged) {
|
||||||
if (!isSameRelaySetConfig(relays)) {
|
if (!isSameRelaySetConfig(relays)) {
|
||||||
if (Client.relays.isNotEmpty()) {
|
if (this.relays.isNotEmpty()) {
|
||||||
RelayPool.disconnect()
|
relayPool.disconnect()
|
||||||
RelayPool.unregister(this)
|
relayPool.unregister(this)
|
||||||
RelayPool.unloadRelays()
|
relayPool.unloadRelays()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relays != null) {
|
if (relays != null) {
|
||||||
val newRelays = relays.map { Relay(it.url, it.read, it.write, it.forceProxy, it.feedTypes) }
|
val newRelays = relays.map(::buildRelay)
|
||||||
RelayPool.register(this)
|
relayPool.register(this)
|
||||||
RelayPool.loadRelays(newRelays)
|
relayPool.loadRelays(newRelays)
|
||||||
RelayPool.requestAndWatch()
|
relayPool.requestAndWatch()
|
||||||
Client.relays = newRelays.toTypedArray()
|
this.relays = newRelays.toTypedArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Client.relays.isNotEmpty()) {
|
if (this.relays.isNotEmpty()) {
|
||||||
RelayPool.disconnect()
|
relayPool.disconnect()
|
||||||
RelayPool.unregister(this)
|
relayPool.unregister(this)
|
||||||
RelayPool.unloadRelays()
|
relayPool.unloadRelays()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relays != null) {
|
if (relays != null) {
|
||||||
val newRelays = relays.map { Relay(it.url, it.read, it.write, it.forceProxy, it.feedTypes) }
|
val newRelays = relays.map(::buildRelay)
|
||||||
RelayPool.register(this)
|
relayPool.register(this)
|
||||||
RelayPool.loadRelays(newRelays)
|
relayPool.loadRelays(newRelays)
|
||||||
RelayPool.requestAndWatch()
|
relayPool.requestAndWatch()
|
||||||
Client.relays = newRelays.toTypedArray()
|
this.relays = newRelays.toTypedArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,8 +108,8 @@ object Client : RelayPool.Listener {
|
|||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
subscriptions = subscriptions + Pair(subscriptionId, filters)
|
subscriptions.add(subscriptionId, filters)
|
||||||
RelayPool.sendFilter(subscriptionId, filters)
|
relayPool.sendFilter(subscriptionId, filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendFilterAndStopOnFirstResponse(
|
fun sendFilterAndStopOnFirstResponse(
|
||||||
@ -129,8 +136,8 @@ object Client : RelayPool.Listener {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
subscriptions = subscriptions + Pair(subscriptionId, filters)
|
subscriptions.add(subscriptionId, filters)
|
||||||
RelayPool.sendFilter(subscriptionId, filters)
|
relayPool.sendFilter(subscriptionId, filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@ -146,7 +153,7 @@ object Client : RelayPool.Listener {
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val size = if (relay != null) 1 else relayList?.size ?: RelayPool.availableRelays()
|
val size = if (relay != null) 1 else relayList?.size ?: relayPool.availableRelays()
|
||||||
val latch = CountDownLatch(size)
|
val latch = CountDownLatch(size)
|
||||||
val relayErrors = mutableMapOf<String, String>()
|
val relayErrors = mutableMapOf<String, String>()
|
||||||
var result = false
|
var result = false
|
||||||
@ -227,8 +234,8 @@ object Client : RelayPool.Listener {
|
|||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
subscriptions = subscriptions + Pair(subscriptionId, filters)
|
subscriptions.add(subscriptionId, filters)
|
||||||
RelayPool.connectAndSendFiltersIfDisconnected()
|
relayPool.connectAndSendFiltersIfDisconnected()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendIfExists(
|
fun sendIfExists(
|
||||||
@ -237,7 +244,7 @@ object Client : RelayPool.Listener {
|
|||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
RelayPool.getRelays(connectedRelay.url).forEach {
|
relayPool.getRelays(connectedRelay.url).forEach {
|
||||||
it.send(signedEvent)
|
it.send(signedEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,14 +256,14 @@ object Client : RelayPool.Listener {
|
|||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
RelayPool.getOrCreateRelay(relayTemplate, onDone) {
|
relayPool.runCreatingIfNeeded(buildRelay(relayTemplate), onDone = onDone) {
|
||||||
it.send(signedEvent)
|
it.send(signedEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(signedEvent: EventInterface) {
|
fun send(signedEvent: EventInterface) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
RelayPool.send(signedEvent)
|
relayPool.send(signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(
|
fun send(
|
||||||
@ -265,7 +272,7 @@ object Client : RelayPool.Listener {
|
|||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
RelayPool.sendToSelectedRelays(relayList, signedEvent)
|
relayPool.sendToSelectedRelays(relayList, signedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPrivately(
|
fun sendPrivately(
|
||||||
@ -275,18 +282,18 @@ object Client : RelayPool.Listener {
|
|||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
relayList.forEach { relayTemplate ->
|
relayList.forEach { relayTemplate ->
|
||||||
RelayPool.getOrCreateRelay(relayTemplate, { }) {
|
relayPool.runCreatingIfNeeded(buildRelay(relayTemplate)) {
|
||||||
it.sendOverride(signedEvent)
|
it.sendOverride(signedEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun close(subscriptionId: String) {
|
fun close(subscriptionId: String) {
|
||||||
RelayPool.close(subscriptionId)
|
relayPool.close(subscriptionId)
|
||||||
subscriptions = subscriptions.minus(subscriptionId)
|
subscriptions.remove(subscriptionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isActive(subscriptionId: String): Boolean = subscriptions.contains(subscriptionId)
|
fun isActive(subscriptionId: String): Boolean = subscriptions.isActive(subscriptionId)
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
override fun onEvent(
|
override fun onEvent(
|
||||||
@ -392,9 +399,13 @@ object Client : RelayPool.Listener {
|
|||||||
listeners = listeners.minus(listener)
|
listeners = listeners.minus(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun allSubscriptions(): Map<String, List<TypedFilter>> = subscriptions
|
fun allSubscriptions(): Map<String, List<TypedFilter>> = subscriptions.allSubscriptions()
|
||||||
|
|
||||||
fun getSubscriptionFilters(subId: String): List<TypedFilter> = subscriptions[subId] ?: emptyList()
|
fun getSubscriptionFilters(subId: String): List<TypedFilter> = subscriptions.getSubscriptionFilters(subId)
|
||||||
|
|
||||||
|
fun connectedRelays() = relayPool.connectedRelays()
|
||||||
|
|
||||||
|
fun relayStatusFlow() = relayPool.statusFlow
|
||||||
|
|
||||||
interface Listener {
|
interface Listener {
|
||||||
/** A new message was received */
|
/** A new message was received */
|
@ -35,6 +35,7 @@ import java.util.UUID
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
abstract class NostrDataSource(
|
abstract class NostrDataSource(
|
||||||
|
val client: NostrClient,
|
||||||
val debugName: String,
|
val debugName: String,
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
@ -67,7 +68,7 @@ abstract class NostrDataSource(
|
|||||||
): Int = 31 * str1.hashCode() + str2.hashCode()
|
): Int = 31 * str1.hashCode() + str2.hashCode()
|
||||||
|
|
||||||
private val clientListener =
|
private val clientListener =
|
||||||
object : Client.Listener {
|
object : NostrClient.Listener {
|
||||||
override fun onEvent(
|
override fun onEvent(
|
||||||
event: Event,
|
event: Event,
|
||||||
subscriptionId: String,
|
subscriptionId: String,
|
||||||
@ -139,14 +140,14 @@ abstract class NostrDataSource(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
Log.d("DataSource", "${this.javaClass.simpleName} Subscribe")
|
Log.d("DataSource", "${this.javaClass.simpleName} Subscribe")
|
||||||
Client.subscribe(clientListener)
|
client.subscribe(clientListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
fun destroy() {
|
||||||
// makes sure to run
|
// makes sure to run
|
||||||
Log.d("DataSource", "${this.javaClass.simpleName} Unsubscribe")
|
Log.d("DataSource", "${this.javaClass.simpleName} Unsubscribe")
|
||||||
stop()
|
stop()
|
||||||
Client.unsubscribe(clientListener)
|
client.unsubscribe(clientListener)
|
||||||
scope.cancel()
|
scope.cancel()
|
||||||
bundler.cancel()
|
bundler.cancel()
|
||||||
}
|
}
|
||||||
@ -170,7 +171,7 @@ abstract class NostrDataSource(
|
|||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
subscriptions.values.forEach { subscription ->
|
subscriptions.values.forEach { subscription ->
|
||||||
Client.close(subscription.id)
|
client.close(subscription.id)
|
||||||
subscription.typedFilters = null
|
subscription.typedFilters = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +182,7 @@ abstract class NostrDataSource(
|
|||||||
Log.d("DataSource", "${this.javaClass.simpleName} Stop")
|
Log.d("DataSource", "${this.javaClass.simpleName} Stop")
|
||||||
|
|
||||||
subscriptions.values.forEach { subscription ->
|
subscriptions.values.forEach { subscription ->
|
||||||
Client.close(subscription.id)
|
client.close(subscription.id)
|
||||||
subscription.typedFilters = null
|
subscription.typedFilters = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,7 +194,7 @@ abstract class NostrDataSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun dismissChannel(subscription: Subscription) {
|
fun dismissChannel(subscription: Subscription) {
|
||||||
Client.close(subscription.id)
|
client.close(subscription.id)
|
||||||
subscriptions = subscriptions.minus(subscription.id)
|
subscriptions = subscriptions.minus(subscription.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,29 +232,29 @@ abstract class NostrDataSource(
|
|||||||
subscriptions.values.forEach { updatedSubscription ->
|
subscriptions.values.forEach { updatedSubscription ->
|
||||||
val updatedSubscriptionNewFilters = updatedSubscription.typedFilters
|
val updatedSubscriptionNewFilters = updatedSubscription.typedFilters
|
||||||
|
|
||||||
val isActive = Client.isActive(updatedSubscription.id)
|
val isActive = client.isActive(updatedSubscription.id)
|
||||||
|
|
||||||
if (!isActive && updatedSubscriptionNewFilters != null) {
|
if (!isActive && updatedSubscriptionNewFilters != null) {
|
||||||
// Filter was removed from the active list
|
// Filter was removed from the active list
|
||||||
if (active) {
|
if (active) {
|
||||||
Client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentFilters.containsKey(updatedSubscription.id)) {
|
if (currentFilters.containsKey(updatedSubscription.id)) {
|
||||||
if (updatedSubscriptionNewFilters == null) {
|
if (updatedSubscriptionNewFilters == null) {
|
||||||
// was active and is not active anymore, just close.
|
// was active and is not active anymore, just close.
|
||||||
Client.close(updatedSubscription.id)
|
client.close(updatedSubscription.id)
|
||||||
} else {
|
} else {
|
||||||
// was active and is still active, check if it has changed.
|
// was active and is still active, check if it has changed.
|
||||||
if (updatedSubscription.hasChangedFiltersFrom(currentFilters[updatedSubscription.id])) {
|
if (updatedSubscription.hasChangedFiltersFrom(currentFilters[updatedSubscription.id])) {
|
||||||
Client.close(updatedSubscription.id)
|
client.close(updatedSubscription.id)
|
||||||
if (active) {
|
if (active) {
|
||||||
Client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// hasn't changed, does nothing.
|
// hasn't changed, does nothing.
|
||||||
if (active) {
|
if (active) {
|
||||||
Client.sendFilterOnlyIfDisconnected(
|
client.sendFilterOnlyIfDisconnected(
|
||||||
updatedSubscription.id,
|
updatedSubscription.id,
|
||||||
updatedSubscriptionNewFilters,
|
updatedSubscriptionNewFilters,
|
||||||
)
|
)
|
||||||
@ -269,9 +270,9 @@ abstract class NostrDataSource(
|
|||||||
if (active) {
|
if (active) {
|
||||||
Log.d(
|
Log.d(
|
||||||
this@NostrDataSource.javaClass.simpleName,
|
this@NostrDataSource.javaClass.simpleName,
|
||||||
"Update Filter 3 ${updatedSubscription.id} ${Client.isSubscribed(clientListener)}",
|
"Update Filter 3 ${updatedSubscription.id} ${client.isSubscribed(clientListener)}",
|
||||||
)
|
)
|
||||||
Client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
client.sendFilter(updatedSubscription.id, updatedSubscriptionNewFilters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,10 @@ package com.vitorpamplona.ammolite.relays
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.vitorpamplona.ammolite.BuildConfig
|
import com.vitorpamplona.ammolite.BuildConfig
|
||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
|
||||||
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebSocket
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebSocketListener
|
||||||
|
import com.vitorpamplona.ammolite.sockets.WebsocketBuilder
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventInterface
|
import com.vitorpamplona.quartz.events.EventInterface
|
||||||
@ -31,10 +33,6 @@ import com.vitorpamplona.quartz.events.RelayAuthEvent
|
|||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import okhttp3.WebSocket
|
|
||||||
import okhttp3.WebSocketListener
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
enum class FeedType {
|
enum class FeedType {
|
||||||
@ -61,6 +59,8 @@ class Relay(
|
|||||||
val write: Boolean = true,
|
val write: Boolean = true,
|
||||||
val forceProxy: Boolean = false,
|
val forceProxy: Boolean = false,
|
||||||
val activeTypes: Set<FeedType>,
|
val activeTypes: Set<FeedType>,
|
||||||
|
val socketBuilder: WebsocketBuilder,
|
||||||
|
val subs: SubscriptionManager,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
// waits 3 minutes to reconnect once things fail
|
// waits 3 minutes to reconnect once things fail
|
||||||
@ -132,13 +132,7 @@ class Relay(
|
|||||||
|
|
||||||
lastConnectTentative = TimeUtils.now()
|
lastConnectTentative = TimeUtils.now()
|
||||||
|
|
||||||
val request =
|
socket = socketBuilder.build(url, false, RelayListener(onConnected))
|
||||||
Request
|
|
||||||
.Builder()
|
|
||||||
.url(url.trim())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
socket = HttpClientManager.getHttpClient(forceProxy).newWebSocket(request, RelayListener(onConnected))
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
|
|
||||||
@ -153,19 +147,15 @@ class Relay(
|
|||||||
|
|
||||||
inner class RelayListener(
|
inner class RelayListener(
|
||||||
val onConnected: (Relay) -> Unit,
|
val onConnected: (Relay) -> Unit,
|
||||||
) : WebSocketListener() {
|
) : WebSocketListener {
|
||||||
override fun onOpen(
|
override fun onOpen(
|
||||||
webSocket: WebSocket,
|
pingInMs: Long,
|
||||||
response: Response,
|
usingCompression: Boolean,
|
||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
Log.d("Relay", "Connect onOpen $url $socket")
|
Log.d("Relay", "Connect onOpen $url $socket")
|
||||||
|
|
||||||
markConnectionAsReady(
|
markConnectionAsReady(pingInMs, usingCompression)
|
||||||
pingInMs = response.receivedResponseAtMillis - response.sentRequestAtMillis,
|
|
||||||
usingCompression =
|
|
||||||
response.headers.get("Sec-WebSocket-Extensions")?.contains("permessage-deflate") ?: false,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Log.w("Relay", "Relay OnOpen, Loading All subscriptions $url")
|
// Log.w("Relay", "Relay OnOpen, Loading All subscriptions $url")
|
||||||
onConnected(this@Relay)
|
onConnected(this@Relay)
|
||||||
@ -173,10 +163,7 @@ class Relay(
|
|||||||
listeners.forEach { it.onRelayStateChange(this@Relay, StateType.CONNECT, null) }
|
listeners.forEach { it.onRelayStateChange(this@Relay, StateType.CONNECT, null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessage(
|
override fun onMessage(text: String) {
|
||||||
webSocket: WebSocket,
|
|
||||||
text: String,
|
|
||||||
) {
|
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
RelayStats.addBytesReceived(url, text.bytesUsedInMemory())
|
RelayStats.addBytesReceived(url, text.bytesUsedInMemory())
|
||||||
@ -193,7 +180,6 @@ class Relay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClosing(
|
override fun onClosing(
|
||||||
webSocket: WebSocket,
|
|
||||||
code: Int,
|
code: Int,
|
||||||
reason: String,
|
reason: String,
|
||||||
) {
|
) {
|
||||||
@ -211,7 +197,6 @@ class Relay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClosed(
|
override fun onClosed(
|
||||||
webSocket: WebSocket,
|
|
||||||
code: Int,
|
code: Int,
|
||||||
reason: String,
|
reason: String,
|
||||||
) {
|
) {
|
||||||
@ -225,9 +210,8 @@ class Relay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(
|
override fun onFailure(
|
||||||
webSocket: WebSocket,
|
|
||||||
t: Throwable,
|
t: Throwable,
|
||||||
response: Response?,
|
responseMessage: String?,
|
||||||
) {
|
) {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
@ -235,19 +219,19 @@ class Relay(
|
|||||||
|
|
||||||
// checks if this is an actual failure. Closing the socket generates an onFailure as well.
|
// checks if this is an actual failure. Closing the socket generates an onFailure as well.
|
||||||
if (!(socket == null && (t.message == "Socket is closed" || t.message == "Socket closed"))) {
|
if (!(socket == null && (t.message == "Socket is closed" || t.message == "Socket closed"))) {
|
||||||
RelayStats.newError(url, response?.message ?: t.message ?: "onFailure event from server: ${t.javaClass.simpleName}")
|
RelayStats.newError(url, responseMessage ?: t.message ?: "onFailure event from server: ${t.javaClass.simpleName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failures disconnect the relay.
|
// Failures disconnect the relay.
|
||||||
markConnectionAsClosed()
|
markConnectionAsClosed()
|
||||||
|
|
||||||
Log.w("Relay", "Relay onFailure $url, ${response?.message} $response ${t.message} $socket")
|
Log.w("Relay", "Relay onFailure $url, $responseMessage $responseMessage ${t.message} $socket")
|
||||||
t.printStackTrace()
|
t.printStackTrace()
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
it.onError(
|
it.onError(
|
||||||
this@Relay,
|
this@Relay,
|
||||||
"",
|
"",
|
||||||
Error("WebSocket Failure. Response: $response. Exception: ${t.message}", t),
|
Error("WebSocket Failure. Response: $responseMessage. Exception: ${t.message}", t),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,7 +445,7 @@ class Relay(
|
|||||||
|
|
||||||
fun renewFilters() {
|
fun renewFilters() {
|
||||||
// Force update all filters after AUTH.
|
// Force update all filters after AUTH.
|
||||||
Client.allSubscriptions().forEach {
|
subs.allSubscriptions().forEach {
|
||||||
sendFilter(requestId = it.key, it.value)
|
sendFilter(requestId = it.key, it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import androidx.compose.runtime.Immutable
|
|||||||
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
import com.vitorpamplona.ammolite.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.quartz.events.Event
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.EventInterface
|
import com.vitorpamplona.quartz.events.EventInterface
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
@ -37,7 +36,7 @@ import kotlinx.coroutines.launch
|
|||||||
/**
|
/**
|
||||||
* RelayPool manages the connection to multiple Relays and lets consumers deal with simple events.
|
* RelayPool manages the connection to multiple Relays and lets consumers deal with simple events.
|
||||||
*/
|
*/
|
||||||
object RelayPool : Relay.Listener {
|
class RelayPool : Relay.Listener {
|
||||||
private var relays = listOf<Relay>()
|
private var relays = listOf<Relay>()
|
||||||
private var listeners = setOf<Listener>()
|
private var listeners = setOf<Listener>()
|
||||||
|
|
||||||
@ -57,57 +56,34 @@ object RelayPool : Relay.Listener {
|
|||||||
|
|
||||||
fun getAll() = relays
|
fun getAll() = relays
|
||||||
|
|
||||||
fun getOrCreateRelay(
|
fun runCreatingIfNeeded(
|
||||||
relayTemplate: RelaySetupInfoToConnect,
|
relay: Relay,
|
||||||
|
timeout: Long = 60000,
|
||||||
onDone: (() -> Unit)? = null,
|
onDone: (() -> Unit)? = null,
|
||||||
whenConnected: (Relay) -> Unit,
|
whenConnected: (Relay) -> Unit,
|
||||||
) {
|
) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
val matching = getRelays(relayTemplate.url)
|
val matching = getRelays(relay.url)
|
||||||
if (matching.isNotEmpty()) {
|
if (matching.isNotEmpty()) {
|
||||||
matching.forEach { whenConnected(it) }
|
matching.forEach { whenConnected(it) }
|
||||||
} else {
|
} else {
|
||||||
/** temporary connection */
|
addRelay(relay)
|
||||||
newSporadicRelay(
|
|
||||||
relayTemplate.url,
|
|
||||||
relayTemplate.read,
|
|
||||||
relayTemplate.write,
|
|
||||||
relayTemplate.forceProxy,
|
|
||||||
relayTemplate.feedTypes,
|
|
||||||
onConnected = whenConnected,
|
|
||||||
onDone = onDone,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
relay.connectAndRun {
|
||||||
fun newSporadicRelay(
|
relay.renewFilters()
|
||||||
url: String,
|
relay.sendOutbox()
|
||||||
read: Boolean,
|
|
||||||
write: Boolean,
|
|
||||||
forceProxy: Boolean,
|
|
||||||
feedTypes: Set<FeedType>?,
|
|
||||||
onConnected: (Relay) -> Unit,
|
|
||||||
onDone: (() -> Unit)?,
|
|
||||||
timeout: Long = 60000,
|
|
||||||
) {
|
|
||||||
val relay = Relay(url, read, write, forceProxy, feedTypes ?: emptySet())
|
|
||||||
addRelay(relay)
|
|
||||||
|
|
||||||
relay.connectAndRun {
|
whenConnected(relay)
|
||||||
relay.renewFilters()
|
|
||||||
relay.sendOutbox()
|
|
||||||
|
|
||||||
onConnected(relay)
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
delay(timeout) // waits for a reply
|
||||||
|
relay.disconnect()
|
||||||
|
removeRelay(relay)
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
if (onDone != null) {
|
||||||
delay(timeout) // waits for a reply
|
onDone()
|
||||||
relay.disconnect()
|
}
|
||||||
removeRelay(relay)
|
}
|
||||||
|
|
||||||
if (onDone != null) {
|
|
||||||
onDone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.ammolite.relays
|
||||||
|
|
||||||
|
class MutableSubscriptionManager : SubscriptionManager {
|
||||||
|
private var subscriptions = mapOf<String, List<TypedFilter>>()
|
||||||
|
|
||||||
|
fun add(
|
||||||
|
subscriptionId: String,
|
||||||
|
filters: List<TypedFilter> = listOf(),
|
||||||
|
) {
|
||||||
|
subscriptions = subscriptions + Pair(subscriptionId, filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(subscriptionId: String) {
|
||||||
|
subscriptions = subscriptions.minus(subscriptionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isActive(subscriptionId: String): Boolean = subscriptions.contains(subscriptionId)
|
||||||
|
|
||||||
|
override fun allSubscriptions(): Map<String, List<TypedFilter>> = subscriptions
|
||||||
|
|
||||||
|
override fun getSubscriptionFilters(subId: String): List<TypedFilter> = subscriptions[subId] ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubscriptionManager {
|
||||||
|
fun isActive(subscriptionId: String): Boolean
|
||||||
|
|
||||||
|
fun allSubscriptions(): Map<String, List<TypedFilter>>
|
||||||
|
|
||||||
|
fun getSubscriptionFilters(subId: String): List<TypedFilter>
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.ammolite.sockets
|
||||||
|
|
||||||
|
interface WebSocket {
|
||||||
|
fun cancel()
|
||||||
|
|
||||||
|
fun send(msg: String): Boolean
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.ammolite.sockets
|
||||||
|
|
||||||
|
interface WebSocketListener {
|
||||||
|
fun onOpen(
|
||||||
|
pingMillis: Long,
|
||||||
|
compression: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onMessage(text: String)
|
||||||
|
|
||||||
|
fun onClosing(
|
||||||
|
code: Int,
|
||||||
|
reason: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onClosed(
|
||||||
|
code: Int,
|
||||||
|
reason: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onFailure(
|
||||||
|
t: Throwable,
|
||||||
|
response: String?,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.ammolite.sockets
|
||||||
|
|
||||||
|
interface WebsocketBuilder {
|
||||||
|
fun build(
|
||||||
|
url: String,
|
||||||
|
forceProxy: Boolean,
|
||||||
|
out: WebSocketListener,
|
||||||
|
): WebSocket
|
||||||
|
}
|
Reference in New Issue
Block a user