diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8cfbed..69fdfa3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(project(":feature:auth")) implementation(project(":feature:home")) implementation(project(":feature:create")) + implementation(project(":feature:profile")) implementation(project(":core:designsystem")) implementation(project(":core:data")) implementation(project(":core:navigation")) diff --git a/app/src/main/java/com/idiotfrogs/memoryseal/MainActivity.kt b/app/src/main/java/com/idiotfrogs/memoryseal/MainActivity.kt index 7f0435a..c2296c9 100644 --- a/app/src/main/java/com/idiotfrogs/memoryseal/MainActivity.kt +++ b/app/src/main/java/com/idiotfrogs/memoryseal/MainActivity.kt @@ -19,6 +19,7 @@ import com.idiotfrogs.create.CreateScreen import com.idiotfrogs.designsystem.theme.MSTheme import com.idiotfrogs.home.HomeScreen import com.idiotfrogs.navigation.Routes +import com.idiotfrogs.profile.ProfileScreen import dagger.hilt.android.AndroidEntryPoint @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @@ -65,7 +66,8 @@ class MainActivity : ComponentActivity() { } composable { HomeScreen( - navigateToCreate = { navController.navigate(Routes.Create) } + navigateToCreate = { navController.navigate(Routes.Create) }, + navigateToProfile = { navController.navigate(Routes.Profile) } ) } composable { @@ -73,6 +75,19 @@ class MainActivity : ComponentActivity() { navigateToBack = { navController.popBackStack() } ) } + composable { + ProfileScreen( + navigateToBack = { navController.popBackStack() }, + navigateToLogin = { + navController.navigate(Routes.Login) { + popUpTo { + inclusive = true + } + launchSingleTop = true + } + } + ) + } } } } diff --git a/common/resource/src/main/res/drawable/ic_chevron_right.xml b/common/resource/src/main/res/drawable/ic_chevron_right.xml new file mode 100644 index 0000000..17a0992 --- /dev/null +++ b/common/resource/src/main/res/drawable/ic_chevron_right.xml @@ -0,0 +1,20 @@ + + + + diff --git a/core/navigation/src/main/java/com/idiotfrogs/navigation/Routes.kt b/core/navigation/src/main/java/com/idiotfrogs/navigation/Routes.kt index af18c8c..0faa1e8 100644 --- a/core/navigation/src/main/java/com/idiotfrogs/navigation/Routes.kt +++ b/core/navigation/src/main/java/com/idiotfrogs/navigation/Routes.kt @@ -11,4 +11,6 @@ sealed interface Routes { data object Home : Routes @Serializable data object Create : Routes + @Serializable + data object Profile : Routes } diff --git a/feature/home/src/main/java/com/idiotfrogs/home/HomeScreen.kt b/feature/home/src/main/java/com/idiotfrogs/home/HomeScreen.kt index 97c9307..43732cf 100644 --- a/feature/home/src/main/java/com/idiotfrogs/home/HomeScreen.kt +++ b/feature/home/src/main/java/com/idiotfrogs/home/HomeScreen.kt @@ -39,6 +39,7 @@ import com.idiotfrogs.home.component.HomeTicket @Composable fun HomeScreen( navigateToCreate: () -> Unit, + navigateToProfile: () -> Unit, ) { var expanded by remember { mutableStateOf(false) } var currentTab by remember { mutableStateOf(HomeTab.CREATED) } @@ -79,7 +80,7 @@ fun HomeScreen( .background(MSTheme.color.bgNormal) .systemBarsPadding() ) { - HomeHeader() + HomeHeader(navigateToProfile = navigateToProfile) HomeTabBar( selectedTab = currentTab, onClick = { currentTab = it }, @@ -131,5 +132,8 @@ fun HomeScreen( @DevicePreview @Composable fun HomeScreenPreview() { - HomeScreen(navigateToCreate = {}) + HomeScreen( + navigateToProfile = {}, + navigateToCreate = {} + ) } \ No newline at end of file diff --git a/feature/home/src/main/java/com/idiotfrogs/home/component/HomeHeader.kt b/feature/home/src/main/java/com/idiotfrogs/home/component/HomeHeader.kt index 3d38e0f..592d462 100644 --- a/feature/home/src/main/java/com/idiotfrogs/home/component/HomeHeader.kt +++ b/feature/home/src/main/java/com/idiotfrogs/home/component/HomeHeader.kt @@ -14,10 +14,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.idiotfrogs.designsystem.component.MSText import com.idiotfrogs.designsystem.theme.MSTheme +import com.idiotfrogs.designsystem.util.noRippleClickable import com.idiotfrogs.resource.R @Composable -fun HomeHeader() { +fun HomeHeader( + navigateToProfile: () -> Unit, +) { Row( modifier = Modifier .background(color = MSTheme.color.white) @@ -32,7 +35,9 @@ fun HomeHeader() { Spacer(modifier = Modifier.weight(1f)) // TODO: 이미지 url 통해 로드 Image( - modifier = Modifier.size(32.dp), + modifier = Modifier + .noRippleClickable(navigateToProfile) + .size(32.dp), painter = painterResource(R.drawable.img_profile), contentDescription = "profile" ) @@ -42,5 +47,7 @@ fun HomeHeader() { @Preview @Composable private fun HomeHeaderPreview() { - HomeHeader() + HomeHeader( + navigateToProfile = {} + ) } \ No newline at end of file diff --git a/feature/profile/src/main/java/com/idiotfrogs/profile/ProfileScreen.kt b/feature/profile/src/main/java/com/idiotfrogs/profile/ProfileScreen.kt new file mode 100644 index 0000000..ef844d7 --- /dev/null +++ b/feature/profile/src/main/java/com/idiotfrogs/profile/ProfileScreen.kt @@ -0,0 +1,188 @@ +package com.idiotfrogs.profile + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +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.layout.size +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.input.rememberTextFieldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.idiotfrogs.designsystem.component.MSDialog +import com.idiotfrogs.designsystem.component.MSText +import com.idiotfrogs.designsystem.component.MSTextField +import com.idiotfrogs.designsystem.theme.MSTheme +import com.idiotfrogs.designsystem.util.noRippleClickable +import com.idiotfrogs.designsystem.util.rememberPickerState +import com.idiotfrogs.profile.component.ProfileHeader +import com.idiotfrogs.profile.component.ProfileOption +import com.idiotfrogs.resource.R +import com.skydoves.landscapist.glide.GlideImage + +@Composable +fun ProfileScreen( + navigateToBack: () -> Unit, + navigateToLogin: () -> Unit, +) { + // TODO: 추후 기존 프로필과 비교 로직 작성 + var isChanged by remember { mutableStateOf(false) } + var showLogoutDialog by remember { mutableStateOf(false) } + var showWithdrawDialog by remember { mutableStateOf(false) } + + val (imageUri, launchImagePicker) = rememberPickerState() + val textFieldState = rememberTextFieldState() + + if (showLogoutDialog) { + MSDialog( + title = "로그아웃", + content = "메실에서 로그아웃 하시겠습니까?", + confirmText = "로그아웃", + cancelText = "유지", + onConfirm = { + /** TODO: 로그아웃 로직 */ + showLogoutDialog = false + navigateToLogin() + }, + onCancel = { + showLogoutDialog = false + } + ) + } + if (showWithdrawDialog) { + MSDialog( + title = "회원탈퇴", + content = "메실 회원을 탈퇴하시겠습니까?\n티켓에 저장된 내용은 삭제되지 않습니다.", + confirmText = "탈퇴", + cancelText = "취소", + onConfirm = { + /** TODO: 탈퇴 로직 */ + showWithdrawDialog = false + navigateToLogin() + }, + onCancel = { + showWithdrawDialog = false + } + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(MSTheme.color.white) + .systemBarsPadding() + .padding(horizontal = 20.dp) + ) { + ProfileHeader( + isChanged = isChanged, + onBack = { navigateToBack() }, + onSave = { + /** TODO: 저장 로직 */ + navigateToBack() + } + ) + Spacer(modifier = Modifier.height(16.dp)) + imageUri?.let { + GlideImage( + imageModel = { imageUri }, + modifier = Modifier + .noRippleClickable { launchImagePicker() } + .size(128.dp) + .clip(CircleShape) + .align(Alignment.CenterHorizontally), + + ) + } ?: Image( + modifier = Modifier + .noRippleClickable { launchImagePicker() } + .size(128.dp) + .align(Alignment.CenterHorizontally), + painter = painterResource(R.drawable.img_empty_profile), + contentDescription = "Profile" + ) + Spacer(modifier = Modifier.height(16.dp)) + MSText( + text = "닉네임", + fontWeight = FontWeight.Normal, + fontSize = 12.dp, + color = MSTheme.color.greyG5 + ) + Spacer(modifier = Modifier.height(8.dp)) + MSTextField( + modifier = Modifier.fillMaxWidth(), + textFieldState = textFieldState, + hint = "" + ) + Spacer(modifier = Modifier.height(16.dp)) + ProfileOption( + option = "앱 버전", + trailingContent = { + MSText( + text = "v0.2", + fontWeight = FontWeight.Normal, + fontSize = 16.dp, + color = MSTheme.color.greyG4 + ) + } + ) + Spacer(modifier = Modifier.height(16.dp)) + ProfileOption( + option = "이용 약관", + trailingContent = { + Image( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = "terms" + ) + } + ) + Spacer(modifier = Modifier.weight(1f)) + ProfileOption( + modifier = Modifier.noRippleClickable { showLogoutDialog = true }, + option = "로그아웃", + trailingContent = { + Image( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = "terms" + ) + } + ) + Spacer(modifier = Modifier.height(16.dp)) + ProfileOption( + modifier = Modifier.noRippleClickable { showWithdrawDialog = true }, + option = "회원탈퇴", + optionColor = MSTheme.color.red, + trailingContent = { + Image( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = "logout" + ) + } + ) + Spacer(modifier = Modifier.height(16.dp)) + } + +} + +@Preview +@Composable +fun ProfileScreenPreview() { + ProfileScreen( + navigateToBack = { }, + navigateToLogin = { } + ) +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileHeader.kt b/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileHeader.kt new file mode 100644 index 0000000..4971730 --- /dev/null +++ b/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileHeader.kt @@ -0,0 +1,69 @@ +package com.idiotfrogs.profile.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.idiotfrogs.designsystem.component.MSText +import com.idiotfrogs.designsystem.theme.MSTheme +import com.idiotfrogs.designsystem.util.noRippleClickable +import com.idiotfrogs.resource.R + +@Composable +fun ProfileHeader( + isChanged: Boolean, + modifier: Modifier = Modifier, + onBack: () -> Unit, + onSave: () -> Unit, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Image( + modifier = Modifier.noRippleClickable(onBack), + painter = painterResource(R.drawable.ic_chevron_left), + contentDescription = "chevron left" + ) + MSText( + text = "프로필", + fontWeight = FontWeight.Bold, + fontSize = 14.dp, + color = MSTheme.color.black + ) + MSText( + modifier = Modifier.noRippleClickable(onSave), + text = "저장", + fontWeight = FontWeight.Bold, + fontSize = 14.dp, + color = if (isChanged) MSTheme.color.primaryNormal else MSTheme.color.greyG2, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun ProfileHeaderPreview() { + var isChanged by remember { mutableStateOf(false) } + ProfileHeader( + modifier = Modifier.padding(horizontal = 20.dp), + isChanged = isChanged, + onBack = { }, + onSave = { isChanged = !isChanged } + ) +} diff --git a/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileOption.kt b/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileOption.kt new file mode 100644 index 0000000..41ae788 --- /dev/null +++ b/feature/profile/src/main/java/com/idiotfrogs/profile/component/ProfileOption.kt @@ -0,0 +1,54 @@ +package com.idiotfrogs.profile.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.idiotfrogs.designsystem.component.MSText +import com.idiotfrogs.designsystem.theme.MSTheme + +@Composable +fun ProfileOption( + modifier: Modifier = Modifier, + option: String, + optionColor: Color = MSTheme.color.greyG5, + trailingContent: @Composable () -> Unit +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + MSText( + text = option, + fontWeight = FontWeight.Normal, + fontSize = 16.dp, + color = optionColor + ) + trailingContent() + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFFFF) +@Composable +fun ProfileOptionPreview() { + ProfileOption( + modifier = Modifier.padding(horizontal = 20.dp), + option = "앱 버전", + trailingContent = { + MSText( + text = "v0.2", + fontWeight = FontWeight.Normal, + fontSize = 16.dp, + color = MSTheme.color.greyG4 + ) + } + ) +}