422e8f0f0f
- Fix SYNC_COMPLETED showing ↑0 ↓0 ✗0 when only deletions occurred: add ✕N for deleted files to the summary message (↑N ↓N ✕N ✗N format) - Fix PairDetail Activity section showing raw "SYNC_STARTED" enum names and "remote" as a plain subtitle: replace dot-based EventRow with the same polished icon-bubble rows as the global Log tab - Extract shared SyncEventRow composable + iconAndTint/label helpers to ui/shared/SyncEventRow.kt; both LogScreen and PairDetailScreen now use it - Add Files tab (4th tab between Log and Accounts): folder browser showing all synced files per pair, grouped by subdirectory, with file-type icons, size, last-synced date, and a summary header (N files, total size) - Add SyncFileStateDao.observeForPair() reactive Flow query for Files tab - Completely redesign app icon: near-black radial gradient background with three bold directional arrows in an S-pattern (coral → silver → teal), each with gradient fills and tip-glow dots — entirely different from the typical circular sync-arrow style - Bump version to 1.0.22 (build 23) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
40 lines
1.5 KiB
Kotlin
40 lines
1.5 KiB
Kotlin
package com.syncflow.ui.files
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import com.syncflow.data.db.SyncFileStateDao
|
|
import com.syncflow.data.db.SyncPairDao
|
|
import com.syncflow.data.db.entities.SyncFileStateEntity
|
|
import com.syncflow.data.db.entities.SyncPairEntity
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
import kotlinx.coroutines.flow.*
|
|
import javax.inject.Inject
|
|
|
|
@OptIn(ExperimentalCoroutinesApi::class)
|
|
@HiltViewModel
|
|
class FilesViewModel @Inject constructor(
|
|
syncPairDao: SyncPairDao,
|
|
private val fileStateDao: SyncFileStateDao,
|
|
) : ViewModel() {
|
|
|
|
val pairs: StateFlow<List<SyncPairEntity>> = syncPairDao.observeAll()
|
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
|
|
|
|
private val _selectedPairId = MutableStateFlow<Long?>(null)
|
|
|
|
val selectedPair: StateFlow<SyncPairEntity?> = combine(_selectedPairId, pairs) { id, list ->
|
|
list.firstOrNull { it.id == id } ?: list.firstOrNull()
|
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
|
|
|
val files: StateFlow<List<SyncFileStateEntity>> = _selectedPairId
|
|
.flatMapLatest { id ->
|
|
if (id == null) pairs.map { it.firstOrNull()?.id }.filterNotNull()
|
|
.flatMapLatest { fileStateDao.observeForPair(it) }
|
|
else fileStateDao.observeForPair(id)
|
|
}
|
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
|
|
|
|
fun selectPair(id: Long) { _selectedPairId.value = id }
|
|
}
|