Skip to content

Commit

Permalink
Merge pull request #604 from bounswe/feature/mobile-600-list-on-map-s…
Browse files Browse the repository at this point in the history
…creen

Implement need and resource lists. Format lists for Map Screen
  • Loading branch information
GulbeycanCagri authored Dec 24, 2023
2 parents 1ece4a8 + 0784b54 commit 1812f8a
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ data class CategoryTreeNode(
val id: Int,
val data: String,
val children: List<CategoryTreeNode>
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ data class Need(
val requestId: Int?,
val status: String,
val createdDate: String,
val size: String
val size: String?
)

Original file line number Diff line number Diff line change
Expand Up @@ -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<String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -65,14 +66,13 @@ interface NeedService {
@Header("Authorization") jwtToken: String
): Call<List<Need>>


@GET("need/viewNeedsByUserId")
fun viewNeedsByUserId(
@Query("userId") userId: Int,
@Header("Authorization") jwtToken: String,
): Call<List<Need>>

}

interface AuthService {
@POST("auth/signin")
suspend fun login(@Body requestBody: LoginRequestBody): Response<LoginResponse>
Expand All @@ -83,25 +83,30 @@ interface AuthService {

interface ProfileService {
@GET("profile/getProfileInfo")
suspend fun getUserInfo(
suspend fun getProfileInfo(
@Query("userId") userId: Int,
@Header("Authorization") jwtToken: String
): Response<ProfileData>

@POST("profile/updateProfile")
suspend fun updateProfile(
@Query("userId") userId: Int,
@Header("Authorization") jwtToken: String,
@Body request: UserInfoRequest
): Response<String>

@POST("user/requestRole")
suspend fun selectRole(
@Query("userId") userId: Int,
@Query("role") requestedRole: String,
@Header("Authorization") jwtToken: String
): Response<String>


@POST("profile/updateProfile")
suspend fun updateProfile(
@GET("user/getUserInfo")
fun getUserInfo(
@Query("userId") userId: Int,
@Header("Authorization") jwtToken: String,
@Body request: UserInfoRequest
): Response<String>
@Header("Authorization") jwtToken: String
): Call<UserInfo>

}

Expand Down Expand Up @@ -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"
)
Expand Down Expand Up @@ -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<UserInfo> {
override fun onResponse(call: Call<UserInfo>, response: Response<UserInfo>) {
if (response.isSuccessful) {
callback(response.body())
} else {
callback(null)
}
}
override fun onFailure(call: Call<UserInfo>, t: Throwable) {
callback(null)
}
})
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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}")
}
}
}
}
}
Expand Down Expand Up @@ -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
}
}
}
Text(text = "User: ${userInfo.value}")
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@ 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("")
val lastKnownLocation = mutableStateOf<Location?>(null)
val needMarkerList = mutableStateOf<List<Need>>(emptyList())
val resourceMarkerList = mutableStateOf<List<Resource>>(emptyList())

// For convert category ids to names
private val _categories = mutableStateOf<List<CategoryTreeNode>>(emptyList())
val categories: State<List<CategoryTreeNode>> = _categories

fun getNeedsByDistance(appContext: Context) {
val api = ResqService(appContext)
api.filterNeedByDistance(
Expand Down Expand Up @@ -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<CategoryTreeNode>, 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)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -32,6 +33,9 @@ class ResourceViewModel : ViewModel() {
private val _createResourceResponse = mutableStateOf<String?>(null)
val createResourceResponse: State<String?> = _createResourceResponse

private val _resourceList = mutableStateOf<List<Resource>>(emptyList())
val resourceList: State<List<Resource>> = _resourceList

fun updateCategory(category: CategoryTreeNode) {
_selectedCategory.value = category
fetchTypesForCategory(category.id)
Expand Down Expand Up @@ -108,4 +112,9 @@ class ResourceViewModel : ViewModel() {
}
return Result.failure(Throwable(message = "No category"))
}

fun getResourcesBySenderId(appContext: Context) {
val api = ResqService(appContext)
/* TODO: implement */
}
}

0 comments on commit 1812f8a

Please sign in to comment.