diff --git a/app/src/main/java/io/xxlabs/messenger/repository/client/ClientRepository.kt b/app/src/main/java/io/xxlabs/messenger/repository/client/ClientRepository.kt index 89d818a7267c3aac12eb19ed2cbb08f6d66bc98f..d12615f852ab51a74a5238f1285d2c7770a2db5e 100644 --- a/app/src/main/java/io/xxlabs/messenger/repository/client/ClientRepository.kt +++ b/app/src/main/java/io/xxlabs/messenger/repository/client/ClientRepository.kt @@ -501,6 +501,7 @@ class ClientRepository @Inject constructor( return Single.create { emitter -> try { if (!areNodesReady()) { + Timber.d("Failed to register-- nodes aren't ready.") emitter.onError(throwNodeError()) return@create } @@ -512,6 +513,7 @@ class ClientRepository @Inject constructor( exportUserContact() emitter.onSuccess(username) } catch (e: Exception) { + Timber.d("Failed to register: ${e.message}") emitter.onError(e) } } @@ -959,7 +961,11 @@ class ClientRepository @Inject constructor( val status = try { clientWrapper.getNodeRegistrationStatus() } catch (e: Exception) { - if (e.isNodeError()) return false + return if (e.isNodeError()) { + Timber.d("Network is not healthy. Waiting $NODES_READY_POLL_INTERVAL before retry.") + Thread.sleep(NODES_READY_POLL_INTERVAL) + recursiveAreNodesReady() + } else throw e } @@ -967,6 +973,10 @@ class ClientRepository @Inject constructor( Timber.v("[NODE REGISTRATION STATUS] Registration rate: $rate") return if (rate < NODES_READY_MINIMUM_RATE && retries <= NODES_READY_MAX_RETRIES) { + Timber.d( + "Nodes not ready after ${retries + 1} attempts. " + + "Waiting $NODES_READY_POLL_INTERVAL before next retry." + ) Thread.sleep(NODES_READY_POLL_INTERVAL) recursiveAreNodesReady(retries+1) } else { diff --git a/app/src/main/java/io/xxlabs/messenger/ui/global/NetworkViewModel.kt b/app/src/main/java/io/xxlabs/messenger/ui/global/NetworkViewModel.kt index 0b32da51779e892783997fcd2ea25fd10a55c59b..77eca506a04ec173ff389ba00420fa313dae6715 100644 --- a/app/src/main/java/io/xxlabs/messenger/ui/global/NetworkViewModel.kt +++ b/app/src/main/java/io/xxlabs/messenger/ui/global/NetworkViewModel.kt @@ -229,6 +229,7 @@ class NetworkViewModel @Inject constructor( Timber.v("[NETWORK VIEWMODEL] has network follower already started: $networkStatus") if (networkStatus == NetworkFollowerStatus.RUNNING) { checkStopNetworkTimer() + onStartCallback?.invoke(true) } else if (networkStatus == NetworkFollowerStatus.STOPPED) { startNetworkFollower(onStartCallback) } @@ -265,8 +266,7 @@ class NetworkViewModel @Inject constructor( .doOnSuccess { Timber.v("[NETWORK VIEWMODEL] Network follower is RUNNING") Timber.v("[NETWORK VIEWMODEL] Started network follower in: ${elapsedTime - System.currentTimeMillis()}ms") - onStartCallback?.invoke(it) - newUserDiscovery() + newUserDiscovery(onStartCallback) } .doOnError { err -> Timber.v("[NETWORK VIEWMODEL] Network follower ERROR - could not start properly: ${err.localizedMessage}") @@ -371,7 +371,7 @@ class NetworkViewModel @Inject constructor( } } - fun newUserDiscovery() { + private fun newUserDiscovery(onCompleteCallback: ((Boolean) -> Unit)? = null) { if (!isUdTryingToRun && !isUserDiscoveryRunning()) { isUdTryingToRun = true Timber.v("Starting user discovery...") @@ -383,13 +383,17 @@ class NetworkViewModel @Inject constructor( Timber.e("[NETWORK VIEWMODEL] Failed to register user discovery: ${err.localizedMessage}") isUdTryingToRun = false userDiscoveryStatus.value = false + onCompleteCallback?.invoke(false) }.doOnSuccess { Timber.v("[NETWORK VIEWMODEL] User discovery registered with success!") setUserDiscoveryRunning() isUdTryingToRun = false userDiscoveryStatus.value = true + onCompleteCallback?.invoke(true) }.subscribe() ) + } else { + onCompleteCallback?.invoke(true) } } diff --git a/app/src/main/java/io/xxlabs/messenger/ui/intro/registration/username/UsernameRegistration.kt b/app/src/main/java/io/xxlabs/messenger/ui/intro/registration/username/UsernameRegistration.kt index c6f4b42d11670a6bc5066a17ddf56ef6c0656885..d029f00f2e1eca695c6397b2f6037a46ca4bc697 100644 --- a/app/src/main/java/io/xxlabs/messenger/ui/intro/registration/username/UsernameRegistration.kt +++ b/app/src/main/java/io/xxlabs/messenger/ui/intro/registration/username/UsernameRegistration.kt @@ -19,12 +19,19 @@ import io.xxlabs.messenger.bindings.wrapper.bindings.bindingsErrorMessage import io.xxlabs.messenger.repository.PreferencesRepository import io.xxlabs.messenger.repository.base.BaseRepository import io.xxlabs.messenger.support.appContext +import io.xxlabs.messenger.support.util.value import io.xxlabs.messenger.ui.dialog.info.InfoDialogUI import io.xxlabs.messenger.ui.dialog.info.SpanConfig import io.xxlabs.messenger.ui.global.NetworkViewModel import kotlinx.coroutines.* +import timber.log.Timber +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import kotlin.random.Random.Default.nextInt +private const val MAX_NETWORK_RETRIES = 29 +private const val NETWORK_POLL_INTERVAL_MS = 1000L + /** * Encapsulates username registration logic. */ @@ -230,7 +237,7 @@ class UsernameRegistration @AssistedInject constructor( } private fun getOrCreateSession(context: Context = appContext()) { - scope.launch { + scope.launch(Dispatchers.IO) { val appFolder = repo.createSessionFolder(context) try { repo.newClient(appFolder, sessionPassword) @@ -243,10 +250,25 @@ class UsernameRegistration @AssistedInject constructor( } } - private fun connectToCmix() { - with (networking) { - checkRegisterNetworkCallback() - tryStartNetworkFollower { onUsernameNextClicked() } + private suspend fun connectToCmix(retries: Int = 0) { + networking.checkRegisterNetworkCallback() + if (retries < MAX_NETWORK_RETRIES) { + if (initializeNetworkFollower()) { + Timber.d("Started network follower after #${retries + 1} attempt(s).") + withContext(Dispatchers.Main) { + onUsernameNextClicked() + } + } else { + delay(NETWORK_POLL_INTERVAL_MS) + Timber.d("Attempting to start network follower, attempt #${retries + 1}.") + connectToCmix(retries + 1) + } + } else throw Exception("Failed to connect to network after ${retries + 1} attempts. Please try again.") + } + + private suspend fun initializeNetworkFollower(): Boolean = suspendCoroutine { continuation -> + networking.tryStartNetworkFollower { successful -> + continuation.resume(successful) } }