nice and clean now

I don't know if this works
This commit is contained in:
Minecon724 2024-08-07 13:37:42 +02:00
parent 5cf1466c54
commit 3ddbe78fe8
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
9 changed files with 79 additions and 110 deletions

View file

@ -44,17 +44,16 @@ class DashboardActivity : ComponentActivity() {
val dashboardViewModel = DashboardViewModel(application) val dashboardViewModel = DashboardViewModel(application)
if (intent.getBooleanExtra("direct", false).not()) { if (intent.getBooleanExtra("direct", false).not()) {
dashboardViewModel.refresh(this) dashboardViewModel.refresh()
} }
dashboardViewModel.checkTermux(this)
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
dashboardViewModel.refreshError.collect { dashboardViewModel.refreshError.collect {
it.forEach { errorMsg -> Toast.makeText(baseContext, it, Toast.LENGTH_SHORT).show()
Toast.makeText(baseContext, errorMsg, Toast.LENGTH_SHORT).show() } // TODO any better way?
}
}
} }
} }
@ -113,9 +112,9 @@ fun MyNavigationBar(items: List<Screen>, navController: NavHostController) {
saveState = true saveState = true
} }
// Avoid multiple copies of the same destination when // Avoid multiple copies of the same destination when
// reselecting the same item // re-selecting the same item
launchSingleTop = true launchSingleTop = true
// Restore state when reselecting a previously selected item // Restore state when re-selecting a previously selected item
restoreState = true restoreState = true
} }

View file

@ -1,5 +1,5 @@
package eu.m724.vastapp.activity.dashboard package eu.m724.vastapp.activity.dashboard
data class DashboardUiState( data class DashboardUiState(
val refreshing: Int = 0 val refreshing: Boolean = false
) )

View file

