Skip to content

Commit d986265

Browse files
authored
Merge pull request #518 from projects200/refactor/compose-block-members-517
[Refactor] 차단된 계정 목록 화면 Compose 전환 #517
2 parents 766bf06 + 2d352fe commit d986265

8 files changed

Lines changed: 234 additions & 191 deletions

File tree

feature/profile/src/main/java/com/project200/undabang/profile/setting/BlockMemberRVAdapter.kt

Lines changed: 0 additions & 75 deletions
This file was deleted.

feature/profile/src/main/java/com/project200/undabang/profile/setting/BlockMembersFragment.kt

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,57 @@
11
package com.project200.undabang.profile.setting
22

3+
import android.os.Bundle
4+
import android.view.LayoutInflater
35
import android.view.View
6+
import android.view.ViewGroup
47
import android.widget.Toast
5-
import androidx.core.view.isVisible
8+
import androidx.compose.runtime.getValue
9+
import androidx.compose.ui.platform.ComposeView
10+
import androidx.fragment.app.Fragment
611
import androidx.fragment.app.viewModels
712
import androidx.lifecycle.Lifecycle
13+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
814
import androidx.lifecycle.lifecycleScope
915
import androidx.lifecycle.repeatOnLifecycle
10-
import androidx.recyclerview.widget.LinearLayoutManager
11-
import com.project200.presentation.base.BindingFragment
16+
import com.project200.presentation.compose.applyAppTheme
1217
import com.project200.undabang.feature.profile.R
13-
import com.project200.undabang.feature.profile.databinding.FragmentBlockMembersBinding
1418
import dagger.hilt.android.AndroidEntryPoint
1519
import kotlinx.coroutines.launch
1620

