Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,72 +46,28 @@ import java.util.Locale
/**
* Card component that displays the result of a completed rover mission.
* Shows final position, success status, and timestamp with expandable original input.
*
* @param missionResult The mission result data to display
* @param modifier Optional modifier for styling
*/
@Suppress("CyclomaticComplexMethod")
@Composable
fun MissionResultCard(
missionResult: MissionResult,
modifier: Modifier = Modifier,
) {
val dateFormatter = SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.getDefault())
val formattedDate = dateFormatter.format(Date(missionResult.timestamp))
var isInputExpanded by remember { mutableStateOf(false) }

MarsCard(
title = stringResource(R.string.mission_result),
modifier = modifier,
contentDescription = stringResource(R.string.cd_mission_card)
) {
// Status row with icon
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Icon(
imageVector =
if (missionResult.isSuccess) {
Icons.Default.CheckCircle
} else {
Icons.Default.Warning
},
contentDescription =
if (missionResult.isSuccess) {
stringResource(R.string.cd_mission_success)
} else {
stringResource(R.string.cd_mission_failed)
},
tint =
if (missionResult.isSuccess) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.error
},
modifier = Modifier.size(24.dp)
)

Spacer(modifier = Modifier.width(8.dp))

Text(
text =
if (missionResult.isSuccess) {
stringResource(R.string.mission_completed_successfully)
} else {
stringResource(R.string.mission_failed)
},
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color =
if (missionResult.isSuccess) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.error
}
)
}
MissionStatusRow(isSuccess = missionResult.isSuccess)

Spacer(modifier = Modifier.height(6.dp))

// Final position
// Final position and mission details
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Expand All @@ -124,60 +80,7 @@ fun MissionResultCard(
// Show original input if available
missionResult.originalInput?.let { input ->
Spacer(modifier = Modifier.height(8.dp))

// Input label with expand/collapse button
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.mission_instructions),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)

if (input.length > Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
IconButton(
onClick = { isInputExpanded = !isInputExpanded },
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = if (isInputExpanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown,
contentDescription =
if (isInputExpanded) {
stringResource(
R.string.cd_collapse_input
)
} else {
stringResource(R.string.cd_expand_input)
},
tint = MaterialTheme.colorScheme.primary
)
}
}
}

// Input text - expandable
Text(
text =
if (isInputExpanded || input.length <= Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
input
} else {
input.take(Constants.UI.MAX_INPUT_PREVIEW_LENGTH) + "..."
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = if (isInputExpanded) Int.MAX_VALUE else 2,
overflow = if (isInputExpanded) TextOverflow.Visible else TextOverflow.Ellipsis,
modifier =
if (input.length > Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
Modifier.clickable { isInputExpanded = !isInputExpanded }
} else {
Modifier
}
)
ExpandableInputSection(input = input)
}

// Completed timestamp - right aligned, placed after mission instructions
Expand All @@ -193,6 +96,133 @@ fun MissionResultCard(
}
}

/**
* Displays the mission status (success/failure) with appropriate icon and text.
*
* @param isSuccess Whether the mission was successful
* @param modifier Optional modifier for styling
*/
@Composable
private fun MissionStatusRow(
isSuccess: Boolean,
modifier: Modifier = Modifier,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier.fillMaxWidth()
) {
Icon(
imageVector =
if (isSuccess) {
Icons.Default.CheckCircle
} else {
Icons.Default.Warning
},
contentDescription =
if (isSuccess) {
stringResource(R.string.cd_mission_success)
} else {
stringResource(R.string.cd_mission_failed)
},
tint =
if (isSuccess) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.error
},
modifier = Modifier.size(24.dp)
)

Spacer(modifier = Modifier.width(8.dp))

Text(
text =
if (isSuccess) {
stringResource(R.string.mission_completed_successfully)
} else {
stringResource(R.string.mission_failed)
},
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
color =
if (isSuccess) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.error
}
)
}
}

/**
* Displays mission input instructions with expand/collapse functionality for long inputs.
*
* @param input The input string to display
* @param modifier Optional modifier for styling
*/
@Composable
private fun ExpandableInputSection(
input: String,
modifier: Modifier = Modifier,
) {
var isInputExpanded by remember { mutableStateOf(false) }

Column(modifier = modifier) {
// Input label with expand/collapse button
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.mission_instructions),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)

// Show expand/collapse button only for long inputs
if (input.length > Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
IconButton(
onClick = { isInputExpanded = !isInputExpanded },
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = if (isInputExpanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown,
contentDescription =
if (isInputExpanded) {
stringResource(R.string.cd_collapse_input)
} else {
stringResource(R.string.cd_expand_input)
},
tint = MaterialTheme.colorScheme.primary
)
}
}
}

// Input text - expandable for long content
Text(
text =
if (isInputExpanded || input.length <= Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
input
} else {
input.take(Constants.UI.MAX_INPUT_PREVIEW_LENGTH) + "..."
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = if (isInputExpanded) Int.MAX_VALUE else 2,
overflow = if (isInputExpanded) TextOverflow.Visible else TextOverflow.Ellipsis,
modifier =
if (input.length > Constants.UI.MAX_INPUT_PREVIEW_LENGTH) {
Modifier.clickable { isInputExpanded = !isInputExpanded }
} else {
Modifier
}
)
}
}

@Preview(name = "Mission Result Card - Success", showBackground = true)
@Composable
private fun MissionResultCardSuccessPreview() {
Expand Down
Loading