Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#441 refactor 프로필 화면 파일 분리 및 중간 부분 UI 작업 #444

Open
wants to merge 9 commits into
base: improve
Choose a base branch
from
32 changes: 0 additions & 32 deletions app/src/main/java/kr/co/lion/modigm/ui/common/ModigmTopAppBar.kt

This file was deleted.

294 changes: 41 additions & 253 deletions app/src/main/java/kr/co/lion/modigm/ui/profile/ProfileFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,20 @@ class ProfileFragment : Fragment() {
var userIdx: Int? = null
var isBottomNavi: Boolean? = null

private val domainIcons = mapOf(
"youtube.com" to R.drawable.icon_youtube_logo,
"github.com" to R.drawable.icon_github_logo,
"linkedin.com" to R.drawable.icon_linkedin_logo,
"velog.io" to R.drawable.icon_velog_logo,
"instagram.com" to R.drawable.icon_instagram_logo,
"notion.com" to R.drawable.icon_notion_logo,
"facebook.com" to R.drawable.icon_facebook_logo,
"twitter.com" to R.drawable.icon_twitter_logo,
"open.kakao.com" to R.drawable.kakaotalk_sharing_btn_small,
"default" to R.drawable.icon_link
)

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
userIdx = arguments?.getInt("userIdx")
isBottomNavi = arguments?.getBoolean("isBottomNavi")

return ComposeView(requireContext()).apply {
setContent {
ModigmTheme {
ProfileScreen()
ProfileScreen(
viewModel = viewModel,
changeToSettingsFragment = { changeToSettingsFragment() },
changeToLinkWebView = { link -> changeToLinkWebView(link) },
changeToDetailFragment = { studyIdx -> changeToDetailFragment(studyIdx) }

)
}
}
}
Expand All @@ -101,51 +94,6 @@ class ProfileFragment : Fragment() {
viewModel.loadPartStudyList(userIdx!!)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ProfileScreen() {
val profileName by viewModel.profileName.collectAsState(initial = "")
val profileIntro by viewModel.profileIntro.collectAsState(initial = "")
val profilePicUrl by viewModel.profileUserImage.collectAsState(initial = "")
val profileInterests by viewModel.profileInterests.collectAsState(initial = "")
val profileLinks by viewModel.profileLinkList.collectAsState(initial = emptyList())
val profileHostStudies by viewModel.profileHostStudyList.collectAsState(initial = emptyList())
val profilePartStudies by viewModel.profilePartStudyList.collectAsState(initial = emptyList())

Scaffold(
topBar = {
ModigmTopAppBar(
title = "프로필",
onSettingsClick = { changeToSettingsFragment() }
)
}
) { paddingValues ->
Column(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
.background(Color.White)
.verticalScroll(rememberScrollState())
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
ProfileHeader(profileName ?: "", profileIntro ?: "", profilePicUrl ?: "")
Spacer(modifier = Modifier.height(16.dp))

InterestsSection(profileInterests ?: "")
Spacer(modifier = Modifier.height(16.dp))

LinksSection(profileLinks)
Spacer(modifier = Modifier.height(16.dp))

StudiesSection("진행한 스터디", profileHostStudies)
Spacer(modifier = Modifier.height(16.dp))

StudiesSection("참여한 스터디", profilePartStudies)
}
}
}

private fun changeToSettingsFragment() {
viewLifecycleOwner.lifecycleScope.launch {
val settingsFragment = SettingsFragment()
Expand All @@ -164,212 +112,52 @@ class ProfileFragment : Fragment() {
}
}

@Composable
fun ProfileHeader(name: String, intro: String, profilePicUrl: String?) {
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data(profilePicUrl)
.crossfade(true)
.build(),
contentScale = ContentScale.Crop,
error = painterResource(id = R.drawable.image_default_profile)
)

Column(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
painter = painter,
contentDescription = "Profile Picture",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
.background(Color.Gray)
)
Text(text = name, fontSize = 18.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 8.dp, bottom = 4.dp))
if (intro.isNotBlank()) Text(text = intro, fontSize = 14.sp, color = Color(0xFF777777))
}
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun InterestsSection(interests: String) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = "관심분야", fontSize = 16.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
FlowRow {
interests.split(",").forEach { interest ->
SuggestionChip(onClick = { /*TODO*/ }, label = { Text(interest) })
}
}
}
}

