From f2788c4ca102574916cea7ad80a114460e219472 Mon Sep 17 00:00:00 2001 From: unam98 Date: Fri, 3 Apr 2026 03:25:14 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20Compose=20=ED=85=8C=EB=A7=88=20?= =?UTF-8?q?=EA=B5=AC=EC=B6=95=20=EB=B0=8F=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20Compose=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RunnectTheme 생성 (Material3 lightColorScheme + Pretendard Typography) - Color.kt: 앱 컬러 팔레트 정의 (M1~M7, G1~G6) - Type.kt: Pretendard 9개 웨이트 + Material3 텍스트 스타일 매핑 - MyPageScreen: 프로필, 레벨 프로그레스, 메뉴, 버전 정보 Compose UI - VisitorModeScreen: 방문자 모드 Compose UI - MyPageFragment: BaseVisitorFragment → Fragment + ComposeView 호스트로 전환 - coil-compose 의존성 추가 --- app/build.gradle | 1 + .../presentation/mypage/MyPageFragment.kt | 174 +++++----- .../presentation/mypage/MyPageScreen.kt | 312 ++++++++++++++++++ .../presentation/mypage/VisitorModeScreen.kt | 72 ++++ .../runnect/presentation/ui/theme/Color.kt | 25 ++ .../runnect/presentation/ui/theme/Theme.kt | 37 +++ .../runnect/presentation/ui/theme/Type.kt | 84 +++++ gradle/libs.versions.toml | 1 + 8 files changed, 618 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt create mode 100644 app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt create mode 100644 app/src/main/java/com/runnect/runnect/presentation/ui/theme/Color.kt create mode 100644 app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt diff --git a/app/build.gradle b/app/build.gradle index f1f75682..387b6aca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -118,6 +118,7 @@ dependencies { implementation libs.glide ksp libs.glide.ksp implementation libs.coil + implementation libs.coil.compose implementation libs.coil.network.okhttp // Firebase diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageFragment.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageFragment.kt index 08c0633b..b6b0f781 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageFragment.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageFragment.kt @@ -1,57 +1,111 @@ package com.runnect.runnect.presentation.mypage import android.app.Activity -import android.app.Activity.RESULT_OK import android.content.Intent import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.view.isVisible +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.commit import androidx.fragment.app.replace -import coil3.load import com.kakao.sdk.common.util.KakaoCustomTabsClient import com.kakao.sdk.talk.TalkApiClient import com.runnect.runnect.BuildConfig import com.runnect.runnect.R -import com.runnect.runnect.binding.BaseVisitorFragment -import com.runnect.runnect.databinding.FragmentMyPageBinding +import com.runnect.runnect.presentation.event.VisitorModeManager +import com.runnect.runnect.presentation.login.LoginActivity import com.runnect.runnect.presentation.mypage.editname.MyPageEditNameActivity import com.runnect.runnect.presentation.mypage.history.MyHistoryActivity import com.runnect.runnect.presentation.mypage.reward.MyRewardActivity import com.runnect.runnect.presentation.mypage.setting.MySettingFragment import com.runnect.runnect.presentation.mypage.upload.MyUploadActivity +import com.runnect.runnect.presentation.ui.theme.RunnectTheme import com.runnect.runnect.util.analytics.Analytics import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_GOAL_REWARD import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_RUNNING_RECORD import com.runnect.runnect.util.analytics.EventName.EVENT_CLICK_UPLOADED_COURSE import com.runnect.runnect.util.extension.getStampResId -import com.runnect.runnect.util.extension.repeatOnStarted -import com.runnect.runnect.util.extension.showSnackbar import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collectLatest +import javax.inject.Inject @AndroidEntryPoint -class MyPageFragment : BaseVisitorFragment(R.layout.fragment_my_page) { +class MyPageFragment : Fragment() { + @Inject + lateinit var visitorModeManager: VisitorModeManager + private val viewModel: MyPageViewModel by activityViewModels() private lateinit var resultEditNameLauncher: ActivityResultLauncher - override val visitorContainer by lazy { binding.clVisitorMode } - override val contentViews by lazy { listOf(binding.constraintInside) } - - override fun onContentModeInit() { - binding.lifecycleOwner = this@MyPageFragment.viewLifecycleOwner - viewModel.intent(MyPageIntent.LoadUserInfo) - addListener() - addObserver() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) setResultEditNameLauncher() } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + RunnectTheme { + if (visitorModeManager.isVisitorMode) { + VisitorModeScreen( + onSignUpClick = { navigateToLogin() } + ) + } else { + val state by viewModel.state.collectAsState() + + val stampResId = if (!state.isLoading) { + getStampResourceId(state.stampId) + } else { + R.drawable.user_profile_basic + } + + MyPageScreen( + state = state.copy(profileImgResId = stampResId), + onEditProfileClick = { navigateToEditName() }, + onHistoryClick = { + Analytics.logClickedItemEvent(EVENT_CLICK_RUNNING_RECORD) + navigateTo() + }, + onRewardClick = { + Analytics.logClickedItemEvent(EVENT_CLICK_GOAL_REWARD) + navigateTo() + }, + onUploadClick = { + Analytics.logClickedItemEvent(EVENT_CLICK_UPLOADED_COURSE) + navigateTo() + }, + onSettingClick = { moveToSettingFragment() }, + onKakaoInquiryClick = { inquiryKakao() } + ) + } + } + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + if (!visitorModeManager.isVisitorMode) { + viewModel.intent(MyPageIntent.LoadUserInfo) + } + } + private fun setResultEditNameLauncher() { resultEditNameLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == RESULT_OK) { + if (result.resultCode == Activity.RESULT_OK) { val name = result.data?.getStringExtra(EXTRA_NICK_NAME) ?: viewModel.currentState.nickname viewModel.intent(MyPageIntent.UpdateNickname(name)) @@ -59,97 +113,41 @@ class MyPageFragment : BaseVisitorFragment(R.layout.fragm } } - private fun addListener() { - with(binding) { - ivMyPageEditFrame.setOnClickListener { - val intent = Intent(requireContext(), MyPageEditNameActivity::class.java) - intent.putExtra(EXTRA_NICK_NAME, viewModel.currentState.nickname) - val stampResId = getStampResourceId() - intent.putExtra(EXTRA_PROFILE, stampResId) - resultEditNameLauncher.launch(intent) - } - - viewMyPageMainRewardFrame.setOnClickListener { - Analytics.logClickedItemEvent(EVENT_CLICK_GOAL_REWARD) - navigateTo() - } - viewMyPageMainHistoryFrame.setOnClickListener { - Analytics.logClickedItemEvent(EVENT_CLICK_RUNNING_RECORD) - navigateTo() - } - - viewMyPageMainUploadFrame.setOnClickListener { - Analytics.logClickedItemEvent(EVENT_CLICK_UPLOADED_COURSE) - navigateTo() - } - viewMyPageMainSettingFrame.setOnClickListener { - moveToSettingFragment() - } - viewMyPageMainKakaoChannelInquiryFrame.setOnClickListener { - inquiryKakao() - } - } + private fun navigateToEditName() { + val intent = Intent(requireContext(), MyPageEditNameActivity::class.java) + intent.putExtra(EXTRA_NICK_NAME, viewModel.currentState.nickname) + val stampResId = getStampResourceId(viewModel.currentState.stampId) + intent.putExtra(EXTRA_PROFILE, stampResId) + resultEditNameLauncher.launch(intent) } private fun moveToSettingFragment() { val bundle = Bundle().apply { putString(ACCOUNT_INFO_TAG, viewModel.currentState.email) } requireActivity().supportFragmentManager.commit { - this.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left) + setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left) replace(R.id.fl_main, args = bundle) } } - private fun addObserver() { - repeatOnStarted { - viewModel.state.collectLatest { state -> - bindState(state) - } - } - } - - private fun bindState(state: MyPageUiState) { - setLoadingState(state.isLoading) - - if (!state.isLoading && state.error == null) { - with(binding) { - tvMyPageUserName.text = state.nickname - tvMyPageUserLv.text = state.level - pbMyPageProgress.progress = state.levelPercent - tvMyPageProgressCurrent.text = state.levelPercent.toString() - ivMyPageProfile.load(state.profileImgResId) - } - - val stampResId = getStampResourceId() - viewModel.intent(MyPageIntent.UpdateProfileImg(stampResId)) - } - - state.error?.let { - context?.showSnackbar(anchorView = binding.root, message = it) - } - } - private fun inquiryKakao() { val url = TalkApiClient.instance.channelChatUrl(BuildConfig.KAKAO_CHANNEL_ID) KakaoCustomTabsClient.openWithDefault(requireActivity(), url) } - private fun getStampResourceId(): Int { + private fun navigateToLogin() { + startActivity(Intent(requireContext(), LoginActivity::class.java)) + requireActivity().finish() + } + + private fun getStampResourceId(stampId: String): Int { return requireContext().getStampResId( - stampId = viewModel.currentState.stampId, + stampId = stampId, resNameParam = RES_NAME, resType = RES_STAMP_TYPE, packageName = requireContext().packageName ) } - private fun setLoadingState(isLoading: Boolean) { - with(binding) { - indeterminateBar.isVisible = isLoading - ivMyPageEditFrame.isClickable = !isLoading - viewMyPageMainSettingFrame.isClickable = !isLoading - } - } - private inline fun navigateTo() { startActivity(Intent(requireContext(), T::class.java)) requireActivity().overridePendingTransition( diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt new file mode 100644 index 00000000..926aebc5 --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt @@ -0,0 +1,312 @@ +package com.runnect.runnect.presentation.mypage + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.runnect.runnect.R +import com.runnect.runnect.presentation.ui.theme.G1 +import com.runnect.runnect.presentation.ui.theme.G2 +import com.runnect.runnect.presentation.ui.theme.G3 +import com.runnect.runnect.presentation.ui.theme.G4 +import com.runnect.runnect.presentation.ui.theme.M1 +import com.runnect.runnect.presentation.ui.theme.M3 +import com.runnect.runnect.presentation.ui.theme.PretendardFontFamily + +@Composable +fun MyPageScreen( + state: MyPageUiState, + onEditProfileClick: () -> Unit, + onHistoryClick: () -> Unit, + onRewardClick: () -> Unit, + onUploadClick: () -> Unit, + onSettingClick: () -> Unit, + onKakaoInquiryClick: () -> Unit, +) { + Column(modifier = Modifier.fillMaxSize()) { + MyPageToolbar() + + if (state.isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = G3) + } + } else { + ProfileSection( + nickname = state.nickname, + profileImgResId = state.profileImgResId, + onEditClick = onEditProfileClick + ) + LevelProgressSection( + level = state.level, + levelPercent = state.levelPercent + ) + MenuSection( + onHistoryClick = onHistoryClick, + onRewardClick = onRewardClick, + onUploadClick = onUploadClick, + onSettingClick = onSettingClick, + onKakaoInquiryClick = onKakaoInquiryClick + ) + VersionSection() + } + } +} + +@Composable +private fun MyPageToolbar() { + Box( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(start = 16.dp), + contentAlignment = Alignment.CenterStart + ) { + Text( + text = stringResource(R.string.my_page_title), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + color = G1 + ) + } +} + +@Composable +private fun ProfileSection( + nickname: String, + profileImgResId: Int, + onEditClick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(85.dp) + .padding(horizontal = 23.dp), + verticalAlignment = Alignment.CenterVertically + ) { + AsyncImage( + model = profileImgResId, + contentDescription = null, + modifier = Modifier.size(63.dp) + ) + Spacer(modifier = Modifier.width(10.dp)) + Text( + text = nickname, + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 17.sp, + color = M1 + ) + Spacer(modifier = Modifier.weight(1f)) + Row( + modifier = Modifier + .clip(RoundedCornerShape(14.dp)) + .clickable(onClick = onEditClick) + .background(MaterialTheme.colorScheme.background) + .padding(horizontal = 11.dp, vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.ic_mypage_nickname_edit), + contentDescription = null + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = stringResource(R.string.my_page_edit), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + color = M1 + ) + } + } +} + +@Composable +private fun LevelProgressSection( + level: String, + levelPercent: Int +) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(M3.copy(alpha = 0.6f)) + .padding(horizontal = 22.dp, vertical = 20.dp) + ) { + Row { + Text( + text = stringResource(R.string.my_page_lv_indicator), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 15.sp, + color = G1 + ) + Text( + text = level, + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 15.sp, + color = G1 + ) + } + Spacer(modifier = Modifier.height(6.dp)) + LinearProgressIndicator( + progress = { levelPercent / 100f }, + modifier = Modifier + .fillMaxWidth() + .height(11.dp) + .clip(RoundedCornerShape(5.dp)), + color = M1, + trackColor = G4, + ) + Spacer(modifier = Modifier.height(10.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Text( + text = levelPercent.toString(), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 13.sp, + color = G1 + ) + Text( + text = stringResource(R.string.my_page_progress_max), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 13.sp, + color = G2 + ) + } + } +} + +@Composable +private fun MenuSection( + onHistoryClick: () -> Unit, + onRewardClick: () -> Unit, + onUploadClick: () -> Unit, + onSettingClick: () -> Unit, + onKakaoInquiryClick: () -> Unit, +) { + Column { + MenuItem( + title = stringResource(R.string.my_page_history_title), + onClick = onHistoryClick + ) + MenuItem( + title = stringResource(R.string.my_page_reward_title), + onClick = onRewardClick + ) + MenuItem( + title = stringResource(R.string.my_page_upload_title), + onClick = onUploadClick + ) + MenuItem( + title = stringResource(R.string.my_page_setting_title), + onClick = onSettingClick + ) + MenuItem( + title = stringResource(R.string.my_page_kakao_channel_inquiry), + onClick = onKakaoInquiryClick, + showDivider = false + ) + } +} + +@Composable +private fun MenuItem( + title: String, + onClick: () -> Unit, + showDivider: Boolean = true +) { + Column { + Row( + modifier = Modifier + .fillMaxWidth() + .height(62.dp) + .clickable(onClick = onClick) + .padding(horizontal = 15.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.all_star), + contentDescription = null + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = title, + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 15.sp, + color = G1 + ) + Spacer(modifier = Modifier.weight(1f)) + Image( + painter = painterResource(R.drawable.all_front_arrow), + contentDescription = null, + modifier = Modifier.padding(end = 9.dp) + ) + } + if (showDivider) { + HorizontalDivider(color = G4, thickness = 1.dp) + } + } +} + +@Composable +private fun VersionSection() { + Row( + modifier = Modifier + .fillMaxWidth() + .height(62.dp) + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = stringResource(R.string.my_page_version_title), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 15.sp, + color = G2 + ) + Text( + text = stringResource(R.string.my_page_version), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = G2 + ) + } +} diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt new file mode 100644 index 00000000..c6b0f41a --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt @@ -0,0 +1,72 @@ +package com.runnect.runnect.presentation.mypage + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.runnect.runnect.R +import com.runnect.runnect.presentation.ui.theme.G2 +import com.runnect.runnect.presentation.ui.theme.M1 +import com.runnect.runnect.presentation.ui.theme.PretendardFontFamily +import com.runnect.runnect.presentation.ui.theme.White + +@Composable +fun VisitorModeScreen( + onSignUpClick: () -> Unit +) { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(R.drawable.finish_run), + contentDescription = null + ) + Spacer(modifier = Modifier.height(13.dp)) + Text( + text = stringResource(R.string.visitor_mode_mypage_message), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 13.sp, + color = G2, + textAlign = TextAlign.Center, + lineHeight = 18.sp + ) + Spacer(modifier = Modifier.height(22.dp)) + Button( + onClick = onSignUpClick, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 73.dp) + .height(40.dp), + shape = RoundedCornerShape(10.dp), + colors = ButtonDefaults.buttonColors(containerColor = M1) + ) { + Text( + text = stringResource(R.string.visitor_mode_signup_btn), + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 15.sp, + color = White + ) + } + } +} diff --git a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Color.kt b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Color.kt new file mode 100644 index 00000000..f5aed94f --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Color.kt @@ -0,0 +1,25 @@ +package com.runnect.runnect.presentation.ui.theme + +import androidx.compose.ui.graphics.Color + +// Primary Brand +val M1 = Color(0xFF593EEC) +val M2 = Color(0xFF7E71FF) +val M3 = Color(0xFFF2F3FF) +val M5 = Color(0xFFD5D4FF) +val M6 = Color(0xFF9C9AFD) +val M7 = Color(0xFFD1C9FF) + +// Grayscale +val G1 = Color(0xFF171717) +val G2 = Color(0xFF8B8B8B) +val G3 = Color(0xFFC1C1C1) +val G4 = Color(0xFFECECEC) +val G5 = Color(0xFFF3F3F3) +val G6 = Color(0xFFD9D9D9) + +// Semantic +val White = Color(0xFFFFFFFF) +val Red = Color(0xFFFF473A) +val Green = Color(0xFF5CFF62) +val Blue = Color(0xFF55B4FF) diff --git a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt new file mode 100644 index 00000000..b6744aff --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt @@ -0,0 +1,37 @@ +package com.runnect.runnect.presentation.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable + +private val RunnectColorScheme = lightColorScheme( + primary = M1, + onPrimary = White, + primaryContainer = M3, + onPrimaryContainer = M1, + secondary = M2, + onSecondary = White, + secondaryContainer = M5, + onSecondaryContainer = M1, + background = White, + onBackground = G1, + surface = White, + onSurface = G1, + surfaceVariant = G5, + onSurfaceVariant = G2, + outline = G3, + outlineVariant = G4, + error = Red, + onError = White, +) + +@Composable +fun RunnectTheme( + content: @Composable () -> Unit +) { + MaterialTheme( + colorScheme = RunnectColorScheme, + typography = RunnectTypography, + content = content + ) +} diff --git a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt new file mode 100644 index 00000000..9fc89faf --- /dev/null +++ b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt @@ -0,0 +1,84 @@ +package com.runnect.runnect.presentation.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import com.runnect.runnect.R + +val PretendardFontFamily = FontFamily( + Font(R.font.pretendard_thin, FontWeight.Thin), + Font(R.font.pretendard_extralight, FontWeight.ExtraLight), + Font(R.font.pretendard_light, FontWeight.Light), + Font(R.font.pretendard_regular, FontWeight.Normal), + Font(R.font.pretendard_medium, FontWeight.Medium), + Font(R.font.pretendard_semibold, FontWeight.SemiBold), + Font(R.font.pretendard_bold, FontWeight.Bold), + Font(R.font.pretendard_extrabold, FontWeight.ExtraBold), + Font(R.font.pretendard_black, FontWeight.Black), +) + +val RunnectTypography = Typography( + displayLarge = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 28.sp, + ), + headlineLarge = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 22.sp, + ), + headlineMedium = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + ), + titleLarge = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 17.sp, + ), + titleMedium = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 15.sp, + ), + titleSmall = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 13.sp, + ), + bodyLarge = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + ), + bodyMedium = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + ), + bodySmall = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + ), + labelLarge = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + ), + labelMedium = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 12.sp, + ), + labelSmall = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + ), +) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 23772504..0af7aaaf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -138,6 +138,7 @@ kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx- glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" } glide-ksp = { group = "com.github.bumptech.glide", name = "ksp", version.ref = "glide" } coil = { group = "io.coil-kt.coil3", name = "coil-android", version.ref = "coil" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } # Firebase (BOM 관리 - 개별 버전 불필요) From 506d53e9f258b429f5016a1063ab67062cfb340d Mon Sep 17 00:00:00 2001 From: unam98 Date: Fri, 3 Apr 2026 10:12:36 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20=E2=80=94=20error=20Snackbar=20=EB=B3=B5=EC=9B=90,?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=B6=94=EA=B0=80,=20levelPercent=20=ED=81=B4?= =?UTF-8?q?=EB=9E=A8=ED=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Scaffold + SnackbarHost로 state.error 시 Snackbar 표시 복원 - LaunchedEffect(state.error)로 에러 발생 시 자동 트리거 - 콘텐츠 영역에 weight(1f) + verticalScroll 추가로 소형 화면 스크롤 지원 - 로딩 스피너를 weight(1f) Box로 감싸 정확한 중앙 배치 - levelPercent를 coerceIn(0, 100)으로 클램핑하여 API 이상값 방어 --- .../presentation/mypage/MyPageScreen.kt | 87 +++++++++++++------ 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt index 926aebc5..5d72e693 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt @@ -14,13 +14,20 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -49,34 +56,56 @@ fun MyPageScreen( onSettingClick: () -> Unit, onKakaoInquiryClick: () -> Unit, ) { - Column(modifier = Modifier.fillMaxSize()) { - MyPageToolbar() + val snackbarHostState = remember { SnackbarHostState() } - if (state.isLoading) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator(color = G3) + LaunchedEffect(state.error) { + state.error?.let { snackbarHostState.showSnackbar(it) } + } + + Scaffold( + snackbarHost = { SnackbarHost(snackbarHostState) } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) { + MyPageToolbar() + + if (state.isLoading) { + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = G3) + } + } else { + Column( + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) + ) { + ProfileSection( + nickname = state.nickname, + profileImgResId = state.profileImgResId, + onEditClick = onEditProfileClick + ) + LevelProgressSection( + level = state.level, + levelPercent = state.levelPercent + ) + MenuSection( + onHistoryClick = onHistoryClick, + onRewardClick = onRewardClick, + onUploadClick = onUploadClick, + onSettingClick = onSettingClick, + onKakaoInquiryClick = onKakaoInquiryClick + ) + VersionSection() + } } - } else { - ProfileSection( - nickname = state.nickname, - profileImgResId = state.profileImgResId, - onEditClick = onEditProfileClick - ) - LevelProgressSection( - level = state.level, - levelPercent = state.levelPercent - ) - MenuSection( - onHistoryClick = onHistoryClick, - onRewardClick = onRewardClick, - onUploadClick = onUploadClick, - onSettingClick = onSettingClick, - onKakaoInquiryClick = onKakaoInquiryClick - ) - VersionSection() } } } @@ -156,6 +185,8 @@ private fun LevelProgressSection( level: String, levelPercent: Int ) { + val clampedPercent = levelPercent.coerceIn(0, 100) + Column( modifier = Modifier .fillMaxWidth() @@ -180,7 +211,7 @@ private fun LevelProgressSection( } Spacer(modifier = Modifier.height(6.dp)) LinearProgressIndicator( - progress = { levelPercent / 100f }, + progress = { clampedPercent / 100f }, modifier = Modifier .fillMaxWidth() .height(11.dp) @@ -194,7 +225,7 @@ private fun LevelProgressSection( horizontalArrangement = Arrangement.End ) { Text( - text = levelPercent.toString(), + text = clampedPercent.toString(), fontFamily = PretendardFontFamily, fontWeight = FontWeight.SemiBold, fontSize = 13.sp, From fe76fa487b694035f9210ad3767fd0c1773d6259 Mon Sep 17 00:00:00 2001 From: unam98 Date: Fri, 3 Apr 2026 10:43:32 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20=E2=80=94=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=9D=BC=EC=9B=90=ED=99=94,=20sp=E2=86=92?= =?UTF-8?q?dp=20=EC=A0=84=ED=99=98,=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RunnectTextStyles 도입: fontFamily+fontWeight+fontSize를 하나의 스타일로 관리 - CompositionLocal로 RunnectTheme.textStyle 접근 제공 - sp → dp.value.sp로 시스템 폰트 크기 영향 제거 - 네이밍: small/medium/large → bold20, semiBold15 등 weight+size 기반 - MyPageScreen, VisitorModeScreen의 모든 Text를 style = textStyle.xxx로 통일 --- .../presentation/mypage/MyPageScreen.kt | 49 ++++------- .../presentation/mypage/VisitorModeScreen.kt | 12 +-- .../runnect/presentation/ui/theme/Theme.kt | 22 +++-- .../runnect/presentation/ui/theme/Type.kt | 82 ++++++++++++------- 4 files changed, 88 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt index 5d72e693..24b042f0 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/MyPageScreen.kt @@ -33,9 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.runnect.runnect.R import com.runnect.runnect.presentation.ui.theme.G1 @@ -44,7 +42,7 @@ import com.runnect.runnect.presentation.ui.theme.G3 import com.runnect.runnect.presentation.ui.theme.G4 import com.runnect.runnect.presentation.ui.theme.M1 import com.runnect.runnect.presentation.ui.theme.M3 -import com.runnect.runnect.presentation.ui.theme.PretendardFontFamily +import com.runnect.runnect.presentation.ui.theme.RunnectTheme @Composable fun MyPageScreen( @@ -112,6 +110,7 @@ fun MyPageScreen( @Composable private fun MyPageToolbar() { + val textStyle = RunnectTheme.textStyle Box( modifier = Modifier .fillMaxWidth() @@ -121,9 +120,7 @@ private fun MyPageToolbar() { ) { Text( text = stringResource(R.string.my_page_title), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 20.sp, + style = textStyle.bold20, color = G1 ) } @@ -135,6 +132,7 @@ private fun ProfileSection( profileImgResId: Int, onEditClick: () -> Unit ) { + val textStyle = RunnectTheme.textStyle Row( modifier = Modifier .fillMaxWidth() @@ -150,9 +148,7 @@ private fun ProfileSection( Spacer(modifier = Modifier.width(10.dp)) Text( text = nickname, - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 17.sp, + style = textStyle.bold17, color = M1 ) Spacer(modifier = Modifier.weight(1f)) @@ -171,9 +167,7 @@ private fun ProfileSection( Spacer(modifier = Modifier.width(4.dp)) Text( text = stringResource(R.string.my_page_edit), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 12.sp, + style = textStyle.medium12, color = M1 ) } @@ -185,6 +179,7 @@ private fun LevelProgressSection( level: String, levelPercent: Int ) { + val textStyle = RunnectTheme.textStyle val clampedPercent = levelPercent.coerceIn(0, 100) Column( @@ -196,16 +191,12 @@ private fun LevelProgressSection( Row { Text( text = stringResource(R.string.my_page_lv_indicator), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 15.sp, + style = textStyle.bold15, color = G1 ) Text( text = level, - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 15.sp, + style = textStyle.bold15, color = G1 ) } @@ -226,16 +217,12 @@ private fun LevelProgressSection( ) { Text( text = clampedPercent.toString(), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 13.sp, + style = textStyle.semiBold13, color = G1 ) Text( text = stringResource(R.string.my_page_progress_max), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 13.sp, + style = textStyle.semiBold13, color = G2 ) } @@ -281,6 +268,7 @@ private fun MenuItem( onClick: () -> Unit, showDivider: Boolean = true ) { + val textStyle = RunnectTheme.textStyle Column { Row( modifier = Modifier @@ -297,9 +285,7 @@ private fun MenuItem( Spacer(modifier = Modifier.width(8.dp)) Text( text = title, - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 15.sp, + style = textStyle.medium15, color = G1 ) Spacer(modifier = Modifier.weight(1f)) @@ -317,6 +303,7 @@ private fun MenuItem( @Composable private fun VersionSection() { + val textStyle = RunnectTheme.textStyle Row( modifier = Modifier .fillMaxWidth() @@ -327,16 +314,12 @@ private fun VersionSection() { ) { Text( text = stringResource(R.string.my_page_version_title), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 15.sp, + style = textStyle.medium15, color = G2 ) Text( text = stringResource(R.string.my_page_version), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 14.sp, + style = textStyle.regular14, color = G2 ) } diff --git a/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt b/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt index c6b0f41a..eccac390 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/mypage/VisitorModeScreen.kt @@ -17,20 +17,20 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.runnect.runnect.R import com.runnect.runnect.presentation.ui.theme.G2 import com.runnect.runnect.presentation.ui.theme.M1 -import com.runnect.runnect.presentation.ui.theme.PretendardFontFamily +import com.runnect.runnect.presentation.ui.theme.RunnectTheme import com.runnect.runnect.presentation.ui.theme.White @Composable fun VisitorModeScreen( onSignUpClick: () -> Unit ) { + val textStyle = RunnectTheme.textStyle Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, @@ -43,9 +43,7 @@ fun VisitorModeScreen( Spacer(modifier = Modifier.height(13.dp)) Text( text = stringResource(R.string.visitor_mode_mypage_message), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 13.sp, + style = textStyle.medium13, color = G2, textAlign = TextAlign.Center, lineHeight = 18.sp @@ -62,9 +60,7 @@ fun VisitorModeScreen( ) { Text( text = stringResource(R.string.visitor_mode_signup_btn), - fontFamily = PretendardFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 15.sp, + style = textStyle.semiBold15, color = White ) } diff --git a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt index b6744aff..69ebb83c 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Theme.kt @@ -3,6 +3,8 @@ package com.runnect.runnect.presentation.ui.theme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable private val RunnectColorScheme = lightColorScheme( primary = M1, @@ -29,9 +31,19 @@ private val RunnectColorScheme = lightColorScheme( fun RunnectTheme( content: @Composable () -> Unit ) { - MaterialTheme( - colorScheme = RunnectColorScheme, - typography = RunnectTypography, - content = content - ) + CompositionLocalProvider( + LocalRunnectTextStyles provides RunnectTextStyles() + ) { + MaterialTheme( + colorScheme = RunnectColorScheme, + content = content + ) + } +} + +object RunnectTheme { + val textStyle: RunnectTextStyles + @Composable + @ReadOnlyComposable + get() = LocalRunnectTextStyles.current } diff --git a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt index 9fc89faf..92d7ebf0 100644 --- a/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt +++ b/app/src/main/java/com/runnect/runnect/presentation/ui/theme/Type.kt @@ -1,10 +1,12 @@ package com.runnect.runnect.presentation.ui.theme -import androidx.compose.material3.Typography +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.runnect.runnect.R @@ -20,65 +22,83 @@ val PretendardFontFamily = FontFamily( Font(R.font.pretendard_black, FontWeight.Black), ) -val RunnectTypography = Typography( - displayLarge = TextStyle( +@Immutable +data class RunnectTextStyles( + val bold28: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Bold, - fontSize = 28.sp, + fontSize = 28.dp.value.sp, ), - headlineLarge = TextStyle( + val bold22: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Bold, - fontSize = 22.sp, + fontSize = 22.dp.value.sp, ), - headlineMedium = TextStyle( + val bold20: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Bold, - fontSize = 20.sp, + fontSize = 20.dp.value.sp, ), - titleLarge = TextStyle( + val bold17: TextStyle = TextStyle( fontFamily = PretendardFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 17.sp, + fontWeight = FontWeight.Bold, + fontSize = 17.dp.value.sp, ), - titleMedium = TextStyle( + val bold15: TextStyle = TextStyle( fontFamily = PretendardFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 15.sp, + fontWeight = FontWeight.Bold, + fontSize = 15.dp.value.sp, ), - titleSmall = TextStyle( + val semiBold17: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.SemiBold, - fontSize = 13.sp, + fontSize = 17.dp.value.sp, ), - bodyLarge = TextStyle( + val semiBold15: TextStyle = TextStyle( fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + fontSize = 15.dp.value.sp, ), - bodyMedium = TextStyle( + val semiBold13: TextStyle = TextStyle( fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + fontSize = 13.dp.value.sp, ), - bodySmall = TextStyle( + val medium15: TextStyle = TextStyle( fontFamily = PretendardFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 12.sp, + fontWeight = FontWeight.Medium, + fontSize = 15.dp.value.sp, ), - labelLarge = TextStyle( + val medium14: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Medium, - fontSize = 14.sp, + fontSize = 14.dp.value.sp, ), - labelMedium = TextStyle( + val medium13: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Medium, - fontSize = 12.sp, + fontSize = 13.dp.value.sp, ), - labelSmall = TextStyle( + val medium12: TextStyle = TextStyle( fontFamily = PretendardFontFamily, fontWeight = FontWeight.Medium, - fontSize = 11.sp, + fontSize = 12.dp.value.sp, + ), + val regular16: TextStyle = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.dp.value.sp, + ), + val regular14: TextStyle = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 14.dp.value.sp, + ), + val regular12: TextStyle = TextStyle( + fontFamily = PretendardFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 12.dp.value.sp, ), ) + +val LocalRunnectTextStyles = staticCompositionLocalOf { RunnectTextStyles() }