Merge pull request 'authentification and navigation' (#31) from authentification into master

Reviewed-on: #31
visualizer
Samuel BERION 1 year ago
commit 509909e13d

@ -73,6 +73,7 @@ dependencies {
implementation(libs.retrofit.adapters.arrow) implementation(libs.retrofit.adapters.arrow)
implementation(libs.arrow.core) implementation(libs.arrow.core)
implementation(libs.androidx.navigation.compose)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)

@ -7,21 +7,29 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.iqball.app.api.EitherBodyConverter import com.iqball.app.api.EitherBodyConverter
import com.iqball.app.api.EitherCallAdapterFactory import com.iqball.app.api.EitherCallAdapterFactory
import com.iqball.app.api.service.IQBallService import com.iqball.app.api.service.IQBallService
import com.iqball.app.page.HomePage
import com.iqball.app.page.LoginPage
import com.iqball.app.page.RegisterPage import com.iqball.app.page.RegisterPage
import com.iqball.app.session.DataSession
import com.iqball.app.session.Session
import com.iqball.app.ui.theme.IQBallTheme import com.iqball.app.ui.theme.IQBallTheme
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import retrofit2.Converter
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create import retrofit2.create
import java.lang.reflect.Type
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -46,8 +54,12 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
IQBallTheme { IQBallTheme {
// A surface container using the 'background' color from the theme // A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Surface(
App(service) modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val sessionState = remember { mutableStateOf<Session>(DataSession()) }
App(service, sessionState)
} }
} }
} }
@ -55,6 +67,36 @@ class MainActivity : ComponentActivity() {
} }
@Composable @Composable
fun App(service: IQBallService) { fun App(service: IQBallService, sessionState: MutableState<Session>) {
RegisterPage(service) val navController = rememberNavController()
NavHost(navController = navController, startDestination = "login") {
composable("login") {
LoginPage(
service = service,
onLoginSuccess = { auth ->
sessionState.value = DataSession(auth)
navController.navigate("home")
},
onNavigateToRegister = {
navController.navigate("register")
}
)
}
composable("register") {
RegisterPage(
service = service,
onRegisterSuccess = { auth ->
sessionState.value = DataSession(auth)
navController.navigate("home")
},
onNavigateToLogin = {
navController.navigate("login")
}
)
}
composable("home") {
HomePage(service = service, session = sessionState.value)
}
}
} }

@ -11,7 +11,7 @@ import retrofit2.http.POST
interface AuthService { interface AuthService {
@Serializable @Serializable
data class AuthResponse(val token: String, val expirationDate: String) data class AuthResponse(val token: String, val expirationDate: Long)
@Serializable @Serializable
data class RegisterRequest(val username: String, val email: String, val password: String) data class RegisterRequest(val username: String, val email: String, val password: String)

@ -0,0 +1,12 @@
package com.iqball.app.page
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import com.iqball.app.api.service.IQBallService
import com.iqball.app.session.Session
@Composable
fun HomePage(service: IQBallService, session: Session) {
Text(text = "HELLO WELCOME")
}

@ -2,8 +2,106 @@ package com.iqball.app.page
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import arrow.core.Either
import com.iqball.app.api.service.AuthService
import com.iqball.app.session.Authentication
import kotlinx.coroutines.runBlocking
@Composable @Composable
fun LoginPage() { fun LoginPage(
Text(text = "Login Page") service: AuthService,
onLoginSuccess: (Authentication) -> Unit,
onNavigateToRegister: () -> Unit
) {
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var errors by remember { mutableStateOf("") }
Surface(
color = Color.White,
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "S'identifier",
fontSize = 28.sp,
color = Color.Black
)
Text(
text = errors,
color = Color.Red,
fontSize = 14.sp,
modifier = Modifier.padding(vertical = 8.dp)
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") },
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.Black,
unfocusedTextColor = Color.Black
)
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.Black,
unfocusedTextColor = Color.Black
)
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = {
runBlocking {
when (val response = service.login(AuthService.LoginRequest(email, password))) {
is Either.Left -> {
errors = response.value.toList()
.flatMap { entry -> entry.second.map { "${entry.first} : ${it}" } }
.joinToString("\n")
}
is Either.Right -> onLoginSuccess(
Authentication(
response.value.token,
response.value.expirationDate.toLong()
)
)
}
}
}) {
Text(text = "Se connecter")
}
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { onNavigateToRegister() }) {
Text(text = "Vous n'avez pas de compte ?")
}
}
}
} }

