Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move additional content to new advanced settings section #210

Merged
merged 1 commit into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading