Compare commits
No commits in common. "c5750e6b5ee7d12868cd6df502f41a2df78d6e5e" and "80cc72bb9d1cb3ea922efe4d9bca3726e2dfe475" have entirely different histories.
c5750e6b5e
...
80cc72bb9d
|
|
@ -0,0 +1 @@
|
|||
LivingAi_Lg
|
||||
|
|
@ -27,7 +27,6 @@ class UserNotFoundException(
|
|||
|
||||
class AuthApiClient(private val context: Context) {
|
||||
|
||||
private val route = "http://10.0.2.2:3000/"
|
||||
private val tokenManager = TokenManager(context)
|
||||
|
||||
val client = HttpClient(CIO) {
|
||||
|
|
@ -65,7 +64,7 @@ class AuthApiClient(private val context: Context) {
|
|||
|
||||
android.util.Log.d("AuthApiClient", "refreshTokens: Calling /auth/refresh endpoint")
|
||||
try {
|
||||
val response: RefreshResponse = client.post("${route}auth/refresh") {
|
||||
val response: RefreshResponse = client.post("http://10.0.2.2:3000/auth/refresh") {
|
||||
markAsRefreshTokenRequest()
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(RefreshRequest(refreshToken))
|
||||
|
|
@ -85,7 +84,7 @@ class AuthApiClient(private val context: Context) {
|
|||
}
|
||||
|
||||
defaultRequest {
|
||||
url(route)
|
||||
url("http://10.0.2.2:3000/")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,87 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.models.AnimalType
|
||||
|
||||
|
||||
@Composable
|
||||
fun AnimalTypeSelector(
|
||||
animalTypes: List<AnimalType>,
|
||||
selectedAnimalType: String?,
|
||||
onAnimalTypeSelected: (String) -> Unit
|
||||
) {
|
||||
val selectedAnimalType: String = selectedAnimalType ?: ""
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(animalTypes.size) { index ->
|
||||
AnimalTypeButton(
|
||||
animalType = animalTypes[index],
|
||||
isSelected = selectedAnimalType == animalTypes[index].id,
|
||||
onClick = { onAnimalTypeSelected(animalTypes[index].id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimalTypeButton(
|
||||
animalType: AnimalType,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(
|
||||
if (isSelected) Color(0xFFEDE9FE) else Color.White,
|
||||
RoundedCornerShape(24.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = animalType.emoji,
|
||||
fontSize = 24.sp
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = animalType.name,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.models.AnimalType
|
||||
|
||||
|
||||
@Composable
|
||||
fun AnimalTypeSelector(
|
||||
animalTypes: List<AnimalType>,
|
||||
selectedAnimalType: MutableState<String?>,
|
||||
onAnimalTypeSelected: (String) -> Unit
|
||||
) {
|
||||
val selectedAnimalType: String = selectedAnimalType.value ?: ""
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(animalTypes.size) { index ->
|
||||
AnimalTypeButton(
|
||||
animalType = animalTypes[index],
|
||||
isSelected = selectedAnimalType == animalTypes[index].id,
|
||||
onClick = { onAnimalTypeSelected(animalTypes[index].id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AnimalTypeButton(
|
||||
animalType: AnimalType,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick
|
||||
)
|
||||
.padding(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(
|
||||
if (isSelected) Color(0xFFEDE9FE) else Color.White,
|
||||
RoundedCornerShape(24.dp)
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = animalType.emoji,
|
||||
fontSize = 24.sp
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
text = animalType.name,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,103 +1,93 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.models.BottomNavItemData
|
||||
|
||||
@Composable
|
||||
fun BottomNavigationBar(
|
||||
modifier: Modifier = Modifier,
|
||||
items: List<BottomNavItemData>,
|
||||
currentItem: String,
|
||||
onItemClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(1.dp, Color(0xFF000000).copy(alpha = 0.1f))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(66.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
items.forEach { item ->
|
||||
val isSelected = item.label == currentItem
|
||||
BottomNavItem(
|
||||
label = item.label,
|
||||
iconRes = item.iconRes,
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (!isSelected) {
|
||||
onItemClick(item.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BottomNavItem(
|
||||
label: String,
|
||||
iconRes: Any,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
val color = if (selected) Color(0xFF1E88E5) else Color(0xFF0A0A0A)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick)
|
||||
) {
|
||||
if(iconRes is Int) {
|
||||
Icon(
|
||||
painter = painterResource(iconRes),
|
||||
contentDescription = label,
|
||||
tint = color,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
} else if(iconRes is ImageVector) {
|
||||
Icon(
|
||||
imageVector = iconRes,
|
||||
contentDescription = label,
|
||||
tint = color,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = color
|
||||
)
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.models.BottomNavItemData
|
||||
|
||||
@Composable
|
||||
fun BottomNavigationBar(
|
||||
modifier: Modifier = Modifier,
|
||||
items: List<BottomNavItemData>,
|
||||
currentItem: String,
|
||||
onItemClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.border(1.dp, Color(0xFF000000).copy(alpha = 0.1f))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(66.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
items.forEach { item ->
|
||||
val isSelected = item.label == currentItem
|
||||
BottomNavItem(
|
||||
label = item.label,
|
||||
iconRes = item.iconRes,
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
if (!isSelected) {
|
||||
onItemClick(item.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BottomNavItem(
|
||||
label: String,
|
||||
iconRes: Int,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
val color = if (selected) Color(0xFF1E88E5) else Color(0xFF0A0A0A)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = onClick)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(iconRes),
|
||||
contentDescription = label,
|
||||
tint = color,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = color
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +1,117 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
import com.example.livingai_lg.ui.theme.FarmTextDark
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DropdownInput(
|
||||
label: String? = null, // optional label
|
||||
selected: String,
|
||||
options: List<String>,
|
||||
expanded: Boolean,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
onSelect: (String) -> Unit,
|
||||
placeholder: String = "Select", // NEW - custom placeholder
|
||||
textColor: Color = Color.Black,
|
||||
modifier: Modifier = Modifier // NEW - allows width control
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier, // <-- now caller can control width
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
// Optional label
|
||||
// if (label != null) {
|
||||
Text(
|
||||
text = label?:" ",
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = FarmTextDark
|
||||
)
|
||||
// } else {
|
||||
// // Reserve label space so layout doesn’t shift
|
||||
// Spacer(modifier = Modifier.height(20.dp)) // ← same height as label text line
|
||||
// }
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { onExpandedChange(!expanded) }
|
||||
) {
|
||||
// Anchor box
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.shadow(2.dp, RoundedCornerShape(16.dp))
|
||||
.background(Color.White, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
onExpandedChange(true)
|
||||
}
|
||||
.padding(horizontal = 16.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
// Custom placeholder support
|
||||
Text(
|
||||
text = selected.ifEmpty { placeholder },
|
||||
fontSize = AppTypography.Body,
|
||||
color = if (selected.isEmpty()) Color(0xFF99A1AF) else textColor
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowDropDown,
|
||||
contentDescription = "Dropdown",
|
||||
tint = FarmTextDark
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Material3 Dropdown
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { onExpandedChange(false) },
|
||||
modifier = Modifier.background(Color.White)
|
||||
) {
|
||||
options.forEach { item ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(item, fontSize = AppTypography.Body, color = FarmTextDark)
|
||||
},
|
||||
onClick = {
|
||||
onSelect(item)
|
||||
onExpandedChange(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.theme.AppTypography
|
||||
import com.example.livingai_lg.ui.theme.FarmTextDark
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DropdownInput(
|
||||
label: String? = null, // optional label
|
||||
selected: String,
|
||||
options: List<String>,
|
||||
expanded: Boolean,
|
||||
onExpandedChange: (Boolean) -> Unit,
|
||||
onSelect: (String) -> Unit,
|
||||
placeholder: String = "Select", // NEW - custom placeholder
|
||||
modifier: Modifier = Modifier // NEW - allows width control
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier, // <-- now caller can control width
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
// Optional label
|
||||
if (label != null) {
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = AppTypography.Body,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = FarmTextDark
|
||||
)
|
||||
} else {
|
||||
// Reserve label space so layout doesn’t shift
|
||||
Spacer(modifier = Modifier.height(20.dp)) // ← same height as label text line
|
||||
}
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { onExpandedChange(!expanded) }
|
||||
) {
|
||||
// Anchor box
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.fillMaxWidth()
|
||||
.height(52.dp)
|
||||
.shadow(2.dp, RoundedCornerShape(16.dp))
|
||||
.background(Color.White, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(16.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
onExpandedChange(true)
|
||||
}
|
||||
.padding(horizontal = 16.dp),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
// Custom placeholder support
|
||||
Text(
|
||||
text = selected.ifEmpty { placeholder },
|
||||
fontSize = AppTypography.Body,
|
||||
color = if (selected.isEmpty()) Color(0xFF99A1AF) else FarmTextDark
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowDropDown,
|
||||
contentDescription = "Dropdown",
|
||||
tint = FarmTextDark
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Material3 Dropdown
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { onExpandedChange(false) },
|
||||
modifier = Modifier.background(Color.White)
|
||||
) {
|
||||
options.forEach { item ->
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(item, fontSize = AppTypography.Body, color = FarmTextDark)
|
||||
},
|
||||
onClick = {
|
||||
onSelect(item)
|
||||
onExpandedChange(false)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,77 +1,74 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.livingai_lg.ui.models.FiltersState
|
||||
import com.example.livingai_lg.ui.screens.FilterScreen
|
||||
|
||||
@Composable
|
||||
fun FilterOverlay(
|
||||
visible: Boolean,
|
||||
appliedFilters: FiltersState,
|
||||
onDismiss: () -> Unit,
|
||||
onSubmitClick: (filters: FiltersState) -> Unit = {},
|
||||
) {
|
||||
BackHandler(enabled = visible) { onDismiss() }
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
// Dimmed background
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.35f))
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onDismiss() }
|
||||
)
|
||||
}
|
||||
|
||||
// Sliding panel
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = slideInHorizontally(
|
||||
initialOffsetX = { it } // from right
|
||||
),
|
||||
exit = slideOutHorizontally(
|
||||
targetOffsetX = { it } // to right
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.align(Alignment.CenterEnd)
|
||||
) {
|
||||
FilterScreen(
|
||||
appliedFilters,
|
||||
onBackClick = onDismiss,
|
||||
onCancelClick = onDismiss,
|
||||
onSubmitClick = { filters ->
|
||||
onSubmitClick(filters)
|
||||
onDismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.example.livingai_lg.ui.screens.FilterScreen
|
||||
|
||||
@Composable
|
||||
fun FilterOverlay(
|
||||
visible: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onSubmitClick: () -> Unit = {},
|
||||
) {
|
||||
BackHandler(enabled = visible) { onDismiss() }
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
// Dimmed background
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.35f))
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onDismiss() }
|
||||
)
|
||||
}
|
||||
|
||||
// Sliding panel
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = slideInHorizontally(
|
||||
initialOffsetX = { it } // from right
|
||||
),
|
||||
exit = slideOutHorizontally(
|
||||
targetOffsetX = { it } // to right
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.align(Alignment.CenterEnd)
|
||||
) {
|
||||
FilterScreen(
|
||||
onBackClick = onDismiss,
|
||||
onCancelClick = onDismiss,
|
||||
onSubmitClick = {
|
||||
onSubmitClick()
|
||||
onDismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,142 +1,137 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun RangeFilter(
|
||||
modifier: Modifier = Modifier,
|
||||
|
||||
label: String,
|
||||
min: Int,
|
||||
max: Int,
|
||||
valueFrom: Int,
|
||||
valueTo: Int,
|
||||
modified: Boolean = false,
|
||||
onValueChange: (from: Int, to: Int) -> Unit,
|
||||
showSlider: Boolean = true,
|
||||
valueFormatter: (Int) -> String = { it.toString() }
|
||||
) {
|
||||
var fromValue by remember(valueFrom) { mutableStateOf(valueFrom) }
|
||||
var toValue by remember(valueTo) { mutableStateOf(valueTo) }
|
||||
|
||||
Column(modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
|
||||
// Label
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color(0xFF364153)
|
||||
)
|
||||
|
||||
// Slider (optional)
|
||||
if (showSlider) {
|
||||
RangeSlider(
|
||||
value = fromValue.toFloat()..toValue.toFloat(),
|
||||
onValueChange = { range ->
|
||||
fromValue = range.start.roundToInt()
|
||||
.coerceIn(min, toValue)
|
||||
toValue = range.endInclusive.roundToInt()
|
||||
.coerceIn(fromValue, max)
|
||||
|
||||
onValueChange(fromValue, toValue)
|
||||
},
|
||||
valueRange = min.toFloat()..max.toFloat(),
|
||||
colors = SliderDefaults.colors(
|
||||
thumbColor = Color(0xFFD9D9D9),
|
||||
activeTrackColor = Color(0xFFD9D9D9),
|
||||
inactiveTrackColor = Color(0xFFE5E7EB)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Pills
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RangePill(
|
||||
modifier = Modifier.weight(1f),
|
||||
value = fromValue,
|
||||
modified = modified,
|
||||
onValueChange = { newFrom ->
|
||||
val safeFrom = newFrom.coerceIn(min, toValue)
|
||||
fromValue = safeFrom
|
||||
onValueChange(safeFrom, toValue)
|
||||
},
|
||||
formatter = valueFormatter
|
||||
)
|
||||
|
||||
Text("to", fontSize = 15.sp)
|
||||
|
||||
RangePill(
|
||||
modifier = Modifier.weight(1f),
|
||||
value = toValue,
|
||||
modified = modified,
|
||||
onValueChange = { newTo ->
|
||||
val safeTo = newTo.coerceIn(fromValue, max)
|
||||
toValue = safeTo
|
||||
onValueChange(fromValue, safeTo)
|
||||
},
|
||||
formatter = valueFormatter
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RangePill(
|
||||
modifier: Modifier = Modifier,
|
||||
value: Int,
|
||||
modified: Boolean = false,
|
||||
onValueChange: (Int) -> Unit,
|
||||
formatter: (Int) -> String
|
||||
) {
|
||||
var text by remember(value) {
|
||||
mutableStateOf(formatter(value))
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(30.dp)
|
||||
.background(Color.White, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, Color(0x12000000), RoundedCornerShape(16.dp))
|
||||
.padding(horizontal = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
var modified = modified;
|
||||
BasicTextField(
|
||||
value = text,
|
||||
onValueChange = { input ->
|
||||
val digits = input.filter { it.isDigit() }
|
||||
text = digits
|
||||
digits.toIntOrNull()?.let(onValueChange)
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = if(modified) Color.Black else Color(0xFF99A1AF)
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun RangeFilter(
|
||||
modifier: Modifier = Modifier,
|
||||
|
||||
label: String,
|
||||
min: Int,
|
||||
max: Int,
|
||||
valueFrom: Int,
|
||||
valueTo: Int,
|
||||
onValueChange: (from: Int, to: Int) -> Unit,
|
||||
showSlider: Boolean = true,
|
||||
valueFormatter: (Int) -> String = { it.toString() }
|
||||
) {
|
||||
var fromValue by remember(valueFrom) { mutableStateOf(valueFrom) }
|
||||
var toValue by remember(valueTo) { mutableStateOf(valueTo) }
|
||||
|
||||
Column(modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
|
||||
// Label
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color(0xFF364153)
|
||||
)
|
||||
|
||||
// Slider (optional)
|
||||
if (showSlider) {
|
||||
RangeSlider(
|
||||
value = fromValue.toFloat()..toValue.toFloat(),
|
||||
onValueChange = { range ->
|
||||
fromValue = range.start.roundToInt()
|
||||
.coerceIn(min, toValue)
|
||||
toValue = range.endInclusive.roundToInt()
|
||||
.coerceIn(fromValue, max)
|
||||
|
||||
onValueChange(fromValue, toValue)
|
||||
},
|
||||
valueRange = min.toFloat()..max.toFloat(),
|
||||
colors = SliderDefaults.colors(
|
||||
thumbColor = Color(0xFFD9D9D9),
|
||||
activeTrackColor = Color(0xFFD9D9D9),
|
||||
inactiveTrackColor = Color(0xFFE5E7EB)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Pills
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RangePill(
|
||||
modifier = Modifier.weight(1f),
|
||||
value = fromValue,
|
||||
onValueChange = { newFrom ->
|
||||
val safeFrom = newFrom.coerceIn(min, toValue)
|
||||
fromValue = safeFrom
|
||||
onValueChange(safeFrom, toValue)
|
||||
},
|
||||
formatter = valueFormatter
|
||||
)
|
||||
|
||||
Text("to", fontSize = 15.sp)
|
||||
|
||||
RangePill(
|
||||
modifier = Modifier.weight(1f),
|
||||
value = toValue,
|
||||
onValueChange = { newTo ->
|
||||
val safeTo = newTo.coerceIn(fromValue, max)
|
||||
toValue = safeTo
|
||||
onValueChange(fromValue, safeTo)
|
||||
},
|
||||
formatter = valueFormatter
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RangePill(
|
||||
modifier: Modifier = Modifier,
|
||||
value: Int,
|
||||
onValueChange: (Int) -> Unit,
|
||||
formatter: (Int) -> String
|
||||
) {
|
||||
var text by remember(value) {
|
||||
mutableStateOf(formatter(value))
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(30.dp)
|
||||
.background(Color.White, RoundedCornerShape(16.dp))
|
||||
.border(1.dp, Color(0x12000000), RoundedCornerShape(16.dp))
|
||||
.padding(horizontal = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
BasicTextField(
|
||||
value = text,
|
||||
onValueChange = { input ->
|
||||
val digits = input.filter { it.isDigit() }
|
||||
text = digits
|
||||
digits.toIntOrNull()?.let(onValueChange)
|
||||
},
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = Color(0xFF99A1AF)
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
package com.example.livingai_lg.ui.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
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.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun WishlistNameOverlay(
|
||||
onSave: (String) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
var name by remember { mutableStateOf("") }
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = true,
|
||||
enter = slideInVertically { -it },
|
||||
exit = slideOutVertically { -it }
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color.White)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Text(
|
||||
text = "Save Filters",
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
placeholder = { Text("Wishlist name") },
|
||||
singleLine = true
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text("Cancel")
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if (name.isNotBlank()) {
|
||||
onSave(name)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,25 @@
|
|||
package com.example.livingai_lg.ui.models
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Build
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
|
||||
data class BottomNavItemData(
|
||||
val label: String,
|
||||
val iconRes: Any,
|
||||
val route: String,
|
||||
)
|
||||
|
||||
val mainBottomNavItems = listOf(
|
||||
BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS),
|
||||
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING),
|
||||
// TODO:
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
|
||||
BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
|
||||
BottomNavItemData("Mandi", R.drawable.ic_shop2, AppScreen.CREATE_PROFILE)
|
||||
)
|
||||
|
||||
val chatBottomNavItems = listOf(
|
||||
BottomNavItemData("Contacts", R.drawable.ic_home ,AppScreen.CONTACTS),
|
||||
BottomNavItemData("Calls", R.drawable.ic_tag, AppScreen.CALLS),
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
|
||||
)
|
||||
package com.example.livingai_lg.ui.models
|
||||
|
||||
import com.example.livingai_lg.R
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
|
||||
data class BottomNavItemData(
|
||||
val label: String,
|
||||
val iconRes: Int,
|
||||
val route: String,
|
||||
)
|
||||
|
||||
val mainBottomNavItems = listOf(
|
||||
BottomNavItemData("Home", R.drawable.ic_home , AppScreen.BUY_ANIMALS),
|
||||
BottomNavItemData("Sell", R.drawable.ic_tag, AppScreen.CREATE_ANIMAL_LISTING),
|
||||
// TODO:
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, AppScreen.CHATS),
|
||||
BottomNavItemData("Services", R.drawable.ic_config, AppScreen.CREATE_PROFILE),
|
||||
BottomNavItemData("Mandi", R.drawable.ic_market, AppScreen.CREATE_PROFILE)
|
||||
)
|
||||
|
||||
val chatBottomNavItems = listOf(
|
||||
BottomNavItemData("Contacts", R.drawable.ic_home ,"home"),
|
||||
BottomNavItemData("Calls", R.drawable.ic_tag, "sell"),
|
||||
BottomNavItemData("Chats", R.drawable.ic_chat, "chats"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
package com.example.livingai_lg.ui.models
|
||||
|
||||
data class TextFilter(
|
||||
val value: String = "",
|
||||
val filterSet: Boolean = false
|
||||
)
|
||||
|
||||
data class RangeFilterState(
|
||||
val min: Int,
|
||||
val max: Int,
|
||||
val filterSet: Boolean = false
|
||||
)
|
||||
|
||||
data class FiltersState(
|
||||
val animal: TextFilter = TextFilter(),
|
||||
val breed: TextFilter = TextFilter(),
|
||||
val distance: TextFilter = TextFilter(),
|
||||
val gender: TextFilter = TextFilter(),
|
||||
|
||||
val price: RangeFilterState = RangeFilterState(0, 90_000),
|
||||
val age: RangeFilterState = RangeFilterState(0, 20),
|
||||
val weight: RangeFilterState = RangeFilterState(0, 9_000),
|
||||
val milkYield: RangeFilterState = RangeFilterState(0, 900),
|
||||
|
||||
val pregnancyStatuses: Set<String> = emptySet(),
|
||||
|
||||
val calving: RangeFilterState = RangeFilterState(0, 10)
|
||||
)
|
||||
|
||||
|
||||
fun FiltersState.isDefault(): Boolean {
|
||||
return animal.filterSet.not() &&
|
||||
breed.filterSet.not() &&
|
||||
distance.filterSet.not() &&
|
||||
gender.filterSet.not() &&
|
||||
price.filterSet.not() &&
|
||||
age.filterSet.not() &&
|
||||
weight.filterSet.not() &&
|
||||
milkYield.filterSet.not() &&
|
||||
pregnancyStatuses.isEmpty() &&
|
||||
calving.filterSet.not()
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package com.example.livingai_lg.ui.models
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.UUID
|
||||
|
||||
data class WishlistEntry(
|
||||
val id: String = UUID.randomUUID().toString(),
|
||||
val name: String,
|
||||
val filters: FiltersState,
|
||||
val createdAt: Long = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
object WishlistStore {
|
||||
|
||||
private val _wishlist =
|
||||
MutableStateFlow<List<WishlistEntry>>(emptyList())
|
||||
|
||||
val wishlist: StateFlow<List<WishlistEntry>> = _wishlist
|
||||
|
||||
fun add(entry: WishlistEntry) {
|
||||
_wishlist.value = _wishlist.value + entry
|
||||
}
|
||||
|
||||
fun remove(id: String) {
|
||||
_wishlist.value = _wishlist.value.filterNot { it.id == id }
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
_wishlist.value = emptyList()
|
||||
}
|
||||
}
|
||||
|
|
@ -281,30 +281,30 @@ fun AppNavigation(
|
|||
)
|
||||
}
|
||||
|
||||
// composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||
// FilterScreen(
|
||||
// onBackClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
// onCancelClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||
FilterScreen(
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
onCancelClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||
// SortScreen(
|
||||
// onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
// onBackClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
// onCancelClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
//
|
||||
// )
|
||||
// }
|
||||
composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||
SortScreen(
|
||||
onApplyClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onCancelClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.SAVED_LISTINGS) {
|
||||
SavedListingsScreen(
|
||||
|
|
|
|||
|
|
@ -95,17 +95,17 @@ fun NavGraphBuilder.mainNavGraph(navController: NavController) {
|
|||
}
|
||||
|
||||
|
||||
// composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||
// FilterScreen(
|
||||
// onBackClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
// onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
// onCancelClick = {
|
||||
// navController.popBackStack()
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
composable(AppScreen.BUY_ANIMALS_FILTERS) {
|
||||
FilterScreen(
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSubmitClick = {navController.navigate(AppScreen.BUY_ANIMALS)},
|
||||
onCancelClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
composable(AppScreen.BUY_ANIMALS_SORT) {
|
||||
SortScreen(
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ fun BuyScreen(
|
|||
// Animal type filter buttons
|
||||
AnimalTypeSelector(
|
||||
animalTypes = animalTypes,
|
||||
selectedAnimalType =
|
||||
selectedAnimalType = selectedAnimalType
|
||||
) { }
|
||||
|
||||
// Ad space banner
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,336 +1,327 @@
|
|||
package com.example.farmmarketplace.ui.screens
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.CallMade
|
||||
import androidx.compose.material.icons.automirrored.filled.CallMissed
|
||||
import androidx.compose.material.icons.automirrored.filled.CallReceived
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.layout.BottomNavScaffold
|
||||
import com.example.livingai_lg.ui.models.chatBottomNavItems
|
||||
|
||||
data class CallRecord(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val callType: CallType,
|
||||
val duration: String,
|
||||
val timestamp: String,
|
||||
val isVideoCall: Boolean = false
|
||||
)
|
||||
|
||||
enum class CallType {
|
||||
INCOMING,
|
||||
OUTGOING,
|
||||
MISSED
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onCallClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onCallItemClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val callHistory = listOf(
|
||||
CallRecord(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
callType = CallType.INCOMING,
|
||||
duration = "5m 23s",
|
||||
timestamp = "Today, 2:30 PM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
callType = CallType.OUTGOING,
|
||||
duration = "2m 10s",
|
||||
timestamp = "Today, 11:45 AM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
callType = CallType.MISSED,
|
||||
duration = "",
|
||||
timestamp = "Yesterday, 8:15 PM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
callType = CallType.OUTGOING,
|
||||
duration = "8m 45s",
|
||||
timestamp = "Yesterday, 5:30 PM",
|
||||
isVideoCall = true
|
||||
),
|
||||
CallRecord(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
callType = CallType.INCOMING,
|
||||
duration = "12m 30s",
|
||||
timestamp = "2 days ago"
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
BottomNavScaffold(
|
||||
items = chatBottomNavItems,
|
||||
currentItem = "Calls",
|
||||
onBottomNavItemClick = onTabClick,
|
||||
) { paddingValues ->
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(paddingValues)
|
||||
) {
|
||||
CallsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onCallClick = onCallClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(callHistory) { call ->
|
||||
CallHistoryItem(
|
||||
call = call,
|
||||
onClick = { onCallItemClick(call.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ContactsBottomNav(
|
||||
// currentTab = ContactsTab.CALLS,
|
||||
// onTabClick = onTabClick
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onCallClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Calls",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Phone,
|
||||
contentDescription = "New Call",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onCallClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallHistoryItem(
|
||||
call: CallRecord,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(76.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = call.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = call.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = when (call.callType) {
|
||||
CallType.INCOMING -> Icons.AutoMirrored.Filled.CallReceived
|
||||
CallType.OUTGOING -> Icons.AutoMirrored.Filled.CallMade
|
||||
CallType.MISSED -> Icons.AutoMirrored.Filled.CallMissed
|
||||
},
|
||||
contentDescription = call.callType.name,
|
||||
tint = when (call.callType) {
|
||||
CallType.INCOMING -> Color(0xFF00A63E)
|
||||
CallType.OUTGOING -> Color(0xFF155DFC)
|
||||
CallType.MISSED -> Color(0xFFE7000B)
|
||||
},
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = when (call.callType) {
|
||||
CallType.INCOMING -> "Incoming • ${call.duration}"
|
||||
CallType.OUTGOING -> "Outgoing • ${call.duration}"
|
||||
CallType.MISSED -> "Missed"
|
||||
},
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = call.timestamp,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = if (call.isVideoCall) Icons.Default.Videocam else Icons.Default.Phone,
|
||||
contentDescription = if (call.isVideoCall) "Video Call" else "Voice Call",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.example.farmmarketplace.ui.screens
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.CallMade
|
||||
import androidx.compose.material.icons.automirrored.filled.CallMissed
|
||||
import androidx.compose.material.icons.automirrored.filled.CallReceived
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
data class CallRecord(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val callType: CallType,
|
||||
val duration: String,
|
||||
val timestamp: String,
|
||||
val isVideoCall: Boolean = false
|
||||
)
|
||||
|
||||
enum class CallType {
|
||||
INCOMING,
|
||||
OUTGOING,
|
||||
MISSED
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onCallClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onCallItemClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val callHistory = listOf(
|
||||
CallRecord(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
callType = CallType.INCOMING,
|
||||
duration = "5m 23s",
|
||||
timestamp = "Today, 2:30 PM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
callType = CallType.OUTGOING,
|
||||
duration = "2m 10s",
|
||||
timestamp = "Today, 11:45 AM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
callType = CallType.MISSED,
|
||||
duration = "",
|
||||
timestamp = "Yesterday, 8:15 PM"
|
||||
),
|
||||
CallRecord(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
callType = CallType.OUTGOING,
|
||||
duration = "8m 45s",
|
||||
timestamp = "Yesterday, 5:30 PM",
|
||||
isVideoCall = true
|
||||
),
|
||||
CallRecord(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
callType = CallType.INCOMING,
|
||||
duration = "12m 30s",
|
||||
timestamp = "2 days ago"
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
CallsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onCallClick = onCallClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(callHistory) { call ->
|
||||
CallHistoryItem(
|
||||
call = call,
|
||||
onClick = { onCallItemClick(call.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ContactsBottomNav(
|
||||
currentTab = ContactsTab.CALLS,
|
||||
onTabClick = onTabClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onCallClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Calls",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Phone,
|
||||
contentDescription = "New Call",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onCallClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallHistoryItem(
|
||||
call: CallRecord,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(76.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = call.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = call.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = when (call.callType) {
|
||||
CallType.INCOMING -> Icons.AutoMirrored.Filled.CallReceived
|
||||
CallType.OUTGOING -> Icons.AutoMirrored.Filled.CallMade
|
||||
CallType.MISSED -> Icons.AutoMirrored.Filled.CallMissed
|
||||
},
|
||||
contentDescription = call.callType.name,
|
||||
tint = when (call.callType) {
|
||||
CallType.INCOMING -> Color(0xFF00A63E)
|
||||
CallType.OUTGOING -> Color(0xFF155DFC)
|
||||
CallType.MISSED -> Color(0xFFE7000B)
|
||||
},
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = when (call.callType) {
|
||||
CallType.INCOMING -> "Incoming • ${call.duration}"
|
||||
CallType.OUTGOING -> "Outgoing • ${call.duration}"
|
||||
CallType.MISSED -> "Missed"
|
||||
},
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = call.timestamp,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = if (call.isVideoCall) Icons.Default.Videocam else Icons.Default.Phone,
|
||||
contentDescription = if (call.isVideoCall) "Video Call" else "Voice Call",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,341 +1,333 @@
|
|||
package com.example.livingai_lg.ui.screens.chat
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.farmmarketplace.ui.screens.ContactsBottomNav
|
||||
import com.example.farmmarketplace.ui.screens.ContactsTab
|
||||
import com.example.livingai_lg.ui.layout.BottomNavScaffold
|
||||
import com.example.livingai_lg.ui.models.chatBottomNavItems
|
||||
|
||||
data class ChatPreview(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val lastMessage: String,
|
||||
val timestamp: String,
|
||||
val isOnline: Boolean = false,
|
||||
val unreadCount: Int = 0
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ChatsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onNewChatClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onChatItemClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val chatList = listOf(
|
||||
ChatPreview(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
lastMessage = "The cows are healthy and ready for viewing",
|
||||
timestamp = "Today, 2:30 PM",
|
||||
isOnline = true,
|
||||
unreadCount = 2
|
||||
),
|
||||
ChatPreview(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
lastMessage = "You: Can you send more photos?",
|
||||
timestamp = "Today, 11:45 AM",
|
||||
isOnline = true,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
lastMessage = "What's the best time to visit?",
|
||||
timestamp = "Yesterday, 8:15 PM",
|
||||
isOnline = false,
|
||||
unreadCount = 1
|
||||
),
|
||||
ChatPreview(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
lastMessage = "You: Thanks for the information",
|
||||
timestamp = "Yesterday, 5:30 PM",
|
||||
isOnline = true,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
lastMessage = "The animal health check is complete",
|
||||
timestamp = "2 days ago",
|
||||
isOnline = false,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "6",
|
||||
name = "Market Vendor",
|
||||
initials = "MV",
|
||||
lastMessage = "You: Available this weekend?",
|
||||
timestamp = "3 days ago",
|
||||
isOnline = false,
|
||||
unreadCount = 0
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
BottomNavScaffold(
|
||||
items = chatBottomNavItems,
|
||||
currentItem = "Chats",
|
||||
onBottomNavItemClick = onTabClick,
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(paddingValues)
|
||||
) {
|
||||
ChatsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onNewChatClick = onNewChatClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(chatList) { chat ->
|
||||
ChatListItem(
|
||||
chat = chat,
|
||||
onClick = { onChatItemClick(chat.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ContactsBottomNav(
|
||||
// currentTab = ContactsTab.CHATS,
|
||||
// onTabClick = onTabClick
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onNewChatClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Chats",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "New Chat",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onNewChatClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatListItem(
|
||||
chat: ChatPreview,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(76.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = chat.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
|
||||
if (chat.isOnline) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.size(12.dp)
|
||||
.background(Color(0xFF00A63E), CircleShape)
|
||||
.border(2.dp, Color.White, CircleShape)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = chat.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Text(
|
||||
text = chat.lastMessage,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = chat.timestamp,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
|
||||
if (chat.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.background(Color(0xFF155DFC), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = chat.unreadCount.toString(),
|
||||
fontSize = 10.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color.White,
|
||||
lineHeight = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.example.livingai_lg.ui.screens.chat
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.farmmarketplace.ui.screens.ContactsBottomNav
|
||||
import com.example.farmmarketplace.ui.screens.ContactsTab
|
||||
|
||||
data class ChatPreview(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val lastMessage: String,
|
||||
val timestamp: String,
|
||||
val isOnline: Boolean = false,
|
||||
val unreadCount: Int = 0
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ChatsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onNewChatClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onChatItemClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val chatList = listOf(
|
||||
ChatPreview(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
lastMessage = "The cows are healthy and ready for viewing",
|
||||
timestamp = "Today, 2:30 PM",
|
||||
isOnline = true,
|
||||
unreadCount = 2
|
||||
),
|
||||
ChatPreview(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
lastMessage = "You: Can you send more photos?",
|
||||
timestamp = "Today, 11:45 AM",
|
||||
isOnline = true,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
lastMessage = "What's the best time to visit?",
|
||||
timestamp = "Yesterday, 8:15 PM",
|
||||
isOnline = false,
|
||||
unreadCount = 1
|
||||
),
|
||||
ChatPreview(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
lastMessage = "You: Thanks for the information",
|
||||
timestamp = "Yesterday, 5:30 PM",
|
||||
isOnline = true,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
lastMessage = "The animal health check is complete",
|
||||
timestamp = "2 days ago",
|
||||
isOnline = false,
|
||||
unreadCount = 0
|
||||
),
|
||||
ChatPreview(
|
||||
id = "6",
|
||||
name = "Market Vendor",
|
||||
initials = "MV",
|
||||
lastMessage = "You: Available this weekend?",
|
||||
timestamp = "3 days ago",
|
||||
isOnline = false,
|
||||
unreadCount = 0
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ChatsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onNewChatClick = onNewChatClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(chatList) { chat ->
|
||||
ChatListItem(
|
||||
chat = chat,
|
||||
onClick = { onChatItemClick(chat.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ContactsBottomNav(
|
||||
currentTab = ContactsTab.CHATS,
|
||||
onTabClick = onTabClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onNewChatClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Chats",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "New Chat",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onNewChatClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatListItem(
|
||||
chat: ChatPreview,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(76.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8), RoundedCornerShape(12.dp))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = chat.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
|
||||
if (chat.isOnline) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.size(12.dp)
|
||||
.background(Color(0xFF00A63E), CircleShape)
|
||||
.border(2.dp, Color.White, CircleShape)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = chat.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Text(
|
||||
text = chat.lastMessage,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.End,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = chat.timestamp,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
|
||||
if (chat.unreadCount > 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.background(Color(0xFF155DFC), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = chat.unreadCount.toString(),
|
||||
fontSize = 10.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color.White,
|
||||
lineHeight = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,432 +1,422 @@
|
|||
package com.example.farmmarketplace.ui.screens
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Chat
|
||||
import androidx.compose.material.icons.automirrored.filled.Message
|
||||
import androidx.compose.material.icons.automirrored.outlined.Message
|
||||
import androidx.compose.material.icons.filled.Chat
|
||||
import androidx.compose.material.icons.filled.Contacts
|
||||
import androidx.compose.material.icons.filled.Message
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.outlined.Phone
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.graphics.vector.path
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.layout.BottomNavScaffold
|
||||
import com.example.livingai_lg.ui.models.chatBottomNavItems
|
||||
import com.example.livingai_lg.ui.models.mainBottomNavItems
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
|
||||
data class Contact(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val status: String,
|
||||
val isOnline: Boolean = false,
|
||||
val phoneNumber: String? = null
|
||||
)
|
||||
|
||||
enum class ContactsTab {
|
||||
CONTACTS,
|
||||
CALLS,
|
||||
CHATS
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onSearchClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onContactClick: (String) -> Unit = {},
|
||||
onCallClick: (String) -> Unit = {},
|
||||
onMessageClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val contacts = listOf(
|
||||
Contact(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
status = "+91 98765 43211",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43211"
|
||||
),
|
||||
Contact(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
status = "+91 98765 43213",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43213"
|
||||
),
|
||||
Contact(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "6",
|
||||
name = "Farm Supply",
|
||||
initials = "FS",
|
||||
status = "+91 98765 43215",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43215"
|
||||
),
|
||||
Contact(
|
||||
id = "7",
|
||||
name = "Transport Co.",
|
||||
initials = "TC",
|
||||
status = "+91 98765 43216",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43216"
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
BottomNavScaffold(
|
||||
items = chatBottomNavItems,
|
||||
currentItem = "Contacts",
|
||||
onBottomNavItemClick = onTabClick,
|
||||
) { paddingValues ->
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(paddingValues)
|
||||
) {
|
||||
ContactsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onSearchClick = onSearchClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
items(contacts) { contact ->
|
||||
ContactItem(
|
||||
contact = contact,
|
||||
onContactClick = { onContactClick(contact.id) },
|
||||
onCallClick = { onCallClick(contact.id) },
|
||||
onMessageClick = { onMessageClick(contact.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ContactsBottomNav(
|
||||
// currentTab = ContactsTab.CONTACTS,
|
||||
// onTabClick = onTabClick
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onSearchClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Contacts",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = "Search",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onSearchClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactItem(
|
||||
contact: Contact,
|
||||
onContactClick: () -> Unit,
|
||||
onCallClick: () -> Unit,
|
||||
onMessageClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(73.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.05f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onContactClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = contact.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = contact.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Text(
|
||||
text = contact.status,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onCallClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Phone,
|
||||
contentDescription = "Call",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMessageClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.Message,
|
||||
contentDescription = "Message",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsBottomNav(
|
||||
currentTab: ContactsTab,
|
||||
onTabClick: (route: String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
.padding(horizontal = 32.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
ContactsTabItem(
|
||||
icon = Icons.AutoMirrored.Filled.Chat,
|
||||
label = "Chats",
|
||||
isSelected = currentTab == ContactsTab.CHATS,
|
||||
onClick = { onTabClick(AppScreen.CHATS) }
|
||||
)
|
||||
ContactsTabItem(
|
||||
icon = Icons.Default.Phone,
|
||||
label = "Calls",
|
||||
isSelected = currentTab == ContactsTab.CALLS,
|
||||
onClick = { onTabClick(AppScreen.CALLS) }
|
||||
)
|
||||
ContactsTabItem(
|
||||
icon = Icons.Default.Contacts,
|
||||
label = "Contacts",
|
||||
isSelected = currentTab == ContactsTab.CONTACTS,
|
||||
onClick = { onTabClick(AppScreen.CONTACTS) }
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsTabItem(
|
||||
icon: ImageVector,
|
||||
label: String,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 16.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
package com.example.farmmarketplace.ui.screens
|
||||
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Chat
|
||||
import androidx.compose.material.icons.automirrored.filled.Message
|
||||
import androidx.compose.material.icons.automirrored.outlined.Message
|
||||
import androidx.compose.material.icons.filled.Chat
|
||||
import androidx.compose.material.icons.filled.Contacts
|
||||
import androidx.compose.material.icons.filled.Message
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.outlined.Phone
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.graphics.vector.path
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.livingai_lg.ui.navigation.AppScreen
|
||||
|
||||
data class Contact(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val initials: String,
|
||||
val status: String,
|
||||
val isOnline: Boolean = false,
|
||||
val phoneNumber: String? = null
|
||||
)
|
||||
|
||||
enum class ContactsTab {
|
||||
CONTACTS,
|
||||
CALLS,
|
||||
CHATS
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsScreen(
|
||||
onBackClick: () -> Unit = {},
|
||||
onSearchClick: () -> Unit = {},
|
||||
onMenuClick: () -> Unit = {},
|
||||
onContactClick: (String) -> Unit = {},
|
||||
onCallClick: (String) -> Unit = {},
|
||||
onMessageClick: (String) -> Unit = {},
|
||||
onTabClick: (route: String) -> Unit = {}
|
||||
) {
|
||||
val contacts = listOf(
|
||||
Contact(
|
||||
id = "1",
|
||||
name = "Farmer Kumar",
|
||||
initials = "FK",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "2",
|
||||
name = "Seller Raj",
|
||||
initials = "SR",
|
||||
status = "+91 98765 43211",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43211"
|
||||
),
|
||||
Contact(
|
||||
id = "3",
|
||||
name = "Buyer Priya",
|
||||
initials = "BP",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "4",
|
||||
name = "Seller 1",
|
||||
initials = "S1",
|
||||
status = "+91 98765 43213",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43213"
|
||||
),
|
||||
Contact(
|
||||
id = "5",
|
||||
name = "Veterinarian",
|
||||
initials = "V",
|
||||
status = "Online",
|
||||
isOnline = true
|
||||
),
|
||||
Contact(
|
||||
id = "6",
|
||||
name = "Farm Supply",
|
||||
initials = "FS",
|
||||
status = "+91 98765 43215",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43215"
|
||||
),
|
||||
Contact(
|
||||
id = "7",
|
||||
name = "Transport Co.",
|
||||
initials = "TC",
|
||||
status = "+91 98765 43216",
|
||||
isOnline = false,
|
||||
phoneNumber = "+91 98765 43216"
|
||||
)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ContactsHeader(
|
||||
onBackClick = onBackClick,
|
||||
onSearchClick = onSearchClick,
|
||||
onMenuClick = onMenuClick
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
items(contacts) { contact ->
|
||||
ContactItem(
|
||||
contact = contact,
|
||||
onContactClick = { onContactClick(contact.id) },
|
||||
onCallClick = { onCallClick(contact.id) },
|
||||
onMessageClick = { onMessageClick(contact.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ContactsBottomNav(
|
||||
currentTab = ContactsTab.CONTACTS,
|
||||
onTabClick = onTabClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsHeader(
|
||||
onBackClick: () -> Unit,
|
||||
onSearchClick: () -> Unit,
|
||||
onMenuClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(26.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onBackClick() }
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Contacts",
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 42.sp
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = "Search",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onSearchClick() }
|
||||
)
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMenuClick() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactItem(
|
||||
contact: Contact,
|
||||
onContactClick: () -> Unit,
|
||||
onCallClick: () -> Unit,
|
||||
onMessageClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(73.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.05f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onContactClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.background(Color(0xFFE5E7EB), CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = contact.initials,
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 21.sp
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = contact.name,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF1E2939),
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = (-0.312).sp
|
||||
)
|
||||
|
||||
Text(
|
||||
text = contact.status,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = Color(0xFF717182),
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onCallClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Phone,
|
||||
contentDescription = "Call",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onMessageClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.Message,
|
||||
contentDescription = "Message",
|
||||
tint = Color(0xFF030213),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsBottomNav(
|
||||
currentTab: ContactsTab,
|
||||
onTabClick: (route: String) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(65.dp)
|
||||
.border(
|
||||
width = 1.078.dp,
|
||||
color = Color(0xFF000000).copy(alpha = 0.1f)
|
||||
)
|
||||
.background(Color(0xFFFCFBF8))
|
||||
.padding(horizontal = 32.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
ContactsTabItem(
|
||||
icon = Icons.AutoMirrored.Filled.Chat,
|
||||
label = "Chats",
|
||||
isSelected = currentTab == ContactsTab.CHATS,
|
||||
onClick = { onTabClick(AppScreen.CHATS) }
|
||||
)
|
||||
ContactsTabItem(
|
||||
icon = Icons.Default.Phone,
|
||||
label = "Calls",
|
||||
isSelected = currentTab == ContactsTab.CALLS,
|
||||
onClick = { onTabClick(AppScreen.CALLS) }
|
||||
)
|
||||
ContactsTabItem(
|
||||
icon = Icons.Default.Contacts,
|
||||
label = "Contacts",
|
||||
isSelected = currentTab == ContactsTab.CONTACTS,
|
||||
onClick = { onTabClick(AppScreen.CONTACTS) }
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsTabItem(
|
||||
icon: ImageVector,
|
||||
label: String,
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
indication = LocalIndication.current,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { onClick() }
|
||||
.padding(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
tint = Color(0xFF0A0A0A),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = Color(0xFF0A0A0A),
|
||||
lineHeight = 16.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue