Skip to content

Conversation

@rhkrwngud445
Copy link
Contributor

@rhkrwngud445 rhkrwngud445 commented Aug 15, 2025

작업 내용

상대프로필, 상대 프로필 편집 작업

확인 방법

Preview (FriendProfile, FriendProfileEditor)

참고 사항

친구 프로필 수정
스크린샷 2025-08-18 오후 11 42 42 스크린샷 2025-08-18 오후 11 43 03

관련 이슈

@rhkrwngud445 rhkrwngud445 linked an issue Aug 15, 2025 that may be closed by this pull request
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @rhkrwngud445, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 친구 프로필 보기 및 편집을 위한 새로운 사용자 인터페이스를 도입합니다. 이는 사용자가 친구의 상세 정보를 확인하고 관리할 수 있도록 하여 애플리케이션의 소셜 기능을 향상시키는 데 중점을 둡니다.

Highlights

  • 새로운 친구 프로필 UI 구현: 친구의 관계, 연락 주기, 생일, 기념일, 메모 등 상세 정보를 표시하고 프로필 및 기록 탭을 포함하는 FriendProfileScreen.kt가 추가되었습니다.
  • 친구 프로필 편집 UI 추가: 친구 프로필 정보를 편집할 수 있는 FriendProfileEditorScreen.kt가 구현되었으며, 이름 입력 필드가 포함됩니다.
  • 새로운 UI 컴포넌트 도입: 새로운 프로필 화면을 지원하기 위해 CallButton.kt, MessageButton.kt, NearTopAppbar.kt와 같은 재사용 가능한 컴포넌트가 추가되었습니다.
  • 내비게이션 통합: 새로운 친구 프로필 및 편집 화면으로 이동할 수 있도록 FriendProfileNavigation.kt 및 FriendProfileEditorNavigation.kt를 통해 내비게이션 경로가 정의되었고, NearNavHost.kt에 통합되었습니다.
  • 새로운 드로어블 리소스 추가: 새로운 UI 컴포넌트에서 사용될 다양한 아이콘 및 이미지 (예: 뒤로 가기 버튼, 전화/메시지 아이콘, 사용자 프로필 이미지)를 위한 드로어블 XML 파일이 추가되었습니다.
  • 문자열 리소스 업데이트: 새로운 UI 요소에 필요한 문자열 리소스(예: 프로필 상세 정보, 버튼, 탭 레이블)가 strings.xml에 추가되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

상대 프로필 및 프로필 편집 화면 UI 구현에 대한 PR 잘 보았습니다. 전반적으로 새로운 화면과 컴포넌트들이 잘 추가되었지만, 몇 가지 개선점과 수정이 필요한 부분이 보입니다. 특히 탭(Tab) 로직의 버그, 컴포넌트의 재사용성, 상태 관리, 그리고 테스트를 위해 임시로 변경된 것으로 보이는 코드들에 대한 리뷰를 남겼습니다. 아래의 상세 리뷰를 확인해주세요.

import com.alarmy.near.presentation.ui.theme.NearTheme

