From 67b5b6ac6c218332e90ae687713f02553049e65c Mon Sep 17 00:00:00 2001 From: Ash Svitan Date: Thu, 16 Apr 2026 14:48:31 +0200 Subject: [PATCH] :construction: Works on authentication --- backend/gradle.properties | 2 +- frontend/app/src/main/AndroidManifest.xml | 4 +- .../java/dev/svitan/antifed/AuthActivity.kt | 216 +++++++++--------- .../antifed/ui/components/DropdownList.kt | 1 - .../app/src/main/res/values-sk/strings.xml | 1 + frontend/app/src/main/res/values/strings.xml | 1 + frontend/gradle.properties | 12 +- frontend/gradle/gradle-daemon-jvm.properties | 13 ++ frontend/gradle/libs.versions.toml | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- frontend/settings.gradle.kts | 3 + 11 files changed, 138 insertions(+), 121 deletions(-) create mode 100644 frontend/gradle/gradle-daemon-jvm.properties diff --git a/backend/gradle.properties b/backend/gradle.properties index ef6192d..020c737 100644 --- a/backend/gradle.properties +++ b/backend/gradle.properties @@ -3,4 +3,4 @@ exposed_version=0.61.0 h2_version=2.3.232 kotlin_version=2.1.10 ktor_version=3.1.3 -logback_version=1.4.14 +logback_version=1.5.13 diff --git a/frontend/app/src/main/AndroidManifest.xml b/frontend/app/src/main/AndroidManifest.xml index f583f25..c6e2329 100644 --- a/frontend/app/src/main/AndroidManifest.xml +++ b/frontend/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + - \ No newline at end of file + diff --git a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt b/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt index 169431b..6321e15 100644 --- a/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt +++ b/frontend/app/src/main/java/dev/svitan/antifed/AuthActivity.kt @@ -4,34 +4,13 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton -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 -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -45,20 +24,26 @@ import dev.svitan.antifed.ui.components.DropdownList 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 @OptIn(ExperimentalMaterial3Api::class) class AuthActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + setContent { AntiFedTheme { + var serverUrl by remember { mutableStateOf("") } var token by remember { mutableStateOf("") } - var authIndex by remember { mutableIntStateOf(-1) } + var authId by remember { mutableStateOf("") } + var authIndex by remember { mutableIntStateOf(-1) } + var auths by remember { mutableStateOf>(emptyList()) } var needToCheckUrl by remember { mutableStateOf(false) } var checkingUrl by remember { mutableStateOf(false) } @@ -68,110 +53,99 @@ class AuthActivity : ComponentActivity() { var checkingToken by remember { mutableStateOf(false) } var isTokenOk by remember { mutableStateOf(false) } - var auths by remember { mutableStateOf(listOf()) } - - val prefs = LocalContext.current.getSharedPreferences( - stringResource(R.string.settings_prefs_key), - MODE_PRIVATE + val context = LocalContext.current + val prefs = context.getSharedPreferences( + stringResource(R.string.settings_prefs_key), MODE_PRIVATE ) - val loadedServerUrl = prefs.getString(getString(R.string.server_url_key), "") ?: "" - if (loadedServerUrl.isNotBlank()) { - serverUrl = loadedServerUrl + + 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), "") ?: "" + isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false) } - val loadedToken = prefs.getString(getString(R.string.token_key), "") ?: "" - if (loadedToken.isNotBlank()) { - token = loadedToken - } - authId = prefs.getString(getString(R.string.auth_key), "") ?: "" - isServerUrlOk = prefs.getBoolean(getString(R.string.server_url_okay_key), false) fun serverUrlMatches(): Boolean { - var re = Regex( - """https?://[a-zA-Z0-9\\.]+\\.[a-zA-Z0-9]+""", - RegexOption.DOT_MATCHES_ALL - ) - return re.containsMatchIn(serverUrl) + val regex = Regex("""https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?""") + return regex.matches(serverUrl) } LaunchedEffect(serverUrl) { if (!needToCheckUrl) return@LaunchedEffect - else { - isServerUrlOk = false - prefs.edit { - putBoolean(getString(R.string.server_url_okay_key), false) - } - } + needToCheckUrl = false - if (serverUrl.isBlank()) return@LaunchedEffect - if (!serverUrlMatches()) return@LaunchedEffect + isServerUrlOk = false + prefs.edit { + putBoolean(getString(R.string.server_url_okay_key), false) + } + if (serverUrl.isBlank() || !serverUrlMatches()) return@LaunchedEffect delay(1000) checkingUrl = true - val response = client.get(serverUrl) + + isServerUrlOk = try { + val response = client.get(serverUrl) + response.status == HttpStatusCode.OK + } catch (_: Exception) { + false + } checkingUrl = false - needToCheckUrl = false - isServerUrlOk = response.status == HttpStatusCode.OK prefs.edit { - putBoolean(getString(R.string.server_url_okay_key), isServerUrlOk) + putBoolean( + getString(R.string.server_url_okay_key), isServerUrlOk + ) } } LaunchedEffect(token) { if (!needToCheckToken) return@LaunchedEffect - else isTokenOk = false + needToCheckToken = false + isTokenOk = false - if (!isServerUrlOk) return@LaunchedEffect - if (token.isBlank()) return@LaunchedEffect + if (!isServerUrlOk || token.isBlank()) return@LaunchedEffect delay(1000) checkingToken = true - val response = client.get("$serverUrl/auth") - if (response.status != HttpStatusCode.OK) { - checkingToken = false - needToCheckToken = false - isTokenOk = false - return@LaunchedEffect - } + try { + val response = client.get("$serverUrl/auth") { + headers { + append("Authorization", "Bearer $token") + } + } - auths = response.body>() - if (authId.isNotBlank()) { - authIndex = auths.indexOfFirst { it.id == authId } + if (response.status == HttpStatusCode.OK) { + auths = response.body() + authIndex = auths.indexOfFirst { it.id == authId } + isTokenOk = true + } + } catch (_: Exception) { + isTokenOk = false } checkingToken = false - needToCheckToken = false - isTokenOk = true } Scaffold( topBar = { TopAppBar( - colors = TopAppBarDefaults.topAppBarColors( - titleContentColor = MaterialTheme.colorScheme.primary - ), - title = { - Text(stringResource(R.string.auth)) - }, + title = { Text(stringResource(R.string.auth)) }, navigationIcon = { IconButton(onClick = { finish() }) { Icon( - imageVector = Icons.AutoMirrored.Outlined.ArrowBack, + ( Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = stringResource(R.string.go_back) ) } - } - ) - }, - modifier = Modifier.fillMaxSize() - ) { innerPadding -> + }) + }) { padding -> Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, modifier = Modifier - .padding(innerPadding) - .fillMaxSize() + .padding(padding) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center ) { Text(stringResource(R.string.auth), fontSize = 24.sp) Spacer(modifier = Modifier.height(16.dp)) @@ -179,69 +153,83 @@ class AuthActivity : ComponentActivity() { TextField( value = serverUrl, onValueChange = { - needToCheckUrl = true serverUrl = it + needToCheckUrl = true prefs.edit { - putString(getString(R.string.server_url_key), it) + putString( + getString(R.string.server_url_key), it + ) } }, label = { Text(stringResource(R.string.server_url)) }, singleLine = true, - isError = !isServerUrlOk, + isError = !isServerUrlOk && serverUrl.isNotBlank(), trailingIcon = { - if (checkingUrl) + if (checkingUrl) { CircularProgressIndicator( - modifier = Modifier.width(36.dp), - color = MaterialTheme.colorScheme.secondary, - trackColor = MaterialTheme.colorScheme.surfaceVariant + modifier = Modifier.size(24.dp) ) - } - ) + } + }) + Spacer(modifier = Modifier.height(12.dp)) TextField( value = token, onValueChange = { - needToCheckToken = true token = it + needToCheckToken = true prefs.edit { - putString(getString(R.string.token_key), it) + putString( + getString(R.string.token_key), it + ) } }, label = { Text(stringResource(R.string.token)) }, singleLine = true, - isError = isTokenOk, + enabled = isServerUrlOk, + isError = !isTokenOk && token.isNotBlank(), visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password + ), trailingIcon = { if (checkingToken) { CircularProgressIndicator( - modifier = Modifier.width(36.dp), - color = MaterialTheme.colorScheme.secondary, - trackColor = MaterialTheme.colorScheme.surfaceVariant + modifier = Modifier.size(24.dp) ) } - } - ) + }) + Spacer(modifier = Modifier.height(12.dp)) - if (auths.isNotEmpty()) + if (auths.isNotEmpty()) { DropdownList( - itemList = auths.map { it -> it.name }, - selectedIndex = 0, - modifier = Modifier, + itemList = auths.map { it.name }, + selectedIndex = authIndex.coerceAtLeast(0), onItemClick = { authIndex = it + authId = auths[it].id prefs.edit { - putInt(getString(R.string.auth_key), it) + putString( + getString(R.string.auth_key), authId + ) } - } + }, + modifier = Modifier ) + } + + Spacer(modifier = Modifier.height(16.dp)) FloatingActionButton( - onClick = { println("creating new auth!!") } - ) { - Icon(Icons.Default.Add, stringResource(R.string.create_auth)) + onClick = { + println("creating new auth!!") + }) { + Icon( + Icons.Default.Add, + contentDescription = stringResource(R.string.create_auth) + ) } } } diff --git a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt b/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt index a5f2142..91376ef 100644 --- a/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt +++ b/frontend/app/src/main/java/dev/svitan/antifed/ui/components/DropdownList.kt @@ -83,7 +83,6 @@ fun DropdownList( Text(text = item) } } - } } } diff --git a/frontend/app/src/main/res/values-sk/strings.xml b/frontend/app/src/main/res/values-sk/strings.xml index 30ca4de..90e3d43 100644 --- a/frontend/app/src/main/res/values-sk/strings.xml +++ b/frontend/app/src/main/res/values-sk/strings.xml @@ -7,4 +7,5 @@ Ísť späť Token Vytvoriť novú auth + Biometrika \ No newline at end of file diff --git a/frontend/app/src/main/res/values/strings.xml b/frontend/app/src/main/res/values/strings.xml index b38d3cb..a9cd420 100644 --- a/frontend/app/src/main/res/values/strings.xml +++ b/frontend/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ token auth_id Create new auth + Biometrics \ No newline at end of file diff --git a/frontend/gradle.properties b/frontend/gradle.properties index 20e2a01..4540b75 100644 --- a/frontend/gradle.properties +++ b/frontend/gradle.properties @@ -20,4 +20,14 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.resvalues=true +android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +android.enableAppCompileTimeRClass=false +android.usesSdkInManifest.disallowed=false +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.r8.optimizedResourceShrinking=false +android.builtInKotlin=false +android.newDsl=false \ No newline at end of file diff --git a/frontend/gradle/gradle-daemon-jvm.properties b/frontend/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..5c34300 --- /dev/null +++ b/frontend/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e99bae143b75f9a10ead10248f02055e/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/04e088f8677de3b384108493cc9481d0/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/56a19bc915b9ba2eb62ba7554c61b919/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/398ffe3949748bfb1d5636f023d228fd/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e55dccbfe27cb97945148c61a39c89c5/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/dbd05c4936d573642f94cd149e1356c8/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/frontend/gradle/libs.versions.toml b/frontend/gradle/libs.versions.toml index c4bee55..7e1dc6c 100644 --- a/frontend/gradle/libs.versions.toml +++ b/frontend/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -agp = "8.10.0" -kotlin = "2.0.21" +agp = "9.1.1" +kotlin = "2.2.10" coreKtx = "1.10.1" junit = "4.13.2" junitVersion = "1.1.5" diff --git a/frontend/gradle/wrapper/gradle-wrapper.properties b/frontend/gradle/wrapper/gradle-wrapper.properties index 61dfb22..c04b16a 100644 --- a/frontend/gradle/wrapper/gradle-wrapper.properties +++ b/frontend/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed May 14 20:14:06 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/frontend/settings.gradle.kts b/frontend/settings.gradle.kts index ee91deb..f8c75df 100644 --- a/frontend/settings.gradle.kts +++ b/frontend/settings.gradle.kts @@ -11,6 +11,9 @@ pluginManagement { gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories {