diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt index 19ea987..541ab39 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardActivity.kt @@ -44,17 +44,16 @@ class DashboardActivity : ComponentActivity() { val dashboardViewModel = DashboardViewModel(application) if (intent.getBooleanExtra("direct", false).not()) { - dashboardViewModel.refresh(this) + dashboardViewModel.refresh() } + dashboardViewModel.checkTermux(this) + lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { dashboardViewModel.refreshError.collect { - it.forEach { errorMsg -> - Toast.makeText(baseContext, errorMsg, Toast.LENGTH_SHORT).show() - } - - } + Toast.makeText(baseContext, it, Toast.LENGTH_SHORT).show() + } // TODO any better way? } } @@ -113,9 +112,9 @@ fun MyNavigationBar(items: List, navController: NavHostController) { saveState = true } // Avoid multiple copies of the same destination when - // reselecting the same item + // re-selecting the same item launchSingleTop = true - // Restore state when reselecting a previously selected item + // Restore state when re-selecting a previously selected item restoreState = true } diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardUiState.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardUiState.kt index 3126ad9..1ff5a16 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardUiState.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardUiState.kt @@ -1,5 +1,5 @@ package eu.m724.vastapp.activity.dashboard data class DashboardUiState( - val refreshing: Int = 0 + val refreshing: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt index fbb1ff8..cade343 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/DashboardViewModel.kt @@ -6,18 +6,17 @@ import android.content.Context import androidx.activity.ComponentActivity import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.Lifecycle +import androidx.lifecycle.viewModelScope import eu.m724.vastapp.R import eu.m724.vastapp.VastApplication import eu.m724.vastapp.activity.Opener import eu.m724.vastapp.activity.PermissionChecker -import eu.m724.vastapp.vastai.ApiRoute -import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback -import eu.m724.vastapp.vastai.api.UserUrlRequestCallback import eu.m724.vastapp.vastai.data.RentedInstance import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch class DashboardViewModel( @@ -25,12 +24,12 @@ class DashboardViewModel( ) : AndroidViewModel(application) { // TODO do something with the user private val _uiState: MutableStateFlow = - MutableStateFlow(DashboardUiState(0)) + MutableStateFlow(DashboardUiState(false)) val uiState: StateFlow = _uiState.asStateFlow() - private val _refreshError: MutableStateFlow> = MutableStateFlow(emptyList()) - val refreshError: StateFlow> = _refreshError.asStateFlow() + private val _refreshError: MutableStateFlow = MutableStateFlow(null) + val refreshError: StateFlow = _refreshError.asStateFlow() private val _termuxAvailable: MutableStateFlow = MutableStateFlow(0) val termuxAvailable: StateFlow = _termuxAvailable.asStateFlow() @@ -39,43 +38,29 @@ class DashboardViewModel( private val vastApi = this.application.vastApi val account = this.application.account!! - fun refresh(activity: ComponentActivity) { - _uiState.value = _uiState.value.copy(refreshing = 2) - _refreshError.value = emptyList() + fun refresh() { + _uiState.update { it.copy(refreshing = true) } + _refreshError.value = null - val userRequest = vastApi.buildRequest( - ApiRoute.SHOW_USER, - UserUrlRequestCallback({ newUser -> - account.updateUser(newUser) - _uiState.update { - it.copy(refreshing = it.refreshing - 1) // TODO I don't like how this looks - } - }, { apiFailure -> - _refreshError.update { it + apiFailure.errorMessage!! } - _uiState.update { - it.copy(refreshing = it.refreshing - 1) - } - }) - ) + val userDeferred = vastApi.getUser() + val rentedInstancesDeferred = vastApi.getRentedInstances() - val instancesRequest = vastApi.buildRequest( - ApiRoute.GET_INSTANCES, - InstancesUrlRequestCallback({ instances -> - account.updateRentedInstances(instances) - _uiState.update { - it.copy(refreshing = it.refreshing - 1) + viewModelScope.launch { + try { + account.updateUser(userDeferred.await()) + account.updateRentedInstances(rentedInstancesDeferred.await()) + } catch (e: Exception) { + _refreshError.update { + "Refresh failed: " + e.message.toString() } - }, { apiFailure -> - _refreshError.update { it + apiFailure.errorMessage!! } - _uiState.update { - it.copy(refreshing = it.refreshing - 1) - } - }) - ) // TODO move all that refreshing to some shared place - - userRequest.start() - instancesRequest.start() + } + _uiState.update { + it.copy(refreshing = false) + } + } + } + fun checkTermux(activity: ComponentActivity) { val context = activity.applicationContext _termuxAvailable.value = @@ -88,8 +73,6 @@ class DashboardViewModel( } else -1 // not available because permission denied } } else -1 // not available - - // TODO I don't like this function especially the last line. I think it should be moved to application } @SuppressLint("SdCardPath") diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt index 915e754..6b38291 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingActivity.kt @@ -32,7 +32,7 @@ class LoadingActivity : ComponentActivity() { .putExtra("direct", true) } else { Intent(this, LoginActivity::class.java) - .putExtra("error", result.error!!) + .putExtra("error", result.error) } this.startActivity(intent) diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt index 5bbb4e0..630c361 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/loading/LoadingViewModel.kt @@ -2,11 +2,10 @@ package eu.m724.vastapp.activity.dashboard.loading import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope import eu.m724.vastapp.VastApplication import eu.m724.vastapp.vastai.Account -import eu.m724.vastapp.vastai.ApiRoute -import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback -import eu.m724.vastapp.vastai.api.UserUrlRequestCallback +import kotlinx.coroutines.launch class LoadingViewModel( application: Application, @@ -15,40 +14,30 @@ class LoadingViewModel( private val application = application as VastApplication fun init() { - val vastApi = application.vastApi val apiKey = application.loadKey() + if (apiKey == null) { + onEnded(LoadingResult(false, null)) + return + } - if (apiKey != null) { - vastApi.apiKey = apiKey + val vastApi = application.vastApi - val request = vastApi.buildRequest( - ApiRoute.SHOW_USER, - UserUrlRequestCallback({ user -> - application.account = Account(user) - loadInstances() - }, { apiFailure -> - onEnded(LoadingResult(false, apiFailure.errorMessage)) - }) - ) + vastApi.setApiKey(apiKey) + val userDeferred = vastApi.getUser() - request.start() + viewModelScope.launch { + try { + val user = userDeferred.await() + application.account = Account(user) + + // TODO should we do this were, or is it better to say you're logged in and handle the error from dashboard + val rentedInstances = vastApi.getRentedInstances().await() + application.account!!.updateRentedInstances(rentedInstances) + + onEnded(LoadingResult(true, null)) + } catch (e: Exception) { + onEnded(LoadingResult(false, e.message.toString())) + } } } - - private fun loadInstances() { - val vastApi = application.vastApi - - val instancesRequest = vastApi.buildRequest( - ApiRoute.GET_INSTANCES, - InstancesUrlRequestCallback({ instances -> - application.account!!.updateRentedInstances(instances) - onEnded(LoadingResult(true, null)) - }, { apiFailure -> - // TODO I don't know what to do yet - onEnded(LoadingResult(true, null)) - }) - ) - - instancesRequest.start() - } } \ No newline at end of file diff --git a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt index ba882a5..9222c6e 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/dashboard/screen/Dashboard.kt @@ -1,6 +1,5 @@ package eu.m724.vastapp.activity.dashboard.screen -import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -24,9 +23,7 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -47,14 +44,13 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) { val user by dashboardViewModel.account.user.collectAsState() val rentedInstances by dashboardViewModel.account.rentedInstances.collectAsState() val remainingTime by dashboardViewModel.account.remainingTime.collectAsState() - val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } } val scrollState = rememberScrollState() PullToRefreshBox( - isRefreshing = isRefreshing, + isRefreshing = uiState.refreshing, state = rememberPullToRefreshState(), - onRefresh = { dashboardViewModel.refresh(context as ComponentActivity) } + onRefresh = { dashboardViewModel.refresh() } ) { Column( horizontalAlignment = Alignment.CenterHorizontally, diff --git a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt index 6be7c72..0d274e3 100644 --- a/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt +++ b/app/src/main/java/eu/m724/vastapp/activity/login/LoginViewModel.kt @@ -4,13 +4,13 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import eu.m724.vastapp.VastApplication -import eu.m724.vastapp.vastai.ApiRoute -import eu.m724.vastapp.vastai.api.UserUrlRequestCallback import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import org.chromium.net.CronetEngine import java.util.concurrent.Executor @@ -30,29 +30,23 @@ class LoginViewModel( private val _apiKey = MutableStateFlow("") var apiKey: StateFlow = _apiKey.asStateFlow() - private val _fullscreenLoading = MutableStateFlow(false) - var fullscreenLoading: StateFlow = _fullscreenLoading.asStateFlow() - private val application = getApplication() fun tryLogin() { - val vastApi = application.vastApi - vastApi.apiKey = apiKey.value + val userDeferred = application.vastApi.getUser() - val request = vastApi.buildRequest( - ApiRoute.SHOW_USER, - UserUrlRequestCallback({ user -> + viewModelScope.launch { + try { + val user = userDeferred.await() application.submitKey(apiKey.value) // TODO toggle for this - _uiState.value = LoginUiState.Success(user) - }, { apiFailure -> - _uiState.value = LoginUiState.Idle - _fullscreenLoading.value = false - _error.postValue(apiFailure.errorMessage) - }) - ) + _uiState.update { LoginUiState.Success(user) } + } catch (e: Exception) { + _uiState.update { LoginUiState.Idle } + _error.postValue(e.toString()) + } + } - _uiState.value = LoginUiState.Loading - request.start() + _uiState.update { LoginUiState.Loading } } fun onApiKeyChange(apiKey: String) { diff --git a/app/src/main/java/eu/m724/vastapp/vastai/RequestMaker.kt b/app/src/main/java/eu/m724/vastapp/vastai/RequestMaker.kt index df9243b..b40af3c 100644 --- a/app/src/main/java/eu/m724/vastapp/vastai/RequestMaker.kt +++ b/app/src/main/java/eu/m724/vastapp/vastai/RequestMaker.kt @@ -7,11 +7,15 @@ import org.chromium.net.UrlRequest import java.util.concurrent.Executor class RequestMaker( - private val apiKey: String, + private var apiKey: String, private val cronetEngine: CronetEngine, private val executor: Executor ) { + fun setApiKey(apiKey: String) { + this.apiKey = apiKey + } + /** * build an api request * don't forget to call .start() on the returned [UrlRequest] diff --git a/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt b/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt index 717c6ab..8b165a4 100644 --- a/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt +++ b/app/src/main/java/eu/m724/vastapp/vastai/VastApi.kt @@ -18,6 +18,10 @@ class VastApi( ) { private val requestMaker = RequestMaker(apiKey, cronetEngine, executor) + fun setApiKey(apiKey: String) { + requestMaker.setApiKey(apiKey) + } + fun getUser(): CompletableDeferred { val deferred = CompletableDeferred()