- Create DataManager.kt with StateFlows for all cached data: - Authentication (token, user) - Residences, tasks, documents, contractors - Subscription status and upgrade triggers - All lookup data (residence types, task categories, etc.) - Theme preferences and state metadata - Add PersistenceManager with platform-specific implementations: - Android: SharedPreferences - iOS: NSUserDefaults - JVM: Properties file - WasmJS: localStorage - Migrate APILayer to update DataManager on every API response - Update Kotlin ViewModels to use DataManager for token access - Deprecate LookupsRepository (delegates to DataManager) - Create iOS DataManagerObservable Swift wrapper for SwiftUI - Update iOS auth flow to use DataManager.isAuthenticated() Data flow: User Action → API Call → DataManager Updated → All Screens React 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
77 lines
3.1 KiB
Kotlin
77 lines
3.1 KiB
Kotlin
package com.example.casera.viewmodel
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import com.example.casera.data.DataManager
|
|
import com.example.casera.models.TaskCompletion
|
|
import com.example.casera.models.TaskCompletionCreateRequest
|
|
import com.example.casera.network.ApiResult
|
|
import com.example.casera.network.TaskCompletionApi
|
|
import com.example.casera.util.ImageCompressor
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import kotlinx.coroutines.launch
|
|
|
|
class TaskCompletionViewModel : ViewModel() {
|
|
private val taskCompletionApi = TaskCompletionApi()
|
|
|
|
private val _createCompletionState = MutableStateFlow<ApiResult<TaskCompletion>>(ApiResult.Idle)
|
|
val createCompletionState: StateFlow<ApiResult<TaskCompletion>> = _createCompletionState
|
|
|
|
fun createTaskCompletion(request: TaskCompletionCreateRequest) {
|
|
viewModelScope.launch {
|
|
_createCompletionState.value = ApiResult.Loading
|
|
val token = DataManager.authToken.value
|
|
if (token != null) {
|
|
_createCompletionState.value = taskCompletionApi.createCompletion(token, request)
|
|
} else {
|
|
_createCompletionState.value = ApiResult.Error("Not authenticated", 401)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create task completion with images.
|
|
*
|
|
* @param request The completion request data
|
|
* @param images List of ImageData (from platform-specific image pickers)
|
|
*/
|
|
fun createTaskCompletionWithImages(
|
|
request: TaskCompletionCreateRequest,
|
|
images: List<com.example.casera.platform.ImageData> = emptyList()
|
|
) {
|
|
viewModelScope.launch {
|
|
_createCompletionState.value = ApiResult.Loading
|
|
val token = DataManager.authToken.value
|
|
if (token != null) {
|
|
// Compress images and prepare for upload
|
|
val compressedImages = images.map { ImageCompressor.compressImage(it) }
|
|
val imageFileNames = images.mapIndexed { index, image ->
|
|
// Always use .jpg extension since we compress to JPEG
|
|
val baseName = image.fileName.ifBlank { "completion_$index" }
|
|
if (baseName.endsWith(".jpg", ignoreCase = true) ||
|
|
baseName.endsWith(".jpeg", ignoreCase = true)) {
|
|
baseName
|
|
} else {
|
|
// Remove any existing extension and add .jpg
|
|
baseName.substringBeforeLast('.', baseName) + ".jpg"
|
|
}
|
|
}
|
|
|
|
_createCompletionState.value = taskCompletionApi.createCompletionWithImages(
|
|
token = token,
|
|
request = request,
|
|
images = compressedImages,
|
|
imageFileNames = imageFileNames
|
|
)
|
|
} else {
|
|
_createCompletionState.value = ApiResult.Error("Not authenticated", 401)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun resetCreateState() {
|
|
_createCompletionState.value = ApiResult.Idle
|
|
}
|
|
}
|