Skip to content

Commit

Permalink
Move additional content to new advanced settings section (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandregpereira authored Sep 30, 2023
1 parent d821df6 commit f70a976
Show file tree
Hide file tree
Showing 22 changed files with 307 additions and 96 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ java {

dependencies {
implementation(project(":core:analytics"))
implementation(project(":core:event"))
implementation(project(":domain:app:data"))
implementation(project(":domain:app:core"))
implementation(project(":feature:folder-detail:event"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import br.alexandregpereira.hunter.analytics.di.analyticsModule
import br.alexandregpereira.hunter.data.di.dataModules
import br.alexandregpereira.hunter.detail.di.monsterDetailModule
import br.alexandregpereira.hunter.domain.di.domainModules
import br.alexandregpereira.hunter.event.systembar.bottomBarEventModule
import br.alexandregpereira.hunter.folder.detail.di.folderDetailModule
import br.alexandregpereira.hunter.folder.insert.di.folderInsertModule
import br.alexandregpereira.hunter.folder.list.di.folderListModule
Expand Down Expand Up @@ -66,7 +67,7 @@ class HunterApplication : Application() {
internal companion object {
private val appModule = module {
factory { Dispatchers.Default }
viewModel { MainViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { MainViewModel(get(), get(), get(), get(), get(), get(), get()) }
}

fun KoinApplication.initKoinModules() {
Expand All @@ -90,6 +91,7 @@ class HunterApplication : Application() {
searchModule,
settingsModule,
spellDetailModule,
bottomBarEventModule
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import br.alexandregpereira.hunter.event.monster.detail.MonsterDetailEvent.OnVis
import br.alexandregpereira.hunter.event.monster.detail.MonsterDetailEvent.OnVisibilityChanges.Show
import br.alexandregpereira.hunter.event.monster.detail.MonsterDetailEventListener
import br.alexandregpereira.hunter.event.monster.detail.collectOnVisibilityChanges
import br.alexandregpereira.hunter.event.systembar.BottomBarEvent.AddTopContent
import br.alexandregpereira.hunter.event.systembar.BottomBarEvent.RemoveTopContent
import br.alexandregpereira.hunter.event.systembar.BottomBarEventManager
import br.alexandregpereira.hunter.event.systembar.dispatchRemoveTopContentEvent
import br.alexandregpereira.hunter.event.systembar.dispatchAddTopContentEvent
import br.alexandregpereira.hunter.folder.preview.event.FolderPreviewEvent
import br.alexandregpereira.hunter.folder.preview.event.FolderPreviewEventDispatcher
import br.alexandregpereira.hunter.monster.content.event.MonsterContentManagerEventListener
import br.alexandregpereira.hunter.monster.content.event.collectOnVisibilityChanges
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class MainViewModel(
private val savedStateHandle: SavedStateHandle,
Expand All @@ -43,50 +49,69 @@ class MainViewModel(
private val folderListResultListener: FolderListResultListener,
private val monsterContentManagerEventListener: MonsterContentManagerEventListener,
private val folderPreviewEventDispatcher: FolderPreviewEventDispatcher,
private val bottomBarEventManager: BottomBarEventManager,
) : ViewModel() {

private val _state = MutableStateFlow(savedStateHandle.getState())
val state: StateFlow<MainViewState> = _state

init {
observeBottomBarEvents()
observeMonsterDetailEvents()
observeFolderDetailResults()
observeFolderListResults()
observeMonsterContentManagerEvents()
}

private fun observeBottomBarEvents() {
bottomBarEventManager.events.onEach { event ->
when (event) {
is AddTopContent -> setState { addTopContentStack(event.topContentId) }
is RemoveTopContent -> setState { removeTopContentStack(event.topContentId) }
}
}.launchIn(viewModelScope)
}

private fun observeMonsterDetailEvents() {
monsterDetailEventListener.collectOnVisibilityChanges { event ->
when (event) {
is Show -> setState { copy(isMonsterDetailShowing = true) }
Hide -> setState { copy(isMonsterDetailShowing = false) }
is Show -> {
dispatchAddTopContentEvent(TopContent.MONSTER_DETAIL.name)
dispatchFolderPreviewEvent(show = false)
}
Hide -> {
dispatchRemoveTopContentEvent(TopContent.MONSTER_DETAIL.name)
dispatchFolderPreviewEvent(show = true)
}
}
dispatchFolderPreviewEvent(show = state.value.isMonsterDetailShowing.not())
}.launchIn(viewModelScope)
}

private fun observeMonsterContentManagerEvents() {
monsterContentManagerEventListener.collectOnVisibilityChanges { isShowing ->
setState { copy(isMonsterContentManagerShowing = isShowing) }
dispatchTopContentEvent(TopContent.MONSTER_CONTENT_MANAGER.name, isShowing)
}.launchIn(viewModelScope)
}

private fun observeFolderDetailResults() {
folderDetailResultListener.collectOnVisibilityChanges { result ->
setState { copy(isFolderDetailShowing = result.isShowing) }
dispatchTopContentEvent(TopContent.FOLDER_DETAIL.name, result.isShowing)
}.launchIn(viewModelScope)
}

private fun observeFolderListResults() {
folderListResultListener.collectOnItemSelectionVisibilityChanges { result ->
setState { copy(isFolderListSelectionShowing = result.isShowing) }
dispatchTopContentEvent(TopContent.FOLDER_LIST_SELECTION.name, result.isShowing)
dispatchFolderPreviewEvent(result.isShowing.not())
}.launchIn(viewModelScope)
}

fun onEvent(event: MainViewEvent) {
when (event) {
is BottomNavigationItemClick -> setState { copy(bottomBarItemSelected = event.item) }
is BottomNavigationItemClick -> {
setState { copy(bottomBarItemSelected = event.item) }
dispatchFolderPreviewEvent(show = event.item != BottomBarItem.SETTINGS)
}
}
}

Expand All @@ -99,7 +124,30 @@ class MainViewModel(
folderPreviewEventDispatcher.dispatchEvent(folderPreviewEvent)
}

private fun dispatchAddTopContentEvent(topContentId: String) {
bottomBarEventManager.dispatchAddTopContentEvent(topContentId)
}

private fun dispatchRemoveTopContentEvent(topContentId: String) {
bottomBarEventManager.dispatchRemoveTopContentEvent(topContentId)
}

private fun dispatchTopContentEvent(topContentId: String, show: Boolean) {
if (show) {
dispatchAddTopContentEvent(topContentId)
} else {
dispatchRemoveTopContentEvent(topContentId)
}
}

private fun setState(block: MainViewState.() -> MainViewState) {
_state.value = state.value.block().saveState(savedStateHandle)
}
}

private enum class TopContent {
MONSTER_DETAIL,
FOLDER_DETAIL,
MONSTER_CONTENT_MANAGER,
FOLDER_LIST_SELECTION,
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,47 @@ import androidx.lifecycle.SavedStateHandle

data class MainViewState(
val bottomBarItemSelected: BottomBarItem = BottomBarItem.COMPENDIUM,
val isFolderDetailShowing: Boolean = false,
val isMonsterDetailShowing: Boolean = false,
val isMonsterContentManagerShowing: Boolean = false,
val isFolderListSelectionShowing: Boolean = false,
internal val topContentStack: List<String> = listOf(),
) {
val showBottomBar: Boolean = isMonsterDetailShowing.not() && isFolderDetailShowing.not()
&& isFolderListSelectionShowing.not() && isMonsterContentManagerShowing.not()

val showBottomBar: Boolean = topContentStack.isEmpty()
}

internal fun MainViewState.addTopContentStack(
topContent: String,
): MainViewState {
return copy(topContentStack = topContentStack + topContent)
}

internal fun MainViewState.removeTopContentStack(
topContent: String,
): MainViewState {
return copy(
topContentStack = topContentStack.toMutableList().apply {
remove(topContent)
}.toList()
)
}

enum class BottomBarItem(val iconRes: Int, val stringRes: Int) {
COMPENDIUM(iconRes = R.drawable.ic_book, stringRes = R.string.compendium),
SEARCH(iconRes = R.drawable.ic_search, stringRes = R.string.search),
FOLDERS(iconRes = R.drawable.ic_folder, stringRes = R.string.folders),
SETTINGS(iconRes = R.drawable.ic_settings, stringRes = R.string.settings)
SETTINGS(iconRes = R.drawable.ic_menu, stringRes = R.string.menu)
}

internal fun SavedStateHandle.getState(): MainViewState {
return MainViewState(
bottomBarItemSelected = BottomBarItem.values()[this["bottomBarItemSelected"] ?: 0],
isFolderDetailShowing = this["isFolderDetailShowing"] ?: false,
isMonsterDetailShowing = this["isMonsterDetailShowing"] ?: false,
isFolderListSelectionShowing = this["isFolderListSelectionShowing"] ?: false,
isMonsterContentManagerShowing = this["isMonsterContentManagerShowing"] ?: false,
bottomBarItemSelected = BottomBarItem.entries[this["bottomBarItemSelected"] ?: 0],
topContentStack = this.get<Array<String>>("topContentStack")?.toMutableList()
?: mutableListOf(),
)
}

internal fun MainViewState.saveState(
savedStateHandle: SavedStateHandle
): MainViewState {
savedStateHandle["bottomBarItemSelected"] = this.bottomBarItemSelected.ordinal
savedStateHandle["isFolderDetailShowing"] = this.isFolderDetailShowing
savedStateHandle["isMonsterDetailShowing"] = this.isMonsterDetailShowing
savedStateHandle["isFolderListSelectionShowing"] = this.isFolderListSelectionShowing
savedStateHandle["isMonsterContentManagerShowing"] = this.isMonsterContentManagerShowing
savedStateHandle["topContentStack"] = this.topContentStack.toTypedArray()
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fun BottomNavigationTransition(
bottomBarItemSelected: BottomBarItem,
contentPadding: PaddingValues = PaddingValues()
) {
Crossfade(targetState = bottomBarItemSelected) { index ->
Crossfade(targetState = bottomBarItemSelected, label = "BottomNavigationTransition") { index ->
when (index) {
BottomBarItem.COMPENDIUM -> MonsterCompendiumFeature(
contentPadding = contentPadding,
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
</vector>
21 changes: 0 additions & 21 deletions app/src/main/res/drawable/ic_settings.xml

This file was deleted.

2 changes: 1 addition & 1 deletion app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
<string name="compendium">Compêndio</string>
<string name="search">Buscar</string>
<string name="folders">Pastas</string>
<string name="settings">Config</string>
<string name="menu">Menu</string>
</resources>
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
<string name="compendium">Compendium</string>
<string name="search">Search</string>
<string name="folders">Folders</string>
<string name="settings">Settings</string>
<string name="menu">Menu</string>
</resources>
19 changes: 19 additions & 0 deletions core/event/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
kotlin("multiplatform")
}

configureJvmTargets()

kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.koin.core)
implementation(libs.kotlin.coroutines.core)
}
}
if (isMac()) {
val iosMain by getting
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package br.alexandregpereira.hunter.event

import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow

interface EventDispatcher<Event> {

fun dispatchEvent(event: Event)
}

interface EventListener<Event> {

val events: Flow<Event>
}

interface EventManager<Event> : EventDispatcher<Event>, EventListener<Event>

internal class DefaultEventManager<Event> : EventManager<Event> {

private val _events: MutableSharedFlow<Event> = MutableSharedFlow(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
override val events: Flow<Event> = _events

override fun dispatchEvent(event: Event) {
_events.tryEmit(event)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package br.alexandregpereira.hunter.event.systembar

import br.alexandregpereira.hunter.event.DefaultEventManager
import br.alexandregpereira.hunter.event.EventDispatcher
import br.alexandregpereira.hunter.event.EventManager

class BottomBarEventManager : EventManager<BottomBarEvent> by DefaultEventManager()

sealed class BottomBarEvent {

data class AddTopContent(val topContentId: String) : BottomBarEvent()

data class RemoveTopContent(val topContentId: String) : BottomBarEvent()
}

fun EventDispatcher<BottomBarEvent>.dispatchAddTopContentEvent(topContentId: String) {
dispatchEvent(BottomBarEvent.AddTopContent(topContentId))
}

fun EventDispatcher<BottomBarEvent>.dispatchRemoveTopContentEvent(topContentId: String) {
dispatchEvent(BottomBarEvent.RemoveTopContent(topContentId))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package br.alexandregpereira.hunter.event.systembar

import br.alexandregpereira.hunter.event.EventDispatcher
import br.alexandregpereira.hunter.event.EventListener
import org.koin.dsl.module

val bottomBarEventModule = module {
single { BottomBarEventManager() }
factory<EventDispatcher<BottomBarEvent>> {
get<BottomBarEventManager>()
}
factory<EventListener<BottomBarEvent>> {
get<BottomBarEventManager>()
}
}
1 change: 1 addition & 0 deletions feature/settings/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ java {

dependencies {
implementation project(':core:analytics')
implementation project(':core:event')
implementation project(':domain:settings:core')
implementation project(':feature:monster-content-manager:event')
implementation project(':feature:sync:event')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,10 @@ fun SettingsFeature(
contentPadding: PaddingValues,
) {
val viewModel: SettingsViewModel = koinViewModel()
val context = LocalContext.current
SettingsScreen(
state = viewModel.state.collectAsState().value,
versionName = versionName,
contentPadding = contentPadding,
onImageBaseUrlChange = viewModel::onImageBaseUrlChange,
onAlternativeSourceBaseUrlChange = viewModel::onAlternativeSourceBaseUrlChange,
onSaveButtonClick = viewModel::onSaveButtonClick,
onManageMonsterContentClick = viewModel::onManageMonsterContentClick,
viewIntent = viewModel,
)
}
Loading

0 comments on commit f70a976

Please sign in to comment.