11package com.project200.undabang.feature.feed.list
22
33import android.os.Bundle
4+ import android.view.LayoutInflater
45import android.view.View
6+ import android.view.ViewGroup
7+ import androidx.compose.runtime.getValue
8+ import androidx.compose.ui.platform.ComposeView
9+ import androidx.fragment.app.Fragment
510import androidx.fragment.app.viewModels
11+ import androidx.lifecycle.Lifecycle
12+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
13+ import androidx.lifecycle.lifecycleScope
14+ import androidx.lifecycle.repeatOnLifecycle
615import androidx.navigation.fragment.findNavController
7- import androidx.recyclerview.widget.LinearLayoutManager
8- import androidx.recyclerview.widget.RecyclerView
9- import com.project200.domain.model.ExerciseType
10- import com.project200.presentation.base.BindingFragment
16+ import com.project200.presentation.compose.applyAppTheme
1117import com.project200.presentation.compose.components.feedback.UndabangBottomSheet
1218import com.project200.presentation.utils.collectToast
13- import com.project200.undabang.feature.feed.R
14- import com.project200.undabang.feature.feed.databinding.FragmentFeedListBinding
1519import dagger.hilt.android.AndroidEntryPoint
20+ import kotlinx.coroutines.launch
1621
1722@AndroidEntryPoint
18- class FeedListFragment : BindingFragment < FragmentFeedListBinding >( R .layout.fragment_feed_list ) {
23+ class FeedListFragment : Fragment ( ) {
1924 private val viewModel: FeedListViewModel by viewModels()
20- private lateinit var feedAdapter: FeedListAdapter
2125
22- override fun getViewBinding (view : View ): FragmentFeedListBinding {
23- return FragmentFeedListBinding .bind(view)
24- }
26+ override fun onCreateView (
27+ inflater : LayoutInflater ,
28+ container : ViewGroup ? ,
29+ savedInstanceState : Bundle ? ,
30+ ): View =
31+ ComposeView (requireContext()).apply {
32+ applyAppTheme {
33+ val feeds by viewModel.feedList.collectAsStateWithLifecycle()
34+ val selectedType by viewModel.selectedType.collectAsStateWithLifecycle()
35+ val isLoading by viewModel.isLoading.collectAsStateWithLifecycle()
36+ val isEmpty by viewModel.isEmpty.collectAsStateWithLifecycle()
37+
38+ FeedListScreen (
39+ feeds = feeds,
40+ selectedTypeName = selectedType?.name,
41+ isLoading = isLoading,
42+ isEmpty = isEmpty,
43+ onFeedClick = { feedId ->
44+ findNavController().navigate(
45+ FeedListFragmentDirections .actionFeedListFragmentToFeedDetailFragment(feedId),
46+ )
47+ },
48+ onAddClick = {
49+ findNavController().navigate(
50+ FeedListFragmentDirections .actionFeedListFragmentToFeedFormFragment(),
51+ )
52+ },
53+ onCategoryClick = viewModel::requestShowCategoryBottomSheet,
54+ onClearCategory = viewModel::clearType,
55+ onLoadMore = {
56+ if (viewModel.canLoadMore()) {
57+ viewModel.loadFeeds()
58+ }
59+ },
60+ )
61+ }
62+ }
2563
2664 override fun onViewCreated (
2765 view : View ,
2866 savedInstanceState : Bundle ? ,
2967 ) {
3068 super .onViewCreated(view, savedInstanceState)
31- initAdapter()
32- initToolbar()
33- initView()
34- initObserver()
3569 observeRefreshSignal()
70+ collectToast(viewModel.toastEvent)
71+
72+ viewLifecycleOwner.lifecycleScope.launch {
73+ repeatOnLifecycle(Lifecycle .State .STARTED ) {
74+ viewModel.showCategoryBottomSheet.collect { types ->
75+ UndabangBottomSheet .showSelection(
76+ fragmentManager = parentFragmentManager,
77+ items = types.map { it.name },
78+ ) { selectedName ->
79+ val selected = types.find { it.name == selectedName }
80+ viewModel.selectType(selected)
81+ }
82+ }
83+ }
84+ }
3685 }
3786
3887 private fun observeRefreshSignal () {
@@ -45,131 +94,6 @@ class FeedListFragment : BindingFragment<FragmentFeedListBinding>(R.layout.fragm
4594 }
4695 }
4796
48- private fun initAdapter () {
49- feedAdapter =
50- FeedListAdapter (
51- onItemClick = { feed ->
52- findNavController().navigate(
53- FeedListFragmentDirections .actionFeedListFragmentToFeedDetailFragment(feed.feedId),
54- )
55- },
56- )
57- }
58-
59- private fun initToolbar () {
60- binding.baseToolbar.apply {
61- setTitle(getString(R .string.feed_title))
62- showBackButton(false )
63- setSubButton(R .drawable.ic_feed_add) {
64- findNavController().navigate(
65- FeedListFragmentDirections .actionFeedListFragmentToFeedFormFragment(),
66- )
67- }
68- setSecondarySubButton(R .drawable.ic_category) {
69- showCategoryBottomSheet()
70- }
71- }
72- }
73-
74- private fun showCategoryBottomSheet () {
75- viewModel.requestShowCategoryBottomSheet()
76- }
77-
78- private fun displayCategoryBottomSheet (exerciseTypes : List <ExerciseType >) {
79- val names = exerciseTypes.map { it.name }
80- UndabangBottomSheet .showSelection(
81- fragmentManager = parentFragmentManager,
82- items = names,
83- ) { selectedName ->
84- val selectedType = exerciseTypes.find { it.name == selectedName }
85- viewModel.selectType(selectedType)
86- }
87- viewModel.onCategoryBottomSheetShown()
88- }
89-
90- private fun initView () {
91- binding.swipeRefreshLayout.setOnRefreshListener {
92- viewModel.loadFeeds(isRefresh = true )
93- }
94-
95- binding.feedListRv.apply {
96- adapter = feedAdapter
97- layoutManager = LinearLayoutManager (context)
98- addOnScrollListener(
99- object : RecyclerView .OnScrollListener () {
100- override fun onScrolled (
101- recyclerView : RecyclerView ,
102- dx : Int ,
103- dy : Int ,
104- ) {
105- super .onScrolled(recyclerView, dx, dy)
106-
107- val layoutManager = recyclerView.layoutManager as LinearLayoutManager
108- val totalItemCount = layoutManager.itemCount
109- val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
110-
111- if (viewModel.canLoadMore() && totalItemCount <= (lastVisibleItem + 5 )) {
112- viewModel.loadFeeds()
113- }
114- }
115- },
116- )
117- }
118- }
119-
120- private fun initObserver () {
121- viewModel.feedList.observe(viewLifecycleOwner) { feeds ->
122- feedAdapter.submitList(feeds)
123- }
124-
125- viewModel.showEmptyView.observe(viewLifecycleOwner) { showEmpty ->
126- binding.emptyTv.visibility = if (showEmpty) View .VISIBLE else View .GONE
127- }
128-
129- viewModel.showCategoryBottomSheet.observe(viewLifecycleOwner) { exerciseTypes ->
130- if (! exerciseTypes.isNullOrEmpty()) {
131- displayCategoryBottomSheet(exerciseTypes)
132- }
133- }
134-
135- viewModel.selectedType.observe(viewLifecycleOwner) { type ->
136- binding.baseToolbar.apply {
137- if (type != null ) {
138- setTitle(type.name)
139- setSecondarySubButton(null )
140- showBackButton(true ) { viewModel.clearType() }
141- } else {
142- setTitle(getString(R .string.feed_title))
143- setSecondarySubButton(R .drawable.ic_category) {
144- showCategoryBottomSheet()
145- }
146- showBackButton(false )
147- }
148- }
149- }
150-
151- viewModel.isLoading.observe(viewLifecycleOwner) { isLoading ->
152- val isInitialLoading = isLoading && feedAdapter.itemCount == 0
153- if (isInitialLoading) {
154- binding.shimmerLayout.visibility = View .VISIBLE
155- binding.shimmerLayout.startShimmer()
156- binding.swipeRefreshLayout.visibility = View .GONE
157- binding.emptyTv.visibility = View .GONE
158- } else {
159- binding.shimmerLayout.stopShimmer()
160- binding.shimmerLayout.visibility = View .GONE
161- binding.swipeRefreshLayout.visibility = View .VISIBLE
162- binding.swipeRefreshLayout.isRefreshing = false
163- }
164- }
165-
166- collectToast(viewModel.toastEvent)
167-
168- viewModel.currentMemberId.observe(viewLifecycleOwner) { memberId ->
169- feedAdapter.setCurrentMemberId(memberId)
170- }
171- }
172-
17397 companion object {
17498 const val REFRESH_KEY = " feed_list_refresh"
17599 }
0 commit comments