Skip to content

Commit

Permalink
Make root navigation accessible for all MP presenters (#68)
Browse files Browse the repository at this point in the history
* - remove showcase example repository from main presenter and move init logs to init

* - make root nav controller accessible to all presenters for ease of MultiPlatform navigation
  • Loading branch information
rodvar authored Nov 22, 2024
1 parent 83cc9bc commit f294f59
Show file tree
Hide file tree
Showing 14 changed files with 55 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import network.bisq.mobile.domain.data.repository.SingleObjectRepository
import network.bisq.mobile.presentation.MainPresenter
import network.bisq.mobile.presentation.ui.App
import org.koin.android.ext.android.inject
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package network.bisq.mobile.client.presentation

import network.bisq.mobile.domain.data.repository.GreetingRepository
import network.bisq.mobile.domain.data.repository.main.bootstrap.ApplicationBootstrapFacade
import network.bisq.mobile.presentation.MainPresenter

@Suppress("UNCHECKED_CAST")
class AndroidClientMainPresenter(
private val applicationBootstrapFacade: ApplicationBootstrapFacade
) : MainPresenter(GreetingRepository()) {
var applicationServiceInited = false
) : MainPresenter() {
private var applicationServiceInited = false
override fun onViewAttached() {
super.onViewAttached()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ val androidNodeModule = module {

// this line showcases both, the possibility to change behaviour of the app by changing one definition
// and binding the same obj to 2 different abstractions
single<MainPresenter> { NodeMainPresenter(get(), get(), get(), get()) } bind AppPresenter::class
single<MainPresenter> { NodeMainPresenter(get(), get(), get()) } bind AppPresenter::class
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@ package network.bisq.mobile.android.node.presentation

import android.app.Activity
import network.bisq.mobile.android.node.AndroidApplicationService
import network.bisq.mobile.android.node.domain.data.repository.NodeGreetingRepository
import network.bisq.mobile.android.node.service.AndroidMemoryReportService
import network.bisq.mobile.domain.data.model.Greeting
import network.bisq.mobile.domain.data.repository.GreetingRepository
import network.bisq.mobile.domain.data.repository.main.bootstrap.ApplicationBootstrapFacade
import network.bisq.mobile.presentation.MainPresenter

@Suppress("UNCHECKED_CAST")
class NodeMainPresenter(
greetingRepository: NodeGreetingRepository,
private val supplier: AndroidApplicationService.Supplier,
private val androidMemoryReportService: AndroidMemoryReportService,
private val applicationBootstrapFacade: ApplicationBootstrapFacade
) : MainPresenter(greetingRepository as GreetingRepository<Greeting>) {
) : MainPresenter() {

var applicationServiceInited = false
override fun onViewAttached() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package network.bisq.mobile.presentation

import androidx.annotation.CallSuper
import androidx.navigation.NavHostController
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -50,6 +51,14 @@ abstract class BasePresenter(private val rootPresenter: MainPresenter?): ViewPre

private val dependants = if (isRoot()) mutableListOf<BasePresenter>() else null

/**
* @throws IllegalStateException if this presenter has no root
* @return Nav controller for navigation from the root
*/
protected val rootNavigator: NavHostController
get() = (rootPresenter ?: throw IllegalStateException("This presenter has no root")).navController


init {
rootPresenter?.registerChild(child = this)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,50 @@
package network.bisq.mobile.presentation

import androidx.navigation.NavHostController
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import network.bisq.mobile.android.node.BuildNodeConfig
import network.bisq.mobile.client.shared.BuildConfig
import network.bisq.mobile.domain.data.BackgroundDispatcher
import network.bisq.mobile.domain.data.model.Greeting
import network.bisq.mobile.domain.data.repository.GreetingRepository
import network.bisq.mobile.presentation.ui.AppPresenter

/**
* Main Presenter as an example of implementation for now.
*/
open class MainPresenter(private val greetingRepository: GreetingRepository<Greeting>) : BasePresenter(null), AppPresenter {
open class MainPresenter() : BasePresenter(null), AppPresenter {
lateinit var navController: NavHostController
private set

override fun setNavController(controller: NavHostController) {
navController = controller
}

private val log = Logger.withTag(this::class.simpleName ?: "MainPresenter")
// Observable state
private val _isContentVisible = MutableStateFlow(false)
override val isContentVisible: StateFlow<Boolean> = _isContentVisible

// passthrough example
private val _greetingText: StateFlow<String> = stateFlowFromRepository(
repositoryFlow = greetingRepository.data,
transform = { it?.greet() ?: "" },
initialValue = "Welcome!"
)

override val greetingText: StateFlow<String> = _greetingText
// private val _greetingText: StateFlow<String> = stateFlowFromRepository(
// repositoryFlow = greetingRepository.data,
// transform = { it?.greet() ?: "" },
// initialValue = "Welcome!"
// )
// override val greetingText: StateFlow<String> = _greetingText

init {
CoroutineScope(BackgroundDispatcher).launch {
greetingRepository.create(Greeting())
}
log.i { "Shared Version: ${BuildConfig.SHARED_LIBS_VERSION}" }
log.i { "iOS Client Version: ${BuildConfig.IOS_APP_VERSION}" }
log.i { "Android Client Version: ${BuildConfig.IOS_APP_VERSION}" }
log.i { "Android Node Version: ${BuildNodeConfig.APP_VERSION}" }
// CoroutineScope(BackgroundDispatcher).launch {
// greetingRepository.create(Greeting())
// }
}

// Toggle action
override fun toggleContentVisibility() {
_isContentVisible.value = !_isContentVisible.value
}

override fun onResume() {
super.onResume()
log.i { "Shared Version: ${BuildConfig.SHARED_LIBS_VERSION}" }
log.i { "iOS Client Version: ${BuildConfig.IOS_APP_VERSION}" }
log.i { "Android Client Version: ${BuildConfig.IOS_APP_VERSION}" }
log.i { "Android Node Version: ${BuildNodeConfig.APP_VERSION}" }
}

override fun onPause() {
super.onPause()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,21 @@ import org.koin.dsl.bind
import org.koin.dsl.module

val presentationModule = module {

single(named("RootNavController")) { getKoin().getProperty<NavHostController>("RootNavController") }
single(named("TabNavController")) { getKoin().getProperty<NavHostController>("TabNavController") }

single<MainPresenter> { MainPresenter(get()) } bind AppPresenter::class
single<MainPresenter> { MainPresenter() } bind AppPresenter::class

// TODO: Since NavController will be required for almost all Presenters for basic navigation
// Added this as top constructor level param. Is this okay?
single { (navController: NavController) ->
single {
SplashPresenter(
get(),
navController = navController,
get()
)
}

single { (navController: NavController) ->
single {
OnBoardingPresenter(
get(),
navController
get()
)
} bind IOnboardingPresenter::class

Expand All @@ -48,18 +43,16 @@ val presentationModule = module {
)
} bind IGettingStarted::class

single { (navController: NavController) ->
single {
CreateProfilePresenter(
get(),
navController = navController,
get()
)
}

single { (navController: NavController) ->
single {
TrustedNodeSetupPresenter(
get(),
navController = navController,
settingsRepository = get()
)
} bind ITrustedNodeSetupPresenter::class
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package network.bisq.mobile.presentation.ui

import androidx.compose.runtime.*
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import cafe.adriel.lyricist.ProvideStrings
import cafe.adriel.lyricist.rememberStrings
Expand All @@ -16,9 +17,9 @@ import network.bisq.mobile.presentation.ui.theme.BisqTheme
import org.koin.mp.KoinPlatform.getKoin

interface AppPresenter: ViewPresenter {
// Observables for state
fun setNavController(controller: NavHostController)
// Observables for state
val isContentVisible: StateFlow<Boolean>
val greetingText: StateFlow<String>

// Actions
fun toggleContentVisibility()
Expand All @@ -33,19 +34,16 @@ fun App() {

val rootNavController = rememberNavController()
val tabNavController = rememberNavController()

var isNavControllerSet by remember { mutableStateOf(false) }

// Looks like the main view composable is not needing the presenter at all - uncomment this if this changes
// val presenter: AppPresenter = koinInject()

val presenter: AppPresenter = koinInject()

LaunchedEffect(rootNavController) {
// For the main presenter use case we leave this for the moment the activity/viewcontroller respectively gets attached
// presenter.onViewAttached()
getKoin().setProperty("RootNavController", rootNavController)
getKoin().setProperty("TabNavController", tabNavController)
isNavControllerSet = true
presenter.setNavController(rootNavController)
}

val lyricist = rememberStrings()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import network.bisq.mobile.domain.data.repository.BisqStatsRepository
import network.bisq.mobile.domain.data.repository.BtcPriceRepository
import network.bisq.mobile.presentation.BasePresenter
import network.bisq.mobile.presentation.MainPresenter
import network.bisq.mobile.presentation.ui.uicases.startup.SplashScreen
import network.bisq.mobile.presentation.ui.navigation.Routes

class GettingStartedPresenter(
mainPresenter: MainPresenter,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package network.bisq.mobile.presentation.ui.uicases.startup

import androidx.navigation.NavController
import co.touchlab.kermit.Logger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -15,7 +14,6 @@ import network.bisq.mobile.presentation.ui.navigation.Routes

open class CreateProfilePresenter(
mainPresenter: MainPresenter,
private val navController: NavController,
private val userProfileService: UserProfileServiceFacade
) : BasePresenter(mainPresenter) {

Expand Down Expand Up @@ -92,7 +90,7 @@ open class CreateProfilePresenter(

CoroutineScope(Dispatchers.Main).launch {
// todo stop busy animation in UI
navController.navigate(Routes.TrustedNodeSetup.name) {
rootNavigator.navigate(Routes.TrustedNodeSetup.name) {
popUpTo(Routes.CreateProfile.name) { inclusive = true }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ val onBoardingPages = listOf(
)

open class OnBoardingPresenter(
mainPresenter: MainPresenter,
private val navController: NavController
mainPresenter: MainPresenter
) : BasePresenter(mainPresenter), IOnboardingPresenter {

private val _pagerState = MutableStateFlow<PagerState?>(null)
Expand All @@ -50,7 +49,7 @@ open class OnBoardingPresenter(
val state = pagerState.value
if (state != null) {
if (state.currentPage == onBoardingPages.lastIndex) {
navController.navigate(Routes.CreateProfile.name) {
rootNavigator.navigate(Routes.CreateProfile.name) {
popUpTo(Routes.Onboarding.name) { inclusive = true }
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import network.bisq.mobile.presentation.ui.navigation.Routes

open class SplashPresenter(
mainPresenter: MainPresenter,
private val navController: NavController,
applicationBootstrapFacade: ApplicationBootstrapFacade
) : BasePresenter(mainPresenter) {
private val coroutineScope = CoroutineScope(Dispatchers.Main)
Expand All @@ -34,15 +33,15 @@ open class SplashPresenter(
// TODO: Conditional nav
// If firstTimeApp launch, goto Onboarding[clientMode] (androidNode / xClient)
// If not, goto TabContainerScreen
/* navController.navigate(Routes.Onboarding.name) {
/* rootNavigator.navigate(Routes.Onboarding.name) {
popUpTo(Routes.Splash.name) { inclusive = true }
}*/

//TODO
/* navController.navigate(Routes.TabContainer.name) {
/* rootNavigator.navigate(Routes.TabContainer.name) {
popUpTo(Routes.TrustedNodeSetup.name) { inclusive = true }
}*/
navController.navigate(Routes.CreateProfile.name) {
rootNavigator.navigate(Routes.CreateProfile.name) {
popUpTo(Routes.Splash.name) { inclusive = true }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import network.bisq.mobile.presentation.ui.navigation.Routes

class TrustedNodeSetupPresenter(
mainPresenter: MainPresenter,
private val navController: NavController,
private val settingsRepository: SettingsRepository
) : BasePresenter(mainPresenter), ITrustedNodeSetupPresenter {

Expand Down Expand Up @@ -44,7 +43,7 @@ class TrustedNodeSetupPresenter(
}

override fun navigateToNextScreen() {
navController.navigate(Routes.TabContainer.name) {
rootNavigator.navigate(Routes.TabContainer.name) {
popUpTo(Routes.TrustedNodeSetup.name) { inclusive = true }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import network.bisq.mobile.domain.data.repository.main.bootstrap.ApplicationBoot
@Suppress("UNCHECKED_CAST")
class IosClientMainPresenter(
private val applicationBootstrapFacade: ApplicationBootstrapFacade
) : MainPresenter(GreetingRepository()) {
) : MainPresenter() {
var applicationServiceInited = false
override fun onViewAttached() {
super.onViewAttached()
Expand Down

0 comments on commit f294f59

Please sign in to comment.