feat: server-presence indicator on Downloads tab
- CredentialStore tracks uploaded filenames in prefs - UploadWorker success marks file as uploaded - Downloads tab shows green CloudDone icon next to files already on server - Upload button skipped for files already on server (dedup)
This commit is contained in:
@@ -94,6 +94,10 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
|
||||
private val _uploadProgress = MutableStateFlow<Map<String, ActiveDownload>>(emptyMap())
|
||||
val uploadProgress: StateFlow<Map<String, ActiveDownload>> = _uploadProgress.asStateFlow()
|
||||
|
||||
// Files already on server (filename set)
|
||||
private val _uploadedNames = MutableStateFlow<Set<String>>(emptySet())
|
||||
val uploadedNames: StateFlow<Set<String>> = _uploadedNames.asStateFlow()
|
||||
|
||||
// USB dashcam state
|
||||
private val _dashcamFiles = MutableStateFlow<List<DashcamFile>>(emptyList())
|
||||
val dashcamFiles: StateFlow<List<DashcamFile>> = _dashcamFiles.asStateFlow()
|
||||
@@ -117,6 +121,7 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
|
||||
init {
|
||||
loadVideos()
|
||||
scanDownloads()
|
||||
_uploadedNames.value = CredentialStore.getUploaded(getApplication())
|
||||
}
|
||||
|
||||
fun loadVideos() {
|
||||
@@ -318,6 +323,8 @@ class DashcamViewModel(application: Application) : AndroidViewModel(application)
|
||||
|
||||
when (info.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
CredentialStore.markUploaded(getApplication(), file.name)
|
||||
_uploadedNames.value = CredentialStore.getUploaded(getApplication())
|
||||
viewModelScope.launch { _downloadEvents.emit("✓ Uploaded: ${file.name}") }
|
||||
_uploadProgress.value = _uploadProgress.value - file.name
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CloudUpload
|
||||
import androidx.compose.material.icons.filled.CloudDone
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
@@ -61,6 +62,7 @@ fun DownloadsScreen(
|
||||
activeUploads: Map<String, ActiveDownload>,
|
||||
deletingNames: Set<String>,
|
||||
credentialsConfigured: Boolean,
|
||||
uploadedNames: Set<String>,
|
||||
onDelete: (DownloadedFile) -> Unit,
|
||||
onCancel: (ActiveDownload) -> Unit,
|
||||
onUpload: (DownloadedFile) -> Unit,
|
||||
@@ -214,7 +216,13 @@ fun DownloadsScreen(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(file.name, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(file.name, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f, fill = false))
|
||||
if (file.name in uploadedNames) {
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Icon(Icons.Filled.CloudDone, "On server", tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(16.dp))
|
||||
}
|
||||
}
|
||||
Text(formatSize(file.size), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
if (isDeleting) {
|
||||
|
||||
@@ -62,6 +62,7 @@ fun YarisMainScreen(client: OkHttpClient) {
|
||||
val activeDownloads by dashcamVm.activeDownloads.collectAsState()
|
||||
val deletingNames by dashcamVm.deletingNames.collectAsState()
|
||||
val uploadProgress by dashcamVm.uploadProgress.collectAsState()
|
||||
val uploadedNames by dashcamVm.uploadedNames.collectAsState()
|
||||
val dashcamFiles by dashcamVm.dashcamFiles.collectAsState()
|
||||
val dashcamTotalSize by dashcamVm.dashcamTotalSize.collectAsState()
|
||||
val dashcamStatus by dashcamVm.dashcamStatus.collectAsState()
|
||||
@@ -129,6 +130,7 @@ fun YarisMainScreen(client: OkHttpClient) {
|
||||
activeUploads = uploadProgress,
|
||||
deletingNames = deletingNames,
|
||||
credentialsConfigured = credentialsConfigured,
|
||||
uploadedNames = uploadedNames,
|
||||
onDelete = { dashcamVm.deleteDownloaded(it) },
|
||||
onCancel = { dashcamVm.cancelDownload(it) },
|
||||
onUpload = { dashcamVm.uploadFile(it) },
|
||||
|
||||
@@ -7,6 +7,7 @@ object CredentialStore {
|
||||
private const val KEY_SERVER_URL = "server_url"
|
||||
private const val KEY_DEVICE_NAME = "device_name"
|
||||
private const val KEY_API_KEY = "api_key"
|
||||
private const val KEY_UPLOADED = "uploaded_files"
|
||||
|
||||
private fun prefs(context: Context) =
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
@@ -25,4 +26,13 @@ object CredentialStore {
|
||||
.putString(KEY_API_KEY, apiKey)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun markUploaded(context: Context, filename: String) {
|
||||
val set = getUploaded(context).toMutableSet()
|
||||
set.add(filename)
|
||||
prefs(context).edit().putStringSet(KEY_UPLOADED, set).apply()
|
||||
}
|
||||
|
||||
fun getUploaded(context: Context): Set<String> =
|
||||
prefs(context).getStringSet(KEY_UPLOADED, emptySet()) ?: emptySet()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user