asegurate de que subimos todo

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 03:10:00 +02:00
parent 18b569b315
commit 929b08b355
@@ -3,22 +3,356 @@ package com.fnregistry.citas_kt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import fn.compose.theme.FnRadius
import fn.compose.theme.FnSpacing
import fn.compose.theme.FnTheme
import fn.compose.ui.FnAlert
import fn.compose.ui.FnAlertVariant
import fn.compose.ui.FnAppShell
import fn.compose.ui.FnAvatar
import fn.compose.ui.FnAvatarSize
import fn.compose.ui.FnBadge
import fn.compose.ui.FnBadgeColor
import fn.compose.ui.FnButton
import fn.compose.ui.FnButtonVariant
import fn.compose.ui.FnCard
import fn.compose.ui.FnDialog
import fn.compose.ui.FnEmptyState
import fn.compose.ui.FnGroup
import fn.compose.ui.FnPageHeader
import fn.compose.ui.FnStack
import fn.compose.ui.FnTabs
import fn.compose.ui.FnText
import fn.compose.ui.FnTextSize
import fn.compose.ui.FnTitle
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
FnTheme {
Surface(modifier = Modifier.fillMaxSize()) {
Text("citas_kt ready")
Surface(modifier = Modifier.fillMaxSize()) { CitasApp() }
}
}
}
}
data class Profile(
val name: String,
val age: Int,
val city: String,
val bio: String,
val interests: List<String>,
val gradient: List<Color>,
)
data class Match(val profile: Profile, val lastMessage: String, val unread: Int)
private val PROFILES = listOf(
Profile(
"Lucia", 27, "Madrid",
"Disenadora UX. Cafe de especialidad, montana y libros de no ficcion.",
listOf("Diseno", "Senderismo", "Lectura", "Cafe"),
listOf(Color(0xFFFF6B9D), Color(0xFFC06CFF)),
),
Profile(
"Marcos", 31, "Barcelona",
"Cocinero apasionado de la pasta fresca. Busco compania para descubrir mercados.",
listOf("Cocina", "Cine", "Viajes"),
listOf(Color(0xFF42A5F5), Color(0xFF26C6DA)),
),
Profile(
"Sofia", 24, "Valencia",
"Estudiante de biologia marina. Surf los domingos y plantas de interior.",
listOf("Surf", "Naturaleza", "Yoga", "Plantas"),
listOf(Color(0xFF66BB6A), Color(0xFF26A69A)),
),
Profile(
"Hugo", 29, "Bilbao",
"Ingeniero de software por el dia, musico amateur por la noche. Toco guitarra.",
listOf("Musica", "Tech", "Cervezas", "Pintxos"),
listOf(Color(0xFFFFB74D), Color(0xFFFF7043)),
),
)
private val MATCHES = listOf(
Match(PROFILES[0], "Que te parece quedar el sabado para tomar algo?", 2),
Match(PROFILES[2], "Jajaja eres muy gracioso, dime mas", 0),
Match(PROFILES[3], "Te paso la cancion que te decia", 1),
)
@Composable
private fun CitasApp() {
var tab by remember { mutableStateOf(0) }
FnAppShell(title = "Latido") { padding ->
FnStack(
modifier = Modifier.padding(padding).fillMaxSize(),
gap = FnSpacing.xs,
) {
FnTabs(
tabs = listOf("Descubrir", "Matches", "Chats", "Perfil"),
selectedIndex = tab,
onTabSelected = { tab = it },
scrollable = false,
)
Box(
modifier = Modifier
.padding(horizontal = FnSpacing.md, vertical = FnSpacing.sm)
.fillMaxSize(),
) {
when (tab) {
0 -> DiscoverTab()
1 -> MatchesTab()
2 -> ChatsTab()
3 -> ProfileTab()
}
}
}
}
}
@Composable
private fun DiscoverTab() {
var index by remember { mutableStateOf(0) }
var liked by remember { mutableStateOf<Profile?>(null) }
var passed by remember { mutableStateOf(0) }
var likes by remember { mutableStateOf(0) }
if (index >= PROFILES.size) {
FnEmptyState(
title = "No quedan perfiles cerca",
description = "Has visto $passed pasados y $likes likes. Vuelve manana.",
icon = "💔",
action = {
FnButton(text = "Reiniciar", onClick = {
index = 0; passed = 0; likes = 0
})
},
)
return
}
val p = PROFILES[index]
FnStack(modifier = Modifier.fillMaxSize(), gap = FnSpacing.md) {
FnCard(modifier = Modifier.fillMaxWidth(), padding = PaddingValues(0.dp)) {
FnStack(gap = FnSpacing.sm) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(280.dp)
.clip(RoundedCornerShape(topStart = FnRadius.md, topEnd = FnRadius.md))
.background(Brush.linearGradient(p.gradient)),
contentAlignment = Alignment.Center,
) {
FnAvatar(
initials = p.name.take(2).uppercase(),
size = FnAvatarSize.Lg,
backgroundColor = Color.White.copy(alpha = 0.25f),
textColor = Color.White,
)
}
FnStack(
modifier = Modifier.padding(horizontal = FnSpacing.md, vertical = FnSpacing.sm),
gap = FnSpacing.xs,
) {
FnTitle(text = "${p.name}, ${p.age}", order = 2)
FnText(text = "📍 ${p.city}", size = FnTextSize.Sm)
FnText(text = p.bio, size = FnTextSize.Sm)
FnGroup(gap = FnSpacing.xs) {
p.interests.forEach { tag ->
FnBadge(text = tag, color = FnBadgeColor.Brand)
}
}
}
}
}
FnGroup(
modifier = Modifier.fillMaxWidth(),
arrangement = Arrangement.SpaceEvenly,
) {
FnButton(
text = "✕ Pasar",
onClick = { passed++; index++ },
variant = FnButtonVariant.Outlined,
)
FnButton(
text = "⭐ Super",
onClick = { liked = p; likes++; index++ },
variant = FnButtonVariant.Secondary,
)
FnButton(
text = "♥ Like",
onClick = { liked = p; likes++; index++ },
variant = FnButtonVariant.Filled,
)
}
FnText(
text = "${likes} likes · ${passed} pasados",
size = FnTextSize.Xs,
align = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)
}
FnDialog(
open = liked != null,
onDismiss = { liked = null },
title = "Es un match!",
description = "A ${liked?.name} tambien le gustas. Escribele ya.",
confirmText = "Enviar mensaje",
cancelText = "Seguir viendo",
onConfirm = { liked = null },
)
}
@Composable
private fun MatchesTab() {
FnStack(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
gap = FnSpacing.md,
) {
FnPageHeader(title = "Tus matches", subtitle = "${MATCHES.size} personas conectadas")
MATCHES.forEach { m ->
FnCard(modifier = Modifier.fillMaxWidth()) {
FnGroup(modifier = Modifier.fillMaxWidth(), gap = FnSpacing.md) {
FnAvatar(
initials = m.profile.name.take(2).uppercase(),
size = FnAvatarSize.Md,
backgroundColor = m.profile.gradient.first(),
textColor = Color.White,
)
FnStack(modifier = Modifier.weight(1f), gap = 4.dp) {
FnText(text = "${m.profile.name}, ${m.profile.age}", size = FnTextSize.Md)
FnText(text = m.profile.city, size = FnTextSize.Xs)
}
if (m.unread > 0) {
FnBadge(text = "${m.unread}", color = FnBadgeColor.Brand)
}
}
}
}
}
}
@Composable
private fun ChatsTab() {
FnStack(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
gap = FnSpacing.md,
) {
FnPageHeader(title = "Mensajes", subtitle = "Conversaciones activas")
if (MATCHES.isEmpty()) {
FnEmptyState(
title = "Sin mensajes todavia",
description = "Cuando alguien te de like apareceran aqui.",
icon = "💬",
)
return@FnStack
}
MATCHES.forEach { m ->
FnCard(modifier = Modifier.fillMaxWidth()) {
FnGroup(modifier = Modifier.fillMaxWidth(), gap = FnSpacing.md) {
FnAvatar(
initials = m.profile.name.take(2).uppercase(),
size = FnAvatarSize.Md,
backgroundColor = m.profile.gradient.first(),
textColor = Color.White,
)
FnStack(modifier = Modifier.weight(1f), gap = 4.dp) {
FnGroup(modifier = Modifier.fillMaxWidth(), gap = FnSpacing.xs) {
FnText(text = m.profile.name, size = FnTextSize.Md, modifier = Modifier.weight(1f))
if (m.unread > 0) FnBadge(text = "${m.unread}", color = FnBadgeColor.Brand)
}
FnText(text = m.lastMessage, size = FnTextSize.Sm)
}
}
}
}
}
}
@Composable
private fun ProfileTab() {
FnStack(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
gap = FnSpacing.md,
) {
FnCard(modifier = Modifier.fillMaxWidth()) {
FnStack(
modifier = Modifier.fillMaxWidth(),
gap = FnSpacing.sm,
align = Alignment.CenterHorizontally,
) {
FnAvatar(
initials = "YO",
size = FnAvatarSize.Lg,
backgroundColor = Color(0xFFEC407A),
textColor = Color.White,
)
FnTitle(text = "Tu, 28", order = 2)
FnText(text = "Madrid · Premium", size = FnTextSize.Sm)
FnGroup(gap = FnSpacing.xs) {
FnBadge(text = "Verificado", color = FnBadgeColor.Green)
FnBadge(text = "Premium", color = FnBadgeColor.Brand)
}
}
}
FnAlert(
title = "Completa tu perfil",
message = "Anade 2 fotos mas y duplica tus matches.",
variant = FnAlertVariant.Info,
)
FnCard(modifier = Modifier.fillMaxWidth()) {
FnStack(gap = FnSpacing.sm) {
FnTitle(text = "Estadisticas", order = 3)
FnGroup(modifier = Modifier.fillMaxWidth(), arrangement = Arrangement.SpaceBetween) {
Stat("Likes dados", "47")
Stat("Matches", "${MATCHES.size}")
Stat("Vistas", "128")
}
}
}
FnButton(
text = "Cerrar sesion",
onClick = {},
variant = FnButtonVariant.Outlined,
modifier = Modifier.fillMaxWidth(),
)
}
}
@Composable
private fun Stat(label: String, value: String) {
FnStack(gap = 4.dp, align = Alignment.CenterHorizontally) {
FnTitle(text = value, order = 3, color = MaterialTheme.colorScheme.primary)
FnText(text = label, size = FnTextSize.Xs)
}
}