Skip to content

Commit a856ac5

Browse files
committed
Merge branch 'feat/show-user-count' into 'master'
Zobrazení počtu hráčů See merge request fmasa/pv239-project!66
2 parents 733ea17 + 829da28 commit a856ac5

File tree

14 files changed

+187
-60
lines changed

14 files changed

+187
-60
lines changed

app/src/main/java/cz/muni/fi/rpg/di/container.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ val appModule = module {
135135
/**
136136
* ViewModels
137137
*/
138-
viewModel { (partyId: UUID) -> PartyViewModel(get(), partyId) }
139138
viewModel { (characterId: CharacterId) -> CharacterStatsViewModel(characterId, get()) }
140139
viewModel { (characterId: CharacterId) -> CharacterMiscViewModel(characterId, get(), get()) }
141-
viewModel { (characterId: CharacterId) -> CharacterViewModel(characterId, get())}
140+
viewModel { (characterId: CharacterId) -> CharacterViewModel(characterId, get(), get())}
141+
viewModel { (partyId: UUID) -> GameMasterViewModel(partyId, get(), get()) }
142142
viewModel { (characterId: CharacterId) -> InventoryViewModel(characterId, get(), get()) }
143143
viewModel { (characterId: CharacterId) -> SkillsViewModel(characterId, get()) }
144144
viewModel { (characterId: CharacterId) -> SpellsViewModel(characterId, get()) }
@@ -149,7 +149,7 @@ val appModule = module {
149149
* Fragments
150150
*/
151151
fragment { CharacterFragment(get()) }
152-
fragment { GameMasterFragment(get(), get()) }
152+
fragment { GameMasterFragment(get()) }
153153
fragment { NavHostFragment() }
154154
fragment { PartyListFragment(get()) }
155155
fragment { CharacterEditFragment(get()) }

app/src/main/java/cz/muni/fi/rpg/model/domain/party/Party.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ data class Party(
3636
require(name.length <= NAME_MAX_LENGTH) {"Party name is too long"}
3737
}
3838

39+
fun getPlayerCounts(): Int = users.size - 1
40+
3941
fun isSinglePlayer() = gameMasterId == null
4042

4143
fun updateAmbitions(ambitions: Ambitions) {

app/src/main/java/cz/muni/fi/rpg/ui/character/CharacterFragment.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import cz.muni.fi.rpg.ui.common.AdManager
1717
import cz.muni.fi.rpg.ui.common.BaseFragment
1818
import cz.muni.fi.rpg.ui.common.StaticFragmentsViewPagerAdapter
1919
import cz.muni.fi.rpg.viewModels.CharacterViewModel
20-
import cz.muni.fi.rpg.viewModels.PartyViewModel
2120
import kotlinx.android.synthetic.main.fragment_character.*
2221
import org.koin.android.viewmodel.ext.android.viewModel
2322
import org.koin.core.parameter.parametersOf
@@ -27,10 +26,9 @@ import timber.log.Timber
2726
class CharacterFragment(
2827
private val adManager: AdManager
2928
) : BaseFragment(R.layout.fragment_character) {
30-
private val args: CharacterFragmentArgs by navArgs()
3129

30+
private val args: CharacterFragmentArgs by navArgs()
3231
private val viewModel: CharacterViewModel by viewModel { parametersOf(args.characterId) }
33-
private val partyVm: PartyViewModel by viewModel { parametersOf(args.characterId.partyId) }
3432

3533
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
3634
super.onViewCreated(view, savedInstanceState)
@@ -47,7 +45,7 @@ class CharacterFragment(
4745
}
4846
}
4947

50-
partyVm.party.right()
48+
viewModel.party.right()
5149
.observe(viewLifecycleOwner) { setSubtitle(it.name) }
5250

5351
pager.adapter = StaticFragmentsViewPagerAdapter(

app/src/main/java/cz/muni/fi/rpg/ui/gameMaster/GameMasterFragment.kt

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,24 @@ import androidx.recyclerview.widget.LinearLayoutManager
99
import com.fasterxml.jackson.databind.json.JsonMapper
1010
import cz.muni.fi.rpg.R
1111
import cz.muni.fi.rpg.model.domain.character.CharacterId
12-
import cz.muni.fi.rpg.model.domain.character.CharacterRepository
1312
import cz.muni.fi.rpg.model.domain.party.Invitation
1413
import cz.muni.fi.rpg.model.right
1514
import cz.muni.fi.rpg.ui.common.BaseFragment
1615
import cz.muni.fi.rpg.ui.common.ChangeAmbitionsDialog
1716
import cz.muni.fi.rpg.ui.gameMaster.adapter.CharacterAdapter
18-
import cz.muni.fi.rpg.viewModels.PartyViewModel
17+
import cz.muni.fi.rpg.viewModels.GameMasterViewModel
1918
import kotlinx.android.synthetic.main.fragment_game_master.*
2019
import org.koin.core.parameter.parametersOf
2120
import org.koin.android.viewmodel.ext.android.viewModel
2221
import timber.log.Timber
2322

2423
class GameMasterFragment(
25-
private val jsonMapper: JsonMapper,
26-
private val characterRepository: CharacterRepository
27-
24+
private val jsonMapper: JsonMapper
2825
) : BaseFragment(R.layout.fragment_game_master) {
2926

3027
private val args: GameMasterFragmentArgs by navArgs()
3128

32-
private val partyViewModel: PartyViewModel by viewModel { parametersOf(args.partyId) }
29+
private val viewModel: GameMasterViewModel by viewModel { parametersOf(args.partyId) }
3330

3431
private lateinit var invitation: Invitation
3532

@@ -48,7 +45,7 @@ class GameMasterFragment(
4845
Timber.d("Created view for GameMasterFragment (partyId = ${args.partyId}")
4946

5047

51-
partyViewModel.party.right().observe(viewLifecycleOwner) { party ->
48+
viewModel.party.right().observe(viewLifecycleOwner) { party ->
5249
setTitle(party.name)
5350
invitation = party.getInvitation()
5451

@@ -59,15 +56,15 @@ class GameMasterFragment(
5956
ChangeAmbitionsDialog
6057
.newInstance(getString(R.string.title_party_ambitions), party.getAmbitions())
6158
.setOnSaveListener {
62-
partyViewModel.updatePartyAmbitions(it)
59+
viewModel.updatePartyAmbitions(it)
6360
}.show(childFragmentManager, "ChangeAmbitionsDialog")
6461
}
6562
}
6663

6764
inviteButton.setOnClickListener { showQrCode() }
6865

69-
characterRepository.inParty(args.partyId).observe(viewLifecycleOwner) { characters ->
70-
if (characters.isNotEmpty()) {
66+
viewModel.getPlayers().observe(viewLifecycleOwner) { players ->
67+
if (players.isNotEmpty()) {
7168
val adapter = CharacterAdapter(layoutInflater)
7269
{
7370
findNavController()
@@ -79,7 +76,7 @@ class GameMasterFragment(
7976
characterListRecycler.adapter = adapter
8077
characterListRecycler.layoutManager = LinearLayoutManager(context)
8178

82-
adapter.submitList(characters)
79+
adapter.submitList(players)
8380

8481
setEmptyCollectionView(false)
8582
} else {

app/src/main/java/cz/muni/fi/rpg/ui/gameMaster/adapter/CharacterAdapter.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,34 @@ import android.view.LayoutInflater
44
import android.view.ViewGroup
55
import androidx.recyclerview.widget.DiffUtil
66
import androidx.recyclerview.widget.ListAdapter
7+
import arrow.core.Either
78
import cz.muni.fi.rpg.R
89
import cz.muni.fi.rpg.common.EntityListener
910
import cz.muni.fi.rpg.model.domain.character.Character
1011

12+
typealias Player = Either<PlayerWithoutCharacter, Character>
13+
1114
class CharacterAdapter(
1215
private val layoutInflater: LayoutInflater,
1316
private val onClickListener: EntityListener<Character>
14-
) : ListAdapter<Character, CharacterHolder>(
15-
object: DiffUtil.ItemCallback<Character>() {
16-
override fun areItemsTheSame(oldItem: Character, newItem: Character): Boolean =
17-
oldItem.userId == newItem.userId
17+
) : ListAdapter<Player, CharacterHolder>(
18+
object : DiffUtil.ItemCallback<Player>() {
19+
override fun areItemsTheSame(oldItem: Player, newItem: Player): Boolean {
20+
return extractUserId(oldItem) == extractUserId(newItem)
21+
}
22+
23+
override fun areContentsTheSame(oldItem: Player, newItem: Player): Boolean {
24+
return oldItem.isLeft() == newItem.isLeft() &&
25+
comparableHash(oldItem) == comparableHash(newItem)
26+
}
27+
28+
private fun extractUserId(player: Player): String {
29+
return player.fold({ it.userId }, { it.userId })
30+
}
1831

19-
override fun areContentsTheSame(oldItem: Character, newItem: Character): Boolean =
20-
oldItem == newItem
32+
private fun comparableHash(player: Player): Int {
33+
return player.fold({ it.hashCode() }, { it.hashCode() })
34+
}
2135
}
2236
) {
2337
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterHolder {

app/src/main/java/cz/muni/fi/rpg/ui/gameMaster/adapter/CharacterHolder.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,24 @@ class CharacterHolder(
1010
itemView: View,
1111
private val onClickListener: EntityListener<Character>
1212
) : RecyclerView.ViewHolder(itemView) {
13-
fun bind(item: Character) {
14-
itemView.character_name.text = item.getName()
15-
itemView.character_race.setText(item.getRace().getReadableNameId())
16-
itemView.character_job.text = item.getCareer()
17-
itemView.setOnClickListener { onClickListener(item) }
13+
fun bind(item: Player) {
14+
item.mapLeft {
15+
listOf(itemView.character_name, itemView.character_job, itemView.character_race)
16+
.forEach { it.visibility = View.GONE }
17+
18+
itemView.waitingForCharacter.visibility = View.VISIBLE
19+
itemView.setOnClickListener { }
20+
itemView.isClickable = false
21+
itemView.isFocusable = false
22+
}
23+
24+
item.map {character ->
25+
itemView.character_name.text = character.getName()
26+
itemView.character_race.setText(character.getRace().getReadableNameId())
27+
itemView.character_job.text = character.getCareer()
28+
itemView.setOnClickListener { onClickListener(character) }
29+
itemView.isClickable = true
30+
itemView.isFocusable = true
31+
}
1832
}
1933
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package cz.muni.fi.rpg.ui.gameMaster.adapter
2+
3+
data class PlayerWithoutCharacter(val userId: String)

app/src/main/java/cz/muni/fi/rpg/ui/partyList/adapter/PartyHolder.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ class PartyHolder(
1313
fun bind(item: Party) {
1414
view.party_item_title.text = item.name
1515

16+
val playersCount = item.getPlayerCounts()
17+
18+
if (playersCount > 0) {
19+
view.playersCount.text = playersCount.toString()
20+
view.playersCount.visibility = View.VISIBLE
21+
view.playersCountIcon.visibility = View.VISIBLE
22+
}
23+
1624
view.setOnClickListener { onClickListener(item) }
1725
}
1826
}
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package cz.muni.fi.rpg.viewModels
22

3+
import androidx.lifecycle.LiveData
34
import androidx.lifecycle.ViewModel
5+
import arrow.core.Either
46
import cz.muni.fi.rpg.model.domain.character.CharacterId
57
import cz.muni.fi.rpg.model.domain.character.CharacterRepository
8+
import cz.muni.fi.rpg.model.domain.party.Party
9+
import cz.muni.fi.rpg.model.domain.party.PartyNotFound
10+
import cz.muni.fi.rpg.model.domain.party.PartyRepository
611
import kotlinx.coroutines.*
712

813
class CharacterViewModel(
9-
val characterId: CharacterId,
10-
characters: CharacterRepository
14+
characterId: CharacterId,
15+
characters: CharacterRepository,
16+
parties: PartyRepository
1117
) : ViewModel(), CoroutineScope by CoroutineScope(Dispatchers.Default) {
1218

1319
val character = characters.getLive(characterId)
20+
21+
val party: LiveData<Either<PartyNotFound, Party>> = parties.getLive(characterId.partyId)
1422
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package cz.muni.fi.rpg.viewModels
2+
3+
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.Transformations
5+
import androidx.lifecycle.ViewModel
6+
import arrow.core.Either
7+
import arrow.core.Left
8+
import arrow.core.Right
9+
import cz.muni.fi.rpg.model.domain.character.Character
10+
import cz.muni.fi.rpg.model.domain.character.CharacterRepository
11+
import cz.muni.fi.rpg.model.domain.common.Ambitions
12+
import cz.muni.fi.rpg.model.domain.party.Party
13+
import cz.muni.fi.rpg.model.domain.party.PartyNotFound
14+
import cz.muni.fi.rpg.model.domain.party.PartyRepository
15+
import cz.muni.fi.rpg.model.right
16+
import cz.muni.fi.rpg.ui.common.CombinedLiveData
17+
import cz.muni.fi.rpg.ui.gameMaster.adapter.Player
18+
import cz.muni.fi.rpg.ui.gameMaster.adapter.PlayerWithoutCharacter
19+
import java.util.*
20+
21+
class GameMasterViewModel(
22+
private val partyId: UUID,
23+
private val parties: PartyRepository,
24+
characterRepository: CharacterRepository
25+
) : ViewModel() {
26+
27+
val party: LiveData<Either<PartyNotFound, Party>> = parties.getLive(partyId)
28+
val characters: LiveData<List<Character>> = characterRepository.inParty(partyId)
29+
30+
/**
31+
* Returns LiveData with either CharacterId of players current character or NULL if user
32+
* didn't create character yet
33+
*/
34+
fun getPlayers(): LiveData<List<Player>> {
35+
return Transformations.map(
36+
CombinedLiveData(party.right(), characters)
37+
) { partyAndCharacters ->
38+
val party = partyAndCharacters.first
39+
val characters = partyAndCharacters.second
40+
.map { character -> character.userId to character }
41+
.toMap()
42+
43+
party.users
44+
.filter { it != party.gameMasterId }
45+
.map {
46+
val character = characters[it]
47+
48+
if (character == null)
49+
Left(PlayerWithoutCharacter(it))
50+
else Right(character)
51+
}
52+
}
53+
}
54+
55+
suspend fun updatePartyAmbitions(ambitions: Ambitions) {
56+
val party = parties.get(partyId)
57+
58+
party.updateAmbitions(ambitions)
59+
60+
parties.save(party)
61+
}
62+
}

0 commit comments

Comments
 (0)