Skip to content

Commit 9dfba1d

Browse files
committed
add method to buy trees
1 parent beb9859 commit 9dfba1d

6 files changed

Lines changed: 155 additions & 37 deletions

File tree

composeApp/src/commonMain/kotlin/neth/iecal/trease/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package neth.iecal.trease
22

33
object Constants {
44
val cdn = "https://trease-focus.github.io/cache-trees-cdn"
5+
val preOwnedTrees = listOf("tree")
56
}

composeApp/src/commonMain/kotlin/neth/iecal/trease/ui/bottomsheet/SelectTreeBottomSheet.kt

Lines changed: 116 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.compose.runtime.*
1313
import androidx.compose.ui.Alignment
1414
import androidx.compose.ui.ExperimentalComposeUiApi
1515
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.draw.alpha
1617
import androidx.compose.ui.draw.clip
1718
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
1819
import androidx.compose.ui.layout.ContentScale
@@ -21,27 +22,39 @@ import androidx.compose.ui.text.font.FontWeight
2122
import androidx.compose.ui.text.style.TextAlign
2223
import androidx.compose.ui.text.style.TextOverflow
2324
import androidx.compose.ui.unit.dp
25+
import androidx.compose.ui.window.Dialog
2426
import coil3.compose.AsyncImage
27+
import kotlinx.coroutines.launch
2528
import kotlinx.serialization.encodeToString
2629
import kotlinx.serialization.json.Json
2730
import neth.iecal.trease.Constants
2831
import neth.iecal.trease.api.TreeRepository
2932
import neth.iecal.trease.models.TreeData
3033
import neth.iecal.trease.models.TreeUiState
3134
import neth.iecal.trease.utils.CacheManager
35+
import neth.iecal.trease.utils.TreePurchaseManager
36+
import neth.iecal.trease.viewmodels.HomeScreenViewModel
3237

