🔨 Refactors API and saving prefs and adds data holders

This commit is contained in:
2026-04-17 12:13:20 +02:00
parent 3218b0b5d6
commit d5806d22eb
3 changed files with 122 additions and 41 deletions
@@ -1,14 +1,55 @@
package dev.svitan.antifed
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json()
class API {
companion object {
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}
suspend fun loadAuths() {
val apiDataHolder = APIDataHolder.getInstance()
val authDataHolder = AuthDataHolder.getInstance()
val response = client.get("${apiDataHolder.serverUrl}/auth") {
headers {
append("Authorization", "Bearer ${apiDataHolder.token}")
}
}
if (response.status == HttpStatusCode.OK) {
authDataHolder.auths = response.body<List<AuthDTO>>()
authDataHolder.authIndex =
authDataHolder.auths.indexOfFirst { it.id == authDataHolder.authId }
} else {
throw Exception("Error loading auths")
}
}
}
}
class APIDataHolder {
var serverUrl: String = ""
var token: String = ""
var isOk: Boolean = false
companion object {
private var holder: APIDataHolder = APIDataHolder()
fun getInstance(): APIDataHolder {
return holder
}
}
}
@@ -18,3 +59,17 @@ data class AuthDTO(
val name: String,
val createdAt: String
)
class AuthDataHolder {
var auths: List<AuthDTO> = listOf()
var authId: String = ""
var authIndex: Int = -1
companion object {
private var holder: AuthDataHolder = AuthDataHolder()
fun getInstance(): AuthDataHolder {
return holder
}
}
}
@@ -23,10 +23,12 @@ import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -44,9 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import dev.svitan.antifed.ui.theme.AntiFedTheme
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay
@@ -61,10 +61,12 @@ class AuthActivity : ComponentActivity() {
var serverUrl by remember { mutableStateOf("") }
var token by remember { mutableStateOf("") }
var showToken by remember { mutableStateOf(false) }
val apiDataHolder = APIDataHolder.getInstance()
var authId by remember { mutableStateOf("") }
var authIndex by remember { mutableIntStateOf(-1) }
var auths by remember { mutableStateOf<List<AuthDTO>>(emptyList()) }
val authDataHolder = AuthDataHolder.getInstance()
var needToCheckUrl by remember { mutableStateOf(false) }
var checkingUrl by remember { mutableStateOf(false) }
@@ -82,7 +84,7 @@ class AuthActivity : ComponentActivity() {
LaunchedEffect(Unit) {
serverUrl = prefs.getString(getString(R.string.server_url_key), "") ?: ""
token = prefs.getString(getString(R.string.token_key), "") ?: ""
authId = prefs.getString(getString(R.string.auth_key), "") ?: ""
authId = prefs.getString(getString(R.string.auth_id_key), "") ?: ""
isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false)
isTokenOk = prefs.getBoolean(getString(R.string.token_okay_key), false)
}
@@ -97,36 +99,25 @@ class AuthActivity : ComponentActivity() {
needToCheckUrl = false
isServerUrlOk = false
prefs.edit {
putBoolean(getString(R.string.server_url_okay_key), false)
}
if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect
delay(1000)
checkingUrl = true
isServerUrlOk = try {
val response = client.get(serverUrl)
val response = API.client.get(serverUrl)
response.status == HttpStatusCode.OK
} catch (_: Exception) {
false
}
checkingUrl = false
prefs.edit {
putBoolean(
getString(R.string.server_url_okay_key), isServerUrlOk
)
}
}
LaunchedEffect(token) {
if (!needToCheckToken) return@LaunchedEffect
needToCheckToken = false
isTokenOk = false
prefs.edit {
putBoolean(getString(R.string.token_okay_key), false)
}
if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect
@@ -134,32 +125,75 @@ class AuthActivity : ComponentActivity() {
checkingToken = true
try {
val response = client.get("$serverUrl/auth") {
headers {
append("Authorization", "Bearer $token")
}
}
if (response.status == HttpStatusCode.OK) {
auths = response.body<List<AuthDTO>>()
authIndex = auths.indexOfFirst { it.id == authId }
isTokenOk = true
}
API.loadAuths()
auths = authDataHolder.auths
authIndex = authDataHolder.authIndex
isTokenOk = true
} catch (e: Exception) {
isTokenOk = false
Log.e("AuthActivity", "Error checking token", e)
}
checkingToken = false
}
// saves data to prefs and data holders
LaunchedEffect(serverUrl) {
apiDataHolder.serverUrl = serverUrl
prefs.edit {
putString(
getString(R.string.server_url_key), serverUrl
)
}
}
LaunchedEffect(token) {
apiDataHolder.token = token
prefs.edit {
putString(
getString(R.string.token_key), token
)
}
}
LaunchedEffect(isServerUrlOk) {
apiDataHolder.isOk = isServerUrlOk && isTokenOk
prefs.edit {
putBoolean(
getString(R.string.server_url_okay_key),
isServerUrlOk
)
}
}
LaunchedEffect(isTokenOk) {
apiDataHolder.isOk = isServerUrlOk && isTokenOk
prefs.edit {
putBoolean(getString(R.string.token_okay_key), isTokenOk)
}
}
LaunchedEffect(authId) {
authDataHolder.authId = authId
prefs.edit {
putString(getString(R.string.auth_id_key), authId)
}
}
LaunchedEffect(auths, authIndex) {
authDataHolder.auths = auths
authDataHolder.authIndex = authIndex
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(R.string.auth)) },
colors = TopAppBarDefaults.topAppBarColors(
titleContentColor = MaterialTheme.colorScheme.primary
),
title = {
Text(stringResource(R.string.auth))
},
navigationIcon = {
IconButton(onClick = { finish() }) {
Icon(
@@ -172,6 +206,7 @@ class AuthActivity : ComponentActivity() {
Column(
modifier = Modifier
.padding(padding)
.padding(bottom = 64.dp)
.imePadding()
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
@@ -185,11 +220,7 @@ class AuthActivity : ComponentActivity() {
onValueChange = {
serverUrl = it
needToCheckUrl = true
prefs.edit {
putString(
getString(R.string.server_url_key), it
)
}
},
label = { Text(stringResource(R.string.server_url)) },
singleLine = true,
@@ -209,11 +240,6 @@ class AuthActivity : ComponentActivity() {
onValueChange = {
token = it
needToCheckToken = true
prefs.edit {
putString(
getString(R.string.token_key), it
)
}
},
label = { Text(stringResource(R.string.token)) },
singleLine = true,
+1 -1
View File
@@ -10,7 +10,7 @@
<string name="server_url_key" translatable="false">server_url</string>
<string name="server_url_okay_key" translatable="false">server_url_okay</string>
<string name="token_key" translatable="false">token</string>
<string name="auth_key" translatable="false">auth_id</string>
<string name="auth_id_key" translatable="false">auth_id</string>
<string name="create_auth">Create new auth</string>
<string name="bio">Biometrics</string>
<string name="show_token">Show token</string>