@ -1,54 +1,130 @@
package com.iqball.app.page package com.iqball.app.page
import android.util.Log 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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import arrow.core.Either import arrow.core.Either
import com.iqball.app.api.service.AuthService import com.iqball.app.api.service.AuthService
import com.iqball.app.api.service.AuthService.RegisterRequest
import com.iqball.app.api.service.IQBallService
import com.iqball.app.session.Authentication import com.iqball.app.session.Authentication
import com.iqball.app.session.MutableSession
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@Composable @Composable
fun RegisterPage(service: IQBallService) { fun RegisterPage(
service: AuthService,
onRegisterSuccess: (Authentication) -> Unit,
onNavigateToLogin: () -> Unit
) {
var username by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var errors by remember { mutableStateOf("") }
Surface(
color = Color.White,
modifier = Modifier.fillMaxSize()
var text by remember { mutableStateOf("No message !") } ) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "S'enregistrer",
fontSize = 28.sp,
color = Color.Black
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = errors,
color = Color.Red,
fontSize = 14.sp,
modifier = Modifier.padding(vertical = 8.dp)
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = username,
onValueChange = { username = it },
label = { Text("Nom d'utilisateur") },
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.Black,
unfocusedTextColor = Color.Black
)
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text("Mot de passe") },
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.Black,
unfocusedTextColor = Color.Black
),
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") },
modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Color.Black,
unfocusedTextColor = Color.Black
)
)
Button(onClick = {
runBlocking { runBlocking {
val result = service.login(AuthService.LoginRequest("maxime@mail.com", "123456")) when (val response =
service.register(AuthService.RegisterRequest(username, email, password))) {
when (result) {
is Either.Left -> { is Either.Left -> {
println("Error : " + result.value) errors = response.value.toList()
text = result.toString() .flatMap { entry -> entry.second.map { "${entry.first} : ${it}" } }
.joinToString("\n")
} }
is Either.Right -> {
val token = result.value.token
val userDataResponse = service.getUserData(token)
when (userDataResponse) { is Either.Right -> {
is Either.Left -> println("Error User Data : " + userDataResponse.value) onRegisterSuccess(
is Either.Right -> println("Success User Data : " + userDataResponse.value) Authentication(
response.value.token,
response.value.expirationDate
)
)
} }
text = userDataResponse.toString()
} }
} }
}) {
println(result) Text(text = "Créer votre compte")
Log.i("%", result.toString()) }
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { onNavigateToLogin() }) {
Text(text = "Vous avez déjà un compte ?")
}
} }
Text(text = text)
} }
}

@ -1,5 +1,3 @@
package com.iqball.app.session package com.iqball.app.session
import kotlinx.datetime.LocalDateTime data class Authentication(val token: String, val expirationDate: Long)
data class Authentication(val token: String, val expirationDate: LocalDateTime)

@ -0,0 +1,3 @@
package com.iqball.app.session
data class DataSession(override val auth: Authentication? = null) : Session

@ -1,5 +0,0 @@
package com.iqball.app.session
interface MutableSession : Session {
override var auth: Authentication
}

@ -1,5 +1,5 @@
package com.iqball.app.session package com.iqball.app.session
interface Session { interface Session {
val auth: Authentication val auth: Authentication?
} }

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.3.0" agp = "8.2.2"
arrowCore = "1.2.1" arrowCore = "1.2.1"
converterGson = "2.9.0" converterGson = "2.9.0"
kotlin = "1.9.0" kotlin = "1.9.0"
@ -15,6 +15,7 @@ composeBom = "2023.08.00"
retrofit = "2.9.0" retrofit = "2.9.0"
retrofit2KotlinxSerializationConverter = "1.0.0" retrofit2KotlinxSerializationConverter = "1.0.0"
retrofitAdaptersArrow = "1.0.9" retrofitAdaptersArrow = "1.0.9"
navigationCompose = "2.7.7"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -38,6 +39,7 @@ kotlinx-serialization-json-jvm = { module = "org.jetbrains.kotlinx:kotlinx-seria
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofit-adapters-arrow = { module = "com.github.skydoves:retrofit-adapters-arrow", version.ref = "retrofitAdaptersArrow" } retrofit-adapters-arrow = { module = "com.github.skydoves:retrofit-adapters-arrow", version.ref = "retrofitAdaptersArrow" }
retrofit2-kotlinx-serialization-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "retrofit2KotlinxSerializationConverter" } retrofit2-kotlinx-serialization-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "retrofit2KotlinxSerializationConverter" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }

Loading…
Cancel
Save