-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 연락처 불러오기 & 주기 설정 기능 #40
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
Conversation
- 32x32 기본 캐릭터 아이콘 추가 (`img_32_contact_square.xml`) - 100x101 기본 캐릭터 아이콘 추가 (`img_100_character_default.xml`) - TODO: NearListModuleBackground에서 화살표 이미지는 추후 추가
- FriendContactCycleScreen: 친구 연락 주기 설정 화면의 전체적인 UI를 구성합니다.
- ContactCycleTopAppBar: 화면 상단의 앱 바 UI를 구성합니다. ("챙길사람 불러오기", "1/2" 텍스트 포함)
- ContactCycleHeader: 화면 헤더 UI를 구성합니다. (캐릭터 이미지, 안내 문구 포함)
- NearListModuleBackground: "연락처에서 불러오기" 리스트 모듈의 배경 UI를 구성합니다.
- ContactCycleButtons: 화면 하단의 "나중에 하기", "다음" 버튼 UI를 구성합니다.
- `NearListModuleBackground` 컴포저블의 수직 padding을 14.dp에서 24.dp로 변경했습니다. - `NearListModuleBackground`에 content 람다를 추가하여 내부 컨텐츠를 커스텀할 수 있도록 개선했습니다. - "나중에 하기" 버튼을 활성화하고, 확인 버튼을 비활성화하도록 `enabled` 상태를 조정했습니다.
- 친구 연락 주기 설정 화면의 기본적인 UI를 구성했습니다. - 연락처에서 친구를 불러오는 기능을 위한 `ContactLoadContent` 컴포저블을 추가했습니다. - 친구 목록 아이템 `FriendListItem` 컴포저블을 추가했습니다. - "나중에 하기", "다음" 버튼을 포함하는 `ContactCycleButtons` 컴포저블을 추가했습니다. - 친구 연락처 데이터를 표현하는 `FriendContactUIModel`을 정의했습니다. - 친구 연락처 관련 로직을 처리할 `FriendContactViewModel`을 추가했습니다. - `NearListModuleBackground` 컴포저블의 UI를 개선했습니다.
- ContactCycleButtons 컴포저블에 버튼 클릭 콜백과 텍스트를 파라미터로 받을 수 있도록 수정
- `FriendListItem` 컴포저블 함수의 친구 제거 버튼을 외부에서 content로 주입받도록 변경했습니다. - `ContactLoadContent`에서 `FriendListItem`의 친구 제거 버튼 클릭 시 수행할 동작을 정의합니다. (현재는 주석 처리된 TODO) - `ContactLoadContent`에서 불필요한 Spacer와 `ContactCycleButtons` 호출을 제거했습니다.
- `ContactCycleContent` Composable 함수를 추가하여 친구 연락 주기 설정 화면의 UI를 구현했습니다. - "한번에 설정" 체크박스 기능을 추가하여 여러 친구의 연락 주기를 일괄적으로 설정할 수 있도록 했습니다. - 체크박스 상태에 따라 일괄 설정 UI가 표시되거나 숨겨집니다. - `LazyColumn`을 사용하여 친구 목록을 표시하고, 각 아이템에는 주기 설정 버튼을 추가했습니다.
- 연락처를 불러오고, 각 연락처에 대한 연락 주기를 설정하는 두 단계로 화면을 구성합니다. - `FriendContactViewModel`에 `currentStep` 상태를 추가하여 현재 단계를 관리하고, `moveToNextStep` 및 `moveToPreviousStep` 함수를 통해 단계 간 이동을 처리합니다. - `ContactCycleStep` enum 클래스를 정의하여 각 단계를 나타냅니다. (LOAD_CONTACTS, SET_CYCLE) - `ContactLoadContent` 컴포저블은 연락처 목록을 표시합니다. - `ContactCycleContent` 컴포저블은 각 연락처에 대한 주기 설정 UI를 표시합니다. (구현 예정) - `ContactCycleButtons` 컴포저블을 추가하여 "나중에 하기/다음" 또는 "이전/완료" 버튼을 현재 단계에 따라 동적으로 표시합니다.
- 불필요한 Spacer 제거
- `ContactLoadContent`에서 친구가 없을 때 표시되는 이미지를 `img_100_character_empty`에서 `ic_24_down`으로 변경하고, `GRAY01_888888` 색상을 적용했습니다. - `ContactCycleContent`의 뒤로가기 아이콘과 주기 선택 아이콘에 `GRAY01_888888` 색상을 적용했습니다. - `ContactCycleContent`의 주기 선택 아이콘을 `ic_back_32_black`에서 `ic_24_down`으로 변경했습니다.
- `ContactCycleStep` enum에 `appbarTitle` 프로퍼티 추가 - `FriendContactCycleScreen`의 `ContactCycleTopAppBar`에 현재 단계에 따른 `pageIndex`와 `title`을 전달하여 동적으로 표시하도록 수정
- ContactCycleHeader composable 함수에 headerTitle, headerSubTitle 파라미터를 추가하여 재사용성을 높였습니다. - 각 ContactCycleStep에 따라 다른 헤더 내용을 표시하도록 수정했습니다.
- Row에 weight를 적용하여 이름이 길어져도 레이아웃이 깨지지 않도록 수정 - Text에 Ellipsis와 maxLines를 적용하여 이름이 길면 ...으로 표시되도록 수정
- ic_back_32_black 아이콘을 ic_32_cancel 아이콘으로 변경했습니다.
- `CycleSettingBottomSheet` 컴포저블 함수 추가 - 연락 주기 목록(`ReminderInterval`) 표시 및 선택 기능 구현 - 선택된 주기에 따라 UI 업데이트 (텍스트 스타일 변경, 체크박스 표시) - `ContactCycleContent`에서 일괄 설정 체크박스 선택 시 바텀시트 표시 로직 추가
- 특정 날짜의 요일을 한국어로 반환하는 기능 추가 - 다음 주 같은 요일의 날짜를 "월/일 요일" 형식으로 반환하는 기능 추가
- `getNextCycleDate` 함수를 추가하여 선택된 주기에 따른 다음 알림 날짜를 계산합니다. - `ReminderInterval` (매일, 매주, 격주, 매월, 반년)에 따라 다음 날짜를 계산하여 "월/일 요일" 형식의 문자열로 반환합니다.
- `CycleSettingBottomSheet`에서 초기 선택된 주기를 `EVERY_WEEK` (매주)로 설정했습니다. - 주의 시작 요일을 현재 요일 기준으로 동적으로 표시하도록 `DateExtension.getTodayDayOfWeekInKorean()`를 사용했습니다. - 선택된 주기에 따라 다음 주기 날짜를 계산하여 표시하도록 `DateExtension.getNextCycleDate()`와 `DateExtension.getNextWeekSameDay()`를 활용했습니다.
- 친구 연락 주기 설정 BottomSheet UI 및 기능 구현 - 선택된 주기를 `ContactCycleContent`에 표시하도록 기능 추가 - `DateExtension`에 선택된 주기를 사용자 친화적인 텍스트로 변환하는 `getCycleText` 함수 추가
- 연락 주기 일괄 설정을 위한 체크박스 및 주기 표시 UI를 추가했습니다. - 체크박스 선택/해제에 따라 일괄 설정 모드를 토글하고, 선택된 주기를 초기화합니다. - 주기 표시 영역 클릭 시 바텀시트를 통해 주기를 설정할 수 있도록 합니다. - 선택된 주기가 없으면 오늘 요일 기준으로 표시합니다.
- `CycleSettingBottomSheet`가 열릴 때, 이전에 선택된 주기가 있으면 해당 주기를 기본값으로 설정하도록 수정했습니다.
- `ContactCycleContent`에서 `CycleSettingBottomSheet`를 호출할 때 현재 선택된 주기(`selectedCycle`)를 `currentSelectedInterval` 파라미터로 전달합니다.
- 연락 주기 텍스트 표시 로직을 수정했습니다:
- "매월" -> "매달"로 변경
- "6개월마다" -> "매년 M/D, M/D" 형식으로 변경 (예: "매년 3/15, 9/15")
- `FriendContactUIEvent` 및 `FriendContactUIState` 클래스를 추가하여 UI 이벤트와 상태를 관리합니다. - `ViewModel`에서 UI 이벤트를 처리하고 UI 상태를 업데이트하는 로직을 구현했습니다. - `FriendContactCycleScreen` 컴포저블이 새로운 UI 상태 및 이벤트 처리 로직을 사용하도록 수정했습니다.
- 친구 연락처 주기 설정 화면으로 이동하는 `navigateToFriendContactCycle` 확장 함수와 NavGraph를 정의하는 `friendContactCycleNavGraph` 함수를 `FriendContactCycleNavigation.kt` 파일에 추가했습니다. - `NearNavHost.kt`에 `friendContactCycleNavGraph`를 추가하여, 친구 연락처 주기 설정 화면에서 홈 화면으로 이동할 수 있도록 네비게이션 로직을 구현했습니다.
- 각 연락처에 대해 개별적으로 연락 주기를 설정하거나, 한 번에 모든 연락처에 동일한 주기를 적용하는 기능을 추가했습니다. - `FriendContactUIState`에 `isAllContactsCycleSet` 프로퍼티를 추가하여 모든 연락처의 주기 설정 완료 여부를 확인합니다. - `FriendContactUIEvent`에 `SetContactCycle` 이벤트를 추가하고, `FriendContactViewModel`에서 해당 이벤트를 처리하여 개별 연락처의 주기를 업데이트합니다. - "한번에 설정" 시 선택된 주기가 모든 연락처에 적용되도록 `completeCycleSetting` 함수를 수정했습니다. - 모든 연락처의 주기가 설정된 경우에만 "완료" 버튼이 활성화되도록 `ContactCycleButtons` 컴포넌트를 수정했습니다. - `FriendContactUIModel`에 `reminderInterval` 프로퍼티를 추가하여 각 연락처의 설정된 주기를 저장합니다.
- `FriendContactUIEvent`에 `DeselectContact` 이벤트를 추가했습니다. - `FriendContactViewModel`에 `deselectContact` 함수를 추가하여, 선택된 연락처 목록에서 특정 연락처를 제거하는 로직을 구현했습니다. - `ContactLoadContent` Composable에 `onDeselectContact` 콜백을 추가하여, X 버튼 클릭 시 해당 연락처를 선택 해제할 수 있도록 수정했습니다. - `FriendContactCycleScreen`에서 `DeselectContact` 이벤트를 처리하도록 로직을 추가하고, `ContactLoadContent`에 `onDeselectContact` 콜백을 전달하도록 수정했습니다.
- `ReminderInterval`을 문자열로 변환하는 `toContactWeekString` 함수를 추가했습니다. - 오늘 요일을 API 요청에 필요한 영어 문자열로 반환하는 `getTodayDayOfWeekInEnglish` 함수를 추가했습니다.
- 친구 초기 설정(Friend Init)에 필요한 Request, Response 데이터 클래스를 정의했습니다. - 서버 응답 모델(`FriendInitItemEntity`)을 앱의 도메인 모델(`Friend`)로 변환하고, UI 모델(`FriendContactUIModel`)을 서버 요청 모델(`FriendInitItemRequest`)로 변환하는 매퍼 함수를 추가했습니다. - 전화번호 형식을 `010-xxxx-xxxx`로 통일하는 확장 함수를 구현했습니다.
- 연락처에 있는 친구들을 서버에 일괄 등록하기 위한 `initFriends` API를 추가하고 관련 로직을 구현했습니다. - 친구 추가 시 연락 주기가 설정된 연락처만 필터링하여 서버에 요청합니다.
- FriendContactViewModel에 API 호출 로직 추가 - 로딩 상태 및 에러 처리 구현 - UI 이벤트 추가
- 로그인 성공 시 홈 화면 대신 친구 연락 주기 설정 화면으로 바로 이동하도록 탐색 로직을 수정했습니다.
- `FriendContactCycle` 관련 화면들에서 하드코딩되어 있던 문자열들을 `strings.xml` 리소스로 대체했습니다. - `strings.xml`에 친구 연락 주기 설정 기능에 사용될 문자열들을 추가했습니다. - `ContactCycleStep` enum의 `appbarTitle`을 `String`에서 `StringRes` ID로 변경했습니다.
- `ContactViewModel`에서 연락처를 정렬하는 작업의 스레드를 `Dispatchers.Default`에서 `Dispatchers.IO`로 변경했습니다.
- 로그인 완료 후와 홈 화면 우상단 메뉴에서 '친구 추가'를 선택했을 때, 연락 주기 설정 화면으로 이동하도록 `onNavigateToFriendContactCycle` 콜백을 구현했습니다. - 로그인 후 연락 주기 설정 화면으로 이동 시, 뒤로 가기 스택이 초기화되도록 `popUpTo(0)` 옵션을 추가했습니다. - 홈 화면의 '친구 추가' 메뉴 아이템에 `onAddContactClick` 핸들러를 연결했습니다.
- `NearBasicDialog`와 `NearOutlinedDialog` 두 가지 스타일의 공용 다이얼로그 컴포저블을 추가했습니다. - 각 다이얼로그는 제목, 본문, 확인/취소 버튼 텍스트 및 클릭 이벤트를 커스텀할 수 있도록 구현되었습니다.
- 연락처 접근 권한이 거부되었을 때 사용자에게 권한 설정 페이지로 이동을 안내하는 다이얼로그를 표시하도록 기능을 추가했습니다.
Summary of ChangesHello @stopstone, 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은 사용자가 기기 연락처를 앱으로 가져와 친구로 등록하고, 각 친구에 대한 연락 알림 주기를 설정할 수 있는 핵심 기능을 도입합니다. 이 기능은 연락처 불러오기, 주기 설정의 두 단계로 구성된 단일 화면 흐름을 통해 제공되며, 연락처 권한 처리와 서버 연동까지 포함합니다. 특히, 연락처 처리 과정에서 발생할 수 있는 UI 멈춤 현상을 방지하기 위해 성능 최적화 작업도 함께 진행되었습니다. Highlights
Using Gemini Code AssistThe 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 by creating a comment using either
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 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
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
이 PR은 연락처를 불러오고 주기 설정을 하는 새로운 기능을 추가합니다. 전반적으로 코드 구조가 잘 잡혀있고, 상태 관리 및 UI 로직이 명확하게 분리되어 있습니다. 특히 ContactViewModel에서 Dispatchers.IO를 사용하여 무거운 연산을 백그라운드 스레드로 옮긴 점은 좋은 개선입니다.
다만, 몇 가지 잠재적인 런타임 오류(크래시) 가능성과 개선점을 발견했습니다. 매퍼 함수에서 안전하지 않은 데이터 변환, ViewModel에서의 이벤트 처리 방식, 하드코딩된 문자열 사용 등에 대한 피드백을 포함했습니다. 이러한 부분들을 수정하면 코드의 안정성과 유지보수성이 더욱 향상될 것입니다.
| private fun String.formatPhoneNumber(): String { | ||
| // 1. 불필요한 문자들 제거 | ||
| val cleaned = | ||
| this | ||
| .replace("//", "") // // 제거 | ||
| .replace("-", "") // 하이픈 제거 (하이픈이 없는 경우 위치값으로 넣어줘야 하기때문에 우선 제거) | ||
| .replace(" ", "") // 공백 제거 | ||
| .trim() | ||
|
|
||
| // 2. 000-0000-0000 형태로 포맷팅 | ||
| return "${cleaned.substring(0, 3)}-${cleaned.substring(3, 7)}-${cleaned.substring(7)}" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleaned 문자열의 길이가 충분하지 않을 경우 substring 메서드에서 StringIndexOutOfBoundsException이 발생하여 앱이 비정상 종료될 수 있습니다. 함수 주석에 '010-0000-0000' 형태로 포맷팅한다고 명시되어 있으므로, 11자리 휴대폰 번호 형식일 때만 포맷팅을 수행하고 그 외의 경우에는 정리된 문자열을 그대로 반환하도록 하여 안정성을 높이는 것을 권장합니다.
private fun String.formatPhoneNumber(): String {
// 1. 불필요한 문자들 제거
val cleaned =
this
.replace("//", "") // // 제거
.replace("-", "") // 하이픈 제거 (하이픈이 없는 경우 위치값으로 넣어줘야 하기때문에 우선 제거)
.replace(" ", "") // 공백 제거
.trim()
// 2. 11자리 휴대폰 번호인 경우에만 010-0000-0000 형태로 포맷팅
if (cleaned.length == 11) {
return "${cleaned.substring(0, 3)}-${cleaned.substring(3, 7)}-${cleaned.substring(7)}"
}
// 그 외의 경우는 정리된 문자열을 그대로 반환
return cleaned
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
libphonenumber-android 라이브러리로 전화번호 포매팅 진행하였습니다
| fun FriendContactUIModel.toFriendInitItemRequest(providerType: String): FriendInitItemRequest = | ||
| FriendInitItemRequest( | ||
| name = name, | ||
| phone = phones.first().formatPhoneNumber(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| fun ContactFrequencyInitEntity.toModel(): ContactFrequency = | ||
| ContactFrequency( | ||
| reminderInterval = ReminderInterval.valueOf(contactWeek), | ||
| dayOfWeek = DayOfWeek.valueOf(dayOfWeek), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버에서 예상치 못한 contactWeek 또는 dayOfWeek 문자열을 보낼 경우, valueOf() 함수는 IllegalArgumentException을 발생시켜 앱을 비정상 종료시킬 수 있습니다. runCatching을 사용하여 예외를 처리하고 기본값을 제공하는 방식으로 안정성을 높이는 것을 권장합니다.
fun ContactFrequencyInitEntity.toModel(): ContactFrequency =
ContactFrequency(
reminderInterval = runCatching { ReminderInterval.valueOf(contactWeek) }.getOrDefault(ReminderInterval.EVERY_WEEK),
dayOfWeek = runCatching { DayOfWeek.valueOf(dayOfWeek) }.getOrDefault(DayOfWeek.MONDAY),
)
Near/app/src/main/java/com/alarmy/near/data/mapper/FriendInitMapper.kt
Outdated
Show resolved
Hide resolved
| ReminderInterval.EVERY_SIX_MONTH -> { | ||
| val calendar = Calendar.getInstance() | ||
| val month = calendar.get(Calendar.MONTH) + 1 // Calendar의 월은 0부터 시작 | ||
| val day = calendar.get(Calendar.DAY_OF_MONTH) | ||
| val nextSixMonth = if (month <= 6) 10 else 4 | ||
| "매년 $month/$day, $nextSixMonth/$day" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EVERY_SIX_MONTH의 다음 주기를 계산하는 로직이 잘못되었습니다. val nextSixMonth = if (month <= 6) 10 else 4는 현재 월로부터 6개월 뒤의 월을 정확히 계산하지 않습니다. 예를 들어, 현재가 1월이면 10월을, 7월이면 4월을 반환하는데 이는 6개월 간격이 아닙니다. 이로 인해 사용자에게 잘못된 날짜 정보가 표시될 수 있습니다.
ReminderInterval.EVERY_SIX_MONTH -> {
val calendar = Calendar.getInstance()
val month = calendar.get(Calendar.MONTH) + 1 // Calendar의 월은 0부터 시작
val day = calendar.get(Calendar.DAY_OF_MONTH)
val nextSixMonthCalendar = (calendar.clone() as Calendar).apply { add(Calendar.MONTH, 6) }
val nextSixMonth = nextSixMonthCalendar.get(Calendar.MONTH) + 1
"매년 $month/$day, $nextSixMonth/$day"
}| LaunchedEffect(viewModel.uiEvent) { | ||
| viewModel.uiEvent.collect { event -> | ||
| when (event) { | ||
| is FriendContactUIEvent.MoveToNextStep -> viewModel.moveToNextStep() | ||
| is FriendContactUIEvent.MoveToPreviousStep -> viewModel.moveToPreviousStep() | ||
| is FriendContactUIEvent.AddSelectedContacts -> viewModel.addSelectedContacts(event.contacts) | ||
| is FriendContactUIEvent.DeselectContact -> viewModel.deselectContact(event.contactId) | ||
| is FriendContactUIEvent.ToggleBulkSetting -> viewModel.toggleBulkSetting() | ||
| is FriendContactUIEvent.OpenBottomSheet -> viewModel.openBottomSheet() | ||
| is FriendContactUIEvent.OpenIndividualBottomSheet -> | ||
| viewModel.openIndividualBottomSheet( | ||
| event.contactId, | ||
| ) | ||
|
|
||
| is FriendContactUIEvent.CloseBottomSheet -> viewModel.closeBottomSheet() | ||
| is FriendContactUIEvent.CompleteCycleSetting -> viewModel.completeCycleSetting(event.reminderInterval) | ||
| is FriendContactUIEvent.CompleteFriendInit -> viewModel.completeFriendInit() | ||
| is FriendContactUIEvent.NavigateToHome -> onNavigateToHome() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
현재 UI 이벤트 처리 방식이 다소 복잡합니다. Composable에서 viewModel.onEvent(...)를 호출하여 이벤트를 보내고, LaunchedEffect에서 이 이벤트를 수집하여 다시 ViewModel의 다른 함수(moveToNextStep 등)를 호출하는 구조입니다. 이로 인해 코드 흐름을 파악하기 어렵고 불필요한 복잡성이 추가됩니다.
Composable에서 직접 ViewModel의 함수(moveToNextStep 등)를 호출하도록 단순화하는 것을 권장합니다. uiEvent 채널은 화면 이동과 같은 UI 레이어에서 처리해야 하는 단발성 이벤트(Side Effect)에만 사용하는 것이 좋습니다.
| _uiState.value = | ||
| _uiState.value.copy( | ||
| isLoading = false, | ||
| error = e.message ?: "친구 초기 설정에 실패했습니다.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| .first() | ||
| .providerType | ||
| }.getOrElse { exception -> | ||
| "KAKAO" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| navOptions { | ||
| popUpTo(RouteLogin) { inclusive = true } | ||
| popUpTo(0) { inclusive = false } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
popUpTo(0)은 가독성이 떨어지고 내비게이션 그래프의 시작점이 변경될 경우 예기치 않은 동작을 유발할 수 있습니다. navController.graph.findStartDestination().id를 사용하여 그래프의 시작 목적지를 명시적으로 참조하고, inclusive = true로 설정하여 로그인 과정을 백스택에서 완전히 제거하는 것이 더 안전하고 명확합니다.
| navOptions { | |
| popUpTo(RouteLogin) { inclusive = true } | |
| popUpTo(0) { inclusive = false } | |
| }, | |
| navOptions { | |
| popUpTo(navController.graph.findStartDestination().id) { inclusive = true } | |
| }, |
| return when (dayOfWeek) { | ||
| Calendar.SUNDAY -> "SUNDAY" | ||
| Calendar.MONDAY -> "MONDAY" | ||
| Calendar.TUESDAY -> "TUESDAY" | ||
| Calendar.WEDNESDAY -> "WEDNESDAY" | ||
| Calendar.THURSDAY -> "THURSDAY" | ||
| Calendar.FRIDAY -> "FRIDAY" | ||
| Calendar.SATURDAY -> "SATURDAY" | ||
| else -> "MONDAY" // 기본값 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when 표현식의 else 블록에서 "MONDAY"를 기본값으로 반환하고 있습니다. Calendar.get(Calendar.DAY_OF_WEEK)가 예상 범위를 벗어나는 경우는 거의 없지만, 만약 그런 일이 발생한다면 버그를 찾기 어려워질 수 있습니다. else 블록에서는 예외를 던져서 예상치 못한 상황을 명확히 인지할 수 있도록 하는 것이 더 안전한 방법입니다.
return when (dayOfWeek) {
Calendar.SUNDAY -> "SUNDAY"
Calendar.MONDAY -> "MONDAY"
Calendar.TUESDAY -> "TUESDAY"
Calendar.WEDNESDAY -> "WEDNESDAY"
Calendar.THURSDAY -> "THURSDAY"
Calendar.FRIDAY -> "FRIDAY"
Calendar.SATURDAY -> "SATURDAY"
else -> throw IllegalStateException("Invalid day of week: $dayOfWeek")
}| * - 01012341234로 하이픈이 없는 경우 | ||
| * 최종적으로 010-0000-0000 형태로 변환합니다. | ||
| */ | ||
| private fun String.formatPhoneNumber(): String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stopstone // 혹시 02, 031같은 010으로 시작하지 않는 번호로 들어올 땐 어떻게 되나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
휴대전화 형식으로만 변환하게 두었습니다..!
지역번호 같은 경우 000-0000-0000, 000-000-0000와 같이 형식이 다양해서 어떻게 수정을 해야할지 좀 더 고민을 해봐야 할 것 같습니다
작업하면서 제 연락처에 있는 케이스 (//010~, 하이픈X, 하이픈O)만 우선 처리하였습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stopstone // libphonenumber을 쓰면 안될까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slg1119 앗 감사합니다 한번 시도해보겠습니다 될 거 같아요!
android에서 사용하려면 공식 포트는 아니라서 verySlow 라고 되어있긴 한데 큰 문제는 없을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stopstone // 감사합니다 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slg1119 유용한 라이브러리 추천 감사합니다 :)
| onGranted: () -> Unit, | ||
| onDenied: @Composable (onRequestPermission: () -> Unit) -> Unit, | ||
| onShowRationale: @Composable (onRequestPermission: () -> Unit) -> Unit = onDenied, | ||
| onPermissionDenied: (() -> Unit)? = null, // 권한 거부 시 콜백 추가 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
세부적인 처리 주셨네요👍 onPermissionDenied람다를 nullable로 설정한 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
권한 허용과 거부를 null을 줘서 처리하려는 의도였습니다..!
다시 생각해보니 isGrant에서 이미 처리를 주고 있어서.. nulllable이 아니라 빈 람다를 넘겨주는 형태가 나을 것 같습니다
말씀 감사합니다 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 저도 빈 람다가 좋을 것 같네요 확인 감사합니다!
| val nextSixMonth = if (month <= 6) 10 else 4 | ||
| "매년 $month/$day, $nextSixMonth/$day" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요쪽에 로직 설명 부탁드려도 될까요? 잼미니 코멘트 중심으로요!
매직넘버가 많아지는 것 같아서, 부분적으로 상수처리 주셔도 좋을 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 감사합니다..!
해당 부분은 확인해보니 아주 심각한.. 버그성 코드입니다 ㅠㅠ
제시받은 코드로 작성하는 것이 6개월 간격을 조정할 때 적절한 것 같아 수정하였습니다!
- 기존의 문자열 기반 전화번호 형식 변환 로직을 `libphonenumber-android` 라이브러리를 사용하도록 개선했습니다. - 다양한 형식의 전화번호(휴대폰, 지역번호 포함)를 일관된 국가 형식(e.g., 010-1234-5678, 02-123-4567)으로 변환합니다. - `PhoneNumberFormatter` 유틸리티 객체를 추가하고 Application 클래스에서 초기화하여 앱 전역에서 사용하도록 구현했습니다.
- `Date.toRepeatDateString()` 확장 함수에서 6개월 후의 월을 계산하는 로직을 `Calendar.add()`를 사용하도록 변경하여 정확도를 개선했습니다.
- ContactPermissionRequester에서 권한이 이미 허용된 경우 자동으로 onGranted() 호출하지 않도록 수정 - FriendContactCycleScreen에서 ContactScreen으로의 무한 전환 문제 해결 - 사용자가 명시적으로 권한을 요청했을 때만 ContactScreen으로 이동하도록 개선
- `FriendContactCycleScreen`에서 `FriendContactUIEvent`를 생성하여 `ViewModel`에 전달하던 방식에서, 각 이벤트에 해당하는 `ViewModel`의 함수를 직접 호출하도록 변경했습니다. - 이 리팩토링으로 인해 더 이상 사용되지 않는 `FriendContactUIEvent`의 대부분의 이벤트를 제거했습니다. - ViewModel의 `onEvent` 래퍼 함수를 삭제하고, 각 UI 이벤트에 해당하는 함수들을 직접 호출하도록 수정했습니다.
| viewModel.onEvent(FriendContactUIEvent.CompleteCycleSetting(reminderInterval)) | ||
| }, | ||
| onCompleteFriendInit = { viewModel.onEvent(FriendContactUIEvent.CompleteFriendInit) }, | ||
| onMoveToNextStep = { viewModel.moveToNextStep() }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onMoveToNextStep = viewMode::moveToNextStep()
이런 방식으로 참조 활용하면 조금 깔끔해질 수 있을 것 같습니다!
반영은 안해주셔도 돼요!
작업 내용
연락처 불러오기를 클릭하면 연락처 권한 허용 유무를 확인합니다.확인 방법
를 확인해주시면 감사드리겠습니다!
참고 사항
ContactScreen으로 navigation되는 과정에서 화면 멈춤이 발생하였습니다.
연락처를 불러오는 연산처리 과정에서 이슈가 있었습니다. (UI 스레드에서 작업하여 로딩상태 화면도 반영 안되는 현상)
Commit ca6f100
해당 커밋의 ContactViewModel에서 Dispachers를 백그라운드로 주었습니다!
Default로 주었다가 이후IO로 변경하였습니다.