3338
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
3439
@Composable
3540
fun GrowTreeBottomSheet(
3641
onDismiss: () -> Unit,
37-
onSelected: (TreeData) -> Unit,
42+
onAddToCanvas: (TreeData) -> Unit,
3843
onShowWitheredTrees:()->Unit,
44+
viewModel: HomeScreenViewModel,
3945
) {
40-
// State
4146
var uiState by remember { mutableStateOf<TreeUiState>(TreeUiState.Loading) }
4247
var selectedTree by remember { mutableStateOf<TreeData?>(null) }
4348

49+
var purchasedTrees by remember { mutableStateOf(emptyList<String>()) }
50+
val coroutine = rememberCoroutineScope()
51+
var showBrokeDialog by remember { mutableStateOf(false) }
52+
53+
4454
LaunchedEffect(Unit) {
55+
val treePurchaseManager = TreePurchaseManager()
56+
purchasedTrees = treePurchaseManager.loadAllPurchasedTrees()
57+
4558
val cacheManager = CacheManager()
4659
cacheManager.readFile("tree.json")?.let {
4760
uiState = TreeUiState.Success(Json.decodeFromString(it))
@@ -58,11 +71,53 @@ fun GrowTreeBottomSheet(
5871
}
5972
}
6073

74+
if(showBrokeDialog) {
75+
Dialog(onDismissRequest = { showBrokeDialog = false }) {
76+
Card(
77+
shape = RoundedCornerShape(24.dp),
78+
modifier = Modifier
79+
.fillMaxWidth()
80+
.padding(16.dp)
81+
) {
82+
Column(
83+
modifier = Modifier
84+
.padding(24.dp)
85+
.fillMaxWidth(),
86+
horizontalAlignment = Alignment.CenterHorizontally,
87+
verticalArrangement = Arrangement.spacedBy(16.dp)
88+
) {
89+
Text(
90+
text = "Too broke to buy this tree!",
91+
style = MaterialTheme.typography.headlineSmall,
92+
fontWeight = FontWeight.Bold,
93+
textAlign = TextAlign.Center,
94+
)
95+
Button(
96+
onClick = { showBrokeDialog = false },
97+
modifier = Modifier.fillMaxWidth(),
98+
shape = RoundedCornerShape(12.dp),
99+
colors = ButtonDefaults.buttonColors(
100+
containerColor = MaterialTheme.colorScheme.primary,
101+
contentColor = MaterialTheme.colorScheme.onPrimary
102+
)
103+
) {
104+
Text(
105+
text = "Close",
106+
modifier = Modifier.padding(vertical = 6.dp),
107+
style = MaterialTheme.typography.titleMedium
108+
)
109+
}
110+
}
111+
112+
}
113+
}
114+
}
115+
61116
ModalBottomSheet(
62117
onDismissRequest = onDismiss,
63118
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
64119
containerColor = MaterialTheme.colorScheme.surface,
65-
tonalElevation = 0.dp // Cleaner flat look
120+
tonalElevation = 0.dp
66121
) {
67122
AnimatedContent(
68123
targetState = selectedTree,
@@ -79,14 +134,33 @@ fun GrowTreeBottomSheet(
79134
TreeDetailView(
80135
tree = tree,
81136
onBack = { selectedTree = null },
82-
onSelect = { onSelected(tree) }
137+
onSelect = { onAddToCanvas(tree) },
138+
isPurchased = purchasedTrees.contains(tree.id),
139+
onPurchased = {
140+
if(viewModel.coins.value >= tree.basePrice){
141+
val tempTree = purchasedTrees.toMutableList()
142+
tempTree.add(tree.id)
143+
purchasedTrees = tempTree.toList()
144+
coroutine.launch {
145+
val purchaseManager = TreePurchaseManager()
146+
purchaseManager.addTree(tree.id)
147+
viewModel.useCoins(tree.basePrice)
148+
onAddToCanvas(tree)
149+
}
150+
}else{
151+
showBrokeDialog = true
152+
}
153+
}
83154
)
84155
} else {
85156
TreeGridView(
86157
uiState = uiState,
87-
onTreeSelected = { onSelected(it) },
158+
onTreeSelected = {
159+
if(purchasedTrees.contains(it.id)) onAddToCanvas(it) else selectedTree = it
160+
},
88161
onTreeLongPress = { selectedTree = it },
89-
onShowWitheredTrees
162+
onShowWitheredTrees = onShowWitheredTrees,
163+
purchasedTrees = purchasedTrees
90164
)
91165
}
92166
}
@@ -99,7 +173,8 @@ private fun TreeGridView(
99173
uiState: TreeUiState,
100174
onTreeSelected: (TreeData) -> Unit,
101175
onTreeLongPress: (TreeData) -> Unit,
102-
onShowWitheredTrees: () -> Unit
176+
onShowWitheredTrees: () -> Unit,
177+
purchasedTrees: List<String>
103178
) {
104179
Column(
105180
modifier = Modifier
@@ -146,7 +221,7 @@ private fun TreeGridView(
146221
items = uiState.trees.filter { it.isGrowable },
147222
key = { it.id }
148223
) { tree ->
149-
TreeGridItem(tree, onTreeSelected, onTreeLongPress)
224+
TreeGridItem(tree, onTreeSelected, onTreeLongPress,purchasedTrees.contains(tree.id))
150225
}
151226
}
152227
}
@@ -158,7 +233,9 @@ private fun TreeGridView(
158233
private fun TreeDetailView(
159234
tree: TreeData,
160235
onBack: () -> Unit,
161-
onSelect: () -> Unit
236+
onSelect: () -> Unit,
237+
isPurchased: Boolean,
238+
onPurchased: ()->Unit,
162239
) {
163240
Column(
164241
modifier = Modifier
@@ -171,9 +248,7 @@ private fun TreeDetailView(
171248
label = { Text("Go Back") },
172249
)
173250

174-
// Main Info
175251
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp),) {
176-
// Main Preview Image
177252
AsyncImage(
178253
model = "${Constants.cdn}/images/${tree.id}_${tree.variants - 1}_grid.png",
179254
contentDescription = null,
@@ -197,6 +272,13 @@ private fun TreeDetailView(
197272
text = tree.description,
198273
style = MaterialTheme.typography.bodyMedium,
199274
)
275+
if(!isPurchased) {
276+
Text(
277+
text = "Price: ${tree.basePrice}",
278+
style = MaterialTheme.typography.bodyMedium,
279+
color = MaterialTheme.colorScheme.error,
280+
)
281+
}
200282

201283
Spacer(Modifier.height(24.dp))
202284

@@ -214,14 +296,24 @@ private fun TreeDetailView(
214296

215297
Spacer(Modifier.height(32.dp))
216298

217-
// Action Button
218-
Button(
219-
onClick = onSelect,
220-
modifier = Modifier.fillMaxWidth().height(50.dp),
221-
shape = RoundedCornerShape(12.dp)
222-
) {
223-
Text("Select This Tree")
299+
if(isPurchased) {
300+
Button(
301+
onClick = onSelect,
302+
modifier = Modifier.fillMaxWidth().height(50.dp),
303+
shape = RoundedCornerShape(12.dp)
304+
) {
305+
Text("Select This Tree")
306+
}
307+
}else{
308+
Button(
309+
onClick = onPurchased,
310+
modifier = Modifier.fillMaxWidth().height(50.dp),
311+
shape = RoundedCornerShape(12.dp)
312+
) {
313+
Text("Buy Tree")
314+
}
224315
}
316+
225317
}
226318
}
227319

@@ -230,7 +322,8 @@ private fun TreeDetailView(
230322
private fun TreeGridItem(
231323
tree: TreeData,
232324
onClick: (TreeData) -> Unit,
233-
onLongClick: (TreeData) -> Unit
325+
onLongClick: (TreeData) -> Unit,
326+
isPurchased: Boolean
234327
) {
235328
val haptic = LocalHapticFeedback.current
236329

@@ -251,7 +344,8 @@ private fun TreeGridItem(
251344
Box(
252345
modifier = Modifier
253346
.size(90.dp)
254-
.padding(8.dp),
347+
.padding(8.dp)
348+
.alpha(if (isPurchased) 1f else 0.3f),
255349
contentAlignment = Alignment.Center
256350
) {
257351
AsyncImage(
@@ -268,7 +362,8 @@ private fun TreeGridItem(
268362
text = tree.name,
269363
style = MaterialTheme.typography.labelMedium,
270364
textAlign = TextAlign.Center,
271-
maxLines = 1,
365+
minLines = 2,
366+
maxLines = 2,
272367
overflow = TextOverflow.Ellipsis
273368
)
274369
}

composeApp/src/commonMain/kotlin/neth/iecal/trease/ui/screens/HomeScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ fun HomeScreen(navController: NavHostController) {
6363
if (isTreeSelectionVisible) {
6464
GrowTreeBottomSheet(
6565
onDismiss = { viewModel.toggleIsTreeSelectionVisible() },
66-
onSelected = {
66+
onAddToCanvas = {
6767
viewModel.selectTree(it)
6868
viewModel.toggleIsTreeSelectionVisible()
6969
},
7070
onShowWitheredTrees = {
7171
viewModel.toggleIsTreeSelectionVisible()
7272
viewModel.toggleIsWitheredTreeSelectionVisible()
73-
74-
}
73+
},
74+
viewModel
7575
)
7676
}
7777

composeApp/src/commonMain/kotlin/neth/iecal/trease/utils/CoinManager.kt

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
package neth.iecal.trease.utils
22

3-
import androidx.compose.runtime.State
4-
import androidx.compose.runtime.collectAsState
5-
import androidx.compose.runtime.rememberCoroutineScope
6-
import androidx.lifecycle.ViewModel
7-
import kotlinx.coroutines.CoroutineScope
8-
import kotlinx.coroutines.Dispatchers
9-
import kotlinx.coroutines.SupervisorJob
10-
import kotlinx.coroutines.flow.MutableStateFlow
11-
import kotlinx.coroutines.flow.StateFlow
12-
import kotlinx.coroutines.flow.asStateFlow
13-
import kotlinx.coroutines.launch
14-
153
class CoinManager(){
164
val cacheManager = CacheManager()
175

@@ -20,9 +8,13 @@ class CoinManager(){
208
val coins = reloadCoins()
219
cacheManager.saveFile("coins",(coins+value).toString())
2210
}
11+
suspend fun removeCoins(value: Int) {
12+
val coins = reloadCoins()
13+
cacheManager.saveFile("coins",(coins-value).toString())
14+
}
2315

2416
suspend fun reloadCoins(): Int{
25-
return cacheManager.readFile("coins")?.toInt() ?: 0
17+
return cacheManager.readFile("coins")?.toInt() ?: 40
2618

2719
}
2820

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package neth.iecal.trease.utils
2+
3+
import kotlinx.serialization.encodeToString
4+
import kotlinx.serialization.json.Json
5+
import kotlinx.serialization.json.internal.decodeStringToJsonTree
6+
import neth.iecal.trease.Constants
7+
8+
class TreePurchaseManager(){
9+
val cacheManager = CacheManager()
10+
11+
suspend fun addTree(treeId: String) {
12+
val trees = loadAllPurchasedTrees().toMutableList()
13+
trees.add(treeId)
14+
cacheManager.saveFile("purchased_trees", Json.encodeToString(trees))
15+
}
16+
17+
suspend fun loadAllPurchasedTrees(): List<String> {
18+
val treesRaw = cacheManager.readFile("purchased_trees")
19+
return if(treesRaw == null) {
20+
Constants.preOwnedTrees
21+
}else{
22+
Json.decodeFromString<List<String>>(treesRaw)
23+
}
24+
}
25+
26+
}

composeApp/src/commonMain/kotlin/neth/iecal/trease/viewmodels/HomeScreenViewModel.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ class HomeScreenViewModel : ViewModel() {
212212
}
213213

214214
}
215+
suspend fun useCoins(value:Int){
216+
coins.value -= value
217+
coinManager.removeCoins(value)
218+
}
215219
fun cleanTimerSession(){
216220
_timerStatus.value = if(_timerStatus.value == TimerStatus.HAS_QUIT)
217221
TimerStatus.POST_QUIT else TimerStatus.POST_WIN

0 commit comments

Comments
 (0)