From 962f6055d88bfceac668f00bcb19bc8e3d390eb7 Mon Sep 17 00:00:00 2001 From: "debanshu.datta" Date: Sat, 14 May 2022 10:58:21 +0530 Subject: [PATCH] Adding UI for the Profile Screen --- app/build.gradle | 1 + .../compose_github/di/AppModule.kt | 6 +- .../network/dataSource/GitHubViewModel.kt | 16 +- .../compose_github/ui/base/Navigation.kt | 4 +- .../base/components/tabHandler/TabHandler.kt | 11 +- .../base/components/tabHandler/TabLayout.kt | 5 +- .../ui/feature_follow/FollowScreen.kt | 5 +- .../ui/feature_profile/ProfileScreen.kt | 197 +++++++++++++++++- .../ui/feature_profile/state/ProfileState.kt | 10 +- .../ui/feature_search/SearchScreen.kt | 4 +- .../ui/feature_trending/TrendingScreen.kt | 5 +- .../components/DeveloperCard.kt | 13 +- 12 files changed, 245 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c992cbd..ee66bc6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,6 +67,7 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" + implementation "androidx.compose.runtime:runtime-livedata:$compose_version" // Coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' diff --git a/app/src/main/java/com/debanshu777/compose_github/di/AppModule.kt b/app/src/main/java/com/debanshu777/compose_github/di/AppModule.kt index dcd8f39..73abd61 100644 --- a/app/src/main/java/com/debanshu777/compose_github/di/AppModule.kt +++ b/app/src/main/java/com/debanshu777/compose_github/di/AppModule.kt @@ -70,9 +70,9 @@ object AppModule { level = LogLevel.ALL } install(HttpTimeout) { - socketTimeoutMillis = 30_000 - requestTimeoutMillis = 30_000 - connectTimeoutMillis = 30_000 + socketTimeoutMillis = 30_0000 + requestTimeoutMillis = 30_0000 + connectTimeoutMillis = 30_0000 } defaultRequest { contentType(ContentType.Application.Json) diff --git a/app/src/main/java/com/debanshu777/compose_github/network/dataSource/GitHubViewModel.kt b/app/src/main/java/com/debanshu777/compose_github/network/dataSource/GitHubViewModel.kt index e58741d..52fdb83 100644 --- a/app/src/main/java/com/debanshu777/compose_github/network/dataSource/GitHubViewModel.kt +++ b/app/src/main/java/com/debanshu777/compose_github/network/dataSource/GitHubViewModel.kt @@ -18,14 +18,21 @@ import com.debanshu777.compose_github.ui.feature_trending.state.RepositoryTrendi import com.debanshu777.compose_github.utils.Resource import composedb.githubDB.DeveloperFollow import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class GitHubViewModel @Inject constructor(private val mainRepository: MainRepository) : ViewModel() { - val userDataState = MutableStateFlow(ProfileState()) + private val _userDataState = MutableLiveData(ProfileState()) + val userDataState:LiveData = _userDataState val trendingRepositoryDataState = MutableStateFlow(RepositoryTrendingState()) val trendingDeveloperDataState = MutableStateFlow(DeveloperTrendingState()) val developerList= mainRepository.getAllDeveloper() @@ -35,7 +42,6 @@ class GitHubViewModel @Inject constructor(private val mainRepository: MainReposi init { getTrendingRepository("monthly") getTrendingDeveloper("monthly") - //followDeveloperDataState.value= DeveloperFollowState(isLoading = false, data = developerList) } private val _searchWidgetState: MutableState = mutableStateOf(value = SearchWidgetState.CLOSED) @@ -56,13 +62,13 @@ class GitHubViewModel @Inject constructor(private val mainRepository: MainReposi fun getUserData(userName: String) = viewModelScope.launch { when (val result = mainRepository.getUserData(userName)) { is Resource.Loading -> { - userDataState.value = ProfileState(isLoading = true) + _userDataState.value = ProfileState(isLoading = true) } is Resource.Success -> { - userDataState.value = ProfileState(data = result.data) + _userDataState.value = ProfileState(data = result.data) } is Resource.Error -> { - searchState.value = SearchState(error = result.message) + _userDataState.value = ProfileState(error = result.message) } } } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/base/Navigation.kt b/app/src/main/java/com/debanshu777/compose_github/ui/base/Navigation.kt index 9768a18..6a84051 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/base/Navigation.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/base/Navigation.kt @@ -20,10 +20,10 @@ fun Navigation(viewModel: GitHubViewModel, navController: NavHostController) { ProfileScreen(viewModel) } composable(route = Screen.TrendingScreen.route) { - TrendingScreen(viewModel) + TrendingScreen(viewModel,navController) } composable(route = Screen.FollowScreen.route) { - FollowScreen() + FollowScreen(navController) } } } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabHandler.kt b/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabHandler.kt index b29421a..0be3534 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabHandler.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabHandler.kt @@ -2,26 +2,27 @@ package com.debanshu777.compose_github.ui.base.components.tabHandler import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable +import androidx.navigation.NavController import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.PagerState @ExperimentalPagerApi @Composable -fun TabHandler(pagerState: PagerState, pageCount: Int, tabNames: List, dataList: List>) { +fun TabHandler(pagerState: PagerState, pageCount: Int, tabNames: List, dataList: List>, navController: NavController) { Column() { Tabs(pagerState = pagerState, tabNames) - TabsContent(pagerState = pagerState, pageCount, dataList) + TabsContent(pagerState = pagerState, pageCount, dataList,navController) } } @ExperimentalPagerApi @Composable -fun TabsContent(pagerState: PagerState, pageCount: Int, dataList: List>) { +fun TabsContent(pagerState: PagerState, pageCount: Int, dataList: List>, navController: NavController) { HorizontalPager(state = pagerState, count = pageCount) { page -> when (page) { - 0 -> TabLayout(dataList[0]) - 1 -> TabLayout(dataList[1]) + 0 -> TabLayout(dataList[0],navController) + 1 -> TabLayout(dataList[1],navController) } } } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabLayout.kt b/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabLayout.kt index 05ec803..c399946 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabLayout.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/base/components/tabHandler/TabLayout.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.ExperimentalUnitApi import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import com.debanshu777.compose_github.network.model.TrendingDeveloperItem import com.debanshu777.compose_github.network.model.TrendingRepositoryItem import com.debanshu777.compose_github.ui.feature_follow.components.FollowDeveloperCard @@ -18,7 +19,7 @@ import composedb.githubDB.DeveloperFollow import composedb.githubDB.RepositoryFollow @Composable -fun TabLayout(data: List) { +fun TabLayout(data: List, navController: NavController) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -41,7 +42,7 @@ fun TabLayout(data: List) { FollowRepositoryCard(item) } if (item is TrendingDeveloperItem) { - DeveloperCard(item) + DeveloperCard(item,navController) } Spacer(modifier = Modifier.height(5.dp)) } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_follow/FollowScreen.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_follow/FollowScreen.kt index a4a1be6..16edf2f 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_follow/FollowScreen.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_follow/FollowScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController import com.debanshu777.compose_github.network.dataSource.GitHubViewModel import com.debanshu777.compose_github.ui.base.components.tabHandler.TabHandler import com.google.accompanist.pager.ExperimentalPagerApi @@ -24,7 +25,7 @@ import composedb.githubDB.DeveloperFollow @OptIn(ExperimentalPagerApi::class) @Composable -fun FollowScreen(viewModel:GitHubViewModel= hiltViewModel()) { +fun FollowScreen(navController: NavController,viewModel:GitHubViewModel= hiltViewModel()) { // val trendingDeveloperDataState by viewModel.trendingDeveloperDataState.collectAsState() val developerFollowList by viewModel.developerList.collectAsState(emptyList()) val repositoryFollowList by viewModel.repositoryList.collectAsState(emptyList()) @@ -32,5 +33,5 @@ fun FollowScreen(viewModel:GitHubViewModel= hiltViewModel()) { val pageCount = 2 val tabList = listOf("Repository", "Developer") val dataList = listOf(repositoryFollowList,developerFollowList) - TabHandler(pagerState, pageCount, tabList, dataList) + TabHandler(pagerState, pageCount, tabList, dataList,navController) } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/ProfileScreen.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/ProfileScreen.kt index c6f544a..1fe3655 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/ProfileScreen.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/ProfileScreen.kt @@ -1,13 +1,204 @@ package com.debanshu777.compose_github.ui.feature_profile +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.ExperimentalUnitApi +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import coil.compose.AsyncImage import com.debanshu777.compose_github.network.dataSource.GitHubViewModel +import com.debanshu777.compose_github.ui.feature_profile.state.ProfileState +@OptIn(ExperimentalUnitApi::class) @Composable -fun ProfileScreen(viewModel: GitHubViewModel) { - val profileData by viewModel.userDataState.collectAsState() - Text(text = profileData.data.toString()) +fun ProfileScreen(viewModel: GitHubViewModel= hiltViewModel()) { + val profileData by viewModel.userDataState.observeAsState() + Column( + modifier = Modifier + .fillMaxSize() + .padding(5.dp), + horizontalAlignment = Alignment.Start + ) { + Row( + modifier= Modifier + .fillMaxWidth() + .padding(top = 15.dp), + verticalAlignment=Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + AsyncImage( + modifier = Modifier + .height(120.dp) + .width(120.dp) + .clip(CircleShape), + model = profileData?.data?.avatarUrl, + contentScale = ContentScale.Fit, + contentDescription = "User Avatar" + ) + Row( + modifier=Modifier.fillMaxWidth(), + verticalAlignment=Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Column { + Text( + text = "Followers", + fontSize = TextUnit(value = 18F, type = TextUnitType.Sp), + ) + Text( + text = "${profileData?.data?.followers ?: "0"}", + fontWeight = FontWeight.Bold, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + Spacer(modifier = Modifier.width(14.dp)) + Column { + Text( + text = "Following", + fontSize = TextUnit(value = 18F, type = TextUnitType.Sp), + ) + Text( + text = "${profileData?.data?.following ?: "0"}", + fontWeight = FontWeight.Bold, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + } + Text( + text = profileData?.data?.name ?: "NA", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.Bold, + fontSize = TextUnit(value = 24F, type = TextUnitType.Sp), + ) + Text( + text = "@${profileData?.data?.login ?: "NA"}", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 18F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.height(15.dp)) + profileData?.data?.bio.let { + Text( + text = profileData?.data?.bio ?: "", + maxLines = 3, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + Spacer(modifier = Modifier.height(10.dp)) + profileData?.data?.company.let { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Company", + fontSize = TextUnit(value = 16F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.width(7.dp)) + Text( + text = profileData?.data?.company ?: "NA", + maxLines = 1, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + profileData?.data?.location.let { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Location", + fontSize = TextUnit(value = 16F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.width(7.dp)) + Text( + text = profileData?.data?.location ?: "NA", + maxLines = 1, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + profileData?.data?.email.let { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Email", + fontSize = TextUnit(value = 16F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.width(7.dp)) + Text( + text = profileData?.data?.email ?: "NA", + maxLines = 1, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + profileData?.data?.blog.let { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Blog", + fontSize = TextUnit(value = 16F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.width(7.dp)) + Text( + text = profileData?.data?.blog ?: "NA", + maxLines = 1, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + profileData?.data?.twitterUsername.let { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Twitter", + fontSize = TextUnit(value = 16F, type = TextUnitType.Sp), + ) + Spacer(modifier = Modifier.width(7.dp)) + Text( + text = "@${profileData?.data?.twitterUsername ?: "NA"}", + maxLines = 1, + fontWeight = FontWeight.Bold, + overflow = TextOverflow.Ellipsis, + fontSize = TextUnit(value = 14F, type = TextUnitType.Sp), + ) + } + } + } } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/state/ProfileState.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/state/ProfileState.kt index b590aba..1990714 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/state/ProfileState.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_profile/state/ProfileState.kt @@ -6,4 +6,12 @@ data class ProfileState( val isLoading: Boolean = false, val data: GitHubUserResponse? = null, val error: String? = "" -) +){ + override fun equals(other: Any?): Boolean { + return false + } + + override fun hashCode(): Int { + return data?.hashCode() ?: 0 + } +} diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_search/SearchScreen.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_search/SearchScreen.kt index 08d3be8..487aa10 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_search/SearchScreen.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_search/SearchScreen.kt @@ -31,7 +31,6 @@ import com.debanshu777.compose_github.ui.feature_search.components.Card @Composable fun SearchScreen(viewModel: GitHubViewModel, navController: NavController) { val searchData by viewModel.searchState.collectAsState() - Scaffold() { LazyColumn( modifier = Modifier.fillMaxWidth(), contentPadding = PaddingValues(10.dp) @@ -51,7 +50,6 @@ fun SearchScreen(viewModel: GitHubViewModel, navController: NavController) { }, verticalAlignment = Alignment.CenterVertically ) { - // Greeting(item.toString()) BadgedBox( modifier = Modifier.padding(start = 10.dp), badge = { @@ -83,6 +81,6 @@ fun SearchScreen(viewModel: GitHubViewModel, navController: NavController) { } } } - } + } } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/TrendingScreen.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/TrendingScreen.kt index fb2937e..44dc80c 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/TrendingScreen.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/TrendingScreen.kt @@ -3,13 +3,14 @@ package com.debanshu777.compose_github.ui.feature_trending import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.navigation.NavController import com.debanshu777.compose_github.network.dataSource.GitHubViewModel import com.debanshu777.compose_github.ui.base.components.tabHandler.TabHandler import com.google.accompanist.pager.* @OptIn(ExperimentalPagerApi::class) @Composable -fun TrendingScreen(viewModel: GitHubViewModel) { +fun TrendingScreen(viewModel: GitHubViewModel, navController: NavController) { val trendingRepositoryDataState by viewModel.trendingRepositoryDataState.collectAsState() val trendingDeveloperDataState by viewModel.trendingDeveloperDataState.collectAsState() @@ -17,5 +18,5 @@ fun TrendingScreen(viewModel: GitHubViewModel) { val pageCount = 2 val tabList = listOf("Repository", "Developer") val dataList = listOf(trendingRepositoryDataState.data, trendingDeveloperDataState.data) - TabHandler(pagerState, pageCount, tabList, dataList) + TabHandler(pagerState, pageCount, tabList, dataList,navController) } diff --git a/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/components/DeveloperCard.kt b/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/components/DeveloperCard.kt index 51068e4..bd5c0c7 100644 --- a/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/components/DeveloperCard.kt +++ b/app/src/main/java/com/debanshu777/compose_github/ui/feature_trending/components/DeveloperCard.kt @@ -29,13 +29,15 @@ import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnitType import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController import coil.compose.AsyncImage import com.debanshu777.compose_github.network.dataSource.GitHubViewModel import com.debanshu777.compose_github.network.model.TrendingDeveloperItem +import com.debanshu777.compose_github.ui.base.Screen @OptIn(ExperimentalUnitApi::class) @Composable -fun DeveloperCard(item: TrendingDeveloperItem,viewModel: GitHubViewModel = hiltViewModel()) { +fun DeveloperCard(item: TrendingDeveloperItem, navController: NavController, viewModel: GitHubViewModel = hiltViewModel()) { Card( modifier = Modifier.height(180.dp) ) { @@ -43,9 +45,12 @@ fun DeveloperCard(item: TrendingDeveloperItem,viewModel: GitHubViewModel = hiltV modifier = Modifier .fillMaxWidth() .wrapContentHeight() - .padding(horizontal = 5.dp,vertical=5.dp) - .clickable { }, - verticalAlignment = Alignment.Top + .padding(horizontal = 5.dp,vertical=5.dp), +// .clickable { +// item.username?.let { it1 -> viewModel.getUserData(it1) } +// navController.navigate(Screen.ProfileScreen.route) +// }, + verticalAlignment = Alignment.CenterVertically ) { AsyncImage( modifier = Modifier