From 857b2d361e57f20a6ba48392e47553a192ad5268 Mon Sep 17 00:00:00 2001 From: VawnDao <134820492+VawnDao@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:56:12 +0700 Subject: [PATCH] =?UTF-8?q?feat(class):=20m=E1=BB=9Di=20ng=C6=B0=E1=BB=9Di?= =?UTF-8?q?=20d=C3=B9ng=20v=C3=A0o=20class=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daocon <134820492+Daocon@users.noreply.github.com> Co-authored-by: Nguyễn Quang Minh (NQM) <94773751+nqmgaming@users.noreply.github.com> Co-authored-by: Nguyen Quang Minh Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../dto/classes/InviteToClassRequestDto.kt | 10 ++ .../dto/classes/InviteToClassResponseDto.kt | 10 ++ .../classes/InviteToClassRequestMapper.kt | 14 +++ .../classes/InviteToClassResponseMapper.kt | 14 +++ .../pwhs/quickmem/data/remote/ApiService.kt | 8 ++ .../remote/repository/ClassRepositoryImpl.kt | 20 ++++ .../classes/InviteToClassRequestModel.kt | 6 ++ .../classes/InviteToClassResponseModel.kt | 6 ++ .../domain/repository/ClassRepository.kt | 7 ++ .../app/classes/detail/ClassDetailScreen.kt | 44 ++++++++- .../app/classes/detail/ClassDetailUiAction.kt | 2 + .../app/classes/detail/ClassDetailUiEvent.kt | 1 + .../app/classes/detail/ClassDetailUiState.kt | 3 + .../classes/detail/ClassDetailViewModel.kt | 92 ++++++++++++++++++ .../component/ClassDetailBottomSheet.kt | 16 ++-- .../detail/component/ClassTextField.kt | 54 +++++++++++ .../component/InviteClassBottomSheet.kt | 96 +++++++++++++++++++ app/src/main/res/drawable/ic_card.xml | 14 ++- app/src/main/res/drawable/ic_folder.xml | 6 +- app/src/main/res/drawable/ic_school.xml | 6 +- 20 files changed, 411 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassRequestDto.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassResponseDto.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassRequestMapper.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassResponseMapper.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassRequestModel.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassResponseModel.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassTextField.kt create mode 100644 app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/InviteClassBottomSheet.kt diff --git a/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassRequestDto.kt b/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassRequestDto.kt new file mode 100644 index 00000000..9bbe236a --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassRequestDto.kt @@ -0,0 +1,10 @@ +package com.pwhs.quickmem.data.dto.classes + +import com.google.gson.annotations.SerializedName + +data class InviteToClassRequestDto( + @SerializedName("classId") + val classId: String, + @SerializedName("username") + val username: String +) \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassResponseDto.kt b/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassResponseDto.kt new file mode 100644 index 00000000..3072a29c --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/data/dto/classes/InviteToClassResponseDto.kt @@ -0,0 +1,10 @@ +package com.pwhs.quickmem.data.dto.classes + +import com.google.gson.annotations.SerializedName + +data class InviteToClassResponseDto( + @SerializedName("message") + val message: String, + @SerializedName("status") + val status: Boolean, +) diff --git a/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassRequestMapper.kt b/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassRequestMapper.kt new file mode 100644 index 00000000..52d42937 --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassRequestMapper.kt @@ -0,0 +1,14 @@ +package com.pwhs.quickmem.data.mapper.classes + +import com.pwhs.quickmem.data.dto.classes.InviteToClassRequestDto +import com.pwhs.quickmem.domain.model.classes.InviteToClassRequestModel + +fun InviteToClassRequestModel.toDto() = InviteToClassRequestDto( + classId = classId, + username = username +) + +fun InviteToClassRequestDto.toModel() = InviteToClassRequestModel( + classId = classId, + username = username +) \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassResponseMapper.kt b/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassResponseMapper.kt new file mode 100644 index 00000000..9a45c37f --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/data/mapper/classes/InviteToClassResponseMapper.kt @@ -0,0 +1,14 @@ +package com.pwhs.quickmem.data.mapper.classes + +import com.pwhs.quickmem.data.dto.classes.InviteToClassResponseDto +import com.pwhs.quickmem.domain.model.classes.InviteToClassResponseModel + +fun InviteToClassResponseDto.toModel() = InviteToClassResponseModel( + message = message, + status = status +) + +fun InviteToClassResponseModel.toDto() = InviteToClassResponseDto( + message = message, + status = status +) \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt b/app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt index 1c214eda..8a1b4b4b 100644 --- a/app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt +++ b/app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt @@ -34,6 +34,8 @@ import com.pwhs.quickmem.data.dto.classes.DeleteStudySetsRequestDto import com.pwhs.quickmem.data.dto.classes.ExitClassRequestDto import com.pwhs.quickmem.data.dto.classes.GetClassByOwnerResponseDto import com.pwhs.quickmem.data.dto.classes.GetClassDetailResponseDto +import com.pwhs.quickmem.data.dto.classes.InviteToClassRequestDto +import com.pwhs.quickmem.data.dto.classes.InviteToClassResponseDto import com.pwhs.quickmem.data.dto.classes.JoinClassRequestDto import com.pwhs.quickmem.data.dto.classes.RemoveMembersRequestDto import com.pwhs.quickmem.data.dto.classes.SaveRecentAccessClassRequestDto @@ -549,6 +551,12 @@ interface ApiService { @Path("userId") userId: String ): List + @POST("class/invite") + suspend fun inviteUserToClass( + @Header("Authorization") token: String, + @Body inviteToClassRequestDto: InviteToClassRequestDto + ): InviteToClassResponseDto + // Streak @GET("streak/{userId}") suspend fun getStreaksByUserId( diff --git a/app/src/main/java/com/pwhs/quickmem/data/remote/repository/ClassRepositoryImpl.kt b/app/src/main/java/com/pwhs/quickmem/data/remote/repository/ClassRepositoryImpl.kt index ebf8bfda..293e3999 100644 --- a/app/src/main/java/com/pwhs/quickmem/data/remote/repository/ClassRepositoryImpl.kt +++ b/app/src/main/java/com/pwhs/quickmem/data/remote/repository/ClassRepositoryImpl.kt @@ -16,6 +16,8 @@ import com.pwhs.quickmem.domain.model.classes.DeleteStudySetsRequestModel import com.pwhs.quickmem.domain.model.classes.ExitClassRequestModel import com.pwhs.quickmem.domain.model.classes.GetClassByOwnerResponseModel import com.pwhs.quickmem.domain.model.classes.GetClassDetailResponseModel +import com.pwhs.quickmem.domain.model.classes.InviteToClassRequestModel +import com.pwhs.quickmem.domain.model.classes.InviteToClassResponseModel import com.pwhs.quickmem.domain.model.classes.JoinClassRequestModel import com.pwhs.quickmem.domain.model.classes.RemoveMembersRequestModel import com.pwhs.quickmem.domain.model.classes.SaveRecentAccessClassRequestModel @@ -279,4 +281,22 @@ class ClassRepositoryImpl @Inject constructor( } } } + + override suspend fun inviteToClass( + token: String, + inviteToClassRequestModel: InviteToClassRequestModel + ): Flow> { + return flow { + emit(Resources.Loading()) + try { + val response = apiService.inviteUserToClass( + token, inviteToClassRequestModel.toDto() + ) + emit(Resources.Success(response.toModel())) + } catch (e: Exception) { + Timber.e(e) + emit(Resources.Error(e.toString())) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassRequestModel.kt b/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassRequestModel.kt new file mode 100644 index 00000000..bdfed493 --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassRequestModel.kt @@ -0,0 +1,6 @@ +package com.pwhs.quickmem.domain.model.classes + +data class InviteToClassRequestModel( + val classId: String, + val username: String +) diff --git a/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassResponseModel.kt b/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassResponseModel.kt new file mode 100644 index 00000000..7ceca2cb --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/domain/model/classes/InviteToClassResponseModel.kt @@ -0,0 +1,6 @@ +package com.pwhs.quickmem.domain.model.classes + +data class InviteToClassResponseModel( + val message: String, + val status: Boolean, +) diff --git a/app/src/main/java/com/pwhs/quickmem/domain/repository/ClassRepository.kt b/app/src/main/java/com/pwhs/quickmem/domain/repository/ClassRepository.kt index 869f8a30..1ce12305 100644 --- a/app/src/main/java/com/pwhs/quickmem/domain/repository/ClassRepository.kt +++ b/app/src/main/java/com/pwhs/quickmem/domain/repository/ClassRepository.kt @@ -9,6 +9,8 @@ import com.pwhs.quickmem.domain.model.classes.DeleteStudySetsRequestModel import com.pwhs.quickmem.domain.model.classes.ExitClassRequestModel import com.pwhs.quickmem.domain.model.classes.GetClassByOwnerResponseModel import com.pwhs.quickmem.domain.model.classes.GetClassDetailResponseModel +import com.pwhs.quickmem.domain.model.classes.InviteToClassRequestModel +import com.pwhs.quickmem.domain.model.classes.InviteToClassResponseModel import com.pwhs.quickmem.domain.model.classes.JoinClassRequestModel import com.pwhs.quickmem.domain.model.classes.RemoveMembersRequestModel import com.pwhs.quickmem.domain.model.classes.SaveRecentAccessClassRequestModel @@ -91,4 +93,9 @@ interface ClassRepository { token: String, userId: String ): Flow>> + + suspend fun inviteToClass( + token: String, + inviteToClassRequestModel: InviteToClassRequestModel + ): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailScreen.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailScreen.kt index 11882376..9a339d74 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailScreen.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailScreen.kt @@ -40,6 +40,7 @@ import com.pwhs.quickmem.domain.model.users.ClassMemberModel import com.pwhs.quickmem.domain.model.users.UserResponseModel import com.pwhs.quickmem.presentation.app.classes.detail.component.ClassDetailBottomSheet import com.pwhs.quickmem.presentation.app.classes.detail.component.ClassDetailTopAppBar +import com.pwhs.quickmem.presentation.app.classes.detail.component.InviteClassBottomSheet import com.pwhs.quickmem.presentation.app.classes.detail.folders.FoldersTabScreen import com.pwhs.quickmem.presentation.app.classes.detail.members.MembersTabScreen import com.pwhs.quickmem.presentation.app.classes.detail.study_sets.StudySetsTabScreen @@ -204,6 +205,10 @@ fun ClassDetailScreen( ClassDetailUiEvent.OnNavigateToRemoveMembers -> { } + + ClassDetailUiEvent.InviteToClassSuccess -> { + Toast.makeText(context, "Invite to class success", Toast.LENGTH_SHORT).show() + } } } } @@ -220,6 +225,11 @@ fun ClassDetailScreen( resultNavigator.navigateBack(true) }, title = uiState.title, + username = uiState.username, + errorMessage = uiState.errorMessage, + onUsernameChanged = { + viewModel.onEvent(ClassDetailUiAction.OnChangeUsername(it)) + }, isLoading = uiState.isLoading, isAllowMember = uiState.allowMember, userResponseModel = uiState.userResponseModel, @@ -268,6 +278,9 @@ fun ClassDetailScreen( onJoinClass = { viewModel.onEvent(ClassDetailUiAction.OnJoinClass) }, + onInviteClass = { + viewModel.onEvent(ClassDetailUiAction.OnInviteClass) + }, onReportClass = { navigator.navigate( ReportScreenDestination( @@ -292,6 +305,9 @@ fun ClassDetail( modifier: Modifier = Modifier, isOwner: Boolean, title: String = "", + username: String = "", + onUsernameChanged: (String) -> Unit = {}, + errorMessage: String = "", isLoading: Boolean = false, isMember: Boolean = false, isAllowMember: Boolean = false, @@ -307,6 +323,7 @@ fun ClassDetail( onEditClass: () -> Unit = {}, onExitClass: () -> Unit = {}, onJoinClass: () -> Unit = {}, + onInviteClass: () -> Unit = {}, onRemoveMembers: (String) -> Unit = {}, onDeleteClass: () -> Unit = {}, onRefresh: () -> Unit = {}, @@ -324,6 +341,7 @@ fun ClassDetail( val sheetShowMoreState = rememberModalBottomSheetState() var showDeleteConfirmationDialog by remember { mutableStateOf(false) } var showExitConfirmationDialog by remember { mutableStateOf(false) } + var showInviteClassBottomSheet by remember { mutableStateOf(false) } val context = LocalContext.current Scaffold( @@ -456,6 +474,27 @@ fun ClassDetail( dismissButtonTitle = "Cancel", ) } + + if (showInviteClassBottomSheet) { + InviteClassBottomSheet( + username = username, + errorMessage = errorMessage, + onUsernameChanged = onUsernameChanged, + showInviteClassBottomSheet = showInviteClassBottomSheet, + sheetShowMoreState = sheetShowMoreState, + onDismissRequest = { + showInviteClassBottomSheet = false + onUsernameChanged("") + }, + onSubmitClick = { + onInviteClass() +// if (!isLoading && errorMessage.isEmpty()) { +// showInviteClassBottomSheet = false +// } + } + ) + } + ClassDetailBottomSheet( onAddStudySetToClass = onNavigateAddStudySets, onAddFolderToClass = onNavigateAddFolder, @@ -468,7 +507,10 @@ fun ClassDetail( showExitConfirmationDialog = true showMoreBottomSheet = false }, - onShareClass = {}, + onInviteClass = { + showInviteClassBottomSheet = true + showMoreBottomSheet = false + }, onReportClass = { onReportClass() showMoreBottomSheet = false diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiAction.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiAction.kt index 45857169..5e4c56e9 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiAction.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiAction.kt @@ -12,5 +12,7 @@ sealed class ClassDetailUiAction { data object OnNavigateToAddFolder : ClassDetailUiAction() data object OnNavigateToAddStudySets : ClassDetailUiAction() data object OnJoinClass : ClassDetailUiAction() + data class OnChangeUsername(val username: String) : ClassDetailUiAction() data object ExitClass : ClassDetailUiAction() + data object OnInviteClass : ClassDetailUiAction() } \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiEvent.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiEvent.kt index 441e989b..195ca1ca 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiEvent.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiEvent.kt @@ -10,4 +10,5 @@ sealed class ClassDetailUiEvent { data object ClassDeleted : ClassDetailUiEvent() data object NavigateToEditClass : ClassDetailUiEvent() data object ExitClass : ClassDetailUiEvent() + data object InviteToClassSuccess : ClassDetailUiEvent() } \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiState.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiState.kt index 3eb3ab29..53697db8 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiState.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailUiState.kt @@ -16,6 +16,9 @@ data class ClassDetailUiState( val allowSet: Boolean = false, val allowMember: Boolean = false, val isMember: Boolean = false, + val username: String = "", + val errorMessage: String = "", + val isInvited: Boolean = false, val userResponseModel: UserResponseModel = UserResponseModel(), val studySets: List = emptyList(), val folders: List = emptyList(), diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailViewModel.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailViewModel.kt index ca392ddf..ea252f07 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailViewModel.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/ClassDetailViewModel.kt @@ -9,6 +9,7 @@ import com.pwhs.quickmem.core.utils.Resources import com.pwhs.quickmem.domain.model.classes.DeleteFolderRequestModel import com.pwhs.quickmem.domain.model.classes.DeleteStudySetsRequestModel import com.pwhs.quickmem.domain.model.classes.ExitClassRequestModel +import com.pwhs.quickmem.domain.model.classes.InviteToClassRequestModel import com.pwhs.quickmem.domain.model.classes.JoinClassRequestModel import com.pwhs.quickmem.domain.model.classes.RemoveMembersRequestModel import com.pwhs.quickmem.domain.model.classes.SaveRecentAccessClassRequestModel @@ -113,6 +114,19 @@ class ClassDetailViewModel @Inject constructor( is ClassDetailUiAction.OnDeleteFolderInClass -> { deleteFolderInClass(event.folderId) } + + is ClassDetailUiAction.OnChangeUsername -> { + _uiState.update { + it.copy( + username = event.username, + errorMessage = "" + ) + } + } + + ClassDetailUiAction.OnInviteClass -> { + inviteToClass() + } } } @@ -415,4 +429,82 @@ class ClassDetailViewModel @Inject constructor( } } } + + private fun inviteToClass() { + viewModelScope.launch { + val token = tokenManager.accessToken.firstOrNull() ?: "" + val classId = _uiState.value.id + val username = _uiState.value.username + if (username.isEmpty()) { + _uiState.update { + it.copy( + errorMessage = "Username cannot be empty" + ) + } + return@launch + } + if (username.length < 4) { + _uiState.update { + it.copy( + errorMessage = "Username must be at least 4 characters" + ) + } + return@launch + } + + classRepository.inviteToClass( + token, + InviteToClassRequestModel(classId, username) + ).collectLatest { resource -> + when (resource) { + is Resources.Loading -> { + _uiState.update { + it.copy( + isLoading = true + ) + } + } + + is Resources.Success -> { + _uiState.update { + it.copy( + isLoading = false, + isInvited = resource.data?.status == true + ) + } + if (_uiState.value.isInvited) { + _uiState.update { + it.copy( + errorMessage = "", + username = "" + ) + } + _uiEvent.send(ClassDetailUiEvent.InviteToClassSuccess) + } else { + _uiState.update { + it.copy( + errorMessage = resource.data?.message ?: "An error occurred" + ) + } + } + } + + is Resources.Error -> { + _uiState.update { + it.copy( + errorMessage = resource.message ?: "An error occurred", + isLoading = false + ) + } + _uiEvent.send( + ClassDetailUiEvent.ShowError( + resource.message ?: "An error occurred" + ) + ) + Timber.d("Error") + } + } + } + } + } } diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassDetailBottomSheet.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassDetailBottomSheet.kt index 1aef205d..ebf6521c 100644 --- a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassDetailBottomSheet.kt +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassDetailBottomSheet.kt @@ -7,7 +7,7 @@ import androidx.compose.material.icons.Icons.Default import androidx.compose.material.icons.Icons.Outlined import androidx.compose.material.icons.automirrored.filled.ExitToApp import androidx.compose.material.icons.filled.DeleteOutline -import androidx.compose.material.icons.filled.IosShare +import androidx.compose.material.icons.filled.PersonAdd import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Folder @@ -34,7 +34,7 @@ fun ClassDetailBottomSheet( onEditClass: () -> Unit = {}, onExitClass: () -> Unit = {}, onDeleteClass: () -> Unit = {}, - onShareClass: () -> Unit = {}, + onInviteClass: () -> Unit = {}, onReportClass: () -> Unit = {}, onJoinClass: () -> Unit = {}, showMoreBottomSheet: Boolean = false, @@ -78,11 +78,13 @@ fun ClassDetailBottomSheet( title = "Join Class" ) } - ItemMenuBottomSheet( - onClick = onShareClass, - icon = Default.IosShare, - title = "Share Class" - ) + if (isOwner) { + ItemMenuBottomSheet( + onClick = onInviteClass, + icon = Default.PersonAdd, + title = "Invite Member" + ) + } if (!isOwner && isMember) { ItemMenuBottomSheet( onClick = onExitClass, diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassTextField.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassTextField.kt new file mode 100644 index 00000000..f886f3c7 --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/ClassTextField.kt @@ -0,0 +1,54 @@ +package com.pwhs.quickmem.presentation.app.classes.detail.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.shapes +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight + +@Composable +fun ClassTextField( + modifier: Modifier = Modifier, + placeholder: String = "", + value: String = "", + onValueChange: (String) -> Unit = {}, + errorMessage: String = "", +) { + TextField( + modifier = modifier + .fillMaxWidth(), + value = value, + maxLines = 1, + onValueChange = onValueChange, + placeholder = { + Text( + text = placeholder, + style = typography.bodyMedium.copy( + color = colorScheme.onSurface.copy(alpha = 0.6f), + fontWeight = FontWeight.Bold + ) + ) + }, + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = Color.Transparent, + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + errorContainerColor = Color.White, + ), + shape = shapes.medium, + isError = errorMessage.isNotEmpty(), + supportingText = { + Text( + text = errorMessage, + color = colorScheme.error, + style = typography.bodyMedium + ) + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/InviteClassBottomSheet.kt b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/InviteClassBottomSheet.kt new file mode 100644 index 00000000..7b764c17 --- /dev/null +++ b/app/src/main/java/com/pwhs/quickmem/presentation/app/classes/detail/component/InviteClassBottomSheet.kt @@ -0,0 +1,96 @@ +package com.pwhs.quickmem.presentation.app.classes.detail.component + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.shapes +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun InviteClassBottomSheet( + modifier: Modifier = Modifier, + username: String, + errorMessage: String, + onSubmitClick: () -> Unit = {}, + showInviteClassBottomSheet: Boolean = false, + onUsernameChanged: (String) -> Unit = {}, + sheetShowMoreState: SheetState = rememberModalBottomSheetState(), + onDismissRequest: () -> Unit = {}, +) { + if (showInviteClassBottomSheet) { + ModalBottomSheet( + modifier = modifier, + onDismissRequest = onDismissRequest, + sheetState = sheetShowMoreState + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "Invite Members", + style = typography.titleLarge.copy( + fontWeight = FontWeight.Bold + ) + ) + Text( + text = "To invite members to this class, add their Quizlet usernames below.", + style = typography.bodyMedium, + modifier = Modifier.padding(top = 8.dp) + ) + ClassTextField( + modifier = Modifier.padding(top = 16.dp, bottom = 8.dp), + value = username, + onValueChange = onUsernameChanged, + errorMessage = errorMessage, + placeholder = "Type username to invite", + ) + Button( + enabled = username.isNotEmpty() && username.length > 4, + onClick = onSubmitClick, + modifier = Modifier + .fillMaxWidth(), + shape = shapes.medium + ) { + Text( + text = "Submit", style = typography.bodyMedium.copy( + fontWeight = FontWeight.Bold + ) + ) + } + OutlinedButton( + onClick = onDismissRequest, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = colorScheme.onSurface.copy(alpha = 0.6f) + ), + modifier = Modifier + .padding(top = 8.dp) + .fillMaxWidth(), + shape = shapes.medium + ) { + Text( + text = "Cancel", + style = typography.bodyMedium.copy( + fontWeight = FontWeight.Bold + ) + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_card.xml b/app/src/main/res/drawable/ic_card.xml index bd70f2e0..99a21289 100644 --- a/app/src/main/res/drawable/ic_card.xml +++ b/app/src/main/res/drawable/ic_card.xml @@ -1,5 +1,11 @@ - - - - + + + + diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml index f5500d91..cacfc197 100644 --- a/app/src/main/res/drawable/ic_folder.xml +++ b/app/src/main/res/drawable/ic_folder.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="960" android:viewportHeight="960"> - + diff --git a/app/src/main/res/drawable/ic_school.xml b/app/src/main/res/drawable/ic_school.xml index 5e0dbb57..7e466f8c 100644 --- a/app/src/main/res/drawable/ic_school.xml +++ b/app/src/main/res/drawable/ic_school.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="960" android:viewportHeight="960"> - +