diff --git a/api/migrations/20231221181946_create-tables.up.sql b/api/migrations/20231221181946_create-tables.up.sql index 1c9f7a7..8ffcd60 100644 --- a/api/migrations/20231221181946_create-tables.up.sql +++ b/api/migrations/20231221181946_create-tables.up.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS public.status ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - name VARCHAR(255) NOT NULL, + name TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), updated_at TIMESTAMP WITH TIME ZONE NULL ); @@ -21,9 +21,9 @@ VALUES CREATE TABLE IF NOT EXISTS public.user ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - email VARCHAR(255) NOT NULL, - password VARCHAR(97) NOT NULL, - name VARCHAR(255) NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + name TEXT NOT NULL, status_id INT NOT NULL REFERENCES status(id) DEFAULT 2, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), updated_at TIMESTAMP WITH TIME ZONE NULL @@ -34,7 +34,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS user_email_uniq_idx ON public.user(email); CREATE TABLE IF NOT EXISTS public.permission ( id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - name VARCHAR(255) NOT NULL, + name TEXT NOT NULL, status_id INT NOT NULL REFERENCES status(id) DEFAULT 1, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), updated_at TIMESTAMP WITH TIME ZONE NULL diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt index 6c20715..b2038d1 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreen.kt @@ -27,9 +27,11 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -41,7 +43,9 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import ing.bikeshedengineer.debtpirate.domain.model.AccountManagerResult import ing.bikeshedengineer.debtpirate.domain.repository.AccountManager +import kotlinx.coroutines.launch @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable @@ -53,6 +57,20 @@ fun LoginScreen( AccountManager(context) } + val coroutineScope = rememberCoroutineScope() + LaunchedEffect(true) { + coroutineScope.launch { + val result = accountManager.getCredentials() + when (result) { + is AccountManagerResult.FoundCredentials -> { + val (emailAddress, password) = result + } + + else -> {} + } + } + } + Scaffold( modifier = Modifier.fillMaxSize() ) { @@ -74,7 +92,7 @@ fun LoginScreen( LoginComponent( emailAddress = emailAddress, onUpdateEmailAddress = { - viewModel.updateState( + viewModel.onAction( LoginScreenStateAction.UpdateEmailAddress( it ) @@ -82,13 +100,20 @@ fun LoginScreen( }, password = password, onUpdatePassword = { - viewModel.updateState( + viewModel.onAction( LoginScreenStateAction.UpdatePassword( it ) ) }, - submitLoginRequest = viewModel::submitLoginRequest + submitLoginRequest = { + viewModel.onAction( + LoginScreenStateAction.ValidateCredentials( + emailAddress.value, + password.value + ) + ) + } ) Separator( diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenStateAction.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenStateAction.kt index 88faa43..c8d00a4 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenStateAction.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenStateAction.kt @@ -3,4 +3,6 @@ package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login sealed interface LoginScreenStateAction { data class UpdateEmailAddress(val emailAddress: String) : LoginScreenStateAction data class UpdatePassword(val password: String) : LoginScreenStateAction + data class ValidateCredentials(val emailAddress: String, val password: String) : LoginScreenStateAction + data class SubmitLoginRequest(val emailAddress: String, val password: String) : LoginScreenStateAction } \ No newline at end of file diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt index 2377853..d825a8b 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/login/LoginScreenViewModel.kt @@ -41,15 +41,13 @@ class LoginScreenViewModel @Inject constructor( private val submitLoginCredentials: SubmitLoginCredentialsUseCase, private val validateLoginCredentials: ValidateLoginCredentialsUseCase, ) : ViewModel() { -// private val storeLoginData = StoreLoginDataUseCase(dataStore) - private val _emailAddress = MutableStateFlow("") val emailAddress = _emailAddress.asStateFlow() private val _password = MutableStateFlow("") val password = _password.asStateFlow() - fun updateState(action: LoginScreenStateAction) { + fun onAction(action: LoginScreenStateAction) { when (action) { is LoginScreenStateAction.UpdateEmailAddress -> { _emailAddress.value = action.emailAddress @@ -58,19 +56,25 @@ class LoginScreenViewModel @Inject constructor( is LoginScreenStateAction.UpdatePassword -> { _password.value = action.password } + + is LoginScreenStateAction.ValidateCredentials -> { + val (emailAddress, password) = action + onValidateLoginCredentials(emailAddress, password) + } + + is LoginScreenStateAction.SubmitLoginRequest -> { + viewModelScope.launch { + val (emailAddress, password) = action + onSubmitLoginRequest(emailAddress, password) + } + } } } - fun submitLoginRequest() { - when (validateLoginCredentials(emailAddress.value, password.value)) { + private fun onValidateLoginCredentials(emailAddress: String, password: String) { + when (validateLoginCredentials(emailAddress, password)) { is LoginCredentialsValidationResult.ValidCredentials -> { - viewModelScope.launch { - try { - val result = submitLoginCredentials(emailAddress.value, password.value) - } catch (err: Throwable) { - // TODO... - } - } + onAction(LoginScreenStateAction.SubmitLoginRequest(emailAddress, password)) } else -> { @@ -80,6 +84,14 @@ class LoginScreenViewModel @Inject constructor( } + private suspend fun onSubmitLoginRequest(emailAddress: String, password: String) { + try { + val result = submitLoginCredentials(emailAddress, password) + } catch (err: Throwable) { + // TODO... + } + } + fun storeAuthData(userId: Int, sessionToken: Token, authToken: Token) { viewModelScope.launch { prefsStore.updateData { currentPrefs -> diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt index 82c113c..1e4bab0 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreen.kt @@ -1,7 +1,6 @@ package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.register import android.annotation.SuppressLint -import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Column diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenAction.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenAction.kt index 3b3b67f..cff7d64 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenAction.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/app/screen/auth/presentation/register/RegistrationScreenAction.kt @@ -6,5 +6,10 @@ sealed interface RegistrationScreenAction { data class UpdatePassword(val password: String) : RegistrationScreenAction data class UpdateConfirmPassword(val confirmPassword: String) : RegistrationScreenAction data object ResetFields : RegistrationScreenAction - data class RegisterNewUser(val emailAddress: String, val name: String, val password: String, val confirmPassword: String) : RegistrationScreenAction + data class RegisterNewUser( + val emailAddress: String, + val name: String, + val password: String, + val confirmPassword: String + ) : RegistrationScreenAction } \ No newline at end of file diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/model/AccountManagerResult.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/model/AccountManagerResult.kt index 45607b7..e7163cd 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/model/AccountManagerResult.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/model/AccountManagerResult.kt @@ -2,7 +2,9 @@ package ing.bikeshedengineer.debtpirate.domain.model sealed interface AccountManagerResult { data object Success : AccountManagerResult + data class FoundCredentials(val emailAddress: String, val password: String) : AccountManagerResult data object Unavailable : AccountManagerResult data object Canceled : AccountManagerResult data object Failure : AccountManagerResult + data object Invalid : AccountManagerResult } \ No newline at end of file diff --git a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt index 3cdf117..f7bd7bb 100644 --- a/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt +++ b/app/app/src/main/java/ing/bikeshedengineer/debtpirate/domain/repository/AccountManager.kt @@ -4,6 +4,9 @@ import android.app.Activity import android.util.Log import androidx.credentials.CreatePasswordRequest import androidx.credentials.CredentialManager +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetPasswordOption +import androidx.credentials.PasswordCredential import androidx.credentials.exceptions.CreateCredentialCancellationException import androidx.credentials.exceptions.CreateCredentialException import androidx.credentials.exceptions.CreateCredentialNoCreateOptionException @@ -30,11 +33,41 @@ class AccountManager(private val context: Activity) { "DebtPirate::AccountManager", "Cannot store credentials; a Google account isn't associated with this device" ) + AccountManagerResult.Unavailable } catch (_: CreateCredentialCancellationException) { AccountManagerResult.Canceled } catch (err: CreateCredentialException) { err.printStackTrace() + Log.i( + "DebtPirate::AccountManager", + "Unable to store credentials: ${err.message}" + ) + + AccountManagerResult.Failure + } + } + + suspend fun getCredentials(): AccountManagerResult { + return try { + val result = credentialManager.getCredential( + context, request = GetCredentialRequest( + credentialOptions = listOf(GetPasswordOption(isAutoSelectAllowed = true)) + ) + ) + + val credentials = result.credential + when (credentials) { + is PasswordCredential -> { + val emailAddress = credentials.id + val password = credentials.password + + AccountManagerResult.FoundCredentials(emailAddress, password) + } + + else -> AccountManagerResult.Invalid + } + } catch (err: Exception) { AccountManagerResult.Failure } }