Skip to content

Commit

Permalink
Merge pull request #21 from jtouzy/feature/android-injection
Browse files Browse the repository at this point in the history
Change screen creation pattern.
  • Loading branch information
jtouzy authored Jan 31, 2020
2 parents daf5016 + c365b9e commit 3e3533a
Show file tree
Hide file tree
Showing 17 changed files with 93 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,43 +1,14 @@
package com.jtouzy.demo.app.di

import com.jtouzy.demo.app.ui.ObservableStore
import com.jtouzy.demo.app.ui.characters.CharactersScreen
import com.jtouzy.demo.app.ui.quote.QuoteScreen
import com.jtouzy.demo.app.ui.NavigationManager
import com.jtouzy.demo.cache.DataStore
import com.jtouzy.demo.cache.InMemoryDataStore
import com.jtouzy.demo.network.BreakingBadApi
import com.jtouzy.demo.ui.Store
import com.jtouzy.demo.ui.characters.CharactersPresenter
import com.jtouzy.demo.ui.characters.CharactersPresenterImpl
import com.jtouzy.demo.ui.characters.CharactersViewState
import com.jtouzy.demo.ui.model.Character
import com.jtouzy.demo.ui.quotes.QuotesPresenter
import com.jtouzy.demo.ui.quotes.QuotesPresenterImpl
import com.jtouzy.demo.ui.quotes.QuotesViewState
import org.koin.core.parameter.parametersOf
import org.koin.core.qualifier.named
import org.koin.dsl.module

