diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/CategoryTreeNode.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/CategoryTreeNode.kt index c5959c4d..0946b73f 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/CategoryTreeNode.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/CategoryTreeNode.kt @@ -4,4 +4,4 @@ data class CategoryTreeNode( val id: Int, val data: String, val children: List -) \ No newline at end of file +) diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Need.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Need.kt index a82a7c44..1d8b7d4c 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Need.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Need.kt @@ -11,6 +11,6 @@ data class Need( val requestId: Int?, val status: String, val createdDate: String, - val size: String + val size: String? ) diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Profile.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Profile.kt index 9e3c28e8..218bcb7f 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Profile.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/models/Profile.kt @@ -31,4 +31,11 @@ data class UserInfoRequest( var height: Int?, val isEmailConfirmed: Boolean? = false, val isPrivacyPolicyAccepted: Boolean? = false, +) + +data class UserInfo( + var email: String, + var name: String, + var surname: String, + var roles: List ) \ No newline at end of file diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/remote/ResqService.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/remote/ResqService.kt index 48c3ecfb..ebcca6ea 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/remote/ResqService.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/data/remote/ResqService.kt @@ -16,6 +16,7 @@ import com.cmpe451.resq.data.models.NotificationItem import com.cmpe451.resq.data.models.ProfileData import com.cmpe451.resq.data.models.RegisterRequestBody import com.cmpe451.resq.data.models.Resource +import com.cmpe451.resq.data.models.UserInfo import com.cmpe451.resq.data.models.UserInfoRequest import com.google.gson.GsonBuilder import okhttp3.ResponseBody @@ -65,14 +66,13 @@ interface NeedService { @Header("Authorization") jwtToken: String ): Call> - @GET("need/viewNeedsByUserId") fun viewNeedsByUserId( @Query("userId") userId: Int, @Header("Authorization") jwtToken: String, ): Call> - } + interface AuthService { @POST("auth/signin") suspend fun login(@Body requestBody: LoginRequestBody): Response @@ -83,11 +83,18 @@ interface AuthService { interface ProfileService { @GET("profile/getProfileInfo") - suspend fun getUserInfo( + suspend fun getProfileInfo( @Query("userId") userId: Int, @Header("Authorization") jwtToken: String ): Response + @POST("profile/updateProfile") + suspend fun updateProfile( + @Query("userId") userId: Int, + @Header("Authorization") jwtToken: String, + @Body request: UserInfoRequest + ): Response + @POST("user/requestRole") suspend fun selectRole( @Query("userId") userId: Int, @@ -95,13 +102,11 @@ interface ProfileService { @Header("Authorization") jwtToken: String ): Response - - @POST("profile/updateProfile") - suspend fun updateProfile( + @GET("user/getUserInfo") + fun getUserInfo( @Query("userId") userId: Int, - @Header("Authorization") jwtToken: String, - @Body request: UserInfoRequest - ): Response + @Header("Authorization") jwtToken: String + ): Call } @@ -259,11 +264,11 @@ class ResqService(appContext: Context) { } @RequiresApi(Build.VERSION_CODES.O) - suspend fun getUserInfo(): ProfileData { + suspend fun getProfileInfo(): ProfileData { val token = userSessionManager.getUserToken() ?: "" val userId = userSessionManager.getUserId() - val response = profileService.getUserInfo( + val response = profileService.getProfileInfo( userId = userId, jwtToken = "Bearer $token" ) @@ -334,4 +339,20 @@ class ResqService(appContext: Context) { Log.d("AAA", "getNotifications: ${response.isSuccessful}") return response } + fun getUserInfo(userId: Int, callback: (UserInfo?) -> Unit) { + val token = userSessionManager.getUserToken() ?: "" + profileService.getUserInfo(userId, "Bearer $token").enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + callback(response.body()) + } else { + callback(null) + } + } + override fun onFailure(call: Call, t: Throwable) { + callback(null) + } + }) + } } + diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MapScreen.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MapScreen.kt index 52077536..fda720ae 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MapScreen.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MapScreen.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavController @@ -76,6 +77,12 @@ fun MapScreen(navController: NavController, appContext: Context, mapViewModel: M val needsList = mapViewModel.needMarkerList.value val resourcesList = mapViewModel.resourceMarkerList.value + LaunchedEffect(key1 = true) { + mapViewModel.fetchMainCategories(appContext) + } + + val categories = mapViewModel.categories.value + Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier @@ -166,33 +173,28 @@ fun MapScreen(navController: NavController, appContext: Context, mapViewModel: M elevation = 4.dp ) { Column(modifier = Modifier.padding(16.dp)) { - Text(text = "Category ID: ${need.categoryTreeId}") + Text( + text = "${mapViewModel.findNodeById(categories, need.categoryTreeId.toInt())?.data}", + fontWeight = FontWeight.Bold + ) Text(text = "Quantity: ${need.quantity}") AnimatedVisibility(visible = need.id == expandedNeedId) { Column { - Text(text = "User ID: ${need.userId}") + UsernameDisplay(mapViewModel, appContext, need.userId) Text(text = "Description: ${need.description}") - Row { - Button( - onClick = { /* TODO: Handle Button 1 action */ }, - colors = ButtonDefaults.buttonColors(backgroundColor = RequestColor, contentColor = Color.White) - ) { - Text("Button 1") - } - Spacer(modifier = Modifier.width(8.dp)) - Button( - onClick = { /* TODO: Handle Button 2 action */ }, - colors = ButtonDefaults.buttonColors(backgroundColor = RequestColor, contentColor = Color.White) - ) { - Text("Button 2") - } - } } + if (need.size != null) { + Text(text = "Size: ${need.size}") + } + } } } } } } - } else { // Resources list + } + + // Resources list + else { LazyColumn( modifier = Modifier .align(Alignment.BottomStart) @@ -212,27 +214,18 @@ fun MapScreen(navController: NavController, appContext: Context, mapViewModel: M elevation = 4.dp ) { Column(modifier = Modifier.padding(16.dp)) { - Text(text = "Category ID: ${resource.categoryTreeId}") + Text( + text = "${mapViewModel.findNodeById(categories, resource.categoryTreeId.toInt())?.data}", + fontWeight = FontWeight.Bold + ) Text(text = "Quantity: ${resource.quantity}") AnimatedVisibility(visible = resource.id == expandedResourceId) { Column { - Text(text = "User ID: ${resource.senderId}") - Text(text = "Size: ${resource.size}") - Row { - Button( - onClick = { /* TODO: Handle Button 1 action */ }, - colors = ButtonDefaults.buttonColors(backgroundColor = ResourceColor, contentColor = Color.White) - ) { - Text("Button 1") - } - Spacer(modifier = Modifier.width(8.dp)) - Button( - onClick = { /* TODO: Handle Button 2 action */ }, - colors = ButtonDefaults.buttonColors(backgroundColor = ResourceColor, contentColor = Color.White) - ) { - Text("Button 2") - } - } } + UsernameDisplay(mapViewModel, appContext, resource.senderId) + if (resource.size != null) { + Text(text = "Size: ${resource.size}") + } + } } } } @@ -362,49 +355,13 @@ fun AddSignUpButton(onClick: () -> Unit) { } @Composable -fun ExpandableItemList() { - // Mock data similar to the JSON response you showed - val items = listOf( - Need(1, 13, "52", "Please help!", 1, 41.08, 29.05, null, "NOT_INVOLVED", "2023-11-26T02:23:04.731365"), - Need(2, 13, "54", "Help me for god's sake", 1, 30.0, 40.0, null, "NOT_INVOLVED", "2023-11-26T10:57:10.71784") - ) - - // State to track expanded items - val expandedItemId = remember { mutableStateOf(-1) } - - LazyColumn { - items(items) { item -> - Card( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - .clickable { - expandedItemId.value = if (expandedItemId.value == item.id) -1 else item.id - }, - elevation = 4.dp - ) { - Column(modifier = Modifier.padding(16.dp)) { - Text(text = "Category ID: ${item.categoryTreeId}") - Text(text = "Quantity: ${item.quantity}") - - AnimatedVisibility(visible = expandedItemId.value == item.id) { - Column { - Text(text = "User ID: ${item.userId}") - Text(text = "Description: ${item.description}") - Row { - Button(onClick = { /* Handle button click */ }) { - Text("Button 1") - } - Spacer(modifier = Modifier.width(8.dp)) - Button(onClick = { /* Handle button click */ }) { - Text("Button 2") - } - // Add more buttons if needed - } - } - } - } - } +fun UsernameDisplay(mapViewModel: MapViewModel, appContext: Context, userId: Int) { + val userInfo = remember { mutableStateOf("Loading...") } + LaunchedEffect(userId) { + mapViewModel.getUserInfoById(appContext, userId) { result -> + userInfo.value = result } } -} \ No newline at end of file + Text(text = "User: ${userInfo.value}") +} + diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MapViewModel.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MapViewModel.kt index d57c01e4..36a73287 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MapViewModel.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MapViewModel.kt @@ -3,12 +3,16 @@ package com.cmpe451.resq.viewmodels import android.annotation.SuppressLint import android.content.Context import android.location.Location +import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.cmpe451.resq.data.models.CategoryTreeNode import com.cmpe451.resq.data.models.Need import com.cmpe451.resq.data.models.Resource import com.cmpe451.resq.data.remote.ResqService import com.google.android.gms.location.FusedLocationProviderClient +import kotlinx.coroutines.launch class MapViewModel : ViewModel() { val searchQuery = mutableStateOf("") @@ -16,6 +20,10 @@ class MapViewModel : ViewModel() { val needMarkerList = mutableStateOf>(emptyList()) val resourceMarkerList = mutableStateOf>(emptyList()) + // For convert category ids to names + private val _categories = mutableStateOf>(emptyList()) + val categories: State> = _categories + fun getNeedsByDistance(appContext: Context) { val api = ResqService(appContext) api.filterNeedByDistance( @@ -65,4 +73,39 @@ class MapViewModel : ViewModel() { // Show error or something } } + + fun fetchMainCategories(appContext: Context) { + viewModelScope.launch { + val api = ResqService(appContext) + + val response = api.getMainCategories() + if (response.isSuccessful) { + _categories.value = response.body() ?: emptyList() + } else { + // TODO: Handle error + } + } + } + + fun findNodeById(rootNodes: List, idToFind: Int): CategoryTreeNode? { + for (node in rootNodes) { + if (node.id == idToFind) { + return node + } + val childResult = findNodeById(node.children, idToFind) + if (childResult != null) { + return childResult + } + } + return null + } + + fun getUserInfoById(appContext: Context, userId: Int, callback: (String) -> Unit) { + val api = ResqService(appContext) + api.getUserInfo(userId) { userInfo -> + val username = userInfo?.let { "${it.name} ${it.surname}" } ?: "Unknown" + callback(username) + } + } + } \ No newline at end of file diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ProfileViewModel.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ProfileViewModel.kt index 639fa5a4..c73134d8 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ProfileViewModel.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ProfileViewModel.kt @@ -31,7 +31,7 @@ class ProfileViewModel() : ViewModel() { viewModelScope.launch { try { - val data = api.getUserInfo() + val data = api.getProfileInfo() Log.d("Service", "getUserData: $data") _profileData.value = data } catch (e: Exception) { diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ResourceViewModel.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ResourceViewModel.kt index 9d687bf2..a405aa53 100644 --- a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ResourceViewModel.kt +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/ResourceViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.cmpe451.resq.data.models.CategoryTreeNode import com.cmpe451.resq.data.models.CreateResourceRequestBody +import com.cmpe451.resq.data.models.Resource import com.cmpe451.resq.data.remote.ResqService import kotlinx.coroutines.launch @@ -32,6 +33,9 @@ class ResourceViewModel : ViewModel() { private val _createResourceResponse = mutableStateOf(null) val createResourceResponse: State = _createResourceResponse + private val _resourceList = mutableStateOf>(emptyList()) + val resourceList: State> = _resourceList + fun updateCategory(category: CategoryTreeNode) { _selectedCategory.value = category fetchTypesForCategory(category.id) @@ -108,4 +112,9 @@ class ResourceViewModel : ViewModel() { } return Result.failure(Throwable(message = "No category")) } + + fun getResourcesBySenderId(appContext: Context) { + val api = ResqService(appContext) + /* TODO: implement */ + } }