diff --git a/androidApp/src/main/java/dev/xinto/argos/di/UiModule.kt b/androidApp/src/main/java/dev/xinto/argos/di/UiModule.kt index ecc1388..bb1b472 100644 --- a/androidApp/src/main/java/dev/xinto/argos/di/UiModule.kt +++ b/androidApp/src/main/java/dev/xinto/argos/di/UiModule.kt @@ -7,12 +7,13 @@ import dev.xinto.argos.ui.screen.course.page.scores.ScoresViewModel import dev.xinto.argos.ui.screen.course.page.syllabus.SyllabusViewModel import dev.xinto.argos.ui.screen.login.LoginViewModel import dev.xinto.argos.ui.screen.main.MainViewModel -import dev.xinto.argos.ui.screen.main.dialog.user.UserViewModel +import dev.xinto.argos.ui.screen.main.dialog.user.UserInfoViewModel import dev.xinto.argos.ui.screen.main.page.home.HomeViewModel import dev.xinto.argos.ui.screen.main.page.messages.MessagesViewModel import dev.xinto.argos.ui.screen.main.page.news.NewsViewModel import dev.xinto.argos.ui.screen.message.MessageViewModel import dev.xinto.argos.ui.screen.notifications.NotificationsViewModel +import dev.xinto.argos.ui.screen.user.UserViewModel import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.dsl.module @@ -23,11 +24,12 @@ val UiModule = module { viewModelOf(::MessagesViewModel) viewModelOf(::NewsViewModel) viewModelOf(::NotificationsViewModel) - viewModelOf(::UserViewModel) + viewModelOf(::UserInfoViewModel) viewModelOf(::MessageViewModel) viewModelOf(::SyllabusViewModel) viewModelOf(::GroupsViewModel) viewModelOf(::ScoresViewModel) viewModelOf(::MaterialsViewModel) viewModelOf(::ClassmatesViewModel) + viewModelOf(::UserViewModel) } \ No newline at end of file diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/MainActivity.kt b/androidApp/src/main/java/dev/xinto/argos/ui/MainActivity.kt index 5a96914..c26b828 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/MainActivity.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/MainActivity.kt @@ -33,6 +33,7 @@ import dev.xinto.argos.ui.screen.login.LoginScreen import dev.xinto.argos.ui.screen.main.MainScreen import dev.xinto.argos.ui.screen.message.MessageScreen import dev.xinto.argos.ui.screen.notifications.NotificationsScreen +import dev.xinto.argos.ui.screen.user.UserScreen import dev.xinto.argos.ui.theme.ArgosTheme import org.koin.android.ext.android.inject @@ -76,6 +77,9 @@ class MainActivity : ComponentActivity() { onNotificationsClick = { rootNavController.navigate(ArgosNavigation.Notifications) }, + onUserClick = { + rootNavController.navigate(ArgosNavigation.User) + }, onMessageClick = { messageId, semesterId -> rootNavController.navigate( ArgosNavigation.Message( @@ -96,6 +100,13 @@ class MainActivity : ComponentActivity() { ) } + is ArgosNavigation.User -> { + UserScreen( + modifier = Modifier.fillMaxSize(), + onBackNavigate = rootNavController::pop + ) + } + is ArgosNavigation.Message -> { MessageScreen( modifier = Modifier.fillMaxSize(), diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/ArgosNavigation.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/ArgosNavigation.kt index 4fd0689..6b2d93d 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/screen/ArgosNavigation.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/ArgosNavigation.kt @@ -14,6 +14,9 @@ sealed interface ArgosNavigation : Parcelable { @Parcelize data object Notifications : ArgosNavigation + @Parcelize + data object User : ArgosNavigation + @Parcelize data class Message(val id: String, val semesterId: String) : ArgosNavigation diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/MainScreen.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/MainScreen.kt index 17f1cc3..ce79998 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/MainScreen.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/MainScreen.kt @@ -45,6 +45,7 @@ import org.koin.androidx.compose.getViewModel @Composable fun MainScreen( onNotificationsClick: () -> Unit, + onUserClick: () -> Unit, onMessageClick: (messageId: String, semesterId: String) -> Unit, onCourseClick: (String) -> Unit, modifier: Modifier = Modifier, @@ -56,6 +57,7 @@ fun MainScreen( state = state, onNotificationsClick = onNotificationsClick, onLogoutClick = viewModel::logout, + onUserClick = onUserClick, onMessageClick = onMessageClick, onCourseClick = onCourseClick ) @@ -64,6 +66,7 @@ fun MainScreen( @Composable fun MainScreen( onNotificationsClick: () -> Unit, + onUserClick: () -> Unit, onLogoutClick: () -> Unit, onMessageClick: (messageId: String, semesterId: String) -> Unit, onCourseClick: (String) -> Unit, @@ -118,6 +121,10 @@ fun MainScreen( onDismiss = { userDialogShown = false }, onBalanceNavigate = { /*TODO*/ }, onLibraryNavigate = { /*TODO*/ }, + onUserNavigate = { + userDialogShown = false + onUserClick() + }, onSettingsNavigate = { /*TODO*/ }, onLogoutClick = { userDialogShown = false diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserDialog.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoDialog.kt similarity index 89% rename from androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserDialog.kt rename to androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoDialog.kt index 34190ca..817bf7c 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserDialog.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoDialog.kt @@ -39,16 +39,18 @@ fun UserDialog( onDismiss: () -> Unit, onBalanceNavigate: () -> Unit, onLibraryNavigate: () -> Unit, + onUserNavigate: () -> Unit, onSettingsNavigate: () -> Unit, onLogoutClick: () -> Unit, ) { - val viewModel: UserViewModel = getViewModel() + val viewModel: UserInfoViewModel = getViewModel() val state by viewModel.state.collectAsStateWithLifecycle() UserDialog( state = state, onDismiss = onDismiss, onBalanceNavigate = onBalanceNavigate, onLibraryNavigate = onLibraryNavigate, + onUserNavigate = onUserNavigate, onSettingsNavigate = onSettingsNavigate, onLogoutClick = onLogoutClick ) @@ -57,10 +59,11 @@ fun UserDialog( @OptIn(ExperimentalMaterial3Api::class) @Composable fun UserDialog( - state: UserState, + state: UserInfoState, onDismiss: () -> Unit, onBalanceNavigate: () -> Unit, onLibraryNavigate: () -> Unit, + onUserNavigate: () -> Unit, onSettingsNavigate: () -> Unit, onLogoutClick: () -> Unit, ) { @@ -83,7 +86,7 @@ fun UserDialog( ) } when (state) { - is UserState.Loading -> { + is UserInfoState.Loading -> { Box( modifier = Modifier.height(200.dp), contentAlignment = Alignment.Center @@ -92,7 +95,7 @@ fun UserDialog( } } - is UserState.Success -> { + is UserInfoState.Success -> { Column( modifier = Modifier .fillMaxWidth() @@ -142,6 +145,13 @@ fun UserDialog( ) } } + HorizontalSegmentedButton(onClick = onUserNavigate) { + Icon( + painter = painterResource(R.drawable.ic_account), + contentDescription = null + ) + Text(stringResource(R.string.user_title)) + } HorizontalSegmentedButton(onClick = onSettingsNavigate) { Icon( painter = painterResource(R.drawable.ic_settings), @@ -160,7 +170,7 @@ fun UserDialog( } } - is UserState.Error -> { + is UserInfoState.Error -> { } } @@ -174,10 +184,11 @@ fun UserDialog( fun UserDialog_Success_Preview() { ArgosTheme { UserDialog( - state = UserState.mockSuccess, + state = UserInfoState.mockSuccess, onDismiss = {}, onLogoutClick = {}, onBalanceNavigate = {}, + onUserNavigate = {}, onLibraryNavigate = {}, onSettingsNavigate = {} ) @@ -189,10 +200,11 @@ fun UserDialog_Success_Preview() { fun UserDialog_Loading_Preview() { ArgosTheme { UserDialog( - state = UserState.Loading, + state = UserInfoState.Loading, onDismiss = {}, onLogoutClick = {}, onBalanceNavigate = {}, + onUserNavigate = {}, onLibraryNavigate = {}, onSettingsNavigate = {} ) diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserState.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoState.kt similarity index 64% rename from androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserState.kt rename to androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoState.kt index 2de5333..9e8ddba 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserState.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoState.kt @@ -5,26 +5,35 @@ import dev.xinto.argos.domain.user.DomainUserInfo import dev.xinto.argos.domain.user.DomainUserState @Immutable -sealed interface UserState { +sealed interface UserInfoState { @Immutable - data object Loading : UserState + data object Loading : UserInfoState @Immutable data class Success( val userInfo: DomainUserInfo, val userState: DomainUserState - ) : UserState + ) : UserInfoState @Immutable - data object Error : UserState + data object Error : UserInfoState companion object { val mockSuccess = Success( userInfo = DomainUserInfo( fullName = "Giorgi Giorgadze", + firstName = "Giorgi", + lastName = "Giorgadze", + birthDate = "01/01/2005", + idNumber = "00000000000", email = "giorgi.giorgadze.1@iliauni.edu.ge", + mobileNumber1 = "(599) 99-99-99", + mobileNumber2 = "", + homeNumber = "", + currentAddress = "", + juridicalAddress = "", photoUrl = null, degree = 1 ), diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserViewModel.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoViewModel.kt similarity index 76% rename from androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserViewModel.kt rename to androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoViewModel.kt index 3666873..b06f90d 100644 --- a/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserViewModel.kt +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/main/dialog/user/UserInfoViewModel.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -class UserViewModel( +class UserInfoViewModel( private val userRepository: UserRepository ) : ViewModel() { @@ -18,18 +18,18 @@ class UserViewModel( userRepository.getUserState().asFlow() ) { info, state -> info to state }.map { when (it) { - is DomainResponse.Loading -> UserState.Loading - is DomainResponse.Success -> UserState.Success( + is DomainResponse.Loading -> UserInfoState.Loading + is DomainResponse.Success -> UserInfoState.Success( userInfo = it.value.first, userState = it.value.second ) - is DomainResponse.Error -> UserState.Error + is DomainResponse.Error -> UserInfoState.Error } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), - initialValue = UserState.Loading + initialValue = UserInfoState.Loading ) } \ No newline at end of file diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserScreen.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserScreen.kt new file mode 100644 index 0000000..eca3542 --- /dev/null +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserScreen.kt @@ -0,0 +1,248 @@ +package dev.xinto.argos.ui.screen.user + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +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.LocalContentColor +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import dev.xinto.argos.R +import org.koin.androidx.compose.getViewModel + +@Composable +fun UserScreen( + onBackNavigate: () -> Unit, + modifier: Modifier = Modifier +) { + val viewModel: UserViewModel = getViewModel() + val state by viewModel.state.collectAsStateWithLifecycle() + val canSave by viewModel.canSave.collectAsStateWithLifecycle() + val saving by viewModel.saving.collectAsStateWithLifecycle() + UserScreen( + modifier = modifier, + onBackNavigate = onBackNavigate, + state = state, + canSave = canSave, + saving = saving, + onSaveClick = viewModel::save, + onMobile1Change = viewModel::updateMobile1, + onMobile2Change = viewModel::updateMobile2, + onHomeNumberChange = viewModel::updateHomeNumber, + onCurrentAddressChange = viewModel::updateCurrentAddress + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UserScreen( + state: UserState, + onBackNavigate: () -> Unit, + canSave: Boolean, + saving: Boolean, + onSaveClick: () -> Unit, + onMobile1Change: (String) -> Unit, + onMobile2Change: (String) -> Unit, + onHomeNumberChange: (String) -> Unit, + onCurrentAddressChange: (String) -> Unit, + modifier: Modifier = Modifier, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.user_title)) }, + navigationIcon = { + IconButton(onClick = onBackNavigate) { + Icon( + painter = painterResource(R.drawable.ic_arrow_back), + contentDescription = null + ) + } + } + ) + }, + floatingActionButton = { + AnimatedVisibility( + visible = canSave, + enter = scaleIn(), + exit = scaleOut() + ) { + FloatingActionButton(onClick = onSaveClick) { + if (saving) { + CircularProgressIndicator(color = LocalContentColor.current) + } else { + Icon( + painter = painterResource(id = R.drawable.ic_save), + contentDescription = null + ) + } + } + } + } + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(it), + contentAlignment = Alignment.Center + ) { + when (state) { + is UserState.Loading -> { + CircularProgressIndicator() + } + is UserState.Success -> { + LazyVerticalGrid( + modifier = Modifier.fillMaxSize(), + columns = GridCells.Fixed(2), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + item(key = "firstname") { + ReadOnlyTextField( + value = state.user.firstName, + label = { Text(stringResource(R.string.user_field_firstname)) }, + ) + } + item(key = "lastname") { + ReadOnlyTextField( + value = state.user.lastName, + label = { Text(stringResource(R.string.user_field_lastname)) }, + ) + } + item(key = "idnumber") { + ReadOnlyTextField( + value = state.user.idNumber, + label = { Text(stringResource(R.string.user_field_idnumber)) }, + ) + } + item(key = "birthdate") { + ReadOnlyTextField( + value = state.user.birthDate, + label = { Text(stringResource(R.string.user_field_birthdate)) }, + ) + } + item( + key = "email", + span = { GridItemSpan(maxCurrentLineSpan) } + ) { + ReadOnlyTextField( + value = state.user.email, + label = { Text(stringResource(R.string.user_field_email)) }, + ) + } + item(key = "mobile1") { + TextField( + value = state.user.mobileNumber1, + onValueChange = onMobile1Change, + label = { Text(stringResource(R.string.user_field_mobilenum1)) }, + ) + } + item(key = "mobile2") { + TextField( + value = state.user.mobileNumber2, + onValueChange = onMobile2Change, + label = { Text(stringResource(R.string.user_field_mobilenum2)) }, + ) + } + item( + key = "home", + span = { GridItemSpan(maxCurrentLineSpan) } + ) { + TextField( + value = state.user.homeNumber, + onValueChange = onHomeNumberChange, + label = { Text(stringResource(R.string.user_field_homenum)) }, + ) + } + item( + key = "juridicaladdr", + span = { GridItemSpan(maxCurrentLineSpan) } + ) { + ReadOnlyTextField( + value = state.user.juridicalAddress, + label = { Text(stringResource(R.string.user_field_address_juridical)) }, + singleLine = false + ) + } + item( + key = "currentaddr", + span = { GridItemSpan(maxCurrentLineSpan) } + ) { + TextField( + value = state.user.currentAddress, + onValueChange = onCurrentAddressChange, + label = { Text(stringResource(R.string.user_field_address_current)) }, + singleLine = false + ) + } + } + } + is UserState.Error -> { + Text("Error") + } + } + } + } +} + +@Composable +private fun ReadOnlyTextField( + value: String, + modifier: Modifier = Modifier, + label: @Composable (() -> Unit)? = null, + icon: @Composable (() -> Unit)? = null, + singleLine: Boolean = true, +) { + OutlinedTextField( + modifier = modifier, + value = value, + onValueChange = {}, + label = label, + singleLine = singleLine, + enabled = false, + readOnly = true + ) +} + +@Composable +fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + label: @Composable (() -> Unit)? = null, + icon: @Composable (() -> Unit)? = null, + singleLine: Boolean = true, +) { + OutlinedTextField( + modifier = modifier, + value = value, + onValueChange = onValueChange, + label = label, + singleLine = singleLine, + ) +} \ No newline at end of file diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserState.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserState.kt new file mode 100644 index 0000000..86d314f --- /dev/null +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserState.kt @@ -0,0 +1,13 @@ +package dev.xinto.argos.ui.screen.user + +import dev.xinto.argos.domain.user.DomainUserInfo + +sealed interface UserState { + + data object Loading : UserState + + data class Success(val user: DomainUserInfo) : UserState + + data object Error : UserState + +} \ No newline at end of file diff --git a/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserViewModel.kt b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserViewModel.kt new file mode 100644 index 0000000..9180a43 --- /dev/null +++ b/androidApp/src/main/java/dev/xinto/argos/ui/screen/user/UserViewModel.kt @@ -0,0 +1,96 @@ +package dev.xinto.argos.ui.screen.user + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dev.xinto.argos.domain.DomainResponse +import dev.xinto.argos.domain.user.DomainUserInfo +import dev.xinto.argos.domain.user.UserRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class UserViewModel(private val userRepository: UserRepository) : ViewModel() { + + private val tempInfo = MutableStateFlow(null) + + private val userInfo = userRepository.getUserInfo() + + val state = combine(userInfo.asFlow(), tempInfo) { userInfo, tempUserInfo -> + when (userInfo) { + is DomainResponse.Loading -> UserState.Loading + is DomainResponse.Success -> { + val info = tempUserInfo ?: userInfo.value + UserState.Success(info) + } + is DomainResponse.Error -> UserState.Error + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = UserState.Loading + ) + + private val _saving = MutableStateFlow(false) + val saving = _saving.asStateFlow() + + val canSave = combine(userInfo.asFlow(), tempInfo) { userInfo, tempInfo -> + userInfo is DomainResponse.Success && tempInfo != null && userInfo.value != tempInfo + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = false + ) + + fun updateMobile1(newNumber: String) { + updateInfo { + it.copy(mobileNumber1 = newNumber) + } + } + + fun updateMobile2(newNumber: String) { + updateInfo { + it.copy(mobileNumber2 = newNumber) + } + } + + fun updateHomeNumber(newNumber: String) { + updateInfo { + it.copy(homeNumber = newNumber) + } + } + + fun updateCurrentAddress(newAddress: String) { + updateInfo { + it.copy(currentAddress = newAddress) + } + } + + fun save() { + if (saving.value) return + if (tempInfo.value == null) return + + viewModelScope.launch { + _saving.value = true + if (userRepository.updateUserInfo(tempInfo.value!!)) { + userInfo.refresh() + tempInfo.value = null + } + _saving.value = false + } + } + + private inline fun updateInfo(update: (DomainUserInfo) -> DomainUserInfo) { + tempInfo.update { + if (it == null) { + val user = (state.value as? UserState.Success)?.user + user?.let(update) + } else { + it.let(update) + } + } + } +} \ No newline at end of file diff --git a/androidApp/src/main/res/drawable/ic_save.xml b/androidApp/src/main/res/drawable/ic_save.xml new file mode 100644 index 0000000..b8c3052 --- /dev/null +++ b/androidApp/src/main/res/drawable/ic_save.xml @@ -0,0 +1,10 @@ + + + diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index 5ee64c0..7bce449 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -4,6 +4,18 @@ Sign out + My Profile + First name + Last name + Birth date + ID number + Email + Mobile number 1 + Mobile number 2 + Home number + Juridical address + Current address + Home Course diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/DomainUserInfo.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/DomainUserInfo.kt index 6a824d7..c6c92cc 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/DomainUserInfo.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/DomainUserInfo.kt @@ -1,10 +1,17 @@ package dev.xinto.argos.domain.user -import kotlin.jvm.JvmInline - data class DomainUserInfo( + val firstName: String, + val lastName: String, val fullName: String, + val birthDate: String, + val idNumber: String, val email: String, + val mobileNumber1: String, + val mobileNumber2: String, + val homeNumber: String, + val juridicalAddress: String, + val currentAddress: String, val photoUrl: String?, val degree: Int, ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/UserRepository.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/UserRepository.kt index 24d29f5..49c1bec 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/UserRepository.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/domain/user/UserRepository.kt @@ -4,6 +4,7 @@ import dev.xinto.argos.domain.DomainResponseSource import dev.xinto.argos.domain.combine import dev.xinto.argos.local.account.ArgosAccountManager import dev.xinto.argos.network.ArgosApi +import dev.xinto.argos.network.request.ApiRequestContact import dev.xinto.argos.util.formatCurrency import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -18,9 +19,18 @@ class UserRepository( private val userInfo = DomainResponseSource({ argosApi.getUserAuth() }) { state -> state.data!!.let { (_, attributes, relationships) -> DomainUserInfo( + firstName = attributes.firstName, + lastName = attributes.lastName, fullName = attributes.fullName, - photoUrl = attributes.photoUrl, + birthDate = attributes.birthDate, + idNumber = attributes.personalNumber, email = attributes.email, + mobileNumber1 = attributes.mobileNumber, + mobileNumber2 = attributes.mobileNumber2, + homeNumber = attributes.homeNumber, + juridicalAddress = attributes.juridicalAddress, + currentAddress = attributes.actualAddress, + photoUrl = attributes.photoUrl, degree = relationships.profiles.data[0].attributes.degree, ) } @@ -47,6 +57,18 @@ class UserRepository( argosAccountManager.logout() } + suspend fun updateUserInfo(domainUserInfo: DomainUserInfo): Boolean { + val response = argosApi.patchUserContactInfo( + ApiRequestContact( + mobileNumber = domainUserInfo.mobileNumber1, + mobileNumber2 = domainUserInfo.mobileNumber2, + homeNumber = domainUserInfo.homeNumber, + actualAddress = domainUserInfo.currentAddress + ) + ) + return response.message == "ok" + } + fun observeLoggedIn(): Flow { return argosAccountManager.isLoggedIn() } diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/network/ArgosApi.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/network/ArgosApi.kt index 91f7a7b..f032e90 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/network/ArgosApi.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/network/ArgosApi.kt @@ -4,6 +4,7 @@ import dev.xinto.argos.domain.messages.DomainMessage import dev.xinto.argos.local.account.ArgosAccountManager import dev.xinto.argos.network.request.ApiRequestAuthRefresh import dev.xinto.argos.network.request.ApiRequestAuth +import dev.xinto.argos.network.request.ApiRequestContact import dev.xinto.argos.network.response.ApiResponseAuth import dev.xinto.argos.network.response.ApiResponseCourseChosenGroup import dev.xinto.argos.network.response.ApiResponseCourseClassmates @@ -12,6 +13,7 @@ import dev.xinto.argos.network.response.ApiResponseCourseGroups import dev.xinto.argos.network.response.ApiResponseCourseMaterials import dev.xinto.argos.network.response.ApiResponseCourseScores import dev.xinto.argos.network.response.ApiResponseCourseSyllabus +import dev.xinto.argos.network.response.ApiResponseEmpty import dev.xinto.argos.network.response.ApiResponseMessage import dev.xinto.argos.network.response.ApiResponseMessagesInbox import dev.xinto.argos.network.response.ApiResponseMessagesOutbox @@ -34,6 +36,7 @@ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.defaultRequest import io.ktor.client.request.get import io.ktor.client.request.parameter +import io.ktor.client.request.patch import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.http.ContentType @@ -137,6 +140,15 @@ class ArgosApi(private val argosAccountManager: ArgosAccountManager) { } } + suspend fun patchUserContactInfo(contact: ApiRequestContact): ApiResponseEmpty { + return withContext(Dispatchers.IO) { + ktorClient.patch("user/contact") { + contentType(ContentType.Application.Json) + setBody(contact) + }.body() + } + } + suspend fun getCurrentSchedule(): ApiResponseSchedules { return withContext(Dispatchers.IO) { ktorClient.get("student/schedules/current").body() diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/network/request/ApiRequestContact.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/network/request/ApiRequestContact.kt new file mode 100644 index 0000000..cf7075f --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/network/request/ApiRequestContact.kt @@ -0,0 +1,11 @@ +package dev.xinto.argos.network.request + +import kotlinx.serialization.Serializable + +@Serializable +data class ApiRequestContact( + val mobileNumber: String, + val mobileNumber2: String, + val homeNumber: String, + val actualAddress: String +) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/ApiResponse.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/ApiResponse.kt index fab8476..62103e0 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/ApiResponse.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/ApiResponse.kt @@ -16,6 +16,8 @@ data class ApiResponse( override val message: String, ) : ApiResponseBase +typealias ApiResponseEmpty = ApiResponse>> + @Serializable data class ApiResponseWithMeta( override val id: String? = null, diff --git a/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/attributes/ApiAttributesUser.kt b/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/attributes/ApiAttributesUser.kt index 20d7906..5383de8 100644 --- a/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/attributes/ApiAttributesUser.kt +++ b/shared/src/commonMain/kotlin/dev/xinto/argos/network/response/attributes/ApiAttributesUser.kt @@ -41,9 +41,9 @@ data class ApiAttributesAuthUser( override val avatar: String, override val photoUrl: String?, override val gender: String, - override val mobileNumber: String?, - override val mobileNumber2: String?, - override val homeNumber: String?, + override val mobileNumber: String, + override val mobileNumber2: String, + override val homeNumber: String, val personalNumber: String, val birthDate: String, val juridicalAddress: String,