From 886a97ce0d7a7fed7a0f1fc13d29e9d75199e309 Mon Sep 17 00:00:00 2001 From: Gautam Shorewala <123801344+GautamCoder4019k@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:14:00 +0530 Subject: [PATCH] fix #45:Migrate home screen to compose --- .../example/findissues/models/home/User.kt | 18 +- .../findissues/ui/fragments/HomeFragment.kt | 121 ++++++----- .../example/findissues/ui/home/HomeAppBar.kt | 37 ++++ .../example/findissues/ui/home/HomeScreen.kt | 202 ++++++++++++++++++ app/src/main/res/values/strings.xml | 4 + .../core/ui/components/IconWithText.kt | 39 ++++ 6 files changed, 364 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/example/findissues/ui/home/HomeAppBar.kt create mode 100644 app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt create mode 100644 core/src/main/java/com/example/core/ui/components/IconWithText.kt diff --git a/app/src/main/java/com/example/findissues/models/home/User.kt b/app/src/main/java/com/example/findissues/models/home/User.kt index d1a7838..5e9737a 100644 --- a/app/src/main/java/com/example/findissues/models/home/User.kt +++ b/app/src/main/java/com/example/findissues/models/home/User.kt @@ -1,13 +1,13 @@ package com.example.findissues.models.home data class User( - val login: String, - val avatar_url: String, - val name: String, - val bio: String, - val company: String, - val location: String, - val twitter_username: String, - val followers: Int, - val following: Int + val login: String="", + val avatar_url: String="", + val name: String="", + val bio: String="", + val company: String="", + val location: String="", + val twitter_username: String="", + val followers: Int=0, + val following: Int=0 ) diff --git a/app/src/main/java/com/example/findissues/ui/fragments/HomeFragment.kt b/app/src/main/java/com/example/findissues/ui/fragments/HomeFragment.kt index 2604acd..3f0d3c9 100644 --- a/app/src/main/java/com/example/findissues/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/example/findissues/ui/fragments/HomeFragment.kt @@ -1,18 +1,28 @@ package com.example.findissues.ui.fragments import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.material3.Button +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.example.findissues.R import com.example.findissues.databinding.FragmentHomeBinding +import com.example.findissues.models.home.User import com.example.findissues.ui.adapters.PinnedRepoAdapter import com.example.findissues.ui.followers.FollowersFragment import com.example.findissues.ui.following.FollowingFragment +import com.example.findissues.ui.home.HomeScreen +import com.example.findissues.ui.issues.IssuesScreen import com.example.findissues.utils.Browser import com.example.findissues.utils.Constants.FOLLOWERS import com.example.findissues.utils.Constants.FOLLOWING @@ -31,8 +41,10 @@ import javax.inject.Inject @AndroidEntryPoint class HomeFragment : Fragment() { - private var _binding: FragmentHomeBinding? = null - private val binding get() = _binding!! +// private var _binding: FragmentHomeBinding? = null +// private val binding get() = _binding!! + + private var user by mutableStateOf(User()) private lateinit var userViewModel: UserViewModel private lateinit var pinnedRepoViewModel: PinnedRepoViewModel @@ -43,36 +55,37 @@ class HomeFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentHomeBinding.inflate(inflater, container, false) - binding.toolbar.root.title = resources.getString(R.string.home) - if(!Network.isConnected(activity)){ - Toaster.show(binding.root,"Connect to internet") - return binding.root - } - binding.rvPinned.apply { - layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - adapter = pinnedRepoAdapter - } +// _binding = FragmentHomeBinding.inflate(inflater, container, false) +// binding.toolbar.root.title = resources.getString(R.string.home) +// if(!Network.isConnected(activity)){ +// Toaster.show(binding.root,"Connect to internet") +// return binding.root +// } +// binding.rvPinned.apply { +// layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) +// adapter = pinnedRepoAdapter +// } userViewModel = ViewModelProvider(this)[UserViewModel::class.java] pinnedRepoViewModel = ViewModelProvider(this)[PinnedRepoViewModel::class.java] userViewModel.getUserDetail() userViewModel.observeUserLiveData().observe(viewLifecycleOwner) { - with(binding) { - name.text = it.name - githubUsername.text = it.login - val glideLoader = GlideLoader(requireContext()) - glideLoader.loadCircularImage(it.avatar_url, profileImage) - bio.text = it.bio.replace("\n", "") - tvCompany.text = it.company - tvLocation.text = it.location - tvTwitter.text = it.twitter_username - tvTwitter.setOnClickListener { - Browser(requireContext()).launch(goToTwitter()) - } - tvFollowers.text = it.followers.toString() + " " + FOLLOWERS - tvFollowing.text = it.following.toString() + " " + FOLLOWING - } +// with(binding) { +// name.text = it.name +// githubUsername.text = it.login +// val glideLoader = GlideLoader(requireContext()) +// glideLoader.loadCircularImage(it.avatar_url, profileImage) +// bio.text = it.bio.replace("\n", "") +// tvCompany.text = it.company +// tvLocation.text = it.location +// tvTwitter.text = it.twitter_username +// tvTwitter.setOnClickListener { +// Browser(requireContext()).launch(goToTwitter()) +// } +// tvFollowers.text = it.followers.toString() + " " + FOLLOWERS +// tvFollowing.text = it.following.toString() + " " + FOLLOWING +// } + user = it } lifecycleScope.launch { @@ -85,30 +98,42 @@ class HomeFragment : Fragment() { pinnedRepoAdapter.setUpPinnedRepoList(it) } - with(binding) { - tvFollowers.setOnClickListener { - setUpFragment(FollowersFragment()) - } - tvFollowing.setOnClickListener { - setUpFragment(FollowingFragment()) - } - tvRepositories.setOnClickListener { - setUpFragment(RepositoryFragment()) - } - tvStarred.setOnClickListener { - setUpFragment(StarredFragment()) +// with(binding) { +// tvFollowers.setOnClickListener { +// setUpFragment(FollowersFragment()) +// } +// tvFollowing.setOnClickListener { +// setUpFragment(FollowingFragment()) +// } +// tvRepositories.setOnClickListener { +// setUpFragment(RepositoryFragment()) +// } +// tvStarred.setOnClickListener { +// setUpFragment(StarredFragment()) +// } +// } + +// return binding.root + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.Default) + setContent { + HomeScreen( + user = user, + onFollowersClick = { setUpFragment(FollowersFragment()) }, + onFollowingClick = { setUpFragment(FollowingFragment()) }, + onRepositoriesClick = { setUpFragment(RepositoryFragment()) }, + onStarredClick = { setUpFragment(StarredFragment()) }, + goToTwitter = { Browser(requireContext()).launch(goToTwitter(it)) } + ) } } - - return binding.root } - private fun goToTwitter(): String { - return TWITTER_BASE_URL + binding.tvTwitter.text.toString() + private fun goToTwitter(userName: String): String { + return TWITTER_BASE_URL + userName } private fun setUpFragment(fragment: Fragment) { - val fragment = fragment val fragmentManager = requireActivity().supportFragmentManager val fragmentTransaction = fragmentManager.beginTransaction() fragmentTransaction.replace(R.id.nav_host_fragment_activity_dashboard, fragment) @@ -116,8 +141,8 @@ class HomeFragment : Fragment() { fragmentTransaction.commit() } - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } +// override fun onDestroyView() { +// super.onDestroyView() +// _binding = null +// } } \ No newline at end of file diff --git a/app/src/main/java/com/example/findissues/ui/home/HomeAppBar.kt b/app/src/main/java/com/example/findissues/ui/home/HomeAppBar.kt new file mode 100644 index 0000000..d0e6c7b --- /dev/null +++ b/app/src/main/java/com/example/findissues/ui/home/HomeAppBar.kt @@ -0,0 +1,37 @@ +package com.example.findissues.ui.home + +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import com.example.findissues.R + +@Composable +fun HomeAppBar() { + TopAppBar( + title = { + Text( + text = stringResource(id = R.string.home), + fontWeight = FontWeight.Medium, + color = Color.White, + style = MaterialTheme.typography.headlineSmall + ) + }, + backgroundColor = Color(0xFF0d1117), + + ) +} + +@Preview( showBackground = true) +@Composable +fun HomeAppBarPreview() { + HomeAppBar() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt b/app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt new file mode 100644 index 0000000..f15ff37 --- /dev/null +++ b/app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt @@ -0,0 +1,202 @@ +package com.example.findissues.ui.home + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.example.core.ui.components.IconWithText +import com.example.findissues.R +import com.example.findissues.models.home.User +import com.example.findissues.utils.Constants + +@Composable +fun HomeScreen( + user: User, + onFollowersClick: () -> Unit = {}, + onFollowingClick: () -> Unit = {}, + onRepositoriesClick: () -> Unit = {}, + onStarredClick: () -> Unit = {}, + goToTwitter: (String) -> Unit = {} +) { + val scrollState = rememberScrollState() + Scaffold(topBar = { HomeAppBar() }, modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .padding(it) + .fillMaxSize() + .verticalScroll(scrollState) + .background(color = Color(0xFF0d1117)) + ) { + UserDetails( + user = user, + onFollowersClick = onFollowersClick, + onFollowingClick = onFollowingClick, + goToTwitter = goToTwitter, + modifier = Modifier + .fillMaxWidth() + ) + Spacer( + modifier = Modifier + .height(18.dp) + .fillMaxWidth() + .background(Color(0xFF000000)) + ) + UserLinks( + onRepositoriesClick = onRepositoriesClick, + onStarredClick = onStarredClick, + modifier = Modifier + .fillMaxSize() + ) + } + } +} + +@Composable +fun UserLinks( + modifier: Modifier = Modifier, + onRepositoriesClick: () -> Unit, + onStarredClick: () -> Unit +) { + Column(modifier = modifier) { + IconWithText( + text = stringResource(R.string.pinned), + iconId = R.drawable.pinned, + modifier = Modifier.padding(8.dp) + ) + IconWithText( + text = stringResource(R.string.repositories), + iconId = R.drawable.repo, + modifier = Modifier + .padding(8.dp) + .clickable { onRepositoriesClick() }, + textColor = Color.White + ) + IconWithText( + text = stringResource(R.string.starred), + iconId = R.drawable.star, + modifier = Modifier + .padding(8.dp) + .clickable { onStarredClick() }, + textColor = Color.White + ) + } +} + +@Composable +fun UserDetails( + user: User, + onFollowersClick: () -> Unit, + onFollowingClick: () -> Unit, + modifier: Modifier = Modifier, + goToTwitter: (String) -> Unit +) { + Column(modifier = modifier) { + Row { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current).data(user.avatar_url).build(), + contentDescription = "avatar", + modifier = Modifier + .size(128.dp) + .padding(16.dp) + .clip( + RoundedCornerShape(100.dp) + ) + ) + + Column(modifier = Modifier.padding(top = 16.dp, end = 16.dp)) { + Text( + text = user.name, + color = Color(0xFFb7b8ba), + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.headlineLarge, + modifier = Modifier.fillMaxWidth() + ) + Text( + text = user.login, color = Color(0xFFb7b8ba), fontSize = 19.sp + ) + } + } + + Text( + text = user.bio, + color = Color(0xFFb7b8ba), + fontSize = 20.sp, + modifier = Modifier.padding(8.dp) + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + IconWithText(text = user.company, iconId = R.drawable.company) + Spacer(modifier = Modifier.padding(12.dp)) + IconWithText(text = user.location, iconId = R.drawable.location) + + } + IconWithText( + text = user.twitter_username, + iconId = R.drawable.twitter, + fontWeight = FontWeight.Bold, + modifier = Modifier + .padding(vertical = 16.dp, horizontal = 8.dp) + .clickable { goToTwitter(user.twitter_username) } + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + IconWithText( + text = stringResource( + R.string.number_with_string, + user.followers, + Constants.FOLLOWERS + ), + iconId = R.drawable.followers, + modifier = Modifier.clickable { onFollowersClick() } + ) + Spacer(modifier = Modifier.padding(12.dp)) + IconWithText( + text = stringResource( + R.string.number_with_string, + user.following, + Constants.FOLLOWING + ), + iconId = R.drawable.following, + modifier = Modifier.clickable { onFollowingClick() } + ) + + } + } + +} + +@Preview(showBackground = true) +@Composable +fun HomeScreenPreview() { + HomeScreen(User()) +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb0d829..34be4ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,5 +8,9 @@ Statistics Profile Please select a tag + Pinned + Repositories + Starred + %1$s %2$s \ No newline at end of file diff --git a/core/src/main/java/com/example/core/ui/components/IconWithText.kt b/core/src/main/java/com/example/core/ui/components/IconWithText.kt new file mode 100644 index 0000000..f6beed7 --- /dev/null +++ b/core/src/main/java/com/example/core/ui/components/IconWithText.kt @@ -0,0 +1,39 @@ +package com.example.core.ui.components + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun IconWithText( + modifier: Modifier = Modifier, + text: String, + iconId: Int, + fontWeight: FontWeight = FontWeight.Normal, + textColor: Color = Color(0xFFb7b8ba) +) { + Row(modifier = modifier) { + Icon( + painter = painterResource(id = iconId), + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(28.dp) + ) + Text( + text = text, + color = textColor, + fontSize = 20.sp, + fontWeight = fontWeight, + modifier = Modifier.padding(start = 12.dp, top = 4.dp), + ) + } +} \ No newline at end of file