1721
@AndroidEntryPoint
18-
class BlockMembersFragment : BindingFragment<FragmentBlockMembersBinding>(R.layout.fragment_block_members) {
22+
class BlockMembersFragment : Fragment() {
1923
private val viewModel: BlockMembersViewModel by viewModels()
20-
private lateinit var blockMemberAdapter: BlockMemberRVAdapter
2124

22-
override fun getViewBinding(view: View): FragmentBlockMembersBinding {
23-
return FragmentBlockMembersBinding.bind(view)
24-
}
25-
26-
override fun setupViews() {
27-
binding.baseToolbar.apply {
28-
setTitle(getString(R.string.block_members))
29-
showBackButton(true) { requireActivity().onBackPressedDispatcher.onBackPressed() }
30-
}
31-
setupRecyclerView()
32-
}
33-
34-
private fun setupRecyclerView() {
35-
blockMemberAdapter =
36-
BlockMemberRVAdapter { member ->
37-
viewModel.unblockMember(member)
25+
override fun onCreateView(
26+
inflater: LayoutInflater,
27+
container: ViewGroup?,
28+
savedInstanceState: Bundle?,
29+
): View =
30+
ComposeView(requireContext()).apply {
31+
applyAppTheme {
32+
val members by viewModel.blockedMembers.collectAsStateWithLifecycle()
33+
BlockMembersScreen(
34+
members = members,
35+
onBackClick = { requireActivity().onBackPressedDispatcher.onBackPressed() },
36+
onUnblockClick = viewModel::unblockMember,
37+
)
3838
}
39-
40-
binding.blockMembersRv.apply {
41-
adapter = blockMemberAdapter
42-
layoutManager = LinearLayoutManager(requireContext())
4339
}
44-
}
4540

46-
override fun setupObservers() {
47-
viewModel.blockedMembers.observe(viewLifecycleOwner) { members ->
48-
blockMemberAdapter.submitList(members)
49-
binding.blockMembersRv.isVisible = !members.isNullOrEmpty()
50-
// 차단된 사용자가 없을 때 표시할 뷰
51-
binding.emptyBlockMembersTv.isVisible = members.isNullOrEmpty()
52-
}
41+
override fun onViewCreated(
42+
view: View,
43+
savedInstanceState: Bundle?,
44+
) {
45+
super.onViewCreated(view, savedInstanceState)
5346

5447
viewLifecycleOwner.lifecycleScope.launch {
5548
repeatOnLifecycle(Lifecycle.State.STARTED) {
5649
viewModel.errorEvent.collect { message ->
57-
Toast.makeText(requireContext(), message ?: getString(R.string.error_unknown), Toast.LENGTH_SHORT).show()
50+
Toast.makeText(
51+
requireContext(),
52+
message ?: getString(R.string.error_unknown),
53+
Toast.LENGTH_SHORT,
54+
).show()
5855
}
5956
}
6057
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package com.project200.undabang.profile.setting
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.clickable
5+
import androidx.compose.foundation.interaction.MutableInteractionSource
6+
import androidx.compose.foundation.layout.Box
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
9+
import androidx.compose.foundation.layout.Spacer
10+
import androidx.compose.foundation.layout.fillMaxSize
11+
import androidx.compose.foundation.layout.fillMaxWidth
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.width
14+
import androidx.compose.foundation.lazy.LazyColumn
15+
import androidx.compose.foundation.lazy.items
16+
import androidx.compose.foundation.shape.RoundedCornerShape
17+
import androidx.compose.material3.MaterialTheme
18+
import androidx.compose.material3.Text
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.remember
21+
import androidx.compose.ui.Alignment
22+
import androidx.compose.ui.Modifier
23+
import androidx.compose.ui.draw.clip
24+
import androidx.compose.ui.draw.drawBehind
25+
import androidx.compose.ui.geometry.Offset
26+
import androidx.compose.ui.res.stringResource
27+
import androidx.compose.ui.text.style.TextAlign
28+
import androidx.compose.ui.tooling.preview.Preview
29+
import androidx.compose.ui.unit.dp
30+
import androidx.compose.ui.unit.sp
31+
import com.project200.domain.model.BlockedMember
32+
import com.project200.presentation.compose.components.display.UndabangAvatar
33+
import com.project200.presentation.compose.components.layout.UndabangTopBar
34+
import com.project200.presentation.compose.theme.AppTheme
35+
import com.project200.presentation.compose.theme.ColorBlack
36+
import com.project200.presentation.compose.theme.ColorGray200
37+
import com.project200.presentation.compose.theme.ColorWhite300
38+
import com.project200.presentation.compose.theme.contentBold
39+
import com.project200.presentation.compose.theme.contentRegular
40+
import com.project200.undabang.feature.profile.R
41+
42+
@Composable
43+
fun BlockMembersScreen(
44+
members: List<BlockedMember>,
45+
onBackClick: () -> Unit,
46+
onUnblockClick: (BlockedMember) -> Unit,
47+
modifier: Modifier = Modifier,
48+
) {
49+
Column(
50+
modifier =
51+
modifier
52+
.fillMaxSize()
53+
.background(ColorWhite300),
54+
) {
55+
UndabangTopBar(
56+
title = stringResource(R.string.block_members),
57+
onNavigationClick = onBackClick,
58+
)
59+
60+
if (members.isEmpty()) {
61+
Box(
62+
modifier = Modifier.fillMaxSize(),
63+
contentAlignment = Alignment.Center,
64+
) {
65+
Text(
66+
text = stringResource(R.string.block_members_empty),
67+
style = MaterialTheme.typography.contentRegular,
68+
color = ColorGray200,
69+
textAlign = TextAlign.Center,
70+
)
71+
}
72+
} else {
73+
LazyColumn(modifier = Modifier.fillMaxSize()) {
74+
items(items = members, key = { it.memberBlockId }) { member ->
75+
BlockMemberItem(
76+
member = member,
77+
onUnblockClick = { onUnblockClick(member) },
78+
)
79+
}
80+
}
81+
}
82+
}
83+
}
84+
85+
@Composable
86+
private fun BlockMemberItem(
87+
member: BlockedMember,
88+
onUnblockClick: () -> Unit,
89+
modifier: Modifier = Modifier,
90+
) {
91+
Row(
92+
modifier =
93+
modifier
94+
.fillMaxWidth()
95+
.drawBehind {
96+
val strokePx = 0.3.dp.toPx()
97+
drawLine(
98+
color = ColorBlack,
99+
start = Offset(0f, size.height - strokePx / 2),
100+
end = Offset(size.width, size.height - strokePx / 2),
101+
strokeWidth = strokePx,
102+
)
103+
}
104+
.padding(20.dp),
105+
verticalAlignment = Alignment.CenterVertically,
106+
) {
107+
UndabangAvatar(
108+
imageUrl = member.thumbnailImageUrl ?: member.profileImageUrl,
109+
size = 50.dp,
110+
borderWidth = 0.dp,
111+
)
112+
Spacer(Modifier.width(15.dp))
113+
Text(
114+
text = member.nickname,
115+
style = MaterialTheme.typography.contentBold,
116+
color = ColorBlack,
117+
modifier = Modifier.weight(1f),
118+
)
119+
UnblockButton(onClick = onUnblockClick)
120+
}
121+
}
122+
123+
@Composable
124+
private fun UnblockButton(
125+
onClick: () -> Unit,
126+
modifier: Modifier = Modifier,
127+
) {
128+
Box(
129+
modifier =
130+
modifier
131+
.clip(RoundedCornerShape(8.dp))
132+
.background(ColorGray200)
133+
.clickable(
134+
interactionSource = remember { MutableInteractionSource() },
135+
indication = null,
136+
onClick = onClick,
137+
)
138+
.padding(horizontal = 13.dp, vertical = 10.dp),
139+
contentAlignment = Alignment.Center,
140+
) {
141+
Text(
142+
text = stringResource(R.string.unblock),
143+
color = ColorWhite300,
144+
fontSize = 14.sp,
145+
style = MaterialTheme.typography.contentBold,
146+
)
147+
}
148+
}
149+
150+
@Preview(showBackground = true, heightDp = 720)
151+
@Composable
152+
private fun BlockMembersScreenPreview() {
153+
AppTheme {
154+
BlockMembersScreen(
155+
members =
156+
listOf(
157+
BlockedMember(1L, "id1", "운다방테스터1", null, null),
158+
BlockedMember(2L, "id2", "운다방테스터2", null, null),
159+
BlockedMember(3L, "id3", "긴닉네임을가진사용자입니다", null, null),
160+
),
161+
onBackClick = {},
162+
onUnblockClick = {},
163+
)
164+
}
165+
}
166+
167+
@Preview(showBackground = true, heightDp = 720)
168+
@Composable
169+
private fun BlockMembersScreenEmptyPreview() {
170+
AppTheme {
171+
BlockMembersScreen(
172+
members = emptyList(),
173+
onBackClick = {},
174+
onUnblockClick = {},
175+
)
176+
}
177+
}

feature/profile/src/main/java/com/project200/undabang/profile/setting/BlockMembersViewModel.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.project200.undabang.profile.setting
22

3-
import androidx.lifecycle.LiveData
4-
import androidx.lifecycle.MutableLiveData
53
import androidx.lifecycle.ViewModel
64
import androidx.lifecycle.viewModelScope
75
import com.project200.domain.model.BaseResult
@@ -10,7 +8,10 @@ import com.project200.domain.usecase.GetBlockedMembersUseCase
108
import com.project200.domain.usecase.UnblockMemberUseCase
119
import dagger.hilt.android.lifecycle.HiltViewModel
1210
import kotlinx.coroutines.flow.MutableSharedFlow
11+
import kotlinx.coroutines.flow.MutableStateFlow
1312
import kotlinx.coroutines.flow.SharedFlow
13+
import kotlinx.coroutines.flow.StateFlow
14+
import kotlinx.coroutines.flow.asStateFlow
1415
import kotlinx.coroutines.launch
1516
import javax.inject.Inject
1617

@@ -21,8 +22,8 @@ class BlockMembersViewModel
2122
private val getBlockedMembersUseCase: GetBlockedMembersUseCase,
2223
private val unblockMemberUseCase: UnblockMemberUseCase,
2324
) : ViewModel() {
24-
private val _blockedMembers = MutableLiveData<List<BlockedMember>>()
25-
val blockedMembers: LiveData<List<BlockedMember>> = _blockedMembers
25+
private val _blockedMembers = MutableStateFlow<List<BlockedMember>>(emptyList())
26+
val blockedMembers: StateFlow<List<BlockedMember>> = _blockedMembers.asStateFlow()
2627

2728
private val _errorEvent = MutableSharedFlow<String?>()
2829
val errorEvent: SharedFlow<String?> = _errorEvent

0 commit comments

Comments
 (0)