Skip to content

Commit

Permalink
feat(invite-to-class): gọi api mời thành viên vô lớp học
Browse files Browse the repository at this point in the history
  • Loading branch information
VawnDao committed Nov 30, 2024
1 parent e65b696 commit b87f46d
Show file tree
Hide file tree
Showing 17 changed files with 391 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
@@ -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,
)
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
@@ -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
)
8 changes: 8 additions & 0 deletions app/src/main/java/com/pwhs/quickmem/data/remote/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -549,6 +551,12 @@ interface ApiService {
@Path("userId") userId: String
): List<GetClassByOwnerResponseDto>

@POST("class/invite")
suspend fun inviteUserToClass(
@Header("Authorization") token: String,
@Body inviteToClassRequestDto: InviteToClassRequestDto
): InviteToClassResponseDto

// Streak
@GET("streak/{userId}")
suspend fun getStreaksByUserId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -279,4 +281,22 @@ class ClassRepositoryImpl @Inject constructor(
}
}
}

override suspend fun inviteToClass(
token: String,
inviteToClassRequestModel: InviteToClassRequestModel
): Flow<Resources<InviteToClassResponseModel>> {
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()))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.pwhs.quickmem.domain.model.classes

data class InviteToClassRequestModel(
val classId: String,
val username: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.pwhs.quickmem.domain.model.classes

data class InviteToClassResponseModel(
val message: String,
val status: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -91,4 +93,9 @@ interface ClassRepository {
token: String,
userId: String
): Flow<Resources<List<GetClassByOwnerResponseModel>>>

suspend fun inviteToClass(
token: String,
inviteToClassRequestModel: InviteToClassRequestModel
): Flow<Resources<InviteToClassResponseModel>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -204,6 +205,10 @@ fun ClassDetailScreen(
ClassDetailUiEvent.OnNavigateToRemoveMembers -> {

}

ClassDetailUiEvent.InviteToClassSuccess -> {
Toast.makeText(context, "Invite to class success", Toast.LENGTH_SHORT).show()
}
}
}
}
Expand All @@ -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,
Expand Down Expand Up @@ -268,6 +278,9 @@ fun ClassDetailScreen(
onJoinClass = {
viewModel.onEvent(ClassDetailUiAction.OnJoinClass)
},
onInviteClass = {
viewModel.onEvent(ClassDetailUiAction.OnInviteClass)
},
onReportClass = {
navigator.navigate(
ReportScreenDestination(
Expand All @@ -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,
Expand All @@ -307,6 +323,7 @@ fun ClassDetail(
onEditClass: () -> Unit = {},
onExitClass: () -> Unit = {},
onJoinClass: () -> Unit = {},
onInviteClass: () -> Unit = {},
onRemoveMembers: (String) -> Unit = {},
onDeleteClass: () -> Unit = {},
onRefresh: () -> Unit = {},
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand All @@ -468,7 +507,10 @@ fun ClassDetail(
showExitConfirmationDialog = true
showMoreBottomSheet = false
},
onShareClass = {},
onInviteClass = {
showInviteClassBottomSheet = true
showMoreBottomSheet = false
},
onReportClass = {
onReportClass()
showMoreBottomSheet = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ sealed class ClassDetailUiEvent {
data object ClassDeleted : ClassDetailUiEvent()
data object NavigateToEditClass : ClassDetailUiEvent()
data object ExitClass : ClassDetailUiEvent()
data object InviteToClassSuccess : ClassDetailUiEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 statusInvite : Boolean = false,
val userResponseModel: UserResponseModel = UserResponseModel(),
val studySets: List<GetStudySetResponseModel> = emptyList(),
val folders: List<GetFolderResponseModel> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}
}
}

Expand Down Expand Up @@ -415,4 +429,78 @@ 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
)
}
Timber.d("Loading")
}

is Resources.Success -> {
_uiState.update {
it.copy(
isLoading = false,
statusInvite = resource.data?.status == true
)
}
if (_uiState.value.statusInvite == true) {
_uiEvent.send(ClassDetailUiEvent.InviteToClassSuccess)
} else {
_uiState.update {
it.copy(
errorMessage = resource.data?.message ?: "An error occurred"
)
}
}
Timber.d("Success")
}

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")
}
}
}
}
}
}
Loading

0 comments on commit b87f46d

Please sign in to comment.