@ -6,18 +6,17 @@ import android.content.Context
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.viewModelScope
import eu.m724.vastapp.R import eu.m724.vastapp.R
import eu.m724.vastapp.VastApplication import eu.m724.vastapp.VastApplication
import eu.m724.vastapp.activity.Opener import eu.m724.vastapp.activity.Opener
import eu.m724.vastapp.activity.PermissionChecker 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 eu.m724.vastapp.vastai.data.RentedInstance
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class DashboardViewModel( class DashboardViewModel(
@ -25,12 +24,12 @@ class DashboardViewModel(
) : AndroidViewModel(application) { // TODO do something with the user ) : AndroidViewModel(application) { // TODO do something with the user
private val _uiState: MutableStateFlow<DashboardUiState> = private val _uiState: MutableStateFlow<DashboardUiState> =
MutableStateFlow(DashboardUiState(0)) MutableStateFlow(DashboardUiState(false))
val uiState: StateFlow<DashboardUiState> = val uiState: StateFlow<DashboardUiState> =
_uiState.asStateFlow() _uiState.asStateFlow()
private val _refreshError: MutableStateFlow<List<String>> = MutableStateFlow(emptyList()) private val _refreshError: MutableStateFlow<String?> = MutableStateFlow(null)
val refreshError: StateFlow<List<String>> = _refreshError.asStateFlow() val refreshError: StateFlow<String?> = _refreshError.asStateFlow()
private val _termuxAvailable: MutableStateFlow<Int> = MutableStateFlow(0) private val _termuxAvailable: MutableStateFlow<Int> = MutableStateFlow(0)
val termuxAvailable: StateFlow<Int> = _termuxAvailable.asStateFlow() val termuxAvailable: StateFlow<Int> = _termuxAvailable.asStateFlow()
@ -39,43 +38,29 @@ class DashboardViewModel(
private val vastApi = this.application.vastApi private val vastApi = this.application.vastApi
val account = this.application.account!! val account = this.application.account!!
fun refresh(activity: ComponentActivity) { fun refresh() {
_uiState.value = _uiState.value.copy(refreshing = 2) _uiState.update { it.copy(refreshing = true) }
_refreshError.value = emptyList() _refreshError.value = null
val userRequest = vastApi.buildRequest( val userDeferred = vastApi.getUser()
ApiRoute.SHOW_USER, val rentedInstancesDeferred = vastApi.getRentedInstances()
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 instancesRequest = vastApi.buildRequest( viewModelScope.launch {
ApiRoute.GET_INSTANCES, try {
InstancesUrlRequestCallback({ instances -> account.updateUser(userDeferred.await())
account.updateRentedInstances(instances) account.updateRentedInstances(rentedInstancesDeferred.await())
_uiState.update { } catch (e: Exception) {
it.copy(refreshing = it.refreshing - 1) _refreshError.update {
"Refresh failed: " + e.message.toString()
}
}
_uiState.update {
it.copy(refreshing = false)
}
} }
}, { 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()
fun checkTermux(activity: ComponentActivity) {
val context = activity.applicationContext val context = activity.applicationContext
_termuxAvailable.value = _termuxAvailable.value =
@ -88,8 +73,6 @@ class DashboardViewModel(
} else -1 // not available because permission denied } else -1 // not available because permission denied
} }
} else -1 // not available } 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") @SuppressLint("SdCardPath")

View file

@ -32,7 +32,7 @@ class LoadingActivity : ComponentActivity() {
.putExtra("direct", true) .putExtra("direct", true)
} else { } else {
Intent(this, LoginActivity::class.java) Intent(this, LoginActivity::class.java)
.putExtra("error", result.error!!) .putExtra("error", result.error)
} }
this.startActivity(intent) this.startActivity(intent)

View file

@ -2,11 +2,10 @@ package eu.m724.vastapp.activity.dashboard.loading
import android.app.Application import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import eu.m724.vastapp.VastApplication import eu.m724.vastapp.VastApplication
import eu.m724.vastapp.vastai.Account import eu.m724.vastapp.vastai.Account
import eu.m724.vastapp.vastai.ApiRoute import kotlinx.coroutines.launch
import eu.m724.vastapp.vastai.api.InstancesUrlRequestCallback
import eu.m724.vastapp.vastai.api.UserUrlRequestCallback
class LoadingViewModel( class LoadingViewModel(
application: Application, application: Application,
@ -15,40 +14,30 @@ class LoadingViewModel(
private val application = application as VastApplication private val application = application as VastApplication
fun init() { fun init() {
val vastApi = application.vastApi
val apiKey = application.loadKey() val apiKey = application.loadKey()
if (apiKey == null) {
if (apiKey != null) { onEnded(LoadingResult(false, null))
vastApi.apiKey = apiKey return
val request = vastApi.buildRequest(
ApiRoute.SHOW_USER,
UserUrlRequestCallback({ user ->
application.account = Account(user)
loadInstances()
}, { apiFailure ->
onEnded(LoadingResult(false, apiFailure.errorMessage))
})
)
request.start()
}
} }
private fun loadInstances() {
val vastApi = application.vastApi val vastApi = application.vastApi
val instancesRequest = vastApi.buildRequest( vastApi.setApiKey(apiKey)
ApiRoute.GET_INSTANCES, val userDeferred = vastApi.getUser()
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() 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()))
}
}
} }
} }

View file

@ -1,6 +1,5 @@
package eu.m724.vastapp.activity.dashboard.screen package eu.m724.vastapp.activity.dashboard.screen
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi 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.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -47,14 +44,13 @@ fun DashboardScreen(dashboardViewModel: DashboardViewModel) {
val user by dashboardViewModel.account.user.collectAsState() val user by dashboardViewModel.account.user.collectAsState()
val rentedInstances by dashboardViewModel.account.rentedInstances.collectAsState() val rentedInstances by dashboardViewModel.account.rentedInstances.collectAsState()
val remainingTime by dashboardViewModel.account.remainingTime.collectAsState() val remainingTime by dashboardViewModel.account.remainingTime.collectAsState()
val isRefreshing by remember(uiState) { derivedStateOf { uiState.refreshing > 0 } }
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
PullToRefreshBox( PullToRefreshBox(
isRefreshing = isRefreshing, isRefreshing = uiState.refreshing,
state = rememberPullToRefreshState(), state = rememberPullToRefreshState(),
onRefresh = { dashboardViewModel.refresh(context as ComponentActivity) } onRefresh = { dashboardViewModel.refresh() }
) { ) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,

View file

@ -4,13 +4,13 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import eu.m724.vastapp.VastApplication 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.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.chromium.net.CronetEngine import org.chromium.net.CronetEngine
import java.util.concurrent.Executor import java.util.concurrent.Executor
@ -30,29 +30,23 @@ class LoginViewModel(
private val _apiKey = MutableStateFlow<String>("") private val _apiKey = MutableStateFlow<String>("")
var apiKey: StateFlow<String> = _apiKey.asStateFlow() var apiKey: StateFlow<String> = _apiKey.asStateFlow()
private val _fullscreenLoading = MutableStateFlow<Boolean>(false)
var fullscreenLoading: StateFlow<Boolean> = _fullscreenLoading.asStateFlow()
private val application = getApplication<VastApplication>() private val application = getApplication<VastApplication>()
fun tryLogin() { fun tryLogin() {
val vastApi = application.vastApi val userDeferred = application.vastApi.getUser()
vastApi.apiKey = apiKey.value
val request = vastApi.buildRequest( viewModelScope.launch {
ApiRoute.SHOW_USER, try {
UserUrlRequestCallback({ user -> val user = userDeferred.await()
application.submitKey(apiKey.value) // TODO toggle for this application.submitKey(apiKey.value) // TODO toggle for this
_uiState.value = LoginUiState.Success(user) _uiState.update { LoginUiState.Success(user) }
}, { apiFailure -> } catch (e: Exception) {
_uiState.value = LoginUiState.Idle _uiState.update { LoginUiState.Idle }
_fullscreenLoading.value = false _error.postValue(e.toString())
_error.postValue(apiFailure.errorMessage) }
}) }
)
_uiState.value = LoginUiState.Loading _uiState.update { LoginUiState.Loading }
request.start()
} }
fun onApiKeyChange(apiKey: String) { fun onApiKeyChange(apiKey: String) {

View file

@ -7,11 +7,15 @@ import org.chromium.net.UrlRequest
import java.util.concurrent.Executor import java.util.concurrent.Executor
class RequestMaker( class RequestMaker(
private val apiKey: String, private var apiKey: String,
private val cronetEngine: CronetEngine, private val cronetEngine: CronetEngine,
private val executor: Executor private val executor: Executor
) { ) {
fun setApiKey(apiKey: String) {
this.apiKey = apiKey
}
/** /**
* build an api request * build an api request
* don't forget to call .start() on the returned [UrlRequest] * don't forget to call .start() on the returned [UrlRequest]

View file

@ -18,6 +18,10 @@ class VastApi(
) { ) {
private val requestMaker = RequestMaker(apiKey, cronetEngine, executor) private val requestMaker = RequestMaker(apiKey, cronetEngine, executor)
fun setApiKey(apiKey: String) {
requestMaker.setApiKey(apiKey)
}
fun getUser(): CompletableDeferred<User> { fun getUser(): CompletableDeferred<User> {
val deferred = CompletableDeferred<User>() val deferred = CompletableDeferred<User>()