diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml
new file mode 100644
index 0000000..3dc2238
--- /dev/null
+++ b/.idea/appInsightsSettings.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..7643783
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..fb7f4a8
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..d016070
--- /dev/null
+++ b/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..1105b2c
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..44ca2d9
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..fdf8d99
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6e4b8a0
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt b/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt
index bdbdfb8..0665798 100644
--- a/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt
+++ b/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt
@@ -1,7 +1,6 @@
package com.stslex.aproselection.controller
import com.stslex.aproselection.core.datastore.AppDataStore
-import com.stslex.aproselection.core.network.client.NetworkClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -12,7 +11,6 @@ import kotlin.coroutines.coroutineContext
class AuthControllerImpl(
private val dataStore: AppDataStore,
- private val networkClient: NetworkClient,
) : AuthController {
private val _isAuth = MutableStateFlow(null)
@@ -22,15 +20,7 @@ class AuthControllerImpl(
override suspend fun init() {
dataStore.token
.onEach { token ->
- if (token.isBlank()) {
- networkClient.regenerateToken()
- }
- }
- .launchIn(CoroutineScope(coroutineContext))
-
- dataStore.uuid
- .onEach { uuid ->
- _isAuth.emit(uuid.isNotBlank())
+ _isAuth.emit(token.isNotBlank())
}
.launchIn(CoroutineScope(coroutineContext))
}
diff --git a/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStore.kt b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStore.kt
index d0df066..1caa121 100644
--- a/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStore.kt
+++ b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStore.kt
@@ -4,13 +4,13 @@ import kotlinx.coroutines.flow.StateFlow
interface AppDataStore {
- val uuid: StateFlow
val token: StateFlow
-
- suspend fun setUuid(uuid: String)
+ val credential: StateFlow
suspend fun setToken(token: String)
+ suspend fun setCredential(credential: UserCredential)
+
suspend fun clear()
suspend fun init()
diff --git a/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStoreImpl.kt b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStoreImpl.kt
index b4d4784..a869fef 100644
--- a/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStoreImpl.kt
+++ b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/AppDataStoreImpl.kt
@@ -23,31 +23,35 @@ class AppDataStoreImpl(
companion object {
private const val DATA_STORE_KEY = "app_data_store"
- private val UUID_KEY = stringPreferencesKey("UUID_KEY")
private val TOKEN_KEY = stringPreferencesKey("TOKEN_KEY")
+ private val USERNAME_KEY = stringPreferencesKey("USERNAME_KEY")
+ private val PASSWORD_KEY = stringPreferencesKey("PASSWORD_KEY")
private val Context.dataStore: DataStore by preferencesDataStore(DATA_STORE_KEY)
}
- private val _uuid = MutableStateFlow("")
- override val uuid: StateFlow
- get() = _uuid.asStateFlow()
-
private val _token = MutableStateFlow("")
override val token: StateFlow
get() = _token.asStateFlow()
- override suspend fun setUuid(uuid: String) {
+ private val _credential = MutableStateFlow(UserCredential())
+ override val credential: StateFlow
+ get() = _credential.asStateFlow()
+
+ override suspend fun setToken(token: String) {
+ _token.emit(token)
context.dataStore.updateData { prefs ->
prefs.toMutablePreferences().apply {
- set(UUID_KEY, uuid)
+ set(TOKEN_KEY, token)
}
}
}
- override suspend fun setToken(token: String) {
+ override suspend fun setCredential(credential: UserCredential) {
+ _credential.emit(credential)
context.dataStore.updateData { prefs ->
prefs.toMutablePreferences().apply {
- set(TOKEN_KEY, token)
+ set(USERNAME_KEY, credential.username)
+ set(PASSWORD_KEY, credential.password)
}
}
}
@@ -58,7 +62,6 @@ class AppDataStoreImpl(
Logger.exception(error)
}
.onEach { prefs ->
- _uuid.value = prefs[UUID_KEY].orEmpty()
_token.value = prefs[TOKEN_KEY].orEmpty()
}
.flowOn(Dispatchers.IO)
@@ -66,7 +69,6 @@ class AppDataStoreImpl(
}
override suspend fun clear() {
- setUuid("")
setToken("")
}
}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/UserCredential.kt b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/UserCredential.kt
new file mode 100644
index 0000000..5744a27
--- /dev/null
+++ b/core/datastore/src/main/java/com/stslex/aproselection/core/datastore/UserCredential.kt
@@ -0,0 +1,6 @@
+package com.stslex.aproselection.core.datastore
+
+data class UserCredential(
+ val username: String = "",
+ val password: String = ""
+)
diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClient.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClient.kt
index 6de8039..83ba85d 100644
--- a/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClient.kt
+++ b/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClient.kt
@@ -1,10 +1,12 @@
package com.stslex.aproselection.core.network.client
+import com.stslex.aproselection.core.network.clients.auth.model.UserAuthResponseModel
import io.ktor.client.HttpClient
+import kotlinx.coroutines.flow.SharedFlow
interface NetworkClient {
val apiClient: HttpClient
- suspend fun regenerateToken(): String
+ suspend fun auth(): UserAuthResponseModel
}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClientImpl.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClientImpl.kt
index d22d67c..cda5568 100644
--- a/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClientImpl.kt
+++ b/core/network/src/main/java/com/stslex/aproselection/core/network/client/NetworkClientImpl.kt
@@ -2,7 +2,8 @@ package com.stslex.aproselection.core.network.client
import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.BuildConfig
-import com.stslex.aproselection.core.network.clients.auth.model.TokenResponse
+import com.stslex.aproselection.core.network.clients.auth.model.UserAuthResponseModel
+import com.stslex.aproselection.core.network.clients.auth.model.UserAuthSendModel
import com.stslex.aproselection.core.network.model.ApiError
import com.stslex.aproselection.core.network.model.ApiErrorRespond
import com.stslex.aproselection.core.network.model.ApiErrorType
@@ -22,13 +23,16 @@ import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.HttpRequest
-import io.ktor.client.request.get
+import io.ktor.client.request.post
+import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.URLProtocol
import io.ktor.http.appendPathSegments
import io.ktor.http.contentType
import io.ktor.http.encodedPath
import io.ktor.serialization.kotlinx.json.json
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
@@ -51,10 +55,6 @@ class NetworkClientImpl(
val token = dataStore.token.value
BearerTokens(token, token)
}
- refreshTokens {
- val token = regenerateToken()
- BearerTokens(token, token)
- }
}
}
@@ -80,9 +80,7 @@ class NetworkClientImpl(
host = BuildConfig.API_SERVER_HOST
encodedPath = BuildConfig.API_VERSION
protocol = URLProtocol.HTTP
- headers.append("API_KEY", BuildConfig.API_KEY)
- headers.append("DEVICE_ID", "test")
- headers.append("uuid", dataStore.uuid.value)
+ headers.append(API_KEY_HEADER, BuildConfig.API_KEY)
contentType(ContentType.Application.Json)
}
}
@@ -102,19 +100,35 @@ class NetworkClientImpl(
)
when (apiError.type) {
- is ApiErrorType.Unauthorized.Token -> regenerateToken()
+ is ApiErrorType.Unauthorized.Token -> {
+ dataStore.setToken(auth().token)
+ }
+
else -> throw apiError
}
}
- override suspend fun regenerateToken(): String {
- val token = apiClient
- .get {
- url.appendPathSegments("token")
- }
- .body()
- .token
- dataStore.setToken(token)
- return token
+ override suspend fun auth(): UserAuthResponseModel = withContext(Dispatchers.IO) {
+ val credential = dataStore.credential.value
+ val username = credential.username
+ val password = credential.password
+ if (username.isBlank() || password.isBlank()) {
+ throw ApiError(
+ message = "need auth",
+ type = ApiErrorType.Authentication.InvalidPassword
+ )
+ }
+ val user = UserAuthSendModel(
+ username = dataStore.credential.value.username,
+ password = dataStore.credential.value.password
+ )
+ apiClient.post {
+ url.appendPathSegments("passport/auth")
+ setBody(user)
+ }.body()
+ }
+
+ companion object {
+ private const val API_KEY_HEADER = "x-api-key"
}
}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClient.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClient.kt
index 8d8c160..5e30945 100644
--- a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClient.kt
+++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClient.kt
@@ -11,5 +11,5 @@ interface AuthNetworkClient {
fun auth(user: UserAuthSendModel): Flow
- fun register(user: UserAuthSendModel): Flow
+ suspend fun register(user: UserAuthSendModel)
}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClientImpl.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClientImpl.kt
index 748dd9d..b1b0b82 100644
--- a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClientImpl.kt
+++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/AuthNetworkClientImpl.kt
@@ -1,9 +1,11 @@
package com.stslex.aproselection.core.network.clients.auth
+import com.stslex.aproselection.core.datastore.AppDataStore
import com.stslex.aproselection.core.network.client.NetworkClient
import com.stslex.aproselection.core.network.clients.auth.model.HelloRequestModel
import com.stslex.aproselection.core.network.clients.auth.model.UserAuthResponseModel
import com.stslex.aproselection.core.network.clients.auth.model.UserAuthSendModel
+import com.stslex.aproselection.core.network.clients.auth.model.toStorage
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.post
@@ -16,7 +18,8 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
class AuthNetworkClientImpl(
- private val networkClient: NetworkClient
+ private val networkClient: NetworkClient,
+ private val dataStore: AppDataStore
) : AuthNetworkClient {
override suspend fun getHello(
@@ -33,28 +36,20 @@ class AuthNetworkClientImpl(
override fun auth(
user: UserAuthSendModel
): Flow = flow {
- val result = networkClient
- .apiClient
- .post {
- url.appendPathSegments("passport/auth")
- setBody(user)
- }
- .body()
- emit(result)
- }
- .flowOn(Dispatchers.IO)
+ dataStore.setCredential(user.toStorage())
+ emit(networkClient.auth())
+ }.flowOn(Dispatchers.IO)
- override fun register(
+ override suspend fun register(
user: UserAuthSendModel
- ): Flow = flow {
- val result = networkClient
- .apiClient
- .post {
- url.appendPathSegments("passport/register")
- setBody(user)
- }
- .body()
- emit(result)
+ ) {
+ withContext(Dispatchers.IO) {
+ networkClient
+ .apiClient
+ .post {
+ url.appendPathSegments("passport/register")
+ setBody(user)
+ }
+ }
}
- .flowOn(Dispatchers.IO)
}
\ No newline at end of file
diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/model/UserAuthMapper.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/model/UserAuthMapper.kt
new file mode 100644
index 0000000..76d3fa3
--- /dev/null
+++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/auth/model/UserAuthMapper.kt
@@ -0,0 +1,8 @@
+package com.stslex.aproselection.core.network.clients.auth.model
+
+import com.stslex.aproselection.core.datastore.UserCredential
+
+fun UserAuthSendModel.toStorage() = UserCredential(
+ username = username,
+ password = password
+)
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/components/buttons/SwipeableButton.kt b/core/ui/src/main/java/com/stslex/aproselection/core/ui/components/buttons/SwipeableButton.kt
new file mode 100644
index 0000000..0bf5feb
--- /dev/null
+++ b/core/ui/src/main/java/com/stslex/aproselection/core/ui/components/buttons/SwipeableButton.kt
@@ -0,0 +1,233 @@
+package com.stslex.aproselection.core.ui.components.buttons
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.stslex.aproselection.core.ui.ext.toPx
+import com.stslex.aproselection.core.ui.theme.AppTheme
+import kotlin.math.PI
+import kotlin.math.abs
+import kotlin.math.cos
+import kotlin.math.roundToInt
+import kotlin.math.sin
+import kotlin.math.sqrt
+
+@Composable
+fun SwipeableButton(
+ modifier: Modifier = Modifier,
+) {
+ val stepsButtonCount = 10
+ val stepsContainerSize = 10
+ val containerSizeInit = 100.dp.toPx
+ val containerSizeStep = 10.dp.toPx
+
+ val stepsCircleSize = 20
+ val circleSizeInit = 4.dp.toPx
+ val circleSizeStep = 1.dp.toPx
+
+ var containerInputSize by remember {
+ mutableFloatStateOf(0f)
+ }
+
+ val containerSize by remember {
+ derivedStateOf {
+ containerSizeInit + (containerInputSize * stepsContainerSize) * containerSizeStep
+ }
+ }
+
+ var circleInputSize by remember {
+ mutableFloatStateOf(0f)
+ }
+
+ val circleSize by remember {
+ derivedStateOf {
+ circleSizeInit + (circleInputSize * stepsCircleSize) * circleSizeStep
+ }
+ }
+
+ var inputCount by remember {
+ mutableFloatStateOf(0f)
+ }
+
+ val buttonsCount by remember {
+ derivedStateOf {
+ (inputCount * stepsButtonCount).roundToInt()
+ }
+ }
+
+ val radDiff = 360f / buttonsCount
+
+
+ var dragAmountX by remember {
+ mutableFloatStateOf(0f)
+ }
+
+ var dragAmountY by remember {
+ mutableFloatStateOf(0f)
+ }
+
+ val dragAmount by remember {
+ derivedStateOf {
+ val min =
+ if (
+ (dragAmountX < 0 && dragAmountY > 0) ||
+ (dragAmountY < 0 && dragAmountX > 0)
+ ) {
+ -1
+ } else {
+ 1
+ }
+ min * sqrt(abs(dragAmountX * dragAmountY))
+ }
+ }
+
+
+ Box(
+ Modifier
+ .fillMaxSize()
+ .pointerInput(Unit) {
+ detectDragGestures { change, dragAmount ->
+ dragAmountX = change.position.x
+ dragAmountY = change.position.y
+ }
+ }
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(top = 32.dp),
+ ) {
+ Text(
+ modifier = Modifier.padding(8.dp),
+ text = "x = $dragAmountX \ny = $dragAmountY \na = $dragAmount",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ Text(
+ modifier = Modifier
+ .padding(8.dp),
+ text = "count = $buttonsCount",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ Slider(
+ modifier = Modifier.padding(8.dp),
+ value = inputCount,
+ onValueChange = {
+ inputCount = it
+ },
+ steps = stepsButtonCount
+ )
+ Text(
+ modifier = Modifier
+ .padding(8.dp),
+ text = "containerSize = $containerSize Px",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ Slider(
+ modifier = Modifier.padding(8.dp),
+ value = containerInputSize,
+ onValueChange = {
+ containerInputSize = it
+ },
+ steps = stepsContainerSize
+ )
+ Text(
+ modifier = Modifier
+ .padding(8.dp),
+ text = "circleSize = $circleSize Px",
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ Slider(
+ modifier = Modifier.padding(8.dp),
+ value = circleInputSize,
+ onValueChange = {
+ circleInputSize = it
+ },
+ steps = stepsCircleSize
+ )
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .size(with(LocalDensity.current) { containerSize.toDp() })
+ .align(Alignment.CenterHorizontally)
+ .drawBehind {
+ val radius = containerSize * .5f
+
+ for (index in 0 until buttonsCount) {
+
+ val radian = (index * radDiff + dragAmount) * (PI.toFloat() / 180f)
+
+ val xRad = sin(radian)
+ val x = xRad * radius + center.x
+
+ val yRad = cos(radian)
+ val y = yRad * radius + center.y
+
+ val offset = Offset(
+ x = x,
+ y = y
+ )
+
+ drawCircle(
+ color = Color.White,
+ center = offset,
+ radius = circleSize
+ )
+ }
+
+ drawCircle(
+ color = Color.White,
+ center = center,
+ radius = radius,
+ style = Stroke(1.dp.toPx())
+ )
+ },
+ )
+ }
+
+
+ }
+
+}
+
+@Preview
+@Composable
+fun SwipeableButtonPreview() {
+ AppTheme(true) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background),
+ ) {
+ SwipeableButton(
+ modifier = Modifier.align(Alignment.Center)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/ext/UiExt.kt b/core/ui/src/main/java/com/stslex/aproselection/core/ui/ext/UiExt.kt
index d28219d..8041506 100644
--- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/ext/UiExt.kt
+++ b/core/ui/src/main/java/com/stslex/aproselection/core/ui/ext/UiExt.kt
@@ -23,4 +23,9 @@ fun Modifier.noRippleClick(
val Dp.toPx: Float
@ReadOnlyComposable
@Composable
- get() = with(LocalDensity.current) { toPx() }
\ No newline at end of file
+ get() = with(LocalDensity.current) { toPx() }
+
+val Dp.roundToPx: Int
+ @ReadOnlyComposable
+ @Composable
+ get() = with(LocalDensity.current) { roundToPx() }
\ No newline at end of file
diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepository.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepository.kt
index 69942ef..614fdd5 100644
--- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepository.kt
+++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepository.kt
@@ -7,5 +7,5 @@ interface AuthRepository {
fun auth(username: String, password: String): Flow
- fun register(username: String, password: String): Flow
+ suspend fun register(username: String, password: String)
}
\ No newline at end of file
diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepositoryImpl.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepositoryImpl.kt
index abab613..af65a7a 100644
--- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepositoryImpl.kt
+++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/data/repository/AuthRepositoryImpl.kt
@@ -1,6 +1,7 @@
package com.stslex.aproselection.feature.auth.data.repository
import com.stslex.aproselection.core.datastore.AppDataStore
+import com.stslex.aproselection.core.datastore.UserCredential
import com.stslex.aproselection.core.network.clients.auth.AuthNetworkClient
import com.stslex.aproselection.core.network.clients.auth.model.UserAuthSendModel
import com.stslex.aproselection.feature.auth.data.model.AuthMapper.toData
@@ -22,23 +23,21 @@ class AuthRepositoryImpl(
)
.onEach { response ->
dataSource.setToken(response.token)
- dataSource.setUuid(response.uuid)
+ dataSource.setCredential(
+ UserCredential(
+ username = username,
+ password = password
+ )
+ )
}
.map { user ->
user.toData()
}
- override fun register(
+ override suspend fun register(
username: String,
password: String
- ): Flow = networkClient.register(
- UserAuthSendModel(username, password)
- )
- .onEach { response ->
- dataSource.setToken(response.token)
- dataSource.setUuid(response.uuid)
- }
- .map { user ->
- user.toData()
- }
+ ) {
+ networkClient.register(UserAuthSendModel(username, password))
+ }
}
\ No newline at end of file
diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractor.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractor.kt
index e5a2355..0948123 100644
--- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractor.kt
+++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractor.kt
@@ -7,5 +7,5 @@ interface AuthInteractor {
fun auth(username: String, password: String): Flow
- fun register(username: String, password: String): Flow
+ suspend fun register(username: String, password: String)
}
\ No newline at end of file
diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractorImpl.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractorImpl.kt
index e62839b..82cbbdb 100644
--- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractorImpl.kt
+++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/domain/interactor/AuthInteractorImpl.kt
@@ -16,11 +16,13 @@ class AuthInteractorImpl(
password = password
)
- override fun register(
+ override suspend fun register(
username: String,
password: String
- ): Flow = repository.register(
- username = username,
- password = password
- )
+ ) {
+ repository.register(
+ username = username,
+ password = password
+ )
+ }
}
\ No newline at end of file
diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt
index 0ddea0d..3d84ae9 100644
--- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt
+++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt
@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
class AuthViewModel(
private val interactor: AuthInteractor,
@@ -89,26 +90,29 @@ class AuthViewModel(
val state = screenState.value
setLoadingState(ScreenLoadingState.Loading)
- interactor
- .register(
- username = state.username,
- password = state.password
- )
- .catch { throwable ->
- _screenEvents.emit(ScreenEvent.Error(throwable))
- setLoadingState(ScreenLoadingState.Content)
- handleError(throwable)
- }
- .onEach {
- _screenState.update { currentState ->
- currentState.copy(
- screenLoadingState = ScreenLoadingState.Content,
- authFieldsState = AuthFieldsState.AUTH
+ viewModelScope.launch {
+ runCatching {
+ interactor
+ .register(
+ username = state.username,
+ password = state.password
)
- }
- _screenEvents.emit(ScreenEvent.SuccessRegistered)
}
- .launchIn(viewModelScope)
+ .onFailure { throwable ->
+ _screenEvents.emit(ScreenEvent.Error(throwable))
+ setLoadingState(ScreenLoadingState.Content)
+ handleError(throwable)
+ }
+ .onSuccess {
+ _screenState.update { currentState ->
+ currentState.copy(
+ screenLoadingState = ScreenLoadingState.Content,
+ authFieldsState = AuthFieldsState.AUTH
+ )
+ }
+ _screenEvents.emit(ScreenEvent.SuccessRegistered)
+ }
+ }
}
private fun auth() {