asegurate de que subimos todo
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user