Skip to content
31 changes: 20 additions & 11 deletions core/ui/src/main/java/co/kr/tnt/ui/model/RecordChip.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ package co.kr.tnt.ui.model

import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import co.kr.tnt.core.ui.R
import co.kr.tnt.core.ui.R.string.core_exercise_back
import co.kr.tnt.core.ui.R.string.core_exercise_cardio
import co.kr.tnt.core.ui.R.string.core_exercise_lower_body
import co.kr.tnt.core.ui.R.string.core_exercise_shoulder
import co.kr.tnt.core.ui.R.string.core_exercise_upper_body
import co.kr.tnt.core.ui.R.string.core_meal_breakfast
import co.kr.tnt.core.ui.R.string.core_meal_dinner
import co.kr.tnt.core.ui.R.string.core_meal_lunch
import co.kr.tnt.core.ui.R.string.core_meal_snack
import co.kr.tnt.core.ui.R.string.core_pt_session
import co.kr.tnt.designsystem.component.chip.model.ChipStyle
import co.kr.tnt.domain.model.RecordType

Expand Down Expand Up @@ -34,10 +43,10 @@ sealed interface RecordChip {
return when (type) {
is RecordType.MealType -> {
val title = when (type) {
RecordType.MealType.BREAKFAST -> stringResource(R.string.meal_breakfast)
RecordType.MealType.LUNCH -> stringResource(R.string.meal_lunch)
RecordType.MealType.DINNER -> stringResource(R.string.meal_dinner)
RecordType.MealType.SNACK -> stringResource(R.string.meal_snack)
RecordType.MealType.BREAKFAST -> stringResource(core_meal_breakfast)
RecordType.MealType.LUNCH -> stringResource(core_meal_lunch)
RecordType.MealType.DINNER -> stringResource(core_meal_dinner)
RecordType.MealType.SNACK -> stringResource(core_meal_snack)
}
val emoji = when (type) {
RecordType.MealType.BREAKFAST -> "🌞"
Expand All @@ -50,11 +59,11 @@ sealed interface RecordChip {

is RecordType.ExerciseType -> {
val title = when (type) {
RecordType.ExerciseType.UPPER_BODY -> stringResource(R.string.exercise_upper_body)
RecordType.ExerciseType.LOWER_BODY -> stringResource(R.string.exercise_lower_body)
RecordType.ExerciseType.BACK -> stringResource(R.string.exercise_back)
RecordType.ExerciseType.SHOULDER -> stringResource(R.string.exercise_shoulder)
RecordType.ExerciseType.CARDIO -> stringResource(R.string.exercise_cardio)
RecordType.ExerciseType.UPPER_BODY -> stringResource(core_exercise_upper_body)
RecordType.ExerciseType.LOWER_BODY -> stringResource(core_exercise_lower_body)
RecordType.ExerciseType.BACK -> stringResource(core_exercise_back)
RecordType.ExerciseType.SHOULDER -> stringResource(core_exercise_shoulder)
RecordType.ExerciseType.CARDIO -> stringResource(core_exercise_cardio)
}
ExerciseChip(
title = title,
Expand All @@ -63,7 +72,7 @@ sealed interface RecordChip {
}

is RecordType.PTSessionType -> {
val title = stringResource(R.string.pt_session, type.sessionCount)
val title = stringResource(core_pt_session, type.sessionCount)
val emoji = "💪"
PTSessionChip(title, type.sessionCount, emoji, ChipStyle.BLUE)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import co.kr.tnt.core.ui.R
import co.kr.tnt.core.ui.R.string.core_close
import co.kr.tnt.core.ui.R.string.core_move_to_setting
import co.kr.tnt.core.ui.R.string.core_ok
import co.kr.tnt.designsystem.component.TnTPopupDialog
import co.kr.tnt.designsystem.theme.TnTTheme

Expand Down Expand Up @@ -32,12 +34,12 @@ fun PermissionRequestDialog(
permission.description
},
),
leftButtonText = stringResource(R.string.close),
leftButtonText = stringResource(core_close),
rightButtonText = stringResource(
if (isPermanentlyDenied) {
R.string.move_to_setting
core_move_to_setting
} else {
R.string.ok
core_ok
},
),
onLeftButtonClick = onDismiss,
Expand Down
13 changes: 8 additions & 5 deletions core/ui/src/main/java/co/kr/tnt/ui/permission/TnTPermission.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package co.kr.tnt.ui.permission
import android.Manifest.permission.POST_NOTIFICATIONS
import android.os.Build
import androidx.annotation.StringRes
import co.kr.tnt.core.ui.R
import co.kr.tnt.core.ui.R.string.core_alarm_permission_description
import co.kr.tnt.core.ui.R.string.core_alarm_permission_permanently_description
import co.kr.tnt.core.ui.R.string.core_alarm_permission_permanently_title
import co.kr.tnt.core.ui.R.string.core_alarm_permission_title
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.MultiplePermissionsState

Expand All @@ -20,10 +23,10 @@ enum class TnTPermission(
} else {
emptyList()
},
title = R.string.alarm_permission_title,
description = R.string.alarm_permission_description,
permanentlyDeniedTitle = R.string.alarm_permission_permanently_title,
permanentlyDeniedDescription = R.string.alarm_permission_permanently_description,
title = core_alarm_permission_title,
description = core_alarm_permission_description,
permanentlyDeniedTitle = core_alarm_permission_permanently_title,
permanentlyDeniedDescription = core_alarm_permission_permanently_description,
),
;

Expand Down
36 changes: 36 additions & 0 deletions core/ui/src/main/java/co/kr/tnt/ui/resource/DisplayText.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package co.kr.tnt.ui.resource

import android.content.Context
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource

sealed interface DisplayText {
fun asString(context: Context): String

@JvmInline
value class Plain(
val value: String,
) : DisplayText {
override fun asString(context: Context): String = value
}

class Resource(
@StringRes val resId: Int,
vararg val args: Any,
) : DisplayText {
override fun asString(context: Context): String = context.getString(resId, *args)
}

@Composable
fun asString(): String {
return when (this) {
is Plain -> value
is Resource -> stringResource(resId, *args)
}
}

companion object {
val EMPTY = Plain("")
}
}
116 changes: 61 additions & 55 deletions core/ui/src/main/res/values/strings.xml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왁.. 진짜 고생하셨습니다🥹

Original file line number Diff line number Diff line change
@@ -1,78 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="session_expired">세션이 만료되었어요</string>
<string name="session_expired_description">장시간 미사용으로 로그인 화면으로 이동해요</string>
<string name="core_failed_to_server_request">서버 요청에 실패했어요</string>

<string name="core_session_expired">세션이 만료되었어요</string>
<string name="core_session_expired_description">장시간 미사용으로 로그인 화면으로 이동해요</string>

<string name="alarm_permission_title">TnT에서 알림을 보내고자 합니다.</string>
<string name="alarm_permission_description">해당 기기로 수업 일정 등 서비스 이용에 도움이 되는 안내 사항을 푸시 알림으로 보내드리겠습니다.
<string name="core_alarm_permission_title">TnT에서 알림을 보내고자 합니다.</string>
<string name="core_alarm_permission_description">해당 기기로 수업 일정 등 서비스 이용에 도움이 되는 안내 사항을 푸시 알림으로 보내드리겠습니다.
\n앱 푸시 알림에 수신 동의 하시겠습니까?</string>

<string name="alarm_permission_permanently_title">TnT에서 알림 권한이 필요합니다.</string>
<string name="alarm_permission_permanently_description">수업 일정 및 중요한 공지 알림을 받기 위해 알림 권한이 필요합니다.\n설정에서 알림 권한을 활성화해주세요.</string>
<string name="core_alarm_permission_permanently_title">TnT에서 알림 권한이 필요합니다.</string>
<string name="core_alarm_permission_permanently_description">수업 일정 및 중요한 공지 알림을 받기 위해 알림 권한이 필요합니다.\n설정에서 알림 권한을 활성화해주세요.</string>

<string name="core_media_permission_title">프로필 사진 설정을 위해 사진 접근 권한이 필요해요</string>
<string name="core_media_permission_description">사진 추가는 프로필 말고도 운동과 식단 기록에도 사용돼요</string>

<string name="media_permission_title">프로필 사진 설정을 위해 사진 접근 권한이 필요해요</string>
<string name="media_permission_description">사진 추가는 프로필 말고도 운동과 식단 기록에도 사용돼요</string>
<string name="core_media_permission_permanently_title">프로필 사진 설정 권한 명시 거부</string>
<string name="core_media_permission_permanently_description">권한 설정을 위해 설정창으로 이동해주세요.</string>

<string name="media_permission_permanently_title">프로필 사진 설정 권한 명시 거부</string>
<string name="media_permission_permanently_description">권한 설정을 위해 설정창으로 이동해주세요.</string>
<string name="close">닫기</string>
<string name="ok">확인</string>
<string name="move_to_setting">설정으로 이동</string>
<string name="core_close">닫기</string>
<string name="core_ok">확인</string>
<string name="core_move_to_setting">설정으로 이동</string>

<string name="entered_wrong_text">잘못된 수치를 입력했어요</string>
<string name="text_length_and_format_warning">%s자 미만의 한글 또는 영문으로 입력해주세요</string>
<string name="core_entered_wrong_text">잘못된 수치를 입력했어요</string>
<string name="core_text_length_and_format_warning">%s자 미만의 한글 또는 영문으로 입력해주세요</string>

<string name="trainee">트레이니</string>
<string name="trainer">트레이너</string>
<string name="core_trainee">트레이니</string>
<string name="core_trainer">트레이너</string>

<string name="next">다음</string>
<string name="connect">연결하기</string>
<string name="start">시작하기</string>
<string name="skip">건너뛰기</string>
<string name="cancel">취소</string>
<string name="next_time">다음에</string>
<string name="core_next">다음</string>
<string name="core_connect">연결하기</string>
<string name="core_start">시작하기</string>
<string name="core_skip">건너뛰기</string>
<string name="core_cancel">취소</string>
<string name="core_next_time">다음에</string>

<string name="name">이름</string>
<string name="age_label">나이</string>
<string name="height_label">키</string>
<string name="weight_label">체중</string>
<string name="age_unit">세</string>
<string name="height_unit">cm</string>
<string name="weight_unit">kg</string>
<string name="core_name">이름</string>
<string name="core_age_label">나이</string>
<string name="core_height_label">키</string>
<string name="core_weight_label">체중</string>
<string name="core_age_unit">세</string>
<string name="core_height_unit">cm</string>
<string name="core_weight_unit">kg</string>

<string name="notification">알림</string>
<string name="no_recent_notifications">최근 받은 알림이 없어요</string>
<string name="core_notification">알림</string>
<string name="core_no_recent_notifications">최근 받은 알림이 없어요</string>

<!-- Dialog -->
<string name="do_not_see_for_three_days">3일 동안 보지 않기</string>
<string name="core_do_not_see_for_three_days">3일 동안 보지 않기</string>

<!-- Meal -->
<string name="meal_breakfast">아침</string>
<string name="meal_lunch">점심</string>
<string name="meal_dinner">저녁</string>
<string name="meal_snack">간식</string>
<string name="core_meal_breakfast">아침</string>
<string name="core_meal_lunch">점심</string>
<string name="core_meal_dinner">저녁</string>
<string name="core_meal_snack">간식</string>

<!-- Exercise -->
<string name="exercise_upper_body">상체 운동</string>
<string name="exercise_lower_body">하체 운동</string>
<string name="exercise_back">등 운동</string>
<string name="exercise_shoulder">어깨 운동</string>
<string name="exercise_cardio">유산소</string>
<string name="core_exercise_upper_body">상체 운동</string>
<string name="core_exercise_lower_body">하체 운동</string>
<string name="core_exercise_back">등 운동</string>
<string name="core_exercise_shoulder">어깨 운동</string>
<string name="core_exercise_cardio">유산소</string>

<!-- PT Session -->
<string name="pt_session">%d회차 수업</string>
<string name="core_pt_session">%d회차 수업</string>

<!-- MyPage -->
<string name="modifying_personal_info">개인정보 수정</string>
<string name="app_push_notification">앱 푸시 알림</string>
<string name="terms_of_service">서비스 이용약관</string>
<string name="privacy_policy">개인정보 처리방침</string>
<string name="app_version">버전 정보</string>
<string name="open_source_license">오픈소스 라이선스</string>
<string name="logout">로그아웃</string>
<string name="delete_account">계정 탈퇴</string>

<string name="logout_title">현재 계정을 로그아웃 할까요?</string>
<string name="logout_content">언제든지 다시 로그인 할 수 있어요!</string>
<string name="logout_complete_title">로그아웃이 완료되었어요</string>
<string name="core_modifying_personal_info">개인정보 수정</string>
<string name="core_app_push_notification">앱 푸시 알림</string>
<string name="core_terms_of_service">서비스 이용약관</string>
<string name="core_privacy_policy">개인정보 처리방침</string>
<string name="core_app_version">버전 정보</string>
<string name="core_open_source_license">오픈소스 라이선스</string>
<string name="core_logout">로그아웃</string>
<string name="core_delete_account">계정 탈퇴</string>

<string name="core_logout_title">현재 계정을 로그아웃 할까요?</string>
<string name="core_logout_content">언제든지 다시 로그인 할 수 있어요!</string>
<string name="core_logout_complete_title">로그아웃이 완료되었어요</string>

<string name="core_length_warning">%d자 미만으로 입력해주세요.</string>
<string name="core_no_records_yet">아직 등록된 기록이 없어요</string>
</resources>
3 changes: 2 additions & 1 deletion feature/login/src/main/java/co/kr/tnt/login/LoginContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import co.kr.tnt.login.model.TermState
import co.kr.tnt.ui.base.UiEvent
import co.kr.tnt.ui.base.UiSideEffect
import co.kr.tnt.ui.base.UiState
import co.kr.tnt.ui.resource.DisplayText

internal class LoginContract {
data class LoginUiState(
Expand All @@ -34,7 +35,7 @@ internal class LoginContract {

sealed interface LoginSideEffect : UiSideEffect {
data object ShowTermBottomSheet : LoginSideEffect
data class ShowToast(val message: String) : LoginSideEffect
data class ShowToast(val message: DisplayText) : LoginSideEffect
data class NavigateToWebView(val url: String) : LoginSideEffect
data class NavigateToHome(val userType: UserType) : LoginSideEffect
data class NavigateToSignup(
Expand Down
5 changes: 3 additions & 2 deletions feature/login/src/main/java/co/kr/tnt/login/LoginScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.kr.tnt.core.ui.R.string.core_next
import co.kr.tnt.designsystem.component.TnTDivider
import co.kr.tnt.designsystem.component.TnTModalBottomSheet
import co.kr.tnt.designsystem.component.button.TnTBottomButton
Expand Down Expand Up @@ -126,7 +127,7 @@ internal fun LoginRoute(
showBottomSheet = true
}

is LoginSideEffect.ShowToast -> snackbar.show(effect.message)
is LoginSideEffect.ShowToast -> snackbar.show(effect.message.asString(context))

is LoginSideEffect.NavigateToWebView -> {
navigateToWebView(effect.url)
Expand Down Expand Up @@ -289,7 +290,7 @@ private fun TermBottomSheetContent(
}
Spacer(modifier = Modifier.height(94.dp))
TnTBottomButton(
text = stringResource(R.string.next),
text = stringResource(core_next),
enabled = isAllTermChecked,
onClick = onClickNext,
)
Expand Down
22 changes: 18 additions & 4 deletions feature/login/src/main/java/co/kr/tnt/login/LoginViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package co.kr.tnt.login

import androidx.lifecycle.viewModelScope
import co.kr.tnt.core.ui.R.string.core_failed_to_server_request
import co.kr.tnt.domain.model.AuthType
import co.kr.tnt.domain.model.LoginResult
import co.kr.tnt.domain.repository.LoginRepository
import co.kr.tnt.feature.login.R
import co.kr.tnt.login.LoginContract.LoginSideEffect
import co.kr.tnt.login.LoginContract.LoginUiEvent
import co.kr.tnt.login.LoginContract.LoginUiState
import co.kr.tnt.login.model.TermState
import co.kr.tnt.ui.base.BaseViewModel
import co.kr.tnt.ui.resource.DisplayText
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -28,7 +31,11 @@ internal class LoginViewModel @Inject constructor(

is LoginUiEvent.OnAuthFail -> {
if (event.throwable !is LoginException.CancelException) {
sendEffect(LoginSideEffect.ShowToast("로그인에 실패하였습니다. 다시 시도해주세요."))
sendEffect(
LoginSideEffect.ShowToast(
DisplayText.Resource(R.string.failed_login_try_again),
),
)
}
}

Expand Down Expand Up @@ -67,8 +74,11 @@ internal class LoginViewModel @Inject constructor(
clearAllChecks()
sendEffect(LoginSideEffect.ShowTermBottomSheet)
}.onFailure {
// TODO resource
sendEffect(LoginSideEffect.ShowToast("알 수 없는 오류가 발생했습니다. 다시 시도해주세요."))
sendEffect(
LoginSideEffect.ShowToast(
DisplayText.Resource(core_failed_to_server_request),
),
)
}
}
}
Expand Down Expand Up @@ -106,7 +116,11 @@ internal class LoginViewModel @Inject constructor(
sendEffect(LoginSideEffect.NavigateToSignup(loginResult, messagingToken))
[email protected] = null
} ?: run {
sendEffect(LoginSideEffect.ShowToast("로그인에 실패하였습니다. 다시 시도해주세요."))
sendEffect(
LoginSideEffect.ShowToast(
DisplayText.Resource(R.string.failed_login_try_again),
),
)
}
}
}
Loading