diff --git a/app/src/main/java/org/session/libsession/network/onion/PathManager.kt b/app/src/main/java/org/session/libsession/network/onion/PathManager.kt index 3ebbf8fea2..b0a9a3bf8b 100644 --- a/app/src/main/java/org/session/libsession/network/onion/PathManager.kt +++ b/app/src/main/java/org/session/libsession/network/onion/PathManager.kt @@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.api.snode.GetInfoApi import org.thoughtcrime.securesms.api.snode.SnodeApiExecutor import org.thoughtcrime.securesms.api.snode.SnodeApiRequest import org.thoughtcrime.securesms.api.snode.execute +import org.thoughtcrime.securesms.util.NetworkConnectivity import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import javax.inject.Provider @@ -48,6 +49,7 @@ open class PathManager @Inject constructor( private val prefs: TextSecurePreferences, private val snodeApiExecutor: Provider, private val getInfoApi: Provider, + private val networkConnectivity: NetworkConnectivity, ) { companion object { private const val STRIKE_THRESHOLD = 3 @@ -75,10 +77,10 @@ open class PathManager @Inject constructor( @OptIn(FlowPreview::class) val status: StateFlow = - combine(_paths, _isBuilding) { paths, building -> + combine(_paths, _isBuilding, networkConnectivity.networkAvailable) { paths, building, hasNetwork -> when { - building -> PathStatus.BUILDING - paths.isEmpty() -> PathStatus.ERROR + hasNetwork && building -> PathStatus.BUILDING + !hasNetwork || paths.isEmpty() -> PathStatus.ERROR else -> PathStatus.READY } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt index b4329b7153..88f309d54f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/PathActivity.kt @@ -13,6 +13,7 @@ import android.widget.TextView import androidx.annotation.ColorRes import androidx.core.content.ContextCompat import androidx.core.view.doOnLayout +import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -21,28 +22,24 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.withIndex import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.databinding.ActivityPathBinding +import org.session.libsession.network.model.PathStatus import org.session.libsession.network.onion.PathManager import org.session.libsession.utilities.NonTranslatableStringConstants.APP_NAME import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY import org.session.libsession.utilities.getColorFromAttr import org.session.libsignal.utilities.Snode import org.thoughtcrime.securesms.ScreenLockActionBarActivity -import org.thoughtcrime.securesms.pro.ProDetailsRepository import org.thoughtcrime.securesms.reviews.InAppReviewManager import org.thoughtcrime.securesms.ui.getSubbedString import org.thoughtcrime.securesms.ui.openUrl @@ -131,14 +128,17 @@ class PathActivity : ScreenLockActionBarActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - pathState.map { it.isEmpty() } + pathManager.status + .map { it == PathStatus.BUILDING || it == PathStatus.ERROR } .collectLatest { isLoading -> - if (isLoading) { - binding.spinner.fadeIn() - } else { - binding.spinner.fadeOut() - } + if (isLoading) { + binding.spinner.fadeIn() + } else { + binding.spinner.fadeOut() } + + binding.pathRowsContainer.isVisible = !isLoading + } } } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/network/PathManagerTest.kt b/app/src/test/java/org/thoughtcrime/securesms/network/PathManagerTest.kt index 9fbfffa55d..c4aff917e2 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/network/PathManagerTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/network/PathManagerTest.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.network import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule @@ -15,6 +16,7 @@ import org.session.libsignal.utilities.Snode import org.thoughtcrime.securesms.database.SnodeDatabase import org.thoughtcrime.securesms.database.SnodeDatabaseTest import org.thoughtcrime.securesms.util.MockLoggingRule +import org.thoughtcrime.securesms.util.NetworkConnectivity @RunWith(RobolectricTestRunner::class) @Config(minSdk = 36) // Setting min sdk 36 to use recent sqlite version as we use some modern features in the app code @@ -25,9 +27,15 @@ class PathManagerTest { lateinit var snodeDb: SnodeDatabase + private lateinit var networkConnectivity: NetworkConnectivity + @Before fun setUp() { snodeDb = SnodeDatabaseTest.createInMemorySnodeDatabase() + + networkConnectivity = mock { + on { networkAvailable } doReturn MutableStateFlow(true) + } } private fun snode(id: String): Snode = @@ -57,6 +65,7 @@ class PathManagerTest { prefs = mock(), snodeApiExecutor = { mock() }, getInfoApi = { mock() }, + networkConnectivity = networkConnectivity, ) val chosen = pm.getPath(exclude = b) @@ -83,6 +92,7 @@ class PathManagerTest { prefs = mock(), snodeApiExecutor = { mock() }, getInfoApi = { mock() }, + networkConnectivity = networkConnectivity, ) pm.handleBadSnode(snode = b, forceRemove = true) @@ -115,6 +125,7 @@ class PathManagerTest { prefs = mock(), snodeApiExecutor = { mock() }, getInfoApi = { mock() }, + networkConnectivity = networkConnectivity, ) pm.handleBadSnode(snode = b, forceRemove = true)