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..bf4c039 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..6be0f8c 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 @@ -4,22 +4,23 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +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.theme.FindIssueTheme import com.example.findissues.utils.Browser -import com.example.findissues.utils.Constants.FOLLOWERS -import com.example.findissues.utils.Constants.FOLLOWING import com.example.findissues.utils.Constants.TWITTER_BASE_URL -import com.example.findissues.utils.GlideLoader -import com.example.findissues.utils.Network -import com.example.findissues.utils.Toaster import com.example.findissues.viewmodels.PinnedRepoViewModel import com.example.findissues.viewmodels.UserViewModel import dagger.hilt.android.AndroidEntryPoint @@ -31,8 +32,7 @@ import javax.inject.Inject @AndroidEntryPoint class HomeFragment : Fragment() { - 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 +43,12 @@ 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 - } 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 - } + user = it } lifecycleScope.launch { @@ -85,30 +61,28 @@ class HomeFragment : Fragment() { pinnedRepoAdapter.setUpPinnedRepoList(it) } - with(binding) { - tvFollowers.setOnClickListener { - setUpFragment(FollowersFragment()) - } - tvFollowing.setOnClickListener { - setUpFragment(FollowingFragment()) - } - tvRepositories.setOnClickListener { - setUpFragment(RepositoryFragment()) - } - tvStarred.setOnClickListener { - setUpFragment(StarredFragment()) + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + FindIssueTheme { + 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 +90,4 @@ class HomeFragment : Fragment() { fragmentTransaction.commit() } - 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/HomeScreen.kt b/app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt new file mode 100644 index 0000000..7c3d7e1 --- /dev/null +++ b/app/src/main/java/com/example/findissues/ui/home/HomeScreen.kt @@ -0,0 +1,208 @@ +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.AppTopBar +import com.example.core.ui.components.IconWithText +import com.example.findissues.R +import com.example.findissues.models.home.User +import com.example.findissues.ui.theme.FindIssueTheme +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 = { AppTopBar(title = R.string.home) }, modifier = Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .padding(it) + .fillMaxSize() + .verticalScroll(scrollState) + .background(color = MaterialTheme.colorScheme.primary) + ) { + UserDetails( + user = user, + onFollowersClick = onFollowersClick, + onFollowingClick = onFollowingClick, + goToTwitter = goToTwitter, + modifier = Modifier + .fillMaxWidth() + ) + Spacer( + modifier = Modifier + .height(18.dp) + .fillMaxWidth() + .background(MaterialTheme.colorScheme.secondary) + ) + 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 = MaterialTheme.colorScheme.tertiary, + fontWeight = FontWeight.Bold, + style = MaterialTheme.typography.headlineLarge, + modifier = Modifier.fillMaxWidth() + ) + Text( + text = user.login, + color = MaterialTheme.colorScheme.tertiary, + fontSize = 19.sp + ) + } + } + + Text( + text = user.bio, + color = MaterialTheme.colorScheme.tertiary, + 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() { + FindIssueTheme { + HomeScreen(User()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/findissues/ui/theme/Color.kt b/app/src/main/java/com/example/findissues/ui/theme/Color.kt new file mode 100644 index 0000000..3cfad5d --- /dev/null +++ b/app/src/main/java/com/example/findissues/ui/theme/Color.kt @@ -0,0 +1,6 @@ +package com.example.findissues.ui.theme + +import androidx.compose.ui.graphics.Color + +val GitHub_Bkg = Color(0xFF0d1117) +val Grey40 = Color(0xFFb7b8ba) diff --git a/app/src/main/java/com/example/findissues/ui/theme/Theme.kt b/app/src/main/java/com/example/findissues/ui/theme/Theme.kt new file mode 100644 index 0000000..4e3b3ac --- /dev/null +++ b/app/src/main/java/com/example/findissues/ui/theme/Theme.kt @@ -0,0 +1,36 @@ +package com.example.findissues.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color + +private val DarkColorScheme = darkColorScheme( + primary = GitHub_Bkg, + secondary = Color.Black, + tertiary = Grey40 +) + +private val LightColorScheme = lightColorScheme( + primary = GitHub_Bkg, + secondary = Color.Black, + tertiary = Grey40 + +) + +@Composable +fun FindIssueTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colorScheme = when { + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} \ 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/AppTopBar.kt b/core/src/main/java/com/example/core/ui/components/AppTopBar.kt new file mode 100644 index 0000000..e0e4378 --- /dev/null +++ b/core/src/main/java/com/example/core/ui/components/AppTopBar.kt @@ -0,0 +1,24 @@ +package com.example.core.ui.components + +import androidx.compose.material.TopAppBar +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 + +@Composable +fun AppTopBar(title: Int) { + TopAppBar( + title = { + Text( + text = stringResource(id = title), + fontWeight = FontWeight.Medium, + color = Color.White, + style = MaterialTheme.typography.headlineSmall + ) + }, + backgroundColor = Color(0xFF0d1117), + ) +} \ 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