Add photo upload retry controls
This commit is contained in:
@@ -33,8 +33,8 @@ android {
|
||||
applicationId = "pl.firmatpp.kierowca"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 26
|
||||
versionName = "1.0.25"
|
||||
versionCode = 28
|
||||
versionName = "1.0.27"
|
||||
setProperty("archivesBaseName", "pl.firmatpp.kierowca")
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -68,6 +68,9 @@ android {
|
||||
release {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = false
|
||||
ndk {
|
||||
debugSymbolLevel = "SYMBOL_TABLE"
|
||||
}
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
|
||||
@@ -61,6 +61,7 @@ import androidx.compose.material.icons.outlined.LocalShipping
|
||||
import androidx.compose.material.icons.outlined.Navigation
|
||||
import androidx.compose.material.icons.outlined.Person
|
||||
import androidx.compose.material.icons.outlined.Phone
|
||||
import androidx.compose.material.icons.outlined.Refresh
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
@@ -645,9 +646,21 @@ private fun PhotoQueueItem(upload: PhotoUploadEntity, onRetryUpload: (PhotoUploa
|
||||
)
|
||||
}
|
||||
}
|
||||
if (status == PhotoUploadStatus.FailedRetryable) {
|
||||
TextButton(onClick = { onRetryUpload(upload) }) {
|
||||
Text("Ponów", color = TppColors.Forest, fontWeight = FontWeight.Bold)
|
||||
if (canRetryPhotoUpload(upload.status)) {
|
||||
Button(
|
||||
onClick = { onRetryUpload(upload) },
|
||||
colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Outlined.Refresh,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text("Ponów", color = Color.White, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1573,6 +1586,25 @@ private fun PendingPhotoTile(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (canRetryPhotoUpload(upload.status)) {
|
||||
IconButton(
|
||||
onClick = { onRetry(upload) },
|
||||
enabled = !isDeleting,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(8.dp)
|
||||
.size(40.dp)
|
||||
.background(TppColors.Surface.copy(alpha = 0.96f), RoundedCornerShape(20.dp))
|
||||
.border(1.dp, TppColors.Forest.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Outlined.Refresh,
|
||||
contentDescription = "Ponów wysłanie zdjęcia",
|
||||
tint = TppColors.Forest,
|
||||
modifier = Modifier.size(20.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
Modifier.align(Alignment.BottomStart).fillMaxWidth().background(Color.White.copy(alpha = 0.94f)).padding(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
@@ -1592,16 +1624,6 @@ private fun PendingPhotoTile(
|
||||
trackColor = TppColors.Outline.copy(alpha = 0.4f),
|
||||
)
|
||||
}
|
||||
if (status == PhotoUploadStatus.FailedRetryable) {
|
||||
Button(
|
||||
onClick = { onRetry(upload) },
|
||||
modifier = Modifier.fillMaxWidth().height(40.dp),
|
||||
colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
) {
|
||||
Text("Ponów", color = Color.White, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ fun routePhotoSortEpochMillis(createdAt: String?, takenAt: String?, fallback: Lo
|
||||
fun confirmedUploadServerPhotoId(status: String, serverPhotoId: String?): String? =
|
||||
serverPhotoId?.takeIf { status == "CONFIRMED" && it.isNotBlank() }
|
||||
|
||||
fun canRetryPhotoUpload(status: String): Boolean =
|
||||
status == "FAILED_RETRYABLE"
|
||||
|
||||
fun localUploadPreviewPhoto(upload: PhotoUploadEntity): RoutePhotoDto? {
|
||||
if (upload.localPath.isBlank()) return null
|
||||
|
||||
|
||||
@@ -100,6 +100,17 @@ class DriverUiRulesTest {
|
||||
assertEquals(null, confirmedUploadServerPhotoId("FAILED_RETRYABLE", "42"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allowsRetryActionOnlyForRetryableFailedUploads() {
|
||||
assertTrue(canRetryPhotoUpload("FAILED_RETRYABLE"))
|
||||
assertFalse(canRetryPhotoUpload("PENDING"))
|
||||
assertFalse(canRetryPhotoUpload("UPLOADING"))
|
||||
assertFalse(canRetryPhotoUpload("VERIFYING"))
|
||||
assertFalse(canRetryPhotoUpload("CONFIRMED"))
|
||||
assertFalse(canRetryPhotoUpload("FAILED_PERMANENT"))
|
||||
assertFalse(canRetryPhotoUpload("CANCELLED"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createsFullScreenPreviewForLocalUploadBeforeRouteRefresh() {
|
||||
val photo = localUploadPreviewPhoto(upload(status = "CONFIRMED", serverPhotoId = "55"))
|
||||
|
||||
Reference in New Issue
Block a user