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
|
||||
|
||||
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.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||
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(
|
||||
private val rollHistoryRepository: RollHistoryRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
rollHistoryRepository: RollHistoryRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
characterRepository: CharacterSheetRepository,
|
||||
alterationRepository: AlterationRepository,
|
||||
campaignRepository: CampaignRepository,
|
||||
private val ribbonFactory: CharacterRibbonFactory,
|
||||
) : ViewModel() {
|
||||
private val rolls = hashMapOf<CharacterId, MutableState<CharacterPortraitRollUio?>>()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow
|
||||
.flatMapMerge { campaign ->
|
||||
if (campaign.npcs.isEmpty()) {
|
||||
flowOf(emptyList())
|
||||
} else {
|
||||
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
|
||||
}
|
||||
ribbonFactory: CharacterRibbonFactory,
|
||||
) : CharacterRibbonViewModel(
|
||||
rollHistoryRepository = rollHistoryRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
characterRepository = characterRepository,
|
||||
alterationRepository = alterationRepository,
|
||||
campaignRepository = campaignRepository,
|
||||
ribbonFactory = ribbonFactory,
|
||||
) {
|
||||
override val Campaign.data get() = npcs
|
||||
}
|
||||
|
|
@ -1,115 +1,28 @@
|
|||
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.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.desktop.lwa.ui.screen.campaign.player.ribbon.CharacterRibbonViewModel
|
||||
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(
|
||||
private val rollHistoryRepository: RollHistoryRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
rollHistoryRepository: RollHistoryRepository,
|
||||
settingsRepository: SettingsRepository,
|
||||
characterRepository: CharacterSheetRepository,
|
||||
alterationRepository: AlterationRepository,
|
||||
campaignRepository: CampaignRepository,
|
||||
private val ribbonFactory: CharacterRibbonFactory,
|
||||
) : ViewModel() {
|
||||
|
||||
private val rolls = hashMapOf<CharacterId, MutableState<CharacterPortraitRollUio?>>()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val characters: StateFlow<List<CharacterPortraitUio>> = campaignRepository.campaignFlow
|
||||
.flatMapMerge { campaign ->
|
||||
if (campaign.characters.isEmpty()) {
|
||||
flowOf(emptyList())
|
||||
} 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
|
||||
}
|
||||
ribbonFactory: CharacterRibbonFactory,
|
||||
) : CharacterRibbonViewModel(
|
||||
rollHistoryRepository = rollHistoryRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
characterRepository = characterRepository,
|
||||
alterationRepository = alterationRepository,
|
||||
campaignRepository = campaignRepository,
|
||||
ribbonFactory = ribbonFactory,
|
||||
) {
|
||||
override val Campaign.data get() = characters
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue