Skip to content

Commit

Permalink
Add recent contribution month to home fragment
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandr7035 committed Nov 9, 2023
1 parent a2cfbc4 commit 4b4d49f
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 109 deletions.
Original file line number Diff line number Diff line change
@@ -1,73 +1,67 @@
package by.alexandr7035.gitstat.view.contributions_grid

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import by.alexandr7035.gitstat.R
import by.alexandr7035.gitstat.data.local.model.ContributionDayEntity
import by.alexandr7035.gitstat.core.extensions.navigateSafe
import by.alexandr7035.gitstat.core.extensions.observeNullSafe
import by.alexandr7035.gitstat.data.local.model.ContributionYearWithMonths
import by.alexandr7035.gitstat.data.local.model.ContributionsMonthWithDays
import by.alexandr7035.gitstat.databinding.FragmentContributionsGridBinding
import by.alexandr7035.gitstat.core.extensions.debug
import by.alexandr7035.gitstat.core.extensions.navigateSafe
import by.alexandr7035.gitstat.core.extensions.observeNullSafe
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.tabs.TabLayout
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale

@AndroidEntryPoint
class ContributionsGridFragment : Fragment(), DayClickListener {

private var binding: FragmentContributionsGridBinding? = null
class ContributionsGridFragment : Fragment(R.layout.fragment_contributions_grid) {
private val binding by viewBinding(FragmentContributionsGridBinding::bind)
private val viewModel by viewModels<ContributionsGridViewModel>()
private val safeArgs by navArgs<ContributionsGridFragmentArgs>()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
binding = FragmentContributionsGridBinding.inflate(inflater, container, false)
return binding?.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding?.toolbar?.title = getString(R.string.year_toolbar_title, safeArgs.contributionYear)
binding?.toolbar?.setNavigationOnClickListener {
binding.toolbar.title = getString(R.string.year_toolbar_title, safeArgs.contributionYear)
binding.toolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}

val adapter = MonthsAdapter(this)
val adapter = MonthsAdapter(onDayClick = { contributionDay ->
findNavController().navigateSafe(
ContributionsGridFragmentDirections.actionContributionsGridFragmentToContributionDayDialogFragment(
contributionDay.count,
contributionDay.date,
contributionDay.color
)
)
})

binding?.monthRecycler?.adapter = adapter
binding?.monthRecycler?.layoutManager = LinearLayoutManager(requireContext())
binding.monthRecycler.adapter = adapter
binding.monthRecycler.layoutManager = LinearLayoutManager(requireContext())

viewModel.getContributionYearsWithMonthsLiveData().observeNullSafe(viewLifecycleOwner, { years ->
viewModel.getContributionYearsWithMonthsLiveData().observeNullSafe(viewLifecycleOwner) { years ->
if (years.isNotEmpty()) {
// Add year tabs depending on years list (reversed)
for (year in years.reversed()) {
val tab = binding?.tabLayout?.newTab()
val tab = binding.tabLayout.newTab()
// Set year as tab text
tab?.text = year.year.id.toString()

if (tab != null) {
binding?.tabLayout?.addTab(tab)
}
tab.text = year.year.id.toString()
binding.tabLayout.addTab(tab)
}


binding?.tabLayout?.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
val year = years.reversed()[tab.position]
adapter.setItems(getMonthsToShow(years, tab.position))
binding?.toolbar?.title = getString(R.string.year_toolbar_title, year.year.id)
binding.toolbar.title = getString(R.string.year_toolbar_title, year.year.id)
}

override fun onTabUnselected(tab: TabLayout.Tab) {
Expand All @@ -77,15 +71,15 @@ class ContributionsGridFragment : Fragment(), DayClickListener {
override fun onTabReselected(tab: TabLayout.Tab) {
val year = years.reversed()[tab.position]
adapter.setItems(getMonthsToShow(years, tab.position))
binding?.toolbar?.title = getString(R.string.year_toolbar_title, year.year.id)
binding.toolbar.title = getString(R.string.year_toolbar_title, year.year.id)
}
})

// Set initial tab position
val initialTab = binding?.tabLayout?.getTabAt(0)
val initialTab = binding.tabLayout.getTabAt(0)
initialTab?.select()
}
})
}

}

Expand Down Expand Up @@ -124,23 +118,4 @@ class ContributionsGridFragment : Fragment(), DayClickListener {

return monthWithDays.reversed()
}

override fun onDestroyView() {
super.onDestroyView()
binding = null
}


// Handle contribution cells clicks here
override fun onDayItemClick(contributionDay: ContributionDayEntity) {
Timber.debug("click in FRAGMENT $contributionDay")

findNavController().navigateSafe(
ContributionsGridFragmentDirections.actionContributionsGridFragmentToContributionDayDialogFragment(
contributionDay.count,
contributionDay.date,
contributionDay.color
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import by.alexandr7035.gitstat.R
import by.alexandr7035.gitstat.core.helpers.TimeHelper
import by.alexandr7035.gitstat.data.local.model.ContributionDayEntity
import by.alexandr7035.gitstat.databinding.ViewContributionsGridCellBinding
import by.alexandr7035.gitstat.core.extensions.debug
import timber.log.Timber

class DaysAdapter(private val dayClickListener: DayClickListener): RecyclerView.Adapter<DaysAdapter.ViewHolder>() {
class DaysAdapter(
private val onDayClick: (ContributionDayEntity) -> Unit
): RecyclerView.Adapter<DaysAdapter.ViewHolder>() {

private var items: List<ContributionDayEntity> = emptyList()

Expand Down Expand Up @@ -57,8 +57,7 @@ class DaysAdapter(private val dayClickListener: DayClickListener): RecyclerView.

override fun onClick(view: View) {
val clickedDay = items[adapterPosition]
Timber.debug("clicked $clickedDay")
dayClickListener.onDayItemClick(clickedDay)
onDayClick(clickedDay)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*

class MonthsAdapter(private val fragmentDayClickListener: DayClickListener): RecyclerView.Adapter<MonthsAdapter.ViewHolder>(), DayClickListener {
class MonthsAdapter(
private val onDayClick: (ContributionDayEntity) -> Unit
) : RecyclerView.Adapter<MonthsAdapter.ViewHolder>() {

// FIXME
private var items: List<ContributionsMonthWithDays> = emptyList()
Expand Down Expand Up @@ -54,19 +56,14 @@ class MonthsAdapter(private val fragmentDayClickListener: DayClickListener): Rec
.sumOf { it.count }
.toString()

val adapter = DaysAdapter(this)
val adapter = DaysAdapter(onDayClick = onDayClick)

holder.binding.cellsRecycler.adapter = adapter
// holder.binding.cellsRecycler.layoutManager = FlexboxLayoutManager(holder.binding.root.context)
holder.binding.cellsRecycler.layoutManager = GridLayoutManager(holder.binding.root.context, 7)

// Set days of the month to cells
adapter.setItems(items[position].contributionDays)
}

class ViewHolder(val binding: ViewMonthContributionsGridBinding): RecyclerView.ViewHolder(binding.root)


override fun onDayItemClick(contributionDay: ContributionDayEntity) {
fragmentDayClickListener.onDayItemClick(contributionDay)
}
class ViewHolder(val binding: ViewMonthContributionsGridBinding) : RecyclerView.ViewHolder(binding.root)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,122 @@ package by.alexandr7035.gitstat.view.profile

import android.os.Bundle
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import by.alexandr7035.gitstat.R
import by.alexandr7035.gitstat.core.extensions.navigateSafe
import by.alexandr7035.gitstat.core.extensions.observeNullSafe
import by.alexandr7035.gitstat.databinding.FragmentProfileBinding
import by.alexandr7035.gitstat.view.MainActivity
import by.alexandr7035.gitstat.view.contributions_grid.DaysAdapter
import by.kirich1409.viewbindingdelegate.viewBinding
import com.squareup.picasso.Picasso
import dagger.hilt.android.AndroidEntryPoint
import java.text.SimpleDateFormat
import java.util.Locale


@AndroidEntryPoint
class ProfileFragment : Fragment() {

private var binding: FragmentProfileBinding? = null
class ProfileFragment : Fragment(R.layout.fragment_profile) {
private val binding by viewBinding(FragmentProfileBinding::bind)
private val viewModel by viewModels<ProfileViewModel>()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
// Inflate the layout for this fragment
binding = FragmentProfileBinding.inflate(inflater, container, false)
return binding!!.root
}


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Update profile data
viewModel.getUserLiveData().observeNullSafe(viewLifecycleOwner, {
viewModel.getUserLiveData().observeNullSafe(viewLifecycleOwner) {

Picasso.get().load(it.avatar_url).into(binding!!.profileImageView)
Picasso.get().load(it.avatar_url).into(binding.profileImageView)

// This field can be empty
if (it.name.isEmpty()) {
binding?.nameView?.visibility = View.GONE
binding.nameView.visibility = View.GONE
} else {
binding?.nameView?.visibility = View.VISIBLE
binding?.nameView?.text = it.name
binding.nameView.visibility = View.VISIBLE
binding.nameView.text = it.name
}

binding!!.loginView.text = getString(R.string.login_string, it.login)
binding.loginView.text = getString(R.string.login_string, it.login)

binding!!.idView.text = it.id.toString()
binding.idView.text = it.id.toString()

val dateFormat = getString(R.string.profile_date_format)
binding!!.createdView.text = DateFormat.format(dateFormat, it.created_at)
binding!!.updatedView.text = DateFormat.format(dateFormat, it.updated_at)
binding.createdView.text = DateFormat.format(dateFormat, it.created_at)
binding.updatedView.text = DateFormat.format(dateFormat, it.updated_at)

binding!!.followersView.text = it.followers.toString()
binding.followersView.text = it.followers.toString()

// This field can be empty
if (it.location.isEmpty()) {
setLocationVisibility(false)
} else {
setLocationVisibility(true)
binding!!.locationView.text = it.location
binding.locationView.text = it.location
}

binding!!.totalReposView.text = it.total_repos_count.toString()
binding!!.privateReposView.text = it.private_repos_count.toString()
binding!!.publicReposView.text = it.public_repos_count.toString()
})
binding.totalReposView.text = it.total_repos_count.toString()
binding.privateReposView.text = it.private_repos_count.toString()
binding.publicReposView.text = it.public_repos_count.toString()
}

binding!!.reposStatDetailedBtn.setOnClickListener {
binding.reposStatDetailedBtn.setOnClickListener {
findNavController().navigateSafe(ProfileFragmentDirections.actionProfileFragmentToReposOverviewFragment())
}

binding?.drawerBtn?.setOnClickListener {
binding.drawerBtn.setOnClickListener {
(requireActivity() as MainActivity).openDrawerMenu()
}

setupContributionsGrid()
}

private fun setLocationVisibility(isVisible: Boolean) {
binding?.locationIcon?.isGone = ! isVisible
binding?.locationLabel?.isGone = ! isVisible
binding?.locationView?.isGone = ! isVisible
binding.locationIcon.isGone = !isVisible
binding.locationLabel.isGone = !isVisible
binding.locationView.isGone = !isVisible
}

override fun onDestroyView() {
super.onDestroyView()
binding = null
}
private fun setupContributionsGrid() {
val adapter = DaysAdapter(onDayClick = { contributionDay ->
findNavController().navigateSafe(
ProfileFragmentDirections.actionProfileFragmentToContributionDayDialogFragment(
contributionDay.count,
contributionDay.date,
contributionDay.color
)
)
})

viewModel.loadRecentYearContributions().observeNullSafe(viewLifecycleOwner) { it ->
val recentYear = it.last().contributionMonths

// Get current month number
val monthFormat = SimpleDateFormat("MM", Locale.US)
val currentMonth = monthFormat.format(System.currentTimeMillis()).toInt()

val recentMonth = recentYear.getOrNull(currentMonth-1)

if (recentMonth != null) {
binding.recentMonthContributions.root.isVisible = true

binding.recentMonthContributions.cellsRecycler.adapter = adapter
binding.recentMonthContributions.cellsRecycler.layoutManager = GridLayoutManager(requireContext(), 7)
binding.recentMonthContributions.monthCardTitle.text = getString(R.string.recent_month)
binding.recentMonthContributions.monthCardTotalContributions.text = recentMonth.contributionDays.sumOf { day ->
day.count
}.toString()

adapter.setItems(recentMonth.contributionDays)
}
else {
binding.recentMonthContributions.root.isVisible = false
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ package by.alexandr7035.gitstat.view.profile

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import by.alexandr7035.gitstat.data.ContributionsRepository
import by.alexandr7035.gitstat.data.UserRepository
import by.alexandr7035.gitstat.data.local.model.ContributionYearWithMonths
import by.alexandr7035.gitstat.data.local.model.UserEntity
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class ProfileViewModel @Inject constructor(private val userRepository: UserRepository): ViewModel() {
class ProfileViewModel @Inject constructor(
private val userRepository: UserRepository,
private val contributionsRepository: ContributionsRepository
) : ViewModel() {

fun getUserLiveData(): LiveData<UserEntity> {
return userRepository.getUserLiveDataFromCache()
}

fun loadRecentYearContributions(): LiveData<List<ContributionYearWithMonths>> {
return contributionsRepository
.getContributionYearsWithMonthsLiveData()
}

}
Loading

0 comments on commit 4b4d49f

Please sign in to comment.