Skip to content

Commit

Permalink
Implement reading bootstrap relays from primal api for onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
AleksandarIlic committed Mar 25, 2024
1 parent 4b957b8 commit e5e490c
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ class CreateAccountHandler @Inject constructor(
) {
val userId = authRepository.login(nostrKey = privateKey)
val postCreateAccountResult = runCatching {
relayRepository.bootstrapUserRelays(userId)
userRepository.setProfileMetadata(userId = userId, profileMetadata = profileMetadata)
val contacts = setOf(userId) + interests.mapToContacts()
profileRepository.setFollowList(userId = userId, contacts = contacts)
relayRepository.bootstrapDefaultUserRelays(userId)
settingsRepository.fetchAndPersistAppSettings(userId = userId)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ enum class PrimalVerb(val identifier: String) {
NEW_DMS_COUNT("directmsg_count_2"),
WALLET("wallet"),
WALLET_MONITOR("wallet_monitor_2"),
DEFAULT_RELAYS("get_default_relays"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package net.primal.android.networking.relays

import net.primal.android.user.domain.toRelay

val BOOTSTRAP_RELAYS = listOf(
val FALLBACK_RELAYS = listOf(
"wss://relay.primal.net",
"wss://relay.damus.io",
"wss://relay.nostr.band",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class RelaysSocketManager @Inject constructor(
private val primalDatabase: PrimalDatabase,
private val userRelaysPool: RelayPool,
private val nwcRelaysPool: RelayPool,
private val bootstrapRelays: RelayPool,
private val fallbackRelays: RelayPool,
) {
private val scope = CoroutineScope(dispatchers.io())
private val relayPoolsMutex = Mutex()
Expand All @@ -35,12 +35,12 @@ class RelaysSocketManager @Inject constructor(
val userRelayPoolStatus = userRelaysPool.relayPoolStatus

init {
initBootstrapRelaysPool()
initFallbackRelaysPool()
observeActiveUserId()
}

private fun initBootstrapRelaysPool() {
bootstrapRelays.changeRelays(BOOTSTRAP_RELAYS)
private fun initFallbackRelaysPool() {
fallbackRelays.changeRelays(FALLBACK_RELAYS)
}

private fun observeActiveUserId() =
Expand Down Expand Up @@ -99,7 +99,7 @@ class RelaysSocketManager @Inject constructor(
if (userRelaysPool.hasRelays()) {
userRelaysPool.publishEvent(nostrEvent)
} else {
bootstrapRelays.publishEvent(nostrEvent)
fallbackRelays.publishEvent(nostrEvent)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ enum class NostrEventKind(val value: Int) {
PrimalCdnResource(value = 10_000_119),
PrimalImageUploadRequest(value = 10_000_120),
PrimalImageUploadResponse(value = 10_000_121),
PrimalDefaultRelaysList(value = 10_000_124),
PrimalLinkPreview(value = 10_000_128),
PrimalNotificationsSummary2(value = 10_000_132),
PrimalUserFollowersCounts(value = 10_000_133),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class NetworkSettingsViewModel @Inject constructor(
viewModelScope.launch {
changeRelayList { userId ->
try {
relayRepository.bootstrapDefaultUserRelays(userId = userId)
relayRepository.bootstrapUserRelays(userId = userId)
} catch (error: NostrPublishException) {
Timber.w(error)
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/kotlin/net/primal/android/user/api/UsersApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ interface UsersApi {
suspend fun getUserFollowing(userId: String): UsersResponse

suspend fun getUserRelays(userId: String): UserRelaysResponse

suspend fun getDefaultRelays(): List<String>
}
13 changes: 13 additions & 0 deletions app/src/main/kotlin/net/primal/android/user/api/UsersApiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import net.primal.android.networking.di.PrimalCacheApiClient
import net.primal.android.networking.primal.PrimalApiClient
import net.primal.android.networking.primal.PrimalCacheFilter
import net.primal.android.networking.primal.PrimalVerb
import net.primal.android.networking.sockets.errors.WssException
import net.primal.android.nostr.model.NostrEventKind
import net.primal.android.user.api.model.FollowListRequestBody
import net.primal.android.user.api.model.UserContactsResponse
Expand Down Expand Up @@ -115,4 +116,16 @@ class UsersApiImpl @Inject constructor(
cachedRelayListEvent = queryResult.findPrimalEvent(NostrEventKind.PrimalUserRelaysList),
)
}

override suspend fun getDefaultRelays(): List<String> {
val queryResult = primalApiClient.query(
message = PrimalCacheFilter(primalVerb = PrimalVerb.DEFAULT_RELAYS),
)

val list = queryResult.findPrimalEvent(NostrEventKind.PrimalDefaultRelaysList)
val content = list?.content
if (content.isNullOrEmpty()) throw WssException("Invalid content.")

return NostrJson.decodeFromString<List<String>>(list.content)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import androidx.room.withTransaction
import javax.inject.Inject
import kotlinx.coroutines.flow.map
import net.primal.android.db.PrimalDatabase
import net.primal.android.networking.relays.BOOTSTRAP_RELAYS
import net.primal.android.networking.relays.FALLBACK_RELAYS
import net.primal.android.networking.relays.errors.NostrPublishException
import net.primal.android.networking.sockets.errors.WssException
import net.primal.android.nostr.publish.NostrPublisher
import net.primal.android.user.accounts.parseNip65Relays
import net.primal.android.user.api.UsersApi
import net.primal.android.user.domain.Relay as RelayDO
import net.primal.android.user.domain.RelayKind
import net.primal.android.user.domain.mapToRelayPO
import net.primal.android.user.domain.toRelay
import timber.log.Timber

class RelayRepository @Inject constructor(
private val primalDatabase: PrimalDatabase,
Expand All @@ -25,9 +28,15 @@ class RelayRepository @Inject constructor(
fun findRelays(userId: String, kind: RelayKind) = primalDatabase.relays().findRelays(userId, kind)

@Throws(NostrPublishException::class)
suspend fun bootstrapDefaultUserRelays(userId: String) {
nostrPublisher.publishRelayList(userId, BOOTSTRAP_RELAYS)
replaceUserRelays(userId, BOOTSTRAP_RELAYS)
suspend fun bootstrapUserRelays(userId: String) {
val relays = try {
usersApi.getDefaultRelays().map { it.toRelay() }
} catch (error: WssException) {
Timber.w(error)
FALLBACK_RELAYS
}
replaceUserRelays(userId, relays)
nostrPublisher.publishRelayList(userId, relays)
}

private suspend fun fetchUserRelays(userId: String): List<RelayDO>? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package net.primal.android.wallet.zaps
import javax.inject.Inject
import net.primal.android.db.PrimalDatabase
import net.primal.android.feed.repository.PostStatsUpdater
import net.primal.android.networking.relays.BOOTSTRAP_RELAYS
import net.primal.android.networking.relays.FALLBACK_RELAYS
import net.primal.android.nostr.notary.NostrNotary
import net.primal.android.user.accounts.UserAccountsStore
import net.primal.android.user.domain.RelayKind
Expand Down Expand Up @@ -37,7 +37,7 @@ class ZapHandler @Inject constructor(
val userAccount = accountsStore.findByIdOrNull(userId = userId)
val userRelays = relayRepository.findRelays(userId, RelayKind.UserRelay)
.map { it.mapToRelayDO() }
.ifEmpty { BOOTSTRAP_RELAYS }
.ifEmpty { FALLBACK_RELAYS }
val targetLnUrlDecoded = target.lnUrlDecoded()
val nostrZapper = userAccount?.resolveZapper()
val defaultZapOptions = userAccount?.appSettings?.zapDefault
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class CreateAccountHandlerTest {
)

coVerify {
relayRepository.bootstrapDefaultUserRelays(
relayRepository.bootstrapUserRelays(
withArg { it shouldBe keyPair.pubKey },
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class RelaysSocketManagerTest {
dispatchers = coroutinesTestRule.dispatcherProvider,
okHttpClient = mockk<OkHttpClient>(),
),
bootstrapRelays = RelayPool(
fallbackRelays = RelayPool(
dispatchers = coroutinesTestRule.dispatcherProvider,
okHttpClient = mockk<OkHttpClient>(),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package net.primal.android.user.repository

import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.kotest.matchers.shouldBe
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import net.primal.android.core.coroutines.CoroutinesTestRule
import net.primal.android.db.PrimalDatabase
import net.primal.android.nostr.publish.NostrPublisher
import net.primal.android.user.domain.RelayKind
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class RelayRepositoryTest {

private lateinit var myDatabase: PrimalDatabase

@get:Rule
val coroutinesTestRule = CoroutinesTestRule()

@Before
fun setup() {
val context = ApplicationProvider.getApplicationContext<Context>()
myDatabase = Room.inMemoryDatabaseBuilder(context, PrimalDatabase::class.java)
.allowMainThreadQueries()
.build()
}

@After
fun tearDown() {
myDatabase.close()
}

@Test
fun bootstrapDefaultUserRelays_replaceUserRelaysInDatabase() = runTest {
val userId = "random"
val expectedRelays = listOf("wss://relay.primal.net", "wss://relay.damus.io")
val nostrPublisher = NostrPublisher(
relaysSocketManager = mockk(relaxed = true),
nostrNotary = mockk(relaxed = true),
primalImportApi = mockk(relaxed = true),
)
val repository = RelayRepository(
nostrPublisher = nostrPublisher,
usersApi = mockk(relaxed = true) {
coEvery { getDefaultRelays() } returns expectedRelays
},
primalDatabase = myDatabase,
)

repository.bootstrapUserRelays(userId = userId)

val actualRelays = myDatabase.relays().findRelays(userId = userId, kind = RelayKind.UserRelay)
actualRelays.map { it.url }.sorted() shouldBe expectedRelays.sorted()
}
}

0 comments on commit e5e490c

Please sign in to comment.