Refactor the RibbonViewModels to add an abstraction layer
This commit is contained in:
parent
27dba5438e
commit
a59444c610
3 changed files with 148 additions and 199 deletions
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRollUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
|
import com.pixelized.shared.lwa.model.campaign.Campaign.CharacterInstance
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.flatMapMerge
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import java.text.Collator
|
||||||
|
|
||||||
|
abstract class CharacterRibbonViewModel(
|
||||||
|
private val rollHistoryRepository: RollHistoryRepository,
|
||||||
|
private val settingsRepository: SettingsRepository,
|
||||||
|
characterRepository: CharacterSheetRepository,
|
||||||
|
alterationRepository: AlterationRepository,
|
||||||
|
campaignRepository: CampaignRepository,
|
||||||
|
private val ribbonFactory: CharacterRibbonFactory,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val rolls = hashMapOf<CharacterInstance.Id, MutableState<CharacterPortraitRollUio?>>()
|
||||||
|
|
||||||
|
abstract val Campaign.data: Map<CharacterInstance.Id, CharacterInstance>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flow is a tad complex so there is an explanation of wtf it's about :
|
||||||
|
* On a campaign update it go through every element of the abstract [data] map and either:
|
||||||
|
* - build and flow of an empty list to handle the case where the map is empty.
|
||||||
|
* - build a flow of flow bind to each character & alteration to update portrait details.
|
||||||
|
* Then sort the result.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow
|
||||||
|
.flatMapMerge { campaign ->
|
||||||
|
when (campaign.data.isEmpty()) {
|
||||||
|
true -> flowOf(emptyList())
|
||||||
|
else -> combine<CharacterPortraitUio?, List<CharacterPortraitUio>>(
|
||||||
|
flows = campaign.data.map { entry ->
|
||||||
|
combine(
|
||||||
|
characterRepository.characterDetailFlow(characterSheetId = entry.key.characterSheetId),
|
||||||
|
alterationRepository.alterationsFlow(characterInstanceId = entry.key),
|
||||||
|
) { sheet, alterations ->
|
||||||
|
ribbonFactory.convertToPlayerPortraitUio(
|
||||||
|
characterSheet = sheet,
|
||||||
|
alterations = alterations,
|
||||||
|
characterInstanceId = entry.key,
|
||||||
|
characterInstance = entry.value,
|
||||||
|
enableDetail = settingsRepository.settings().isGM,
|
||||||
|
displayCharacterStats = settingsRepository.settings().isGM,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform = { headers ->
|
||||||
|
headers.mapNotNull { it }
|
||||||
|
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
fun roll(
|
||||||
|
characterId: CharacterInstance.Id,
|
||||||
|
): State<CharacterPortraitRollUio?> {
|
||||||
|
val state = rolls.getOrPut(characterId) { mutableStateOf(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(characterId) {
|
||||||
|
combine(
|
||||||
|
settingsRepository.settingsFlow(),
|
||||||
|
rollHistoryRepository.rolls,
|
||||||
|
) { settings, roll ->
|
||||||
|
if (settings.dynamicDice &&
|
||||||
|
characterId.equals(roll.prefix, roll.characterSheetId, roll.instanceId)
|
||||||
|
) {
|
||||||
|
state.value = CharacterPortraitRollUio(
|
||||||
|
characterId = CharacterInstance.Id(
|
||||||
|
prefix = characterId.prefix,
|
||||||
|
characterSheetId = characterId.characterSheetId,
|
||||||
|
instanceId = characterId.instanceId,
|
||||||
|
),
|
||||||
|
value = roll.rollValue,
|
||||||
|
label = roll.resultLabel?.split(" ")?.joinToString(separator = "\n") { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.launchIn(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPortraitRollRightClick(
|
||||||
|
characterId: CharacterInstance.Id,
|
||||||
|
) {
|
||||||
|
rolls[characterId]?.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,114 +1,28 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.npc
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.npc
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRollUio
|
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitUio
|
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.flatMapMerge
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import java.text.Collator
|
|
||||||
|
|
||||||
private typealias CharacterId = Campaign.CharacterInstance.Id
|
|
||||||
|
|
||||||
class NpcRibbonViewModel(
|
class NpcRibbonViewModel(
|
||||||
private val rollHistoryRepository: RollHistoryRepository,
|
rollHistoryRepository: RollHistoryRepository,
|
||||||
private val settingsRepository: SettingsRepository,
|
settingsRepository: SettingsRepository,
|
||||||
characterRepository: CharacterSheetRepository,
|
characterRepository: CharacterSheetRepository,
|
||||||
alterationRepository: AlterationRepository,
|
alterationRepository: AlterationRepository,
|
||||||
campaignRepository: CampaignRepository,
|
campaignRepository: CampaignRepository,
|
||||||
private val ribbonFactory: CharacterRibbonFactory,
|
ribbonFactory: CharacterRibbonFactory,
|
||||||
) : ViewModel() {
|
) : CharacterRibbonViewModel(
|
||||||
private val rolls = hashMapOf<CharacterId, MutableState<CharacterPortraitRollUio?>>()
|
rollHistoryRepository = rollHistoryRepository,
|
||||||
|
settingsRepository = settingsRepository,
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
characterRepository = characterRepository,
|
||||||
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow
|
alterationRepository = alterationRepository,
|
||||||
.flatMapMerge { campaign ->
|
campaignRepository = campaignRepository,
|
||||||
if (campaign.npcs.isEmpty()) {
|
ribbonFactory = ribbonFactory,
|
||||||
flowOf(emptyList())
|
) {
|
||||||
} else {
|
override val Campaign.data get() = npcs
|
||||||
combine<CharacterPortraitUio?, List<CharacterPortraitUio>>(
|
|
||||||
flows = campaign.npcs.map { entry ->
|
|
||||||
combine(
|
|
||||||
characterRepository.characterDetailFlow(characterSheetId = entry.key.characterSheetId),
|
|
||||||
alterationRepository.alterationsFlow(characterInstanceId = entry.key),
|
|
||||||
) { sheet, alterations ->
|
|
||||||
ribbonFactory.convertToPlayerPortraitUio(
|
|
||||||
characterSheet = sheet,
|
|
||||||
alterations = alterations,
|
|
||||||
characterInstanceId = entry.key,
|
|
||||||
characterInstance = entry.value,
|
|
||||||
enableDetail = settingsRepository.settings().isGM,
|
|
||||||
displayCharacterStats = settingsRepository.settings().isGM,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transform = { headers ->
|
|
||||||
headers.mapNotNull { it }
|
|
||||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
fun roll(
|
|
||||||
characterId: Campaign.CharacterInstance.Id,
|
|
||||||
): State<CharacterPortraitRollUio?> {
|
|
||||||
val state = rolls.getOrPut(characterId) { mutableStateOf(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(characterId) {
|
|
||||||
combine(
|
|
||||||
settingsRepository.settingsFlow(),
|
|
||||||
rollHistoryRepository.rolls,
|
|
||||||
) { settings, roll ->
|
|
||||||
if (settings.dynamicDice &&
|
|
||||||
characterId.equals(roll.prefix, roll.characterSheetId, roll.instanceId)
|
|
||||||
) {
|
|
||||||
state.value = CharacterPortraitRollUio(
|
|
||||||
characterId = Campaign.CharacterInstance.Id(
|
|
||||||
prefix = characterId.prefix,
|
|
||||||
characterSheetId = characterId.characterSheetId,
|
|
||||||
instanceId = characterId.instanceId,
|
|
||||||
),
|
|
||||||
value = roll.rollValue,
|
|
||||||
label = roll.resultLabel?.split(" ")?.joinToString(separator = "\n") { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.launchIn(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onPortraitRollRightClick(
|
|
||||||
characterId: Campaign.CharacterInstance.Id,
|
|
||||||
) {
|
|
||||||
rolls[characterId]?.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,115 +1,28 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
|
||||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
|
||||||
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitRollUio
|
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterPortraitUio
|
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CharacterRibbonFactory
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||||
import com.pixelized.shared.lwa.model.campaign.Campaign
|
import com.pixelized.shared.lwa.model.campaign.Campaign
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.flatMapMerge
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import java.text.Collator
|
|
||||||
|
|
||||||
private typealias CharacterId = Campaign.CharacterInstance.Id
|
|
||||||
|
|
||||||
class PlayerRibbonViewModel(
|
class PlayerRibbonViewModel(
|
||||||
private val rollHistoryRepository: RollHistoryRepository,
|
rollHistoryRepository: RollHistoryRepository,
|
||||||
private val settingsRepository: SettingsRepository,
|
settingsRepository: SettingsRepository,
|
||||||
characterRepository: CharacterSheetRepository,
|
characterRepository: CharacterSheetRepository,
|
||||||
alterationRepository: AlterationRepository,
|
alterationRepository: AlterationRepository,
|
||||||
campaignRepository: CampaignRepository,
|
campaignRepository: CampaignRepository,
|
||||||
private val ribbonFactory: CharacterRibbonFactory,
|
ribbonFactory: CharacterRibbonFactory,
|
||||||
) : ViewModel() {
|
) : CharacterRibbonViewModel(
|
||||||
|
rollHistoryRepository = rollHistoryRepository,
|
||||||
private val rolls = hashMapOf<CharacterId, MutableState<CharacterPortraitRollUio?>>()
|
settingsRepository = settingsRepository,
|
||||||
|
characterRepository = characterRepository,
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
alterationRepository = alterationRepository,
|
||||||
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow
|
campaignRepository = campaignRepository,
|
||||||
.flatMapMerge { campaign ->
|
ribbonFactory = ribbonFactory,
|
||||||
if (campaign.characters.isEmpty()) {
|
) {
|
||||||
flowOf(emptyList())
|
override val Campaign.data get() = characters
|
||||||
} else {
|
|
||||||
combine<CharacterPortraitUio?, List<CharacterPortraitUio>>(
|
|
||||||
flows = campaign.characters.map { entry ->
|
|
||||||
combine(
|
|
||||||
characterRepository.characterDetailFlow(characterSheetId = entry.key.characterSheetId),
|
|
||||||
alterationRepository.alterationsFlow(characterInstanceId = entry.key),
|
|
||||||
) { sheet, alterations ->
|
|
||||||
ribbonFactory.convertToPlayerPortraitUio(
|
|
||||||
characterSheet = sheet,
|
|
||||||
alterations = alterations,
|
|
||||||
characterInstanceId = entry.key,
|
|
||||||
characterInstance = entry.value,
|
|
||||||
enableDetail = true,
|
|
||||||
displayCharacterStats = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transform = { headers ->
|
|
||||||
headers.mapNotNull { it }
|
|
||||||
.sortedWith(compareBy(Collator.getInstance()) { it.name })
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
scope = viewModelScope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
fun roll(
|
|
||||||
characterId: Campaign.CharacterInstance.Id,
|
|
||||||
): State<CharacterPortraitRollUio?> {
|
|
||||||
val state = rolls.getOrPut(characterId) { mutableStateOf(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(characterId) {
|
|
||||||
combine(
|
|
||||||
settingsRepository.settingsFlow(),
|
|
||||||
rollHistoryRepository.rolls,
|
|
||||||
) { settings, roll ->
|
|
||||||
if (settings.dynamicDice &&
|
|
||||||
characterId.equals(roll.prefix, roll.characterSheetId, roll.instanceId)
|
|
||||||
) {
|
|
||||||
state.value = CharacterPortraitRollUio(
|
|
||||||
characterId = Campaign.CharacterInstance.Id(
|
|
||||||
prefix = characterId.prefix,
|
|
||||||
characterSheetId = characterId.characterSheetId,
|
|
||||||
instanceId = characterId.instanceId,
|
|
||||||
),
|
|
||||||
value = roll.rollValue,
|
|
||||||
label = roll.resultLabel?.split(" ")?.joinToString(separator = "\n") { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.launchIn(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onPortraitRollRightClick(
|
|
||||||
characterId: Campaign.CharacterInstance.Id,
|
|
||||||
) {
|
|
||||||
rolls[characterId]?.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue