Add photo upload retry controls
This commit is contained in:
@@ -33,8 +33,8 @@ android {
|
|||||||
applicationId = "pl.firmatpp.kierowca"
|
applicationId = "pl.firmatpp.kierowca"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 26
|
versionCode = 28
|
||||||
versionName = "1.0.25"
|
versionName = "1.0.27"
|
||||||
setProperty("archivesBaseName", "pl.firmatpp.kierowca")
|
setProperty("archivesBaseName", "pl.firmatpp.kierowca")
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
@@ -68,6 +68,9 @@ android {
|
|||||||
release {
|
release {
|
||||||
signingConfig = signingConfigs.getByName("release")
|
signingConfig = signingConfigs.getByName("release")
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
ndk {
|
||||||
|
debugSymbolLevel = "SYMBOL_TABLE"
|
||||||
|
}
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"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.Navigation
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
import androidx.compose.material.icons.outlined.Phone
|
import androidx.compose.material.icons.outlined.Phone
|
||||||
|
import androidx.compose.material.icons.outlined.Refresh
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
@@ -645,9 +646,21 @@ private fun PhotoQueueItem(upload: PhotoUploadEntity, onRetryUpload: (PhotoUploa
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (status == PhotoUploadStatus.FailedRetryable) {
|
if (canRetryPhotoUpload(upload.status)) {
|
||||||
TextButton(onClick = { onRetryUpload(upload) }) {
|
Button(
|
||||||
Text("Ponów", color = TppColors.Forest, fontWeight = FontWeight.Bold)
|
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(
|
Column(
|
||||||
Modifier.align(Alignment.BottomStart).fillMaxWidth().background(Color.White.copy(alpha = 0.94f)).padding(10.dp),
|
Modifier.align(Alignment.BottomStart).fillMaxWidth().background(Color.White.copy(alpha = 0.94f)).padding(10.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
@@ -1592,16 +1624,6 @@ private fun PendingPhotoTile(
|
|||||||
trackColor = TppColors.Outline.copy(alpha = 0.4f),
|
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? =
|
fun confirmedUploadServerPhotoId(status: String, serverPhotoId: String?): String? =
|
||||||
serverPhotoId?.takeIf { status == "CONFIRMED" && it.isNotBlank() }
|
serverPhotoId?.takeIf { status == "CONFIRMED" && it.isNotBlank() }
|
||||||
|
|
||||||
|
fun canRetryPhotoUpload(status: String): Boolean =
|
||||||
|
status == "FAILED_RETRYABLE"
|
||||||
|
|
||||||
fun localUploadPreviewPhoto(upload: PhotoUploadEntity): RoutePhotoDto? {
|
fun localUploadPreviewPhoto(upload: PhotoUploadEntity): RoutePhotoDto? {
|
||||||
if (upload.localPath.isBlank()) return null
|
if (upload.localPath.isBlank()) return null
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,17 @@ class DriverUiRulesTest {
|
|||||||
assertEquals(null, confirmedUploadServerPhotoId("FAILED_RETRYABLE", "42"))
|
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
|
@Test
|
||||||
fun createsFullScreenPreviewForLocalUploadBeforeRouteRefresh() {
|
fun createsFullScreenPreviewForLocalUploadBeforeRouteRefresh() {
|
||||||
val photo = localUploadPreviewPhoto(upload(status = "CONFIRMED", serverPhotoId = "55"))
|
val photo = localUploadPreviewPhoto(upload(status = "CONFIRMED", serverPhotoId = "55"))
|
||||||
|
|||||||
Reference in New Issue
Block a user