Skip to content

Commit 06c119d

Browse files
authored
Merge pull request #80 from fmasa/feat/encounter-complete
Completed Encounters
2 parents 646bf43 + 82e19b8 commit 06c119d

File tree

6 files changed

+198
-65
lines changed

6 files changed

+198
-65
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.material.Surface
7+
import androidx.compose.runtime.Composable
8+
import androidx.compose.ui.Alignment
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.unit.dp
11+
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.Spacing
12+
13+
@Composable
14+
fun FilterBar(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
15+
Surface(elevation = 2.dp) {
16+
Box(
17+
modifier = modifier.fillMaxWidth().padding(Spacing.tiny),
18+
contentAlignment = Alignment.Center,
19+
) {
20+
content()
21+
}
22+
}
23+
}

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/encounters/EncounterDetailScreen.kt

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package cz.frantisekmasa.wfrp_master.common.encounters
22

33
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Arrangement
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.Spacer
69
import androidx.compose.foundation.layout.fillMaxSize
710
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.height
812
import androidx.compose.foundation.layout.padding
913
import androidx.compose.foundation.layout.width
1014
import androidx.compose.foundation.rememberScrollState
@@ -18,6 +22,7 @@ import androidx.compose.material.IconButton
1822
import androidx.compose.material.LocalContentAlpha
1923
import androidx.compose.material.MaterialTheme
2024
import androidx.compose.material.Scaffold
25+
import androidx.compose.material.Switch
2126
import androidx.compose.material.Text
2227
import androidx.compose.material.TextButton
2328
import androidx.compose.material.TopAppBar
@@ -65,11 +70,13 @@ import cz.frantisekmasa.wfrp_master.common.core.ui.forms.NumberPicker
6570
import cz.frantisekmasa.wfrp_master.common.core.ui.menu.DropdownMenuItem
6671
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.ContextMenu
6772
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.EmptyUI
73+
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.FullScreenProgress
6874
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.ItemIcon
6975
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.Spacing
7076
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.VisualOnlyIconDescription
7177
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.rememberScreenModel
7278
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.OptionsAction
79+
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.SubheadBar
7380
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.Subtitle
7481
import cz.frantisekmasa.wfrp_master.common.encounters.domain.Encounter
7582
import cz.frantisekmasa.wfrp_master.common.localization.LocalStrings
@@ -130,22 +137,25 @@ class EncounterDetailScreen(
130137
)
131138
},
132139
content = {
133-
if (encounter != null) {
134-
MainContainer(encounter, screenModel)
135-
136-
if (startCombatDialogVisible) {
137-
val navigator = LocalNavigator.currentOrThrow
138-
139-
StartCombatDialog(
140-
encounter = encounter,
141-
onDismissRequest = { startCombatDialogVisible = false },
142-
screenModel = rememberScreenModel(arg = encounterId.partyId),
143-
onComplete = {
144-
startCombatDialogVisible = false
145-
navigator.push(ActiveCombatScreen(encounterId.partyId))
146-
},
147-
)
148-
}
140+
if (encounter == null) {
141+
FullScreenProgress()
142+
return@Scaffold
143+
}
144+
145+
MainContainer(encounter, screenModel)
146+
147+
if (startCombatDialogVisible) {
148+
val navigator = LocalNavigator.currentOrThrow
149+
150+
StartCombatDialog(
151+
encounter = encounter,
152+
onDismissRequest = { startCombatDialogVisible = false },
153+
screenModel = rememberScreenModel(arg = encounterId.partyId),
154+
onComplete = {
155+
startCombatDialogVisible = false
156+
navigator.push(ActiveCombatScreen(encounterId.partyId))
157+
},
158+
)
149159
}
150160
}
151161
)
@@ -235,8 +245,28 @@ class EncounterDetailScreen(
235245
.fillMaxSize()
236246
.background(MaterialTheme.colors.background)
237247
.verticalScroll(rememberScrollState())
238-
.padding(top = 6.dp, bottom = Spacing.bottomPaddingUnderFab),
248+
.padding(bottom = Spacing.bottomPaddingUnderFab),
239249
) {
250+
SubheadBar {
251+
Row(
252+
horizontalArrangement = Arrangement.SpaceBetween,
253+
verticalAlignment = Alignment.CenterVertically,
254+
modifier = Modifier.fillMaxWidth(),
255+
) {
256+
Text(LocalStrings.current.encounters.labelCompleted)
257+
Switch(
258+
checked = encounter.completed,
259+
onCheckedChange = {
260+
coroutineScope.launch(Dispatchers.IO) {
261+
screenModel.updateEncounter(encounter.copy(completed = it))
262+
}
263+
}
264+
)
265+
}
266+
}
267+
268+
Spacer(Modifier.height(Spacing.small))
269+
240270
DescriptionCard(screenModel)
241271

242272
val navigator = LocalNavigator.currentOrThrow

common/src/commonMain/kotlin/cz/frantisekmasa/wfrp_master/common/encounters/EncountersScreen.kt

Lines changed: 115 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,56 @@
11
package cz.frantisekmasa.wfrp_master.common.encounters
22

3-
import androidx.compose.foundation.Image
43
import androidx.compose.foundation.background
54
import androidx.compose.foundation.clickable
5+
import androidx.compose.foundation.layout.Arrangement
66
import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.Row
88
import androidx.compose.foundation.layout.fillMaxHeight
99
import androidx.compose.foundation.layout.fillMaxSize
1010
import androidx.compose.foundation.layout.fillMaxWidth
11-
import androidx.compose.foundation.layout.padding
12-
import androidx.compose.foundation.layout.size
11+
import androidx.compose.foundation.layout.height
12+
import androidx.compose.foundation.lazy.LazyColumn
13+
import androidx.compose.foundation.lazy.items
1314
import androidx.compose.foundation.rememberScrollState
1415
import androidx.compose.foundation.verticalScroll
15-
import androidx.compose.material.Card
16+
import androidx.compose.material.Divider
1617
import androidx.compose.material.FloatingActionButton
1718
import androidx.compose.material.Icon
19+
import androidx.compose.material.ListItem
1820
import androidx.compose.material.MaterialTheme
1921
import androidx.compose.material.Scaffold
22+
import androidx.compose.material.Surface
2023
import androidx.compose.material.Text
2124
import androidx.compose.material.TopAppBar
2225
import androidx.compose.material.icons.Icons
2326
import androidx.compose.material.icons.rounded.Add
27+
import androidx.compose.material.icons.rounded.Done
28+
import androidx.compose.material.icons.rounded.DragIndicator
2429
import androidx.compose.runtime.Composable
2530
import androidx.compose.runtime.getValue
2631
import androidx.compose.runtime.mutableStateOf
32+
import androidx.compose.runtime.remember
2733
import androidx.compose.runtime.saveable.rememberSaveable
2834
import androidx.compose.runtime.setValue
29-
import androidx.compose.ui.Alignment
3035
import androidx.compose.ui.Modifier
31-
import androidx.compose.ui.graphics.ColorFilter
32-
import androidx.compose.ui.text.style.TextAlign
36+
import androidx.compose.ui.graphics.Color
3337
import androidx.compose.ui.unit.dp
3438
import cafe.adriel.voyager.core.screen.Screen
3539
import cafe.adriel.voyager.navigator.LocalNavigator
3640
import cafe.adriel.voyager.navigator.currentOrThrow
3741
import cz.frantisekmasa.wfrp_master.common.core.domain.identifiers.EncounterId
3842
import cz.frantisekmasa.wfrp_master.common.core.domain.party.PartyId
3943
import cz.frantisekmasa.wfrp_master.common.core.shared.Resources
40-
import cz.frantisekmasa.wfrp_master.common.core.shared.drawableResource
4144
import cz.frantisekmasa.wfrp_master.common.core.ui.buttons.HamburgerButton
4245
import cz.frantisekmasa.wfrp_master.common.core.ui.flow.collectWithLifecycle
46+
import cz.frantisekmasa.wfrp_master.common.core.ui.forms.CheckboxWithText
4347
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.DraggableListFor
4448
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.EmptyUI
4549
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.FullScreenProgress
46-
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.Spacing
50+
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.ItemIcon
4751
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.VisualOnlyIconDescription
4852
import cz.frantisekmasa.wfrp_master.common.core.ui.primitives.rememberScreenModel
53+
import cz.frantisekmasa.wfrp_master.common.core.ui.scaffolding.FilterBar
4954
import cz.frantisekmasa.wfrp_master.common.encounters.domain.Encounter
5055
import cz.frantisekmasa.wfrp_master.common.localization.LocalStrings
5156

@@ -56,7 +61,14 @@ class EncountersScreen(
5661
@Composable
5762
override fun Content() {
5863
val screenModel: EncountersScreenModel = rememberScreenModel(arg = partyId)
59-
val encounters = screenModel.encounters.collectWithLifecycle(null).value
64+
65+
var showCompleted by rememberSaveable { mutableStateOf(false) }
66+
67+
val encounters = (
68+
if (showCompleted)
69+
screenModel.allEncounters
70+
else screenModel.notCompletedEncounters
71+
).collectWithLifecycle(null).value
6072

6173
if (encounters == null) {
6274
FullScreenProgress()
@@ -89,11 +101,33 @@ class EncountersScreen(
89101

90102
val navigator = LocalNavigator.currentOrThrow
91103

92-
EncounterList(
93-
encounters,
94-
screenModel,
95-
onClick = { navigator.push(EncounterDetailScreen(EncounterId(partyId, it.id))) },
96-
)
104+
Column {
105+
FilterBar {
106+
Row(
107+
modifier = Modifier.fillMaxWidth(),
108+
horizontalArrangement = Arrangement.Center,
109+
) {
110+
CheckboxWithText(
111+
checked = showCompleted,
112+
text = LocalStrings.current.encounters.buttonShowCompleted,
113+
onCheckedChange = { showCompleted = !showCompleted },
114+
)
115+
}
116+
}
117+
118+
val onClick: (Encounter) -> Unit = {
119+
navigator.push(EncounterDetailScreen(EncounterId(partyId, it.id)))
120+
}
121+
if (showCompleted) {
122+
AllEncountersList(encounters, onClick)
123+
} else {
124+
ActiveEncounterList(
125+
encounters,
126+
screenModel,
127+
onClick = onClick,
128+
)
129+
}
130+
}
97131
}
98132
}
99133
}
@@ -106,10 +140,63 @@ private fun AddEncounterButton(onCreateEncounterRequest: () -> Unit) {
106140
}
107141

108142
@Composable
109-
private fun EncounterList(
143+
private fun AllEncountersList(encounters: List<Encounter>, onClick: (Encounter) -> Unit) {
144+
LazyColumn {
145+
items(encounters) { encounter ->
146+
EncounterItem(
147+
encounter,
148+
onClick,
149+
draggable = false,
150+
)
151+
}
152+
}
153+
}
154+
155+
@Composable
156+
fun EncounterItem(
157+
encounter: Encounter,
158+
onClick: (Encounter) -> Unit,
159+
draggable: Boolean,
160+
) {
161+
Column {
162+
ListItem(
163+
modifier = Modifier.clickable { onClick(encounter) },
164+
icon = {
165+
if (draggable) {
166+
Icon(
167+
Icons.Rounded.DragIndicator,
168+
VisualOnlyIconDescription,
169+
modifier = Modifier.height(
170+
with(ItemIcon.Size.Small) { dimensions + padding * 2 }
171+
)
172+
)
173+
}
174+
},
175+
text = {
176+
Text(encounter.name)
177+
},
178+
secondaryText = {
179+
val npcCount = remember(encounter.characters) { encounter.characters.values.sum() }
180+
Text("$npcCount ${LocalStrings.current.npcs.titlePlural}")
181+
},
182+
trailing = {
183+
if (encounter.completed) {
184+
Icon(
185+
Icons.Rounded.Done,
186+
contentDescription = LocalStrings.current.encounters.labelCompleted,
187+
)
188+
}
189+
}
190+
)
191+
Divider()
192+
}
193+
}
194+
195+
@Composable
196+
private fun ActiveEncounterList(
110197
encounters: List<Encounter>,
111198
screenModel: EncountersScreenModel,
112-
onClick: (Encounter) -> Unit
199+
onClick: (Encounter) -> Unit,
113200
) {
114201
if (encounters.isEmpty()) {
115202
val strings = LocalStrings.current.encounters
@@ -122,50 +209,32 @@ private fun EncounterList(
122209
return
123210
}
124211

125-
val icon = drawableResource(Resources.Drawable.Encounter)
126-
val iconSize = 28.dp
127-
128212
Column(
129213
Modifier
130214
.background(MaterialTheme.colors.background)
131215
.fillMaxSize()
132-
.verticalScroll(rememberScrollState())
133-
.padding(Spacing.bodyPaddingWithFab),
134-
horizontalAlignment = Alignment.CenterHorizontally,
216+
.verticalScroll(rememberScrollState()),
135217
) {
136218
DraggableListFor(
137219
encounters,
138220
modifier = Modifier.fillMaxHeight(),
139-
itemSpacing = Spacing.small,
140221
onReorder = {
141222
screenModel.reorderEncounters(
142223
it.mapIndexed { index, encounter -> encounter.id to index }.toMap()
143224
)
144225
},
145226
) { _, encounter, isDragged ->
146-
Card(
147-
elevation = if (isDragged) 6.dp else 2.dp,
148-
modifier = Modifier
149-
.fillMaxWidth()
150-
.clickable { onClick(encounter) }
227+
Surface(
228+
color = if (isDragged)
229+
MaterialTheme.colors.surface
230+
else Color.Transparent,
231+
elevation = if (isDragged) 1.dp else 0.dp,
151232
) {
152-
Row(
153-
Modifier.padding(12.dp),
154-
verticalAlignment = Alignment.CenterVertically
155-
) {
156-
Image(
157-
icon,
158-
VisualOnlyIconDescription,
159-
Modifier.size(iconSize),
160-
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface)
161-
)
162-
Text(
163-
encounter.name,
164-
modifier = Modifier.padding(start = 8.dp),
165-
textAlign = TextAlign.Center,
166-
style = MaterialTheme.typography.body1
167-
)
168-
}
233+
EncounterItem(
234+
encounter,
235+
onClick = onClick,
236+
draggable = true,
237+
)
169238
}
170239
}
171240
}

0 commit comments

Comments
 (0)