Start saving data to proto datastore from successful rest api requests
This commit is contained in:
parent
78a4b75acd
commit
ece116a411
14 changed files with 131 additions and 59 deletions
|
@ -111,13 +111,13 @@ async fn register_new_user_request(
|
|||
|
||||
UserRegistrationResponse {
|
||||
user_id,
|
||||
expires_at,
|
||||
expires_at: None,
|
||||
session_token: None,
|
||||
}
|
||||
} else {
|
||||
UserRegistrationResponse {
|
||||
user_id,
|
||||
expires_at,
|
||||
expires_at: Some(expires_at),
|
||||
session_token: Some(verification_token),
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ pub struct UserRegistrationResponse {
|
|||
pub user_id: i32,
|
||||
|
||||
#[serde(serialize_with = "humantime_serde::serialize")]
|
||||
pub expires_at: SystemTime,
|
||||
pub expires_at: Option<SystemTime>,
|
||||
|
||||
pub session_token: Option<String>,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.datastore.core.DataStore
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import ing.bikeshedengineer.debtpirate.PrefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.AppDataStore
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.SubmitNewUserVerificationRequestUseCase
|
||||
import ing.bikeshedengineer.debtpirate.navigation.Navigator
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -16,7 +16,7 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class ConfirmationScreenViewModel @Inject constructor(
|
||||
private val navigator: Navigator,
|
||||
private val prefsStore: DataStore<PrefsDataStore>,
|
||||
private val prefsStore: DataStore<AppDataStore>,
|
||||
private val verifyNewUser: SubmitNewUserVerificationRequestUseCase
|
||||
) : ViewModel() {
|
||||
private var _isLoading = MutableStateFlow(true)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.background
|
||||
|
@ -76,8 +75,10 @@ fun LoginScreen(
|
|||
val toastMessages = viewModel.toastMessages.collectAsState("")
|
||||
LaunchedEffect(toastMessages.value) {
|
||||
val message = toastMessages.value
|
||||
Log.d("LoginScreen", message)
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
|
||||
if (message.isNotEmpty()) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
package ing.bikeshedengineer.debtpirate.app.screen.auth.presentation.login
|
||||
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import ing.bikeshedengineer.debtpirate.PrefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.LoginCredentialsValidationResult
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.SubmitLoginCredentialsUseCase
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.ValidateLoginCredentialsUseCase
|
||||
import ing.bikeshedengineer.debtpirate.domain.model.Token
|
||||
import ing.bikeshedengineer.debtpirate.domain.repository.InvalidCredentialsException
|
||||
import ing.bikeshedengineer.debtpirate.domain.usecase.UpdateStoreDataUseCase
|
||||
import ing.bikeshedengineer.debtpirate.navigation.Destination
|
||||
import ing.bikeshedengineer.debtpirate.navigation.Navigator
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
@ -37,13 +34,12 @@ enum class InvalidReason {
|
|||
PasswordTooShort
|
||||
}
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class LoginScreenViewModel @Inject constructor(
|
||||
private val navigator: Navigator,
|
||||
private val prefsStore: DataStore<PrefsDataStore>,
|
||||
private val submitLoginCredentials: SubmitLoginCredentialsUseCase,
|
||||
private val validateLoginCredentials: ValidateLoginCredentialsUseCase,
|
||||
private val updateStoreData: UpdateStoreDataUseCase
|
||||
) : ViewModel() {
|
||||
private val _emailAddress = MutableStateFlow("")
|
||||
val emailAddress = _emailAddress.asStateFlow()
|
||||
|
@ -93,28 +89,23 @@ class LoginScreenViewModel @Inject constructor(
|
|||
|
||||
private suspend fun onSubmitLoginRequest(emailAddress: String, password: String) {
|
||||
try {
|
||||
val result = submitLoginCredentials(emailAddress, password)
|
||||
val (userId, auth, session) = submitLoginCredentials(emailAddress, password)
|
||||
updateStoreData(
|
||||
userId = userId,
|
||||
authToken = auth.token,
|
||||
authTokenExpiresAt = auth.expiresAt,
|
||||
sessionToken = session.token,
|
||||
sessionTokenExpiresAt = session.expiresAt
|
||||
)
|
||||
} catch (err: Exception) {
|
||||
_toastMessages.emit("Invalid Email Address or Password")
|
||||
}
|
||||
}
|
||||
when (err) {
|
||||
is InvalidCredentialsException -> {
|
||||
_toastMessages.emit("Invalid Email Address or Password")
|
||||
}
|
||||
|
||||
fun storeAuthData(userId: Int, sessionToken: Token, authToken: Token) {
|
||||
viewModelScope.launch {
|
||||
prefsStore.updateData { currentPrefs ->
|
||||
val updatedSessionToken = currentPrefs.sessionToken.toBuilder()
|
||||
.setToken(sessionToken.token)
|
||||
.setExpiresAt(sessionToken.expiresAt.toEpochSecond())
|
||||
|
||||
val updatedAuthToken = currentPrefs.authToken.toBuilder()
|
||||
.setToken(authToken.token)
|
||||
.setExpiresAt(authToken.expiresAt.toEpochSecond())
|
||||
|
||||
currentPrefs.toBuilder()
|
||||
.setUserId(userId)
|
||||
.setSessionToken(updatedSessionToken)
|
||||
.setAuthToken(updatedAuthToken)
|
||||
.build()
|
||||
else -> {
|
||||
_toastMessages.emit("Cannot Login, Please Try Again Later")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -231,9 +232,19 @@ private fun RegistrationComponent(
|
|||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.None,
|
||||
keyboardType = KeyboardType.Password,
|
||||
imeAction = ImeAction.Done
|
||||
imeAction = ImeAction.Send
|
||||
),
|
||||
onValueChange = { viewModel.onAction(RegistrationScreenAction.UpdateConfirmPassword(it)) },
|
||||
keyboardActions = KeyboardActions(
|
||||
onSend = {
|
||||
viewModel.registerNewAccount(
|
||||
emailAddress.value,
|
||||
name.value,
|
||||
password.value,
|
||||
confirmPassword.value
|
||||
)
|
||||
}
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(PaddingValues(top = 4.dp))
|
||||
|
|
|
@ -6,6 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.NewAccountRegistrationValidationResult
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.SubmitAccountRegistrationRequestUseCase
|
||||
import ing.bikeshedengineer.debtpirate.app.screen.auth.usecase.ValidateNewAccountRegistrationUseCase
|
||||
import ing.bikeshedengineer.debtpirate.domain.usecase.UpdateStoreDataUseCase
|
||||
import ing.bikeshedengineer.debtpirate.navigation.Destination
|
||||
import ing.bikeshedengineer.debtpirate.navigation.Navigator
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
@ -21,6 +22,7 @@ class RegistrationScreenViewModel @Inject constructor(
|
|||
private val navigator: Navigator,
|
||||
private val validateNewAccount: ValidateNewAccountRegistrationUseCase,
|
||||
private val submitAccountRegistrationRequest: SubmitAccountRegistrationRequestUseCase,
|
||||
private val updateStoreData: UpdateStoreDataUseCase,
|
||||
) : ViewModel() {
|
||||
fun navigateUp() {
|
||||
viewModelScope.launch {
|
||||
|
@ -113,10 +115,17 @@ class RegistrationScreenViewModel @Inject constructor(
|
|||
if (fieldsAreValid) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// TODO: Store the registration result data in the store
|
||||
val result =
|
||||
submitAccountRegistrationRequest(emailAddress, name, confirmPassword)
|
||||
|
||||
val (userId, expiresAt, sessionToken) = result
|
||||
|
||||
updateStoreData(
|
||||
userId = result.userId,
|
||||
sessionToken = sessionToken,
|
||||
sessionTokenExpiresAt = expiresAt
|
||||
)
|
||||
|
||||
_onRegistrationComplete.emit(Pair(emailAddress, confirmPassword))
|
||||
} catch (err: Throwable) {
|
||||
// TODO...
|
||||
|
|
|
@ -6,26 +6,26 @@ import androidx.datastore.core.DataStore
|
|||
import androidx.datastore.core.Serializer
|
||||
import androidx.datastore.dataStore
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import ing.bikeshedengineer.debtpirate.PrefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.AppDataStore
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
object PrefsDataStoreSerializer : Serializer<PrefsDataStore> {
|
||||
override val defaultValue: PrefsDataStore
|
||||
get() = PrefsDataStore.getDefaultInstance()
|
||||
object PrefsDataStoreSerializer : Serializer<AppDataStore> {
|
||||
override val defaultValue: AppDataStore
|
||||
get() = AppDataStore.getDefaultInstance()
|
||||
|
||||
override suspend fun readFrom(input: InputStream): PrefsDataStore {
|
||||
override suspend fun readFrom(input: InputStream): AppDataStore {
|
||||
try {
|
||||
return PrefsDataStore.parseFrom(input)
|
||||
return AppDataStore.parseFrom(input)
|
||||
} catch (exception: InvalidProtocolBufferException) {
|
||||
throw CorruptionException("Cannot read proto file.", exception)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeTo(t: PrefsDataStore, output: OutputStream) = t.writeTo(output)
|
||||
override suspend fun writeTo(t: AppDataStore, output: OutputStream) = t.writeTo(output)
|
||||
}
|
||||
|
||||
val Context.prefsDataStore: DataStore<PrefsDataStore> by dataStore(
|
||||
fileName = "prefs_data_store.proto",
|
||||
val Context.appDataStore: DataStore<AppDataStore> by dataStore(
|
||||
fileName = "app_data_store.proto",
|
||||
serializer = PrefsDataStoreSerializer
|
||||
)
|
|
@ -1,9 +1,16 @@
|
|||
package ing.bikeshedengineer.debtpirate.data.remote.model.auth
|
||||
|
||||
import com.google.gson.annotations.JsonAdapter
|
||||
import ing.bikeshedengineer.debtpirate.domain.adapter.OffsetDateTimeAdapter
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
data class AuthLoginPostResponse(
|
||||
val userId: Int,
|
||||
val auth: AuthLoginPostResponseTokenData,
|
||||
val session: AuthLoginPostResponseTokenData,
|
||||
val auth: AuthLoginPostResponseTokenData
|
||||
)
|
||||
|
||||
data class AuthLoginPostResponseTokenData(val token: String, val expiresAt: String)
|
||||
data class AuthLoginPostResponseTokenData(
|
||||
val token: String,
|
||||
@JsonAdapter(OffsetDateTimeAdapter::class) val expiresAt: OffsetDateTime
|
||||
)
|
|
@ -7,6 +7,6 @@ import java.time.OffsetDateTime
|
|||
data class UserCreatePostResponse(
|
||||
val userId: Int,
|
||||
@JsonAdapter(OffsetDateTimeAdapter::class)
|
||||
val expiresAt: OffsetDateTime,
|
||||
val expiresAt: OffsetDateTime? = null,
|
||||
val sessionToken: String? = null
|
||||
)
|
|
@ -6,17 +6,17 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ing.bikeshedengineer.debtpirate.PrefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.data.pref.prefsDataStore
|
||||
import ing.bikeshedengineer.debtpirate.AppDataStore
|
||||
import ing.bikeshedengineer.debtpirate.data.pref.appDataStore
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object PrefsDataStoreProvider {
|
||||
object AppDataStoreProvider {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePrefsDataStore(application: Application): DataStore<PrefsDataStore> {
|
||||
return application.prefsDataStore
|
||||
fun provideAppDataStore(application: Application): DataStore<AppDataStore> {
|
||||
return application.appDataStore
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package ing.bikeshedengineer.debtpirate.di
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ing.bikeshedengineer.debtpirate.AppDataStore
|
||||
import ing.bikeshedengineer.debtpirate.domain.usecase.UpdateStoreDataUseCase
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object UseCaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUpdateStoreDataUseCase(store: DataStore<AppDataStore>) = UpdateStoreDataUseCase(store)
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package ing.bikeshedengineer.debtpirate.domain.usecase
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import ing.bikeshedengineer.debtpirate.AppDataStore
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
class UpdateStoreDataUseCase(val store: DataStore<AppDataStore>) {
|
||||
suspend operator fun invoke(
|
||||
userId: Int? = null,
|
||||
authToken: String? = null,
|
||||
authTokenExpiresAt: OffsetDateTime? = null,
|
||||
sessionToken: String? = null,
|
||||
sessionTokenExpiresAt: OffsetDateTime? = null
|
||||
) {
|
||||
store.updateData { currentStore ->
|
||||
currentStore.toBuilder().apply {
|
||||
if (userId != null) {
|
||||
this.setUserId(userId)
|
||||
}
|
||||
|
||||
if (authToken != null && authTokenExpiresAt != null) {
|
||||
this.authToken.toBuilder().setToken(authToken).setExpiresAt(authTokenExpiresAt.toEpochSecond())
|
||||
.build()
|
||||
}
|
||||
|
||||
if (sessionToken != null && sessionTokenExpiresAt != null) {
|
||||
this.sessionToken.toBuilder().setToken(sessionToken)
|
||||
.setExpiresAt(sessionTokenExpiresAt.toEpochSecond()).build()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,8 @@ message Token {
|
|||
int64 expires_at = 2;
|
||||
}
|
||||
|
||||
message PrefsDataStore {
|
||||
string current_route = 1;
|
||||
int32 user_id = 2;
|
||||
Token auth_token = 3;
|
||||
Token session_token = 4;
|
||||
message AppDataStore {
|
||||
int32 user_id = 1;
|
||||
Token auth_token = 2;
|
||||
Token session_token = 3;
|
||||
}
|
Loading…
Add table
Reference in a new issue