package com.craigvg.lichun_android.ui.screens.home import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.craigvg.lichun_android.R import com.craigvg.lichun_android.ui.screens.home.components.* import com.craigvg.lichun_android.ui.theme.AppColors import com.craigvg.lichun_android.ui.theme.AppSpacing import com.craigvg.lichun_android.ui.theme.AppTypography import com.craigvg.lichun_android.utils.HapticFeedback import com.craigvg.lichun_android.viewmodel.GameStateViewModel import com.craigvg.lichun_android.viewmodel.PlayerViewModel /** * Home screen - Main dashboard * Ported from iOS HomeView.swift */ @Composable fun HomeScreen( gameStateViewModel: GameStateViewModel = hiltViewModel(), playerViewModel: PlayerViewModel = hiltViewModel(), onSettings: () -> Unit = {}, onMessages: () -> Unit = {}, onCharacters: () -> Unit = {}, onMore: () -> Unit = {}, onCharacterTap: () -> Unit = {}, onEnergyRefill: () -> Unit = {}, onDailyQuests: () -> Unit = {}, onDailyRewards: () -> Unit = {}, onLifeGoals: () -> Unit = {}, onPerformActivity: () -> Unit = {} ) { val person by playerViewModel.person.collectAsStateWithLifecycle() val player by playerViewModel.player.collectAsStateWithLifecycle() val lifeEvents by gameStateViewModel.lifeEvents.collectAsStateWithLifecycle() val isConnected by gameStateViewModel.isConnected.collectAsStateWithLifecycle() val formattedTime by gameStateViewModel.formattedTime.collectAsStateWithLifecycle() val season by gameStateViewModel.season.collectAsStateWithLifecycle() val seasonEmoji by gameStateViewModel.seasonEmoji.collectAsStateWithLifecycle() val date by gameStateViewModel.date.collectAsStateWithLifecycle() val gameSpeed by gameStateViewModel.gameSpeed.collectAsStateWithLifecycle() val energy by gameStateViewModel.energy.collectAsStateWithLifecycle() val money by gameStateViewModel.money.collectAsStateWithLifecycle() val diamonds by gameStateViewModel.diamonds.collectAsStateWithLifecycle() val health by gameStateViewModel.health.collectAsStateWithLifecycle() val happiness by gameStateViewModel.happiness.collectAsStateWithLifecycle() val intelligence by gameStateViewModel.intelligence.collectAsStateWithLifecycle() val prestige by gameStateViewModel.prestige.collectAsStateWithLifecycle() // What-now hub inputs. val currentQuestion by gameStateViewModel.currentQuestion.collectAsStateWithLifecycle() val currentMessageEvent by gameStateViewModel.currentMessageEvent.collectAsStateWithLifecycle() val unclaimedEventCount by gameStateViewModel.unclaimedEventCount.collectAsStateWithLifecycle() val dailyRewardState by gameStateViewModel.dailyRewardState.collectAsStateWithLifecycle() val dailyQuestsState by gameStateViewModel.dailyQuestsState.collectAsStateWithLifecycle() val scrollState = rememberScrollState() val view = LocalView.current val tooltipMessage = stringResource(R.string.tooltip_home_claim_rewards) LaunchedEffect(Unit) { gameStateViewModel.showTooltipIfNeeded("home_claim_rewards", tooltipMessage) } val hasPendingEvent = currentQuestion != null || currentMessageEvent != null val canClaimDailyReward = dailyRewardState?.let { it.canClaim && !it.todaysClaimed } ?: false val claimableQuestCount = dailyQuestsState?.quests?.count { it.canClaim } ?: 0 val conversationCount = player.activeConversations.size val hasAnythingToDo = hasPendingEvent || conversationCount > 0 || unclaimedEventCount > 0 || canClaimDailyReward || claimableQuestCount > 0 Column( modifier = Modifier .fillMaxSize() .background(AppColors.background) .verticalScroll(scrollState) .padding(horizontal = AppSpacing.md) ) { Spacer(modifier = Modifier.height(AppSpacing.md)) StatusHeaderCard( date = date, time = formattedTime, season = season, seasonEmoji = seasonEmoji, name = person.fullName, isConnected = isConnected, onCharacters = onCharacters, onMessages = onMessages, onSettings = onSettings ) Spacer(modifier = Modifier.height(AppSpacing.md)) ResourcesRow( energy = energy, money = money, diamonds = diamonds, onEnergyClick = onEnergyRefill ) Spacer(modifier = Modifier.height(AppSpacing.md)) MainCharacterView( imageUrl = person.image, name = person.fullName, age = person.ageYears, mood = person.mood, onTap = onCharacterTap ) Spacer(modifier = Modifier.height(AppSpacing.md)) // "What now?" hub — surface the things waiting for the player. When the // player is all caught up, an idle nudge points them at a useful action. if (hasAnythingToDo) { WhatNowHub( unclaimedEventCount = unclaimedEventCount, conversationCount = conversationCount, canClaimDailyReward = canClaimDailyReward, claimableQuestCount = claimableQuestCount, onMessages = onMessages, onDailyRewards = onDailyRewards, onDailyQuests = onDailyQuests, onClaimNudge = { HapticFeedback.light(view) } ) } else { IdleNudgeCard(onPerformActivity = onPerformActivity, onLifeGoals = onLifeGoals) } Spacer(modifier = Modifier.height(AppSpacing.md)) QuickStatsCard( health = health, happiness = happiness, intelligence = intelligence, prestige = prestige ) Spacer(modifier = Modifier.height(AppSpacing.md)) GameControlsCard( gameSpeed = gameSpeed, onSpeedChange = { speed -> gameStateViewModel.setSpeed(speed) }, onRestart = { gameStateViewModel.restart() } ) Spacer(modifier = Modifier.height(AppSpacing.md)) LifeTimelineCard( events = lifeEvents, onClaimEvent = { event -> gameStateViewModel.claimEvent(event) } ) Spacer(modifier = Modifier.height(AppSpacing.xl)) } } @Composable private fun WhatNowHub( unclaimedEventCount: Int, conversationCount: Int, canClaimDailyReward: Boolean, claimableQuestCount: Int, onMessages: () -> Unit, onDailyRewards: () -> Unit, onDailyQuests: () -> Unit, onClaimNudge: () -> Unit ) { androidx.compose.material3.Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = androidx.compose.material3.CardDefaults.cardColors(containerColor = AppColors.surfaceElevated), elevation = androidx.compose.material3.CardDefaults.cardElevation(defaultElevation = 2.dp) ) { Column(modifier = Modifier.padding(AppSpacing.md)) { Row(verticalAlignment = Alignment.CenterVertically) { androidx.compose.material3.Icon( Icons.Default.AutoAwesome, null, tint = AppColors.primary, modifier = Modifier.size(16.dp) ) Spacer(Modifier.width(AppSpacing.xs)) androidx.compose.material3.Text("What now?", style = AppTypography.bodyBold, color = AppColors.primaryText) } Spacer(Modifier.height(AppSpacing.xs)) if (unclaimedEventCount > 0) { WhatNowRow( icon = Icons.Default.CardGiftcard, tint = AppColors.primary, title = "Claim your rewards", subtitle = "$unclaimedEventCount life event${if (unclaimedEventCount == 1) "" else "s"} waiting below", onClick = onClaimNudge ) } if (conversationCount > 0) { WhatNowRow( icon = Icons.AutoMirrored.Filled.Chat, tint = AppColors.loveInterest, title = "Catch up on messages", subtitle = "$conversationCount conversation${if (conversationCount == 1) "" else "s"}", onClick = onMessages ) } if (canClaimDailyReward) { WhatNowRow( icon = Icons.Default.CalendarMonth, tint = AppColors.money, title = "Daily reward ready", subtitle = "Claim today's login bonus", onClick = onDailyRewards ) } if (claimableQuestCount > 0) { WhatNowRow( icon = Icons.Default.Checklist, tint = AppColors.happiness, title = "Finish daily quests", subtitle = "$claimableQuestCount reward${if (claimableQuestCount == 1) "" else "s"} to claim", onClick = onDailyQuests ) } } } } @Composable private fun WhatNowRow( icon: ImageVector, tint: Color, title: String, subtitle: String, onClick: () -> Unit ) { Row( modifier = Modifier .fillMaxWidth() .clickable(onClick = onClick) .padding(vertical = AppSpacing.xs), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(36.dp) .background(tint.copy(alpha = 0.15f), CircleShape), contentAlignment = Alignment.Center ) { androidx.compose.material3.Icon(icon, null, tint = tint, modifier = Modifier.size(16.dp)) } Spacer(Modifier.width(AppSpacing.sm)) Column(modifier = Modifier.weight(1f)) { androidx.compose.material3.Text(title, style = AppTypography.bodyBold, color = AppColors.primaryText) androidx.compose.material3.Text(subtitle, style = AppTypography.caption, color = AppColors.secondaryText, maxLines = 1) } androidx.compose.material3.Icon( Icons.AutoMirrored.Filled.KeyboardArrowRight, null, tint = AppColors.secondaryText.copy(alpha = 0.6f), modifier = Modifier.size(16.dp) ) } } @Composable private fun IdleNudgeCard( onPerformActivity: () -> Unit, onLifeGoals: () -> Unit ) { androidx.compose.material3.Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(AppSpacing.cornerRadius), colors = androidx.compose.material3.CardDefaults.cardColors(containerColor = AppColors.primary.copy(alpha = 0.08f)), elevation = androidx.compose.material3.CardDefaults.cardElevation(defaultElevation = 0.dp) ) { Column(modifier = Modifier.padding(AppSpacing.md)) { Row(verticalAlignment = Alignment.CenterVertically) { androidx.compose.material3.Icon( Icons.Default.Lightbulb, null, tint = AppColors.accentDark, modifier = Modifier.size(18.dp) ) Spacer(Modifier.width(AppSpacing.xs)) androidx.compose.material3.Text( "All caught up!", style = AppTypography.bodyBold, color = AppColors.primaryText ) } Spacer(Modifier.height(AppSpacing.xxs)) androidx.compose.material3.Text( "Take charge of your day - do an activity to grow your character, or check your life goals.", style = AppTypography.caption, color = AppColors.secondaryText ) Spacer(Modifier.height(AppSpacing.sm)) Row(horizontalArrangement = Arrangement.spacedBy(AppSpacing.sm)) { androidx.compose.material3.Button( onClick = onPerformActivity, modifier = Modifier.weight(1f), shape = RoundedCornerShape(AppSpacing.smallCornerRadius), colors = androidx.compose.material3.ButtonDefaults.buttonColors(containerColor = AppColors.primary, contentColor = Color.White) ) { androidx.compose.material3.Text("Do an Activity", style = AppTypography.captionBold) } androidx.compose.material3.OutlinedButton( onClick = onLifeGoals, modifier = Modifier.weight(1f), shape = RoundedCornerShape(AppSpacing.smallCornerRadius), colors = androidx.compose.material3.ButtonDefaults.outlinedButtonColors(contentColor = AppColors.primary) ) { androidx.compose.material3.Text("Life Goals", style = AppTypography.captionBold) } } } } }