@Composable
fun FriendProfileRoute(onShowErrorSnackBar: (throwable: Throwable?) -> Unit) {

Choose a reason for hiding this comment

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

critical

FriendProfileRouteFriendProfileScreenfriendId와 같은 매개변수를 받지 않습니다. 특정 친구의 프로필을 표시하려면 이 화면에 식별자를 전달하고 이를 사용하여 해당 데이터를 가져와야 합니다.

import com.alarmy.near.presentation.ui.theme.NearTheme

@Composable
fun FriendProfileEditorRoute(onShowErrorSnackBar: (throwable: Throwable?) -> Unit) {

Choose a reason for hiding this comment

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

critical

FriendProfileEditorRouteFriendProfileEditorScreenfriendId와 같은 매개변수를 받지 않습니다. 특정 친구의 프로필을 편집하려면 이 화면에 식별자를 전달하고 이를 사용하여 해당 데이터를 로드하고 저장해야 합니다.

Comment on lines 78 to 82
NearTextField(
value = "test",
onValueChange = {
},
)

Choose a reason for hiding this comment

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

critical

NearTextFieldvalue가 하드코딩되어 있고 onValueChange가 비어 있어 텍스트 필드가 작동하지 않습니다. 편집 가능하게 만들려면 상태 홀더(예: ViewModel 또는 remember 상태)에 연결하고 onValueChange 콜백에서 상태를 업데이트해야 합니다.

Comment on lines +139 to +209
TabRow(
modifier =
Modifier
.padding(horizontal = 25.dp)
.width(170.dp),
containerColor = NearTheme.colors.WHITE_FFFFFF,
selectedTabIndex = 0,
divider = {},
indicator = {
TabRowDefaults.SecondaryIndicator(
modifier =
Modifier
.customTabIndicatorOffset(
it[currentTabPosition.intValue],
80.dp,
), // 넓이, 애니메이션 지정
// 모양 지정
height = 3.dp,
color = NearTheme.colors.BLUE01_5AA2E9,
)
},
) {
Tab(
modifier =
Modifier
.width(85.dp)
.height(50.dp),
selected = true,
onClick = {
currentTabPosition.intValue = 0
},
) {
if (currentTabPosition.intValue == 0) {
Text(
text = stringResource(R.string.friend_profile_tab_text_profile),
style = NearTheme.typography.B2_14_BOLD,
color = NearTheme.colors.BLACK_1A1A1A,
)
} else {
Text(
text = stringResource(R.string.friend_profile_tab_text_profile),
style = NearTheme.typography.B2_14_MEDIUM,
color = NearTheme.colors.GRAY02_B7B7B7,
)
}
}
Tab(
modifier =
Modifier
.width(85.dp)
.height(50.dp),
selected = true,
onClick = {
currentTabPosition.intValue = 1
},
) {
if (currentTabPosition.intValue == 1) {
Text(
text = stringResource(R.string.friend_profile_tab_text_record),
style = NearTheme.typography.B2_14_BOLD,
color = NearTheme.colors.BLACK_1A1A1A,
)
} else {
Text(
text = stringResource(R.string.friend_profile_tab_text_record),
style = NearTheme.typography.B2_14_MEDIUM,
color = NearTheme.colors.GRAY02_B7B7B7,
)
}
}
}

Choose a reason for hiding this comment

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

high

TabRow 구현에 몇 가지 문제가 있습니다:

  1. selectedTabIndex0으로 하드코딩되어 있습니다. currentTabPosition.intValue에 바인딩되어야 합니다.
  2. Tabselected 속성이 true로 하드코딩되어 있습니다. currentTabPosition.intValue와의 비교를 기반으로 해야 합니다.
  3. Tab의 콘텐츠에 if/else 블록 내에 중복된 Text 컴포저블이 있습니다. stylecolor를 동적으로 설정하는 단일 Text 컴포저블을 사용하여 단순화할 수 있습니다.
            TabRow(
                modifier =
                    Modifier
                        .padding(horizontal = 25.dp)
                        .width(170.dp),
                containerColor = NearTheme.colors.WHITE_FFFFFF,
                selectedTabIndex = currentTabPosition.intValue,
                divider = {},
                indicator = {
                    TabRowDefaults.SecondaryIndicator(
                        modifier =
                            Modifier
                                .customTabIndicatorOffset(
                                    it[currentTabPosition.intValue],
                                    80.dp,
                                ), // 넓이, 애니메이션 지정
                        // 모양 지정
                        height = 3.dp,
                        color = NearTheme.colors.BLUE01_5AA2E9,
                    )
                },
            ) {
                Tab(
                    modifier =
                        Modifier
                            .width(85.dp)
                            .height(50.dp),
                    selected = currentTabPosition.intValue == 0,
                    onClick = {
                        currentTabPosition.intValue = 0
                    },
                ) {
                    Text(
                        text = stringResource(R.string.friend_profile_tab_text_profile),
                        style = if (currentTabPosition.intValue == 0) NearTheme.typography.B2_14_BOLD else NearTheme.typography.B2_14_MEDIUM,
                        color = if (currentTabPosition.intValue == 0) NearTheme.colors.BLACK_1A1A1A else NearTheme.colors.GRAY02_B7B7B7,
                    )
                }
                Tab(
                    modifier =
                        Modifier
                            .width(85.dp)
                            .height(50.dp),
                    selected = currentTabPosition.intValue == 1,
                    onClick = {
                        currentTabPosition.intValue = 1
                    },
                ) {
                    Text(
                        text = stringResource(R.string.friend_profile_tab_text_record),
                        style = if (currentTabPosition.intValue == 1) NearTheme.typography.B2_14_BOLD else NearTheme.typography.B2_14_MEDIUM,
                        color = if (currentTabPosition.intValue == 1) NearTheme.colors.BLACK_1A1A1A else NearTheme.colors.GRAY02_B7B7B7,
                    )
                }
            }

fun CallButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
enabled: Boolean = false,

Choose a reason for hiding this comment

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

high

enabled의 기본값이 false입니다. 이로 인해 FriendProfileScreen에서 사용할 때 버튼이 기본적으로 비활성화됩니다. 특별한 이유가 없는 한 상호작용 요소는 기본적으로 활성화하는 것이 좋습니다.

Suggested change
enabled: Boolean = false,
enabled: Boolean = true,

fun MessageButton(
modifier: Modifier = Modifier,
onClick: () -> Unit,
enabled: Boolean = false,

Choose a reason for hiding this comment

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

high

enabled의 기본값이 false입니다. 이로 인해 FriendProfileScreen에서 사용할 때 버튼이 기본적으로 비활성화됩니다. 특별한 이유가 없는 한 상호작용 요소는 기본적으로 활성화하는 것이 좋습니다.

Suggested change
enabled: Boolean = false,
enabled: Boolean = true,

Comment on lines +434 to +443
val currentTabWidth by animateDpAsState(
targetValue = tabWidth,
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
label = "",
)
val indicatorOffset by animateDpAsState(
targetValue = ((currentTabPosition.left + currentTabPosition.right - tabWidth) / 2),
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
label = "",
)

Choose a reason for hiding this comment

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

medium

animateDpAsState 호출에 label이 비어 있습니다. 설명적인 레이블을 제공하면 Layout Inspector와 같은 도구에서 애니메이션을 디버깅하는 데 도움이 되므로 좋은 습관입니다.

        val currentTabWidth by animateDpAsState(
            targetValue = tabWidth,
            animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
            label = "tabWidth",
        )
        val indicatorOffset by animateDpAsState(
            targetValue = ((currentTabPosition.left + currentTabPosition.right - tabWidth) / 2),
            animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing),
            label = "indicatorOffset",
        )

ButtonDefaults.buttonColors(
containerColor = NearTheme.colors.BG02_F4F9FD,
contentColor = NearTheme.colors.BLACK_1A1A1A,
disabledContainerColor = Color(0xfff7f7f7),

Choose a reason for hiding this comment

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

medium

Color(0xfff7f7f7) 색상이 하드코딩되어 있습니다. 일관된 디자인 시스템을 유지하고 유지보수성을 향상시키려면 이 색상을 NearTheme.colors에 정의하고 참조해야 합니다.

ButtonDefaults.buttonColors(
containerColor = NearTheme.colors.BG02_F4F9FD,
contentColor = NearTheme.colors.BLACK_1A1A1A,
disabledContainerColor = Color(0xfff7f7f7),

Choose a reason for hiding this comment

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

medium

Color(0xfff7f7f7) 색상이 하드코딩되어 있습니다. 일관된 디자인 시스템을 유지하고 유지보수성을 향상시키려면 이 색상을 NearTheme.colors에 정의하고 참조해야 합니다.

modifier = modifier,
navController = navController,
startDestination = RouteHome,
startDestination = RouteFriendProfileEditor,

Choose a reason for hiding this comment

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

medium

startDestinationRouteFriendProfileEditor로 설정되어 있습니다. 개발 및 테스트 목적이었던 것으로 보입니다. 병합하기 전에 올바른 시작 대상(예: RouteHome)으로 되돌려주세요.

Suggested change
startDestination = RouteFriendProfileEditor,
startDestination = RouteHome,

@rhkrwngud445 rhkrwngud445 merged commit 0779b76 into dev Aug 18, 2025
1 check passed
@rhkrwngud445 rhkrwngud445 deleted the feat/#10-상대프로필_ui branch August 18, 2025 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 상대프로필 UI

2 participants