diff --git a/androidApp/src/androidTest/java/com/mbta/tid/mbta_app/android/component/ErrorBannerTests.kt b/androidApp/src/androidTest/java/com/mbta/tid/mbta_app/android/component/ErrorBannerTests.kt
index 736485d83..b49d19e08 100644
--- a/androidApp/src/androidTest/java/com/mbta/tid/mbta_app/android/component/ErrorBannerTests.kt
+++ b/androidApp/src/androidTest/java/com/mbta/tid/mbta_app/android/component/ErrorBannerTests.kt
@@ -71,7 +71,7 @@ class ErrorBannerTests {
}
@Test
- fun testPredictionsStale() {
+ fun testPluralPredictionsStale() {
val staleRepo =
MockErrorBannerStateRepository(
state =
@@ -89,6 +89,25 @@ class ErrorBannerTests {
composeTestRule.onNodeWithText("Updated 2 minutes ago").assertExists()
}
+ @Test
+ fun testSinglePredictionsStale() {
+ val staleRepo =
+ MockErrorBannerStateRepository(
+ state =
+ ErrorBannerState.StalePredictions(
+ lastUpdated = Clock.System.now().minus(1.minutes),
+ action = {}
+ )
+ )
+ val staleVM = ErrorBannerViewModel(false, staleRepo, MockSettingsRepository())
+ composeTestRule.setContent {
+ LaunchedEffect(null) { staleVM.activate() }
+ ErrorBanner(staleVM)
+ }
+
+ composeTestRule.onNodeWithText("Updated 1 minute ago").assertExists()
+ }
+
@Test
fun testLoadingWhenPredictionsStale() {
val staleRepo =
diff --git a/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/ErrorBanner.kt b/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/ErrorBanner.kt
index d411d9919..1066d0e19 100644
--- a/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/ErrorBanner.kt
+++ b/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/ErrorBanner.kt
@@ -4,26 +4,29 @@ 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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material.icons.rounded.Refresh
-import androidx.compose.material3.Button
import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -40,7 +43,12 @@ fun ErrorBanner(vm: ErrorBannerViewModel) {
when (state) {
is ErrorBannerState.DataError -> {
ErrorCard(
- details = { Text(stringResource(R.string.error_loading_data)) },
+ details = {
+ Text(
+ stringResource(R.string.error_loading_data),
+ style = MaterialTheme.typography.headlineSmall
+ )
+ },
button = {
RefreshButton(label = stringResource(R.string.reload_data)) {
(state as ErrorBannerState.DataError).action()
@@ -53,10 +61,11 @@ fun ErrorBanner(vm: ErrorBannerViewModel) {
ErrorCard(
details = {
Row(verticalAlignment = Alignment.CenterVertically) {
- Icon(Icons.Rounded.Clear, contentDescription = "")
+ Icon(painterResource(R.drawable.wifi_slash), contentDescription = "")
Text(
stringResource(R.string.unable_to_connect),
- modifier = Modifier.padding(start = 8.dp)
+ modifier = Modifier.padding(start = 12.dp),
+ style = MaterialTheme.typography.headlineSmall
)
Spacer(Modifier.weight(1f))
}
@@ -70,14 +79,21 @@ fun ErrorBanner(vm: ErrorBannerViewModel) {
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.weight(1f))
- IndeterminateLoadingIndicator()
+ IndeterminateLoadingIndicator(Modifier.width(48.dp))
Spacer(modifier = Modifier.weight(1f))
}
} else {
ErrorCard(
details = {
val minutes = (state as ErrorBannerState.StalePredictions).minutesAgo()
- Text(stringResource(R.string.updated_mins_ago, minutes))
+ Text(
+ pluralStringResource(
+ R.plurals.updated_mins_ago,
+ minutes.toInt(),
+ minutes
+ ),
+ style = MaterialTheme.typography.headlineSmall
+ )
},
button = {
RefreshButton(label = stringResource(R.string.refresh_predictions)) {
@@ -98,14 +114,13 @@ private fun ErrorCard(details: @Composable () -> Unit, button: (@Composable () -
modifier =
Modifier.padding(16.dp)
.heightIn(60.dp)
- .background(Color.Gray.copy(alpha = 0.1f))
- .clip(RoundedCornerShape(15.dp)),
+ .background(Color.Gray.copy(alpha = 0.1f), shape = RoundedCornerShape(15.dp)),
verticalAlignment = Alignment.CenterVertically
) {
- Box(modifier = Modifier.padding(horizontal = 8.dp)) { details() }
+ Box(modifier = Modifier.padding(horizontal = 16.dp)) { details() }
Spacer(Modifier.weight(1f))
if (button != null) {
- Box(modifier = Modifier.padding(horizontal = 8.dp)) { button() }
+ Box(modifier = Modifier.padding(horizontal = 16.dp)) { button() }
}
}
}
@@ -116,7 +131,11 @@ private fun RefreshButton(
label: String = stringResource(R.string.refresh),
action: () -> Unit
) {
- Button(onClick = action) {
+ TextButton(
+ onClick = action,
+ contentPadding = PaddingValues(0.dp),
+ modifier = Modifier.height(20.dp).width(20.dp)
+ ) {
Box {
if (loading) {
IndeterminateLoadingIndicator()
@@ -124,7 +143,8 @@ private fun RefreshButton(
Icon(
Icons.Rounded.Refresh,
contentDescription = label,
- modifier = Modifier.width(20.dp)
+ modifier = Modifier.width(20.dp),
+ tint = Color.Unspecified
)
}
}
@@ -155,10 +175,14 @@ private fun ErrorBannerPreviews() {
LaunchedEffect(null) { dataErrorVM.activate() }
LaunchedEffect(null) { staleVM.activate() }
LaunchedEffect(null) { staleLoadingVM.activate() }
- Column(verticalArrangement = Arrangement.SpaceEvenly) {
+ Column(
+ modifier = Modifier.background(MaterialTheme.colorScheme.background),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
ErrorBanner(networkErrorVM)
ErrorBanner(dataErrorVM)
ErrorBanner(staleVM)
ErrorBanner(staleLoadingVM)
+ Spacer(modifier = Modifier.height(16.dp))
}
}
diff --git a/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/IndeterminateLoadingIndicator.kt b/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/IndeterminateLoadingIndicator.kt
index a26078641..d7e80ffc2 100644
--- a/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/IndeterminateLoadingIndicator.kt
+++ b/androidApp/src/main/java/com/mbta/tid/mbta_app/android/component/IndeterminateLoadingIndicator.kt
@@ -2,16 +2,17 @@ package com.mbta.tid.mbta_app.android.component
import androidx.compose.foundation.layout.width
import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
+import com.mbta.tid.mbta_app.android.R
@Composable
fun IndeterminateLoadingIndicator(modifier: Modifier = Modifier.width(20.dp)) {
CircularProgressIndicator(
modifier = modifier,
- color = MaterialTheme.colorScheme.secondary,
- trackColor = MaterialTheme.colorScheme.surfaceVariant,
+ color = colorResource(R.color.contrast),
+ trackColor = colorResource(R.color.contrast).copy(alpha = 0.25f),
)
}
diff --git a/androidApp/src/main/res/drawable/wifi_slash.xml b/androidApp/src/main/res/drawable/wifi_slash.xml
new file mode 100644
index 000000000..e10c2a9e6
--- /dev/null
+++ b/androidApp/src/main/res/drawable/wifi_slash.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/androidApp/src/main/res/values/plurals.xml b/androidApp/src/main/res/values/plurals.xml
new file mode 100644
index 000000000..731a23b93
--- /dev/null
+++ b/androidApp/src/main/res/values/plurals.xml
@@ -0,0 +1,7 @@
+
+
+
+ - Updated %d minute ago
+ - Updated %d minutes ago
+
+