@Composable
fun LinksSection(links: List<String>) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = "링크", fontSize = 16.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
links.forEach { link ->
Row(
modifier = Modifier
.padding(vertical = 4.dp)
.fillMaxWidth()
) {
val domain = extractDomain(link)
val iconRes = domainIcons[domain] ?: R.drawable.icon_link

Image(
painter = painterResource(id = iconRes),
contentDescription = "$domain icon",
modifier = Modifier
.size(30.dp)
.padding(end = 8.dp)
.clickable {
viewLifecycleOwner.lifecycleScope.launch {
// bundle 에 필요한 정보를 담는다
val bundle = Bundle()
bundle.putString("link", link)

// 이동할 프래그먼트로 bundle을 넘긴다
val profileWebFragment = ProfileWebFragment()
profileWebFragment.arguments = bundle

// Fragment 교체
requireActivity().supportFragmentManager.commit {
setCustomAnimations(
R.anim.slide_in,
R.anim.fade_out,
R.anim.fade_in,
R.anim.slide_out
)
replace(R.id.containerMain, profileWebFragment)
addToBackStack(FragmentName.PROFILE_WEB.str)
}
}
}
)
}
}
}
}
private fun changeToLinkWebView(link: String) {
viewLifecycleOwner.lifecycleScope.launch {
// bundle 에 필요한 정보를 담는다
val bundle = Bundle()
bundle.putString("link", link)

private fun extractDomain(url: String): String {
return try {
val uri = URL(url)
val domain = uri.host
if (domain.startsWith("www.")) domain.substring(4) else domain
} catch (e: Exception) {
"invalid"
}
}
// 이동할 프래그먼트로 bundle을 넘긴다
val profileWebFragment = ProfileWebFragment()
profileWebFragment.arguments = bundle

@Composable
fun StudiesSection(title: String, studies: List<StudyData>?) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = title, fontSize = 18.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(8.dp))
if (studies.isNullOrEmpty()) {
Text(
text = "데이터가 없습니다",
fontSize = 16.sp,
modifier = Modifier.padding(16.dp)
// Fragment 교체
requireActivity().supportFragmentManager.commit {
setCustomAnimations(
R.anim.slide_in,
R.anim.fade_out,
R.anim.fade_in,
R.anim.slide_out
)
} else {
Column {
studies.forEach { study ->
StudyItem(study)
HorizontalDivider(thickness = 0.5.dp, color = Color.LightGray)
}
}
replace(R.id.containerMain, profileWebFragment)
addToBackStack(FragmentName.PROFILE_WEB.str)
}
}
}

@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun StudyItem(study: StudyData) {
Row(modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.clickable {
viewLifecycleOwner.lifecycleScope.launch {
val detailFragment = DetailFragment()
private fun changeToDetailFragment(studyIdx: Int) {
viewLifecycleOwner.lifecycleScope.launch {
val detailFragment = DetailFragment()

// Bundle 생성 및 현재 사용자 uid 담기
val bundle = Bundle()
bundle.putInt("studyIdx", study.studyIdx)
// Bundle 생성 및 현재 사용자 uid 담기
val bundle = Bundle()
bundle.putInt("studyIdx", studyIdx)

// Bundle을 ProfileFragment에 설정
detailFragment.arguments = bundle
// Bundle을 ProfileFragment에 설정
detailFragment.arguments = bundle

requireActivity().supportFragmentManager.commit {
setCustomAnimations(
R.anim.slide_in,
R.anim.fade_out,
R.anim.fade_in,
R.anim.slide_out
)
replace(R.id.containerMain, detailFragment)
addToBackStack(FragmentName.DETAIL.str)
}
}
}) {
Card(
shape = RoundedCornerShape(8.dp),
modifier = Modifier.size(70.dp)
) {
GlideImage(
model = study.studyPic,
contentDescription = "Study Image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
text = study.studyTitle,
fontSize = 16.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
requireActivity().supportFragmentManager.commit {
setCustomAnimations(
R.anim.slide_in,
R.anim.fade_out,
R.anim.fade_in,
R.anim.slide_out
)
Spacer(modifier = Modifier.height(4.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painter = painterResource(id = R.drawable.icon_category_24px),
contentDescription = "Category Icon",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(text = study.studyType, fontSize = 14.sp)

Spacer(modifier = Modifier.width(16.dp))

Icon(
painter = painterResource(id = R.drawable.icon_location_on_24px),
contentDescription = "Location Icon",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(text = study.studyOnOffline, fontSize = 14.sp)

Spacer(modifier = Modifier.width(16.dp))

Icon(
painter = painterResource(id = R.drawable.icon_person_24px),
contentDescription = "Member Icon",
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(text = study.studyCanApply, fontSize = 14.sp)
}
replace(R.id.containerMain, detailFragment)
addToBackStack(FragmentName.DETAIL.str)
}
}
}
}

@Preview
@Composable
fun ProfileScreenPreview() {
ProfileScreen()
}
}
Loading