diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepository.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepository.kt index 7796cbb3..3c6a62bc 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepository.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepository.kt @@ -6,6 +6,7 @@ import com.bounswe.predictionpolls.data.remote.model.request.CreateDiscretePollR import com.bounswe.predictionpolls.data.remote.model.request.PollCommentRequest import com.bounswe.predictionpolls.data.remote.services.PollService import com.bounswe.predictionpolls.domain.poll.Comment +import com.bounswe.predictionpolls.domain.poll.Poll class PollRepository( private val pollService: PollService @@ -75,4 +76,16 @@ class PollRepository( pollService.getPollComments(pollId).map { it.toComment() } } } + + override suspend fun getOpenedPolls(username: String): List { + return execute { + pollService.getOpenedPolls(username).map { it.toPollDomainModel() } + } + } + + override suspend fun getOpenedPollsForMe(): List { + return execute { + pollService.getOpenedPollsForMe().map { it.toPollDomainModel() } + } + } } \ No newline at end of file diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepositoryInterface.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepositoryInterface.kt index 33419bc3..e2fa8833 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepositoryInterface.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/repositories/PollRepositoryInterface.kt @@ -1,6 +1,7 @@ package com.bounswe.predictionpolls.data.remote.repositories import com.bounswe.predictionpolls.domain.poll.Comment +import com.bounswe.predictionpolls.domain.poll.Poll interface PollRepositoryInterface { /** @@ -41,4 +42,10 @@ interface PollRepositoryInterface { suspend fun getComments( pollId: Int ): List + + suspend fun getOpenedPolls( + username: String, + ): List + + suspend fun getOpenedPollsForMe(): List } \ No newline at end of file diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/services/PollService.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/services/PollService.kt index cb78f3df..48b0b1af 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/services/PollService.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/data/remote/services/PollService.kt @@ -13,6 +13,7 @@ import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path +import retrofit2.http.Query interface PollService { @POST("/polls/discrete") @@ -57,4 +58,12 @@ interface PollService { suspend fun getPollComments( @Path("pollId") pollId: Int ): List + + @GET("/polls/opened") + suspend fun getOpenedPolls( + @Query("username") username: String + ): List + + @GET("/polls/opened/me") + suspend fun getOpenedPollsForMe(): List } \ No newline at end of file diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollComposable.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollComposable.kt index 4443f17e..d7fecad1 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollComposable.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollComposable.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.bounswe.predictionpolls.R import com.bounswe.predictionpolls.ui.theme.MontserratFontFamily import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme @@ -44,9 +43,7 @@ fun PollComposable( optionsContent: @Composable () -> Unit, dueDate: String, rejectionText: String, - commentCount: Int, onProfileCardClicked: () -> Unit, - onShareClicked: () -> Unit, modifier: Modifier = Modifier ) { Box( @@ -95,40 +92,6 @@ fun PollComposable( ) } } - Row( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(vertical = 8.dp), - horizontalArrangement = Arrangement.SpaceAround - ) { - Column { - PollIcon( - id = R.drawable.ic_comment, - modifier = Modifier.background( - MaterialTheme.colorScheme.primary, - PollIconShape - ) - ) - Text( - commentCount.toString(), - modifier = Modifier.align(Alignment.CenterHorizontally), - fontFamily = MontserratFontFamily, - color = MaterialTheme.colorScheme.scrim, - textAlign = TextAlign.Center, - fontSize = 14.sp - ) - } - PollIcon( - id = R.drawable.ic_share, - modifier = Modifier.background(MaterialTheme.colorScheme.primary, PollIconShape).clickable(onClick = onShareClicked) - ) - PollIcon( - id = R.drawable.ic_warning, - modifier = Modifier.background(MaterialTheme.colorScheme.error, PollIconShape) - ) - - } } } } @@ -256,9 +219,7 @@ private fun PollComposablePreview() { }, dueDate = "21 Nov 2023", rejectionText = "Last 5 Days", - commentCount = 265, onProfileCardClicked = {}, - onShareClicked = {}, modifier = Modifier.padding(16.dp) ) } diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollsComposable.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollsComposable.kt index 66dc42f8..7358c1be 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollsComposable.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/common/poll/PollsComposable.kt @@ -15,7 +15,6 @@ import com.bounswe.predictionpolls.R import com.bounswe.predictionpolls.domain.poll.Poll import com.bounswe.predictionpolls.domain.poll.PollOption import com.bounswe.predictionpolls.extensions.fromISO8601 -import com.bounswe.predictionpolls.utils.shareLink import kotlinx.collections.immutable.ImmutableList @@ -55,13 +54,9 @@ fun Polls( }, dueDate = it.dueDate?.fromISO8601() ?: "", rejectionText = it.rejectionText ?: "", - commentCount = it.commentCount, onProfileCardClicked = { onProfileClicked(it.pollCreatorUsername) }, - onShareClicked = { - context.shareLink(frontEndUrl+ "/vote/" + it.polId) - }, ) } } diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileScreen.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileScreen.kt index 6b625e6a..682298e3 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileScreen.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileScreen.kt @@ -13,6 +13,7 @@ import androidx.navigation.compose.composable import com.bounswe.predictionpolls.domain.annotation.PollAnnotationPages import com.bounswe.predictionpolls.ui.common.annotation.AnnotationViewModel import com.bounswe.predictionpolls.ui.editProfile.navigateToEditProfileScreen +import com.bounswe.predictionpolls.ui.vote.navigateToPollVoteScreen const val MY_PROFILE_SCREEN_ROUTE = "MY_PROFILE_SCREEN_ROUTE" @@ -50,6 +51,9 @@ fun NavGraphBuilder.myProfileScreen(navController: NavController) { }, onFollowClicked = { }, + onPollClicked = { + navController.navigateToPollVoteScreen(it) + } ) } } diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileViewModel.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileViewModel.kt index 7594f580..aa60a1f1 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileViewModel.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/MyProfileViewModel.kt @@ -1,15 +1,17 @@ package com.bounswe.predictionpolls.ui.profile -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.bounswe.predictionpolls.common.Result -import com.bounswe.predictionpolls.domain.feed.GetFeedUseCase +import com.bounswe.predictionpolls.core.BaseViewModel +import com.bounswe.predictionpolls.data.remote.repositories.PollRepositoryInterface import com.bounswe.predictionpolls.domain.poll.Poll import com.bounswe.predictionpolls.domain.profile.FollowUnfollowUseCase import com.bounswe.predictionpolls.domain.profile.GetCurrentUserProfileUseCase import com.bounswe.predictionpolls.domain.profile.ProfileInfo import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -17,7 +19,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import javax.inject.Inject private data class MyProfileViewModelState( val profileInfo: ProfileInfo? = null, @@ -63,8 +64,8 @@ private data class MyProfileViewModelState( class MyProfileViewModel @Inject constructor( private val getProfileInfoUseCase: GetCurrentUserProfileUseCase, private val followUnfollowUseCase: FollowUnfollowUseCase, - private val getFeedUseCase: GetFeedUseCase -) : ViewModel() { + private val pollRepository: PollRepositoryInterface, +) : BaseViewModel() { private val _profileScreenUiState: MutableStateFlow = MutableStateFlow(MyProfileViewModelState()) @@ -156,25 +157,27 @@ class MyProfileViewModel @Inject constructor( fun fetchFeed(page: Int) = viewModelScope.launch { _profileScreenUiState.update { it.copy(isLoading = true) } - when (val result = getFeedUseCase(page)) { - is Result.Success -> { + launchCatching( + onSuccess = { polls -> _profileScreenUiState.update { it.copy( isLoading = false, - feed = result.data, + feed = polls.toImmutableList(), + error = null ) } - } - - is Result.Error -> { + }, + onError = { exception -> _profileScreenUiState.update { it.copy( isLoading = false, - error = result.exception.message, + error = exception?.message, feed = null ) } } + ) { + pollRepository.getOpenedPollsForMe() } } } \ No newline at end of file diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt index 023e35ae..3838b990 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreen.kt @@ -1,6 +1,7 @@ package com.bounswe.predictionpolls.ui.profile import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -23,7 +24,6 @@ import com.bounswe.predictionpolls.ui.common.poll.DiscreteVoteOption import com.bounswe.predictionpolls.ui.common.poll.PollComposable import com.bounswe.predictionpolls.ui.common.poll.ReadOnlyDiscretePollOptions import com.bounswe.predictionpolls.ui.theme.PredictionPollsTheme -import com.bounswe.predictionpolls.utils.shareLink @Composable @@ -32,6 +32,7 @@ fun ProfileScreen( onProfileClicked: (String) -> Unit, onEditProfileClicked: (() -> Unit)?, onFollowClicked: () -> Unit, + onPollClicked: (String) -> Unit, modifier: Modifier = Modifier ) { InternalProfileScreen( @@ -103,13 +104,12 @@ fun ProfileScreen( }, dueDate = it.dueDate?.fromISO8601() ?: "", rejectionText = it.rejectionText ?: "", - commentCount = it.commentCount, onProfileCardClicked = { onProfileClicked(it.pollCreatorUsername) }, - onShareClicked = { - context.shareLink(frontEndUrl + "/vote/" + it.polId) - } + modifier = Modifier.clickable { + onPollClicked(it.polId) + }, ) } } @@ -203,9 +203,7 @@ private fun ProfileScreenPreview() { modifier = Modifier.padding(16.dp), dueDate = "", rejectionText = "Last 5 days", - commentCount = 530, onProfileCardClicked = {}, - onShareClicked = {} ) } } diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenNavigation.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenNavigation.kt index 87a6582a..efa53c25 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenNavigation.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenNavigation.kt @@ -12,6 +12,7 @@ import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.bounswe.predictionpolls.domain.annotation.PollAnnotationPages import com.bounswe.predictionpolls.ui.common.annotation.AnnotationViewModel +import com.bounswe.predictionpolls.ui.vote.navigateToPollVoteScreen const val PROFILE_SCREEN_ROUTE = "profile/{username}" @@ -33,22 +34,30 @@ fun NavGraphBuilder.profileScreen(navController: NavController) { PollAnnotationPages.PROFILE(username) ) profileViewModel.fetchProfileInfo(username) - profileViewModel.fetchFeed(0) // Updated to pass the username + profileViewModel.fetchFeed(username) // Updated to pass the username } } val profileScreenUiState by profileViewModel.profileScreenUiState.collectAsStateWithLifecycle() - ProfileScreen(profileScreenUiState, onProfileClicked = { - navController.navigateToProfileScreen(it) - }, null, onFollowClicked = { - (profileScreenUiState as? ProfileScreenUiState.ProfileAndFeedFetched)?.let { profileScreenUiState -> - if (profileScreenUiState.isFollowedByLoggedUser == true) { - profileViewModel.unfollowUser(profileScreenUiState.profileInfo.userId) - } else - profileViewModel.followUser(profileScreenUiState.profileInfo.userId) + ProfileScreen( + profileScreenUiState, + onProfileClicked = { + navController.navigateToProfileScreen(it) + }, + null, + onFollowClicked = { + (profileScreenUiState as? ProfileScreenUiState.ProfileAndFeedFetched)?.let { profileScreenUiState -> + if (profileScreenUiState.isFollowedByLoggedUser == true) { + profileViewModel.unfollowUser(profileScreenUiState.profileInfo.userId) + } else + profileViewModel.followUser(profileScreenUiState.profileInfo.userId) + } + }, + onPollClicked = { + navController.navigateToPollVoteScreen(it) } - }) + ) } } diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenUiState.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenUiState.kt index 4612e166..ce414769 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenUiState.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenUiState.kt @@ -2,7 +2,6 @@ package com.bounswe.predictionpolls.ui.profile import com.bounswe.predictionpolls.domain.poll.Poll import com.bounswe.predictionpolls.domain.profile.ProfileInfo -import kotlinx.collections.immutable.ImmutableList sealed interface ProfileScreenUiState { data object Loading : ProfileScreenUiState @@ -26,7 +25,7 @@ sealed interface ProfileScreenUiState { data class ProfileAndFeedFetched( val profileInfo: ProfileInfo, - val feed: ImmutableList, + val feed: List, val followerCount: Int, val followedCount: Int, val isFollowedByLoggedUser: Boolean? diff --git a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenViewModel.kt b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenViewModel.kt index 4e3b9e44..3bb876d5 100644 --- a/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenViewModel.kt +++ b/prediction-polls/android/app/src/main/java/com/bounswe/predictionpolls/ui/profile/ProfileScreenViewModel.kt @@ -1,16 +1,16 @@ package com.bounswe.predictionpolls.ui.profile -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.bounswe.predictionpolls.common.Result -import com.bounswe.predictionpolls.domain.feed.GetFeedUseCase +import com.bounswe.predictionpolls.core.BaseViewModel +import com.bounswe.predictionpolls.data.remote.repositories.PollRepositoryInterface import com.bounswe.predictionpolls.domain.poll.Poll import com.bounswe.predictionpolls.domain.profile.FollowUnfollowUseCase import com.bounswe.predictionpolls.domain.profile.GetCurrentUserProfileUseCase import com.bounswe.predictionpolls.domain.profile.GetProfileInfoUseCase import com.bounswe.predictionpolls.domain.profile.ProfileInfo import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.collections.immutable.ImmutableList +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import javax.inject.Inject private data class ProfileScreenViewModelState( val profileInfo: ProfileInfo? = null, @@ -26,7 +25,7 @@ private data class ProfileScreenViewModelState( val isCurrentUserFollowed: Boolean = false, val followerCount: Int = 0, val followedCount: Int = 0, - val feed: ImmutableList? = null, + val feed: List = emptyList(), val isLoading: Boolean = true, val error: String? = null ) { @@ -64,9 +63,9 @@ private data class ProfileScreenViewModelState( class ProfileScreenViewModel @Inject constructor( private val getProfileInfoUseCase: GetProfileInfoUseCase, private val getCurrentUserProfileUseCase: GetCurrentUserProfileUseCase, - private val getFeedUseCase: GetFeedUseCase, + private val pollRepository: PollRepositoryInterface, private val followUnfollowUseCase: FollowUnfollowUseCase -) : ViewModel() { +) : BaseViewModel() { private val _profileScreenUiState: MutableStateFlow = MutableStateFlow(ProfileScreenViewModelState()) @@ -79,7 +78,6 @@ class ProfileScreenViewModel @Inject constructor( private var loggedUserName: String? = null private var loggedUserId: String? = null - init { viewModelScope.launch { getCurrentUserProfileUseCase.invoke() @@ -234,28 +232,29 @@ class ProfileScreenViewModel @Inject constructor( } } - fun fetchFeed(page: Int) = viewModelScope.launch { + fun fetchFeed(userName: String) = viewModelScope.launch { _profileScreenUiState.update { it.copy(isLoading = true) } - when (val result = getFeedUseCase(page)) { - is Result.Success -> { + launchCatching( + onSuccess = { polls -> _profileScreenUiState.update { it.copy( isLoading = false, - feed = result.data, + feed = polls, + error = null ) } - } - - is Result.Error -> { + }, + onError = { _profileScreenUiState.update { it.copy( isLoading = false, - error = result.exception.message, - feed = null + error = it.error ?: it.error ) } } + ) { + pollRepository.getOpenedPolls(userName) } } } \ No newline at end of file