val appModule = module {

single { NavigationManager() }
single { BreakingBadApi() }
single<DataStore> { InMemoryDataStore(get()) }

// Characters
val characterQualifier = named("Characters")
single<Store<CharactersViewState>>(characterQualifier) { ObservableStore(CharactersViewState.Loading) }
factory<CharactersPresenter> { CharactersPresenterImpl(get(characterQualifier), get()) }
factory { CharactersScreen(get(characterQualifier), get()) }

// Quotes
val quoteQualifier = named("Quotes")
single<Store<QuotesViewState>>(quoteQualifier) { (character: Character) ->
ObservableStore(QuotesViewState.Loading(character.name))
}
factory<QuotesPresenter> { (character: Character) ->
QuotesPresenterImpl(get(quoteQualifier) { parametersOf(character) }, get(), character)
}
factory { (character: Character) ->
QuoteScreen(get(quoteQualifier) { parametersOf(character) }, get { parametersOf(character) })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,29 @@ import androidx.ui.material.MaterialTheme
import com.jtouzy.demo.app.ui.characters.CharactersScreen
import com.jtouzy.demo.app.ui.common.themeColors
import com.jtouzy.demo.app.ui.quote.QuoteScreen
import org.koin.android.ext.android.get
import org.koin.core.parameter.parametersOf
import com.jtouzy.demo.cache.DataStore
import org.koin.android.ext.android.inject

class MainActivity : AppCompatActivity() {

private val navigationManager by inject<NavigationManager>()
private val dataStore by inject<DataStore>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { KomposeApp() }
}

override fun onBackPressed() {
if (!NavigationManager.popBackStack()) super.onBackPressed()
if (!navigationManager.pop()) super.onBackPressed()
}

@Composable
private fun KomposeApp() {
MaterialTheme(colors = themeColors) {
when (val screen = NavigationManager.currentScreen) {
Screen.Home -> get<CharactersScreen>().MainScreen()
is Screen.Quote -> get<QuoteScreen> { parametersOf(screen.character) }.MainScreen()
when (val screen = navigationManager.currentScreen) {
Screen.Home -> CharactersScreen.show(navigationManager, dataStore)
is Screen.Quote -> QuoteScreen.show(navigationManager, dataStore, screen.character)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import androidx.compose.Model
import com.jtouzy.demo.ui.model.Character

@Model
object NavigationManager {
class NavigationManager {

var currentScreen: Screen = Screen.Home
private set
Expand All @@ -15,7 +15,7 @@ object NavigationManager {
currentScreen = screen
}

fun popBackStack(): Boolean =
fun pop(): Boolean =
if (screenStack.size > 1) {
screenStack.removeAt(screenStack.lastIndex)
navigateTo(screenStack.last())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import com.jtouzy.demo.ui.Store
import com.jtouzy.demo.ui.ViewState

@Model
class ObservableStore<T : ViewState>(override var currentState: T) : Store<T>
class ObservableStore<T : ViewState>(override var viewState: T) : Store<T>
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@ import androidx.ui.material.ripple.Ripple
import androidx.ui.res.stringResource
import com.jtouzy.demo.app.R
import com.jtouzy.demo.app.ui.NavigationManager
import com.jtouzy.demo.app.ui.ObservableStore
import com.jtouzy.demo.app.ui.Screen
import com.jtouzy.demo.app.ui.common.ErrorScreen
import com.jtouzy.demo.app.ui.common.LoadingScreen
import com.jtouzy.demo.app.ui.common.VectorImage
import com.jtouzy.demo.app.ui.common.image
import com.jtouzy.demo.cache.DataStore
import com.jtouzy.demo.ui.Store
import com.jtouzy.demo.ui.characters.CharactersPresenter
import com.jtouzy.demo.ui.characters.CharactersPresenterImpl
import com.jtouzy.demo.ui.characters.CharactersViewState
import com.jtouzy.demo.ui.model.Character

class CharactersScreen(
private val navigationManager: NavigationManager,
private val store: Store<CharactersViewState>,
presenter: CharactersPresenter
) {
Expand All @@ -38,13 +43,14 @@ class CharactersScreen(
}

@Composable
fun MainScreen() {
private fun MainScreen() {
Column {
TopAppBar(title = { Text(text = +stringResource(R.string.app_name)) })
Crossfade(store.currentState) { state ->
Crossfade(store.viewState) { state ->
when (state) {
CharactersViewState.Loading -> LoadingScreen()
is CharactersViewState.Content -> CharacterList(state.characters)
CharactersViewState.Error -> ErrorScreen()
}
}
}
Expand All @@ -63,7 +69,7 @@ class CharactersScreen(
private fun CharacterItem(character: Character) {
Ripple(bounded = true) {
Clickable(onClick = {
NavigationManager.navigateTo(Screen.Quote(character))
navigationManager.navigateTo(Screen.Quote(character))
}) {
Column {
Row {
Expand All @@ -86,4 +92,12 @@ class CharactersScreen(
}
}
}

companion object {
fun show(navigationManager: NavigationManager, dataStore: DataStore) {
val store = ObservableStore<CharactersViewState>(CharactersViewState.Loading)
val presenter = CharactersPresenterImpl(store, dataStore)
CharactersScreen(navigationManager, store, presenter).MainScreen()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.jtouzy.demo.app.ui.common

import androidx.compose.Composable
import androidx.compose.unaryPlus
import androidx.ui.core.Text
import androidx.ui.layout.Center
import androidx.ui.res.stringResource
import com.jtouzy.demo.app.R

@Composable
fun ErrorScreen() {
Center {
Text(text = +stringResource(R.string.generic_message_error))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ import androidx.ui.material.TopAppBar
import androidx.ui.res.stringResource
import com.jtouzy.demo.app.R
import com.jtouzy.demo.app.ui.NavigationManager
import com.jtouzy.demo.app.ui.ObservableStore
import com.jtouzy.demo.app.ui.common.ErrorScreen
import com.jtouzy.demo.app.ui.common.LoadingScreen
import com.jtouzy.demo.app.ui.common.VectorImageButton
import com.jtouzy.demo.cache.DataStore
import com.jtouzy.demo.ui.Store
import com.jtouzy.demo.ui.model.Character
import com.jtouzy.demo.ui.model.Quote
import com.jtouzy.demo.ui.quotes.QuotesPresenter
import com.jtouzy.demo.ui.quotes.QuotesPresenterImpl
import com.jtouzy.demo.ui.quotes.QuotesViewState

class QuoteScreen(
private val navigationManager: NavigationManager,
private val store: Store<QuotesViewState>,
presenter: QuotesPresenter
) {
Expand All @@ -32,21 +38,22 @@ class QuoteScreen(
}

@Composable
fun MainScreen() {
private fun MainScreen() {
Column {
TopAppBar(
title = { Text(store.currentState.title) },
title = { Text(store.viewState.title) },
navigationIcon = {
VectorImageButton(R.drawable.ic_back) {
NavigationManager.popBackStack()
navigationManager.pop()
}
}
)
Crossfade(store.currentState) { state ->
Crossfade(store.viewState) { state ->
when (state) {
is QuotesViewState.Loading -> LoadingScreen()
is QuotesViewState.Content -> QuoteList(state.quotes)
is QuotesViewState.NoQuote -> NoQuote()
is QuotesViewState.Error -> ErrorScreen()
}
}
}
Expand Down Expand Up @@ -80,4 +87,12 @@ class QuoteScreen(
Divider(color = (+MaterialTheme.colors()).onBackground)
}
}

companion object {
fun show(navigationManager: NavigationManager, dataStore: DataStore, character: Character) {
val store = ObservableStore<QuotesViewState>(QuotesViewState.Loading(character.name))
val presenter = QuotesPresenterImpl(store, dataStore, character)
QuoteScreen(navigationManager, store, presenter).MainScreen()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@

<string name="app_name">Kompose</string>
<string name="no_quotes">No quotes :(</string>
<string name="generic_message_error">An error occurred</string>
</resources>
4 changes: 2 additions & 2 deletions Examples/DemoApp/iosApp/DemoApp/UI/ObservableStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import shared
class ObservableStore<T: ViewState>: Store, ObservableObject {
var currentState: ViewState {
didSet {
guard let newState = currentState as? T else {
guard let newState = viewState as? T else {
fatalError("Cannot be casted")
}
state = newState
Expand All @@ -20,7 +20,7 @@ class ObservableStore<T: ViewState>: Store, ObservableObject {
@Published var state: T

init(baseState: T) {
currentState = baseState
viewState = baseState
state = baseState
}
}
2 changes: 2 additions & 0 deletions Examples/DemoApp/shared/locales/locales.twine
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[[General]]
[app_name]
en = Kompose
[generic_message_error]
en = An error occurred

[[Quotes]]
[no_quotes]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class BreakingBadApi {
encodedPath = "characters"
}
}.run {
Json.parse(CharacterDto.serializer().list, this)
Json.nonstrict.parse(CharacterDto.serializer().list, this)
}

suspend fun getCharacterQuotes(name: String): List<QuoteDto> = client.get<String> {
Expand All @@ -48,7 +48,7 @@ class BreakingBadApi {
parameter("author", name)
}
}.run {
Json.parse(QuoteDto.serializer().list, this)
Json.nonstrict.parse(QuoteDto.serializer().list, this)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ data class CharacterDto(
val nickname: String,
val appearance: List<Int>,
val portrayed: String,
val category: String,
val better_call_saul_appearance: List<String>
val category: String
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.jtouzy.demo.ui

interface Store<T : ViewState> {
var currentState: T
var viewState: T
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ class CharactersPresenterImpl(

override fun loadCharacters() {
GlobalScope.launch(mainDispatcher) {
store.currentState = CharactersViewState.Loading
val characters = withContext(ioDispatcher) { dataStore.getCharacters() }
store.currentState = CharactersViewState.Content(characters.map { Character(it) })
store.viewState = CharactersViewState.Loading
try {
val characters = withContext(ioDispatcher) { dataStore.getCharacters() }
store.viewState = CharactersViewState.Content(characters.map { Character(it) })
} catch (exception: Exception) {
store.viewState = CharactersViewState.Error
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import com.jtouzy.demo.ui.model.Character
sealed class CharactersViewState : ViewState {
object Loading : CharactersViewState()
data class Content(val characters: List<Character>) : CharactersViewState()
object Error : CharactersViewState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ class QuotesPresenterImpl(

override fun loadQuotes() {
GlobalScope.launch(mainDispatcher) {
store.currentState = QuotesViewState.Loading(character.name)
val quotes = withContext(ioDispatcher) { dataStore.getCharacterQuotes(character.name) }
store.currentState = if (quotes.isEmpty()) {
QuotesViewState.NoQuote(character.name)
} else {
QuotesViewState.Content(character.name, quotes.map { Quote(it) })
store.viewState = QuotesViewState.Loading(character.name)
try {
val quotes = withContext(ioDispatcher) { dataStore.getCharacterQuotes(character.name) }
store.viewState = if (quotes.isEmpty()) {
QuotesViewState.NoQuote(character.name)
} else {
QuotesViewState.Content(character.name, quotes.map { Quote(it) })
}
} catch (exception: Exception) {
store.viewState = QuotesViewState.Error(character.name)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ sealed class QuotesViewState(open val title: String) : ViewState {
data class Loading(override val title: String) : QuotesViewState(title)
data class Content(override val title: String, val quotes: List<Quote>) : QuotesViewState(title)
data class NoQuote(override val title: String) : QuotesViewState(title)
data class Error(override val title: String) : QuotesViewState(title)
}

0 comments on commit 3e3533a

Please sign in to comment.