🔨 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,20 +1,75 @@
package dev.svitan.antifed package dev.svitan.antifed
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.engine.cio.CIO 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 io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class API {
companion object {
val client = HttpClient(CIO) { val client = HttpClient(CIO) {
install(ContentNegotiation) { install(ContentNegotiation) {
json() 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
}
}
}
@Serializable @Serializable
data class AuthDTO( data class AuthDTO(
val id: String, val id: String,
val name: String, val name: String,
val createdAt: 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.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@@ -44,9 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.content.edit import androidx.core.content.edit
import dev.svitan.antifed.ui.theme.AntiFedTheme import dev.svitan.antifed.ui.theme.AntiFedTheme
import io.ktor.client.call.body
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -61,10 +61,12 @@ class AuthActivity : ComponentActivity() {
var serverUrl by remember { mutableStateOf("") } var serverUrl by remember { mutableStateOf("") }
var token by remember { mutableStateOf("") } var token by remember { mutableStateOf("") }
var showToken by remember { mutableStateOf(false) } var showToken by remember { mutableStateOf(false) }
val apiDataHolder = APIDataHolder.getInstance()
var authId by remember { mutableStateOf("") } var authId by remember { mutableStateOf("") }
var authIndex by remember { mutableIntStateOf(-1) } var authIndex by remember { mutableIntStateOf(-1) }
var auths by remember { mutableStateOf<List<AuthDTO>>(emptyList()) } var auths by remember { mutableStateOf<List<AuthDTO>>(emptyList()) }
val authDataHolder = AuthDataHolder.getInstance()
var needToCheckUrl by remember { mutableStateOf(false) } var needToCheckUrl by remember { mutableStateOf(false) }
var checkingUrl by remember { mutableStateOf(false) } var checkingUrl by remember { mutableStateOf(false) }
@@ -82,7 +84,7 @@ class AuthActivity : ComponentActivity() {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
serverUrl = prefs.getString(getString(R.string.server_url_key), "") ?: "" serverUrl = prefs.getString(getString(R.string.server_url_key), "") ?: ""
token = prefs.getString(getString(R.string.token_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) isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false)
isTokenOk = prefs.getBoolean(getString(R.string.token_okay_key), false) isTokenOk = prefs.getBoolean(getString(R.string.token_okay_key), false)
} }
@@ -97,36 +99,25 @@ class AuthActivity : ComponentActivity() {
needToCheckUrl = false needToCheckUrl = false
isServerUrlOk = false isServerUrlOk = false
prefs.edit {
putBoolean(getString(R.string.server_url_okay_key), false)
}
if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect
delay(1000) delay(1000)
checkingUrl = true checkingUrl = true
isServerUrlOk = try { isServerUrlOk = try {
val response = client.get(serverUrl) val response = API.client.get(serverUrl)
response.status == HttpStatusCode.OK response.status == HttpStatusCode.OK
} catch (_: Exception) { } catch (_: Exception) {
false false
} }
checkingUrl = false checkingUrl = false
prefs.edit {
putBoolean(
getString(R.string.server_url_okay_key), isServerUrlOk
)
}
} }
LaunchedEffect(token) { LaunchedEffect(token) {
if (!needToCheckToken) return@LaunchedEffect if (!needToCheckToken) return@LaunchedEffect
needToCheckToken = false needToCheckToken = false
isTokenOk = false isTokenOk = false
prefs.edit {
putBoolean(getString(R.string.token_okay_key), false)
}
if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect
@@ -134,32 +125,75 @@ class AuthActivity : ComponentActivity() {
checkingToken = true checkingToken = true
try { try {
val response = client.get("$serverUrl/auth") { API.loadAuths()
headers { auths = authDataHolder.auths
append("Authorization", "Bearer $token") authIndex = authDataHolder.authIndex
}
}
if (response.status == HttpStatusCode.OK) {
auths = response.body<List<AuthDTO>>()
authIndex = auths.indexOfFirst { it.id == authId }
isTokenOk = true isTokenOk = true
}
} catch (e: Exception) { } catch (e: Exception) {
isTokenOk = false isTokenOk = false
Log.e("AuthActivity", "Error checking token", e) Log.e("AuthActivity", "Error checking token", e)
} }
checkingToken = false 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 { prefs.edit {
putBoolean(getString(R.string.token_okay_key), isTokenOk) 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( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.auth)) }, colors = TopAppBarDefaults.topAppBarColors(
titleContentColor = MaterialTheme.colorScheme.primary
),
title = {
Text(stringResource(R.string.auth))
},
navigationIcon = { navigationIcon = {
IconButton(onClick = { finish() }) { IconButton(onClick = { finish() }) {
Icon( Icon(
@@ -172,6 +206,7 @@ class AuthActivity : ComponentActivity() {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(padding) .padding(padding)
.padding(bottom = 64.dp)
.imePadding() .imePadding()
.fillMaxSize(), .fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@@ -185,11 +220,7 @@ class AuthActivity : ComponentActivity() {
onValueChange = { onValueChange = {
serverUrl = it serverUrl = it
needToCheckUrl = true needToCheckUrl = true
prefs.edit {
putString(
getString(R.string.server_url_key), it
)
}
}, },
label = { Text(stringResource(R.string.server_url)) }, label = { Text(stringResource(R.string.server_url)) },
singleLine = true, singleLine = true,
@@ -209,11 +240,6 @@ class AuthActivity : ComponentActivity() {
onValueChange = { onValueChange = {
token = it token = it
needToCheckToken = true needToCheckToken = true
prefs.edit {
putString(
getString(R.string.token_key), it
)
}
}, },
label = { Text(stringResource(R.string.token)) }, label = { Text(stringResource(R.string.token)) },
singleLine = true, singleLine = true,
+1 -1
View File
@@ -10,7 +10,7 @@
<string name="server_url_key" translatable="false">server_url</string> <string name="server_url_key" translatable="false">server_url</string>
<string name="server_url_okay_key" translatable="false">server_url_okay</string> <string name="server_url_okay_key" translatable="false">server_url_okay</string>
<string name="token_key" translatable="false">token</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="create_auth">Create new auth</string>
<string name="bio">Biometrics</string> <string name="bio">Biometrics</string>
<string name="show_token">Show token</string> <string name="show_token">Show token</string>