Skip to content

Conversation

@SeoHyun1024
Copy link
Collaborator

@SeoHyun1024 SeoHyun1024 commented Dec 19, 2025

#378

Summary by CodeRabbit

  • 새 기능

    • 찜한 상품 화면 추가(무한스크롤, 상세 이동, 리스트 내 좋아요 토글)
    • 프로필 이미지 업로드·닉네임 편집 및 프로필 저장 API 연동
  • 디자인

    • 아이콘 세트 갱신(카메라·편집·프로필 좋아요 등 추가/교체)
    • 범위 슬라이더 시각 개선 및 햅틱 피드백 도입
    • 기본 프로필 아바타 처리 및 마이페이지 UI 개선, 앱바 하단 경계 옵션 추가
  • 개선

    • 위치 인증 흐름 단순화 및 주소 로딩 보강
    • 네비게이션이 결과를 반환하도록 개선(페이지 이동에서 결과 대기 가능)
    • 사용자 모델에 좋아요 수·검색 반경 반영으로 설정/표시 동기화

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 19, 2025

Walkthrough

아이콘 상수명 변경·추가, Member 모델 필드 추가 및 (de)직렬화 반영, navigateTo 반환형 변경, RangeSlider에 햅틱·렌더링 재작성, 마이페이지 대규모 UI/로직 변경(좋아요 목록·프로필 편집·위치 인증 등)과 관련 API 확장 등이 포함된 변경입니다.

Changes

Cohort / File(s) Summary
아이콘 정의 및 구성
lib/icons/app_icons.dart, lib/icons/config.json
arrow_upwardarrowUpward로 상수명 변경; camera, edit, profilelikecount IconData 추가; romromRLogoText 삭제; config.json 아이콘 UID/css/code/path/search 재배치·추가·삭제
멤버 모델 및 직렬화
lib/models/apis/objects/member.dart, lib/models/apis/objects/member.g.dart
totalLikeCount(int?) 및 searchRadiusInMeters(double?) 필드 추가, 생성자 및 JSON (de)직렬화 반영
API 변경/추가
lib/services/apis/item_api.dart, lib/services/apis/member_api.dart
ItemApi.getLikeList(ItemRequest) 추가(POST /api/item/like/get); MemberApi.updateMemberProfile(String nickname, String profileUrl) 추가(프로필 multipart 업데이트)
네비게이션 유틸리티
lib/utils/common_utils.dart
navigateTo가 제네릭 Future<T?> 반환으로 변경되어 라우트 결과를 await 가능하도록 수정; 내부 createRoute 시그니처 정리
마이페이지·새 화면 및 수정
lib/screens/my_page/my_like_list_screen.dart, lib/screens/my_page/my_profile_edit_screen.dart, lib/screens/my_page/my_location_verification_screen.dart, lib/screens/my_page/my_category_settings_screen.dart, lib/screens/my_page_tab_screen.dart
MyLikeListScreen 추가(페이지네이션, 비동기 지오코딩, 상세에서 반환 처리); 프로필 편집에 이미지 픽커·업로드·저장 흐름 추가; 위치 인증 화면 리팩터링; 마이페이지 네비게이션·레이아웃·메뉴 재구성
UI 컴포넌트·위젯 개선
lib/screens/chat_room_screen.dart, lib/screens/item_detail_description_screen.dart, lib/screens/search_range_setting_screen.dart, lib/widgets/common_app_bar.dart, lib/widgets/common/range_slider_widget.dart, lib/widgets/user_profile_circular_avatar.dart
AppIcons 명칭 반영, 아이템 상세에서 조건부 pop 반환, CommonAppBar에 showBottomBorder 파라미터 추가, RangeSlider에 햅틱·커스텀 페인터·레이아웃 재작성, 사용자 아바타에 로컬 SVG 기본 폴백 추가
기타 사소한 수정
lib/screens/chat_room_screen.dart, 기타 스타일/포맷팅 파일
AppIcons 호출명 변경 반영 및 일부 포맷팅/스타일 조정

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MyLikeListScreen
    participant ItemApi
    participant Server
    participant ItemDetailDescriptionScreen

    User->>MyLikeListScreen: 화면 진입
    MyLikeListScreen->>ItemApi: getLikeList(pageNumber, pageSize)
    ItemApi->>Server: POST /api/item/like/get
    Server-->>ItemApi: ItemResponse
    ItemApi-->>MyLikeListScreen: ItemResponse
    MyLikeListScreen->>MyLikeListScreen: Item -> _LikedItem 변환
    MyLikeListScreen->>MyLikeListScreen: 비동기 지오코딩(좌표 → 주소)
    MyLikeListScreen->>User: 목록 렌더링

    User->>MyLikeListScreen: 항목 탭 (await 네비게이트)
    MyLikeListScreen->>ItemDetailDescriptionScreen: 네비게이트 (await)
    ItemDetailDescriptionScreen->>User: 상세 표시
    User->>ItemDetailDescriptionScreen: 뒤로가기 (조건부로 itemId 반환)
    ItemDetailDescriptionScreen-->>MyLikeListScreen: itemId (nullable) 반환
    MyLikeListScreen->>MyLikeListScreen: 반환값으로 목록 갱신(있으면 제거)
Loading
sequenceDiagram
    participant User
    participant MyProfileEditScreen
    participant ImagePicker
    participant ImageApi
    participant MemberApi
    participant Server

    User->>MyProfileEditScreen: 진입
    MyProfileEditScreen->>MemberApi: GET /api/members/profile
    MemberApi->>Server: 요청
    Server-->>MemberApi: 사용자 데이터
    MemberApi-->>MyProfileEditScreen: 프로필 반영

    User->>MyProfileEditScreen: 이미지 선택
    MyProfileEditScreen->>ImagePicker: 이미지 선택 요청
    ImagePicker-->>MyProfileEditScreen: XFile 반환

    alt 이미지 선택됨
        MyProfileEditScreen->>ImageApi: 이미지 업로드
        ImageApi->>Server: POST /api/upload/image
        Server-->>ImageApi: imageUrl 반환
        ImageApi-->>MyProfileEditScreen: imageUrl 수신
    end

    User->>MyProfileEditScreen: 저장 클릭
    MyProfileEditScreen->>MemberApi: updateMemberProfile(nickname, imageUrl)
    MemberApi->>Server: multipart POST /api/members/profile/update
    Server-->>MemberApi: 성공 응답
    MemberApi-->>MyProfileEditScreen: 완료
    MyProfileEditScreen->>User: 성공 스낵바 및 화면 닫기
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • 집중 검토 권장 영역:
    • lib/screens/my_page/my_like_list_screen.dart — 페이지네이션 로직, 비동기 지오코딩, 항목 변환과 상태 동기화
    • lib/screens/my_page/my_profile_edit_screen.dart — 이미지 픽킹·업로드, multipart 요청과 에러/로드 상태 처리, 언로드 확인 흐름
    • lib/widgets/common/range_slider_widget.dart — 햅틱 트리거 경계 조건과 CustomPainter 수식/성능
    • lib/utils/common_utils.dart — navigateTo 반환형 변경에 따른 호출부 영향(await 사용 사례)
    • lib/models/apis/objects/member.* — 생성자·(de)serialization 일관성 및 null 처리

Possibly related PRs

Suggested reviewers

  • Cassiiopeia

Poem

🐰 깡충깡충 아이콘 바꾸고,
좋아요 수를 하나 더해요.
햅틱 톡톡, 지도 휙휙,
토끼가 코드를 밟으며 외쳐요 — 배포 축하해요! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 마이페이지 피그마 명세 수정과 관련된 실제 변경사항을 부분적으로 반영하고 있으나, 전체 변경사항의 핵심을 정확히 요약하지 못함.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20251202_#378_기능개선_마이페이지_피그마_명세대로_수정

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

❌ 프로젝트 빌드 실패

실패한 단계: 알 수 없는 단계

빌드 로그를 확인하여 문제를 해결해주세요.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (12)
lib/widgets/user_profile_circular_avatar.dart (1)

103-111: AppColors를 사용해주세요.

코딩 가이드라인에 따라 Colors.grey[300]Colors.grey[700] 대신 AppColors에 정의된 색상 상수를 사용해야 합니다.

🔎 제안하는 수정
   Widget _buildDefaultImage() {
     return Container(
-      color: Colors.grey[300],
+      color: AppColors.opacity10White, // 또는 적절한 AppColors 상수
       child: Icon(
         Icons.person,
         size: 40,
-        color: Colors.grey[700],
+        color: AppColors.opacity60White, // 또는 적절한 AppColors 상수
       ),
     );
   }
lib/services/apis/member_api.dart (1)

143-157: 반환 타입 일관성 및 불필요한 .toString() 호출

두 가지 개선 사항이 있습니다:

  1. 일관성 없는 반환 타입: 다른 유사한 메서드들(saveTermsAgreement, deleteMember, saveSearchRadius)은 성공 여부를 bool로 반환하지만, 이 메서드는 void를 반환합니다. 호출자가 성공/실패를 알 수 없습니다.

  2. 불필요한 .toString() 호출: nicknameprofileUrl은 이미 String 타입이므로 .toString() 호출이 불필요합니다.

🔎 제안하는 수정
-  Future<void> updateMemberProfile(String nickname, String profileUrl) async {
+  Future<bool> updateMemberProfile(String nickname, String profileUrl) async {
     const String url = '${AppUrls.baseUrl}/api/members/profile/update';
+    bool isSuccess = false;

     await ApiClient.sendMultipartRequest(
       url: url,
       fields: {
-        'nickname': nickname.toString(),
-        'profileUrl': profileUrl.toString(),
+        'nickname': nickname,
+        'profileUrl': profileUrl,
       },
       isAuthRequired: true,
       onSuccess: (_) {
         debugPrint('회원 프로필 변경 성공: $nickname');
+        isSuccess = true;
       },
     );
+    return isSuccess;
   }
lib/icons/app_icons.dart (1)

22-22: 네이밍 일관성: profilelikecountprofileLikeCount

다른 아이콘들(arrowUpward, addItemPlus, itemRegisterHeart)은 camelCase를 사용하고 있으므로, profilelikecountprofileLikeCount로 변경하는 것이 일관성 있습니다.

🔎 제안하는 수정
-  static const IconData profilelikecount = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
+  static const IconData profileLikeCount = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);
lib/screens/my_page/my_like_list_screen.dart (3)

106-158: 순차 geocoding 호출로 인한 성능 저하 가능성

_convertToLikeItems에서 각 아이템마다 순차적으로 getAddressFromCoordinates를 호출합니다. 10개 아이템 기준, 네트워크 지연이 누적되어 사용자 경험이 저하될 수 있습니다.

개선 방안:

  1. Future.wait로 병렬 처리
  2. 위치 정보를 서버에서 미리 변환하여 전달
  3. 위치 표시를 지연 로딩으로 처리
🔎 병렬 처리 예시
Future<List<_LikedItem>> _convertToLikeItems(List<Item> details) async {
  return Future.wait(details.asMap().entries.map((entry) async {
    final index = entry.key;
    final d = entry.value;
    // ... 변환 로직
  }));
}

113-128: 빈 catch 블록은 디버깅을 어렵게 합니다.

catch (_) {}는 예외를 무시하므로 예상치 못한 문제 발생 시 원인 파악이 어렵습니다. 최소한 디버그 로그를 추가하는 것을 권장합니다.

🔎 제안하는 수정
       try {
         cond = item_condtion_enum.ItemCondition.values.firstWhere(
           (e) => e.serverName == d.itemCondition,
         );
-      } catch (_) {}
+      } catch (e) {
+        debugPrint('ItemCondition 매핑 실패: ${d.itemCondition}, $e');
+      }

204-223: context.navigateTo() 사용 권장

코딩 가이드라인에 따라 iOS 스와이프 제스처 지원을 위해 Navigator.push 대신 context.navigateTo()를 사용해야 합니다.

🔎 제안하는 수정
-                      final result = await Navigator.push<dynamic>(
-                        context,
-                        MaterialPageRoute(
-                          builder: (_) => ItemDetailDescriptionScreen(
-                            itemId: itemId,
-                            imageSize: Size(
-                              MediaQuery.of(context).size.width,
-                              400.h,
-                            ),
-                            currentImageIndex: 0,
-                            heroTag: 'itemImage_${itemId}_0',
-                            isMyItem: false,
-                            isRequestManagement: false,
-                          ),
-                        ),
-                      );
+                      final result = await context.navigateTo<dynamic>(
+                        ItemDetailDescriptionScreen(
+                          itemId: itemId,
+                          imageSize: Size(
+                            MediaQuery.of(context).size.width,
+                            400.h,
+                          ),
+                          currentImageIndex: 0,
+                          heroTag: 'itemImage_${itemId}_0',
+                          isMyItem: false,
+                          isRequestManagement: false,
+                        ),
+                      );
lib/screens/my_page/my_location_verification_screen.dart (3)

26-30: 변수명 오타 수정이 필요합니다.

currentAdresscurrentAddress로, eupMyoenDongeupMyeonDong으로 수정해야 합니다. 오타가 코드베이스 전반에 일관되게 사용되고 있는지 확인이 필요합니다.

🔎 제안된 수정
-  String currentAdress = '';
+  String currentAddress = '';
   String siDo = '';
   String siGunGu = '';
-  String eupMyoenDong = '';
+  String eupMyeonDong = '';
   String? ri;

209-228: 중복 래퍼 메서드 제거를 고려해주세요.

getAddressByNaverApi는 단순히 _loadAddressInfo를 호출하는 래퍼입니다. 하나의 메서드로 통합하거나, 래퍼가 필요한 이유가 있다면 주석으로 명시하는 것이 좋습니다.

🔎 제안된 수정
-  // 주소 정보 로드 메서드
-  Future<void> _loadAddressInfo(NLatLng position) async {
+  // NaverMap에서 주소 가져오는 메서드
+  Future<void> getAddressByNaverApi(NLatLng position) async {
     final addressInfo = await _locationService.getAddressFromCoordinates(
       position,
     );

     if (addressInfo != null) {
       setState(() {
         siDo = addressInfo.siDo;
         siGunGu = addressInfo.siGunGu;
-        eupMyoenDong = addressInfo.eupMyoenDong;
+        eupMyeonDong = addressInfo.eupMyeonDong;
         ri = addressInfo.ri;
-        currentAdress = addressInfo.currentAddress;
+        currentAddress = addressInfo.currentAddress;
       });
     }
   }
-
-  // NaverMap에서 주소 가져오는 메서드를 새 메서드로 호출
-  Future<void> getAddressByNaverApi(NLatLng position) async {
-    await _loadAddressInfo(position);
-  }
 }

54-55: Expanded flex 값에 대한 의도 명확화가 필요합니다.

flex: 350flex: 370은 일반적이지 않은 값입니다. 의도가 명확하다면 주석을 추가하거나, 비율로 단순화하는 것을 고려해주세요 (예: 35:37 → 7:7.4 또는 정수 비율).

Also applies to: 103-104

lib/screens/my_page/my_profile_edit_screen.dart (2)

173-196: async/await 패턴 사용을 권장합니다.

.then().catchError() 체인 대신 async/awaittry/catch를 사용하면 가독성이 향상됩니다.

🔎 제안된 수정
-                onTap: () async {
-                  if (_isProfileEdited && (_nickname.isNotEmpty)) {
-                    await MemberApi()
-                        .updateMemberProfile(nicknameController.text, imageUrl)
-                        .then((_) {
-                          if (context.mounted) {
-                            CommonSnackBar.show(
-                              context: context,
-                              message: '프로필이 성공적으로 업데이트되었습니다.',
-                              type: SnackBarType.success,
-                            );
-                            Navigator.of(context).pop(true);
-                          }
-                        })
-                        .catchError((e) {
-                          if (context.mounted) {
-                            CommonSnackBar.show(
-                              context: context,
-                              message: '프로필 업데이트에 실패했습니다: $e',
-                              type: SnackBarType.error,
-                            );
-                          }
-                        });
-                  }
-                },
+                onTap: () async {
+                  if (_isProfileEdited && _nickname.isNotEmpty) {
+                    try {
+                      await MemberApi().updateMemberProfile(
+                        nicknameController.text,
+                        imageUrl,
+                      );
+                      if (context.mounted) {
+                        CommonSnackBar.show(
+                          context: context,
+                          message: '프로필이 성공적으로 업데이트되었습니다.',
+                          type: SnackBarType.success,
+                        );
+                        Navigator.of(context).pop(true);
+                      }
+                    } catch (e) {
+                      if (context.mounted) {
+                        CommonSnackBar.show(
+                          context: context,
+                          message: '프로필 업데이트에 실패했습니다: $e',
+                          type: SnackBarType.error,
+                        );
+                      }
+                    }
+                  }
+                },

362-362: EdgeInsets에 ScreenUtil 스케일링이 누락되었습니다.

다른 곳에서는 .w.h 확장을 사용하는데, 여기서는 const EdgeInsets.all(8)을 사용합니다. 일관성을 위해 EdgeInsets.all(8.w)를 사용하는 것이 좋습니다.

🔎 제안된 수정
-                            contentPadding: const EdgeInsets.all(8),
+                            contentPadding: EdgeInsets.all(8.w),
lib/screens/my_page_tab_screen.dart (1)

242-251: Wrap 대신 Column 사용 검토

현재 코드의 각 메뉴 항목은 고정 높이(54.h)의 전체 너비 아이템으로 세로로 배열됩니다. WraprunSpacing은 줄 바꿈 시 행 간의 수직 간격을 위한 속성이므로, 의미론적으로 Columnspacing: 8.w를 사용하는 것이 더 적합합니다.

참고: 현재 구현은 모든 코딩 가이드라인(CustomTextStyles, AppColors, context.navigateTo)을 올바르게 따르고 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e324f8 and ebae7a5.

⛔ Files ignored due to path filters (5)
  • assets/fonts/AppIcons.ttf is excluded by !**/*.ttf
  • assets/images/camera.svg is excluded by !**/*.svg
  • assets/images/editPen.svg is excluded by !**/*.svg
  • ios/Podfile.lock is excluded by !**/*.lock
  • macos/Podfile.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • lib/icons/app_icons.dart (1 hunks)
  • lib/icons/config.json (3 hunks)
  • lib/models/apis/objects/member.dart (2 hunks)
  • lib/models/apis/objects/member.g.dart (2 hunks)
  • lib/screens/chat_room_screen.dart (1 hunks)
  • lib/screens/item_detail_description_screen.dart (7 hunks)
  • lib/screens/my_page/my_category_settings_screen.dart (5 hunks)
  • lib/screens/my_page/my_like_list_screen.dart (1 hunks)
  • lib/screens/my_page/my_location_verification_screen.dart (1 hunks)
  • lib/screens/my_page/my_profile_edit_screen.dart (7 hunks)
  • lib/screens/my_page_tab_screen.dart (13 hunks)
  • lib/screens/search_range_setting_screen.dart (10 hunks)
  • lib/services/apis/item_api.dart (1 hunks)
  • lib/services/apis/member_api.dart (4 hunks)
  • lib/utils/common_utils.dart (3 hunks)
  • lib/widgets/common/range_slider_widget.dart (10 hunks)
  • lib/widgets/user_profile_circular_avatar.dart (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension method context.navigateTo() for screen navigation to support iOS swipe gesture handling

Files:

  • lib/models/apis/objects/member.dart
  • lib/services/apis/item_api.dart
  • lib/icons/app_icons.dart
  • lib/screens/my_page/my_like_list_screen.dart
  • lib/models/apis/objects/member.g.dart
  • lib/screens/item_detail_description_screen.dart
  • lib/screens/my_page/my_location_verification_screen.dart
  • lib/widgets/user_profile_circular_avatar.dart
  • lib/screens/my_page/my_profile_edit_screen.dart
  • lib/widgets/common/range_slider_widget.dart
  • lib/screens/chat_room_screen.dart
  • lib/screens/search_range_setting_screen.dart
  • lib/screens/my_page/my_category_settings_screen.dart
  • lib/services/apis/member_api.dart
  • lib/screens/my_page_tab_screen.dart
  • lib/utils/common_utils.dart
**/lib/widgets/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use unnecessary _widget suffix in widget filenames

Files:

  • lib/widgets/user_profile_circular_avatar.dart
  • lib/widgets/common/range_slider_widget.dart
🧠 Learnings (5)
📓 Common learnings
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
📚 Learning: 2025-06-25T01:35:06.847Z
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.

Applied to files:

  • lib/screens/my_page/my_like_list_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use the team's common extension method `context.navigateTo()` for screen navigation to support iOS swipe gesture handling

Applied to files:

  • lib/screens/my_page/my_location_verification_screen.dart
  • lib/widgets/common/range_slider_widget.dart
  • lib/screens/my_page_tab_screen.dart
  • lib/utils/common_utils.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use AppColors for all color values - never use direct Color codes or Colors constants

Applied to files:

  • lib/widgets/common/range_slider_widget.dart
  • lib/screens/my_page_tab_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly

Applied to files:

  • lib/widgets/common/range_slider_widget.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (20)
lib/services/apis/item_api.dart (1)

276-299: LGTM!

새로운 getLikeList 메서드가 기존 API 패턴을 잘 따르고 있습니다. pageNumberpageSize를 사용한 페이지네이션 처리가 다른 목록 조회 API와 일관성 있게 구현되어 있습니다.

lib/screens/item_detail_description_screen.dart (1)

801-808: 좋아요 취소 상태 반환 로직

좋아요가 취소된 상태(isLikedVN.value == false)일 때 itemId를 반환하여 목록 화면에서 해당 아이템을 갱신할 수 있도록 하는 로직이 적절합니다. 다만, 초기에 좋아요가 없던 아이템을 조회만 하고 나가는 경우에도 itemId가 반환될 수 있으므로, 실제로 좋아요 상태가 변경되었는지 추적하는 것이 더 정확할 수 있습니다.

lib/utils/common_utils.dart (2)

14-56: navigateTo의 제네릭 반환 타입 개선

navigateTo가 이제 Future<T?>를 반환하도록 변경되어 호출자가 네비게이션 결과를 받을 수 있게 되었습니다. iOS 스와이프 제스처를 지원하면서도 결과 값을 처리할 수 있어 유용한 개선입니다.


138-164: 시간 유틸리티 함수 개선

isSameMinuteformatMessageTime 함수에서 UTC 변환 및 null 체크가 잘 처리되어 있습니다. 12시간제 변환 로직도 올바르게 구현되었습니다.

lib/screens/search_range_setting_screen.dart (1)

126-151: 위치 초기화 로직 개선

try-catch로 에러 처리를 감싸고, MemberApi에서 저장된 searchRadiusInMeters를 가져와 초기 범위 인덱스를 설정하는 로직이 잘 구현되어 있습니다. 기본값 7500.0도 적절합니다.

lib/screens/chat_room_screen.dart (1)

746-746: LGTM!

아이콘 이름이 AppIcons.arrowUpward로 변경된 것은 PR 전체의 아이콘 네이밍 컨벤션(camelCase) 변경과 일치합니다.

lib/models/apis/objects/member.dart (1)

24-25: LGTM!

totalLikeCountsearchRadiusInMeters 필드가 올바르게 추가되었습니다. nullable 타입 사용이 적절합니다.

Also applies to: 45-46

lib/models/apis/objects/member.g.dart (1)

1-55: 생성된 파일입니다.

이 파일은 build_runner에 의해 자동 생성된 파일이므로 수동 검토가 필요하지 않습니다.

lib/screens/my_page/my_category_settings_screen.dart (1)

83-88: 레이아웃 구조 확인 필요

SizedBox(height: 1.h)는 매우 작은 높이(약 1 논리적 픽셀)이며, Expanded(child: Container())는 빈 공간 확보용으로 보입니다. 의도한 디자인이 맞는지 확인해주세요.

만약 칩 영역과 버튼 사이 공간을 확보하려는 의도라면, Spacer() 위젯을 사용하는 것이 더 명확합니다.

🔎 대안 제안
-                  SizedBox(height: 1.h,),
                   // 카테고리 칩 표시
                   SingleChildScrollView(
                     child: _buildCategoryChips(context),
                   ),
-                  Expanded(child: Container()),
+                  const Spacer(),
lib/screens/my_page/my_like_list_screen.dart (1)

391-396: 좋아요 토글이 로컬 상태만 업데이트합니다.

현재 좋아요 버튼 탭 시 로컬 isLiked 상태만 변경되고 서버 API 호출이 없습니다. 화면을 새로고침하면 변경사항이 사라집니다.

서버와의 동기화가 의도적으로 누락된 것인지 확인해주세요.

lib/screens/my_page/my_location_verification_screen.dart (1)

41-48: LGTM!

AppColors, CustomTextStyles를 올바르게 사용하고 있으며, 뒤로가기 동작은 Navigator.pop을 사용하는 것이 적절합니다.

lib/screens/my_page_tab_screen.dart (3)

96-118: LGTM!

context.navigateTo()를 사용하여 iOS 스와이프 제스처를 지원하는 네비게이션을 올바르게 구현했습니다. 코딩 가이드라인을 잘 따르고 있습니다.


156-165: LGTM!

프로필 편집 화면에서 돌아올 때 결과값을 받아 사용자 정보를 새로고침하는 패턴이 잘 구현되어 있습니다.


182-186: LGTM!

UserProfileCircularAvatar 위젯을 사용하여 프로필 이미지를 표시하고, AppColorsCustomTextStyles를 일관되게 사용하고 있습니다.

lib/widgets/common/range_slider_widget.dart (3)

36-37: LGTM!

햅틱 피드백 구현이 잘 되어 있습니다. _lastHapticIndex를 사용하여 중복 피드백을 방지하고, kIsWeb 체크로 웹 환경에서의 오류를 방지합니다.

Also applies to: 107-113, 120-122, 130-132


303-319: LGTM!

노브(knob)와 그림자 렌더링이 잘 구현되어 있습니다. withValues(alpha: 0.25)는 deprecated된 withOpacity 대신 올바른 최신 API 사용입니다.


323-328: LGTM!

shouldRepaint에서 primaryColorinactiveColor 변경을 체크하도록 확장하여 불필요한 리페인트를 방지합니다.

lib/screens/my_page/my_profile_edit_screen.dart (1)

243-288: LGTM!

프로필 이미지 섹션이 잘 구현되어 있습니다. 로딩 상태 표시, UserProfileCircularAvatar 사용, 카메라 아이콘 오버레이가 적절합니다.

lib/icons/config.json (2)

248-249: LGTM!

arrow_upwardarrowUpward로 변경하여 camelCase 네이밍 컨벤션을 따릅니다.


275-316: LGTM!

새 아이콘들 (camera, edit, profilelikecount)이 일관된 포맷으로 추가되었습니다.

} catch (e) {
setState(() {
_avatarUrl = null;
_avatarUrl = 'assets/images/basicProfile.svg';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

로컬 에셋 경로가 Image.network에서 작동하지 않습니다.

_avatarUrl에 로컬 에셋 경로 ('assets/images/basicProfile.svg')를 할당하지만, 빌드 메서드(라인 91-97)에서는 Image.network로 렌더링합니다. 이는 다음 두 가지 문제를 야기합니다:

  1. Image.network는 HTTP/HTTPS URL만 지원하므로 로컬 에셋 경로는 로드되지 않습니다.
  2. SVG 파일은 Image.network 또는 Image.asset으로 렌더링할 수 없으며, flutter_svg 패키지가 필요합니다.

에러 발생 시 _avatarUrlnull로 설정하여 _buildDefaultImage()가 호출되도록 하거나, SVG 에셋을 별도로 처리하는 로직이 필요합니다.

🔎 제안하는 수정 방안
     } catch (e) {
       setState(() {
-        _avatarUrl = 'assets/images/basicProfile.svg';
+        _avatarUrl = null;
         _isLoading = false;
       });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_avatarUrl = 'assets/images/basicProfile.svg';
} catch (e) {
setState(() {
_avatarUrl = null;
_isLoading = false;
});
}
🤖 Prompt for AI Agents
In lib/widgets/user_profile_circular_avatar.dart around line 63, _avatarUrl is
set to a local SVG asset but the build uses Image.network (lines ~91-97); fix by
distinguishing asset vs network and handling SVGs: if _avatarUrl is a network
URL use Image.network with errorBuilder that sets _avatarUrl = null to trigger
_buildDefaultImage(), otherwise if the path is a local asset and
endsWith('.svg') use flutter_svg's SvgPicture.asset (add import and dependency),
and for non-SVG local assets use Image.asset; ensure fallback paths and error
handling (try/catch or errorBuilder) set _avatarUrl=null so default image is
shown on failure.

@github-actions
Copy link
Contributor

❌ 프로젝트 빌드 실패

실패한 단계: 알 수 없는 단계

빌드 로그를 확인하여 문제를 해결해주세요.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/screens/item_detail_description_screen.dart (1)

824-831: context.navigateTo() 사용으로 iOS 스와이프 제스처 지원 추가

코딩 가이드라인에 따라 context.navigateTo()를 사용해야 합니다. 이 메서드는 iOS에서 CupertinoPageRoute를 사용하여 스와이프 백 제스처를 지원하며, 제네릭 타입 <bool>로 결과값 반환을 완전히 지원합니다.

final bool? reported = await context.navigateTo<bool>(
  screen: ReportScreen(itemId: widget.itemId),
);
🧹 Nitpick comments (7)
lib/screens/item_detail_description_screen.dart (1)

801-808: 불필요한 async 키워드

onTap 콜백이 async로 선언되어 있지만 내부에서 await를 사용하지 않습니다. 불필요한 async는 제거하는 것이 좋습니다.

🔎 수정 제안
-            onTap: () async {
+            onTap: () {
               // 뒤로갈 때 좋아요가 취소된 상태면 목록에 반영되도록 정보 반환
               if (isLikedVN.value == false) {
                 Navigator.of(context).pop(widget.itemId);
               } else {
                 Navigator.of(context).pop();
               }
             },
-
lib/screens/my_page/my_like_list_screen.dart (3)

6-18: 중복 import 정리 필요

ItemCondition이 두 번 import되어 있습니다 (라인 6과 18). alias가 필요한 경우 하나의 import만 유지하세요.

🔎 수정 제안
-import 'package:romrom_fe/enums/item_condition.dart';
 import 'package:romrom_fe/enums/item_status.dart';
 import 'package:romrom_fe/enums/item_trade_option.dart';
 ...
-import 'package:romrom_fe/enums/item_condition.dart' as item_condition_enum;
+import 'package:romrom_fe/enums/item_condition.dart';

그리고 라인 115에서 alias 없이 사용:

-        cond = item_condition_enum.ItemCondition.values.firstWhere(
+        cond = ItemCondition.values.firstWhere(

109-155: 순차적 geocoding으로 인한 성능 저하

각 아이템에 대해 순차적으로 await하여 geocoding을 수행하고 있습니다. 아이템이 많을 경우 로딩 시간이 크게 증가할 수 있습니다.

Future.wait를 사용하여 병렬 처리하거나, geocoding 실패 시에도 다른 아이템에 영향을 주지 않도록 개선을 고려해 주세요.

🔎 병렬 처리 예시
Future<List<_LikedItem>> _convertToLikeItems(List<Item> details) async {
  return Future.wait(details.asMap().entries.map((entry) async {
    final index = entry.key;
    final d = entry.value;
    
    // ... condition/options 매핑 로직 ...
    
    String locationText = '미지정';
    if (d.latitude != null && d.longitude != null) {
      try {
        final address = await LocationService().getAddressFromCoordinates(
          NLatLng(d.latitude!, d.longitude!),
        );
        if (address != null) {
          locationText = '${address.siDo} ${address.siGunGu} ${address.eupMyoenDong}';
        }
      } catch (_) {
        // 개별 실패는 무시
      }
    }
    
    return _LikedItem(/* ... */);
  }));
}

204-223: 코딩 가이드라인: context.navigateTo() 사용 검토

코딩 가이드라인에 따르면 iOS 스와이프 제스처 지원을 위해 context.navigateTo()를 사용해야 합니다. navigateTo가 결과값 반환을 지원하는 경우, Navigator.push 대신 사용하는 것을 검토해 주세요.

lib/icons/app_icons.dart (1)

34-37: 아이콘 추가 확인

새로운 아이콘들이 올바르게 추가되었습니다. 다만 profilelikecount는 다른 아이콘들(arrowUpward, currentLocation 등)과 일관성을 위해 profileLikeCount로 명명하는 것이 더 좋을 수 있습니다.

lib/screens/my_page/my_profile_edit_screen.dart (2)

180-203: async/awaittry/catch 사용 권장

.then().catchError() 패턴 대신 async/awaittry/catch를 사용하면 가독성과 에러 처리가 더 명확해집니다.

또한 _nickname.isNotEmpty 대신 nicknameController.text.isNotEmpty를 사용하면 상태 동기화 문제를 방지할 수 있습니다.

🔎 수정 제안
onTap: () async {
  final nickname = nicknameController.text;
  if (_isProfileEdited && nickname.isNotEmpty) {
    try {
      await MemberApi().updateMemberProfile(nickname, imageUrl);
      if (context.mounted) {
        CommonSnackBar.show(
          context: context,
          message: '프로필이 성공적으로 업데이트되었습니다.',
          type: SnackBarType.success,
        );
        Navigator.of(context).pop(true);
      }
    } catch (e) {
      if (context.mounted) {
        CommonSnackBar.show(
          context: context,
          message: '프로필 업데이트에 실패했습니다: $e',
          type: SnackBarType.error,
        );
      }
    }
  }
},

258-271: 로딩 상태 조건 검토

_hasImageBeenTouched && imageFile != null 조건으로 로딩 스피너를 표시하고 있습니다. 그러나 _hasImageBeenTouched는 이미지 선택 시작 시 true가 되고, imageFile은 선택 완료 후에 설정됩니다.

이미지 업로드 중인 상태를 나타내려면 _isUploading과 같은 명확한 플래그를 사용하는 것이 더 직관적일 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebae7a5 and 125e976.

⛔ Files ignored due to path filters (1)
  • assets/fonts/AppIcons.ttf is excluded by !**/*.ttf
📒 Files selected for processing (7)
  • lib/icons/app_icons.dart (2 hunks)
  • lib/icons/config.json (3 hunks)
  • lib/screens/item_detail_description_screen.dart (7 hunks)
  • lib/screens/my_page/my_like_list_screen.dart (1 hunks)
  • lib/screens/my_page/my_profile_edit_screen.dart (7 hunks)
  • lib/screens/search_range_setting_screen.dart (10 hunks)
  • lib/widgets/user_profile_circular_avatar.dart (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/screens/search_range_setting_screen.dart
🧰 Additional context used
📓 Path-based instructions (2)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension method context.navigateTo() for screen navigation to support iOS swipe gesture handling

Files:

  • lib/screens/my_page/my_like_list_screen.dart
  • lib/screens/my_page/my_profile_edit_screen.dart
  • lib/screens/item_detail_description_screen.dart
  • lib/widgets/user_profile_circular_avatar.dart
  • lib/icons/app_icons.dart
**/lib/widgets/**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use unnecessary _widget suffix in widget filenames

Files:

  • lib/widgets/user_profile_circular_avatar.dart
🧠 Learnings (3)
📓 Common learnings
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
📚 Learning: 2025-06-25T01:35:06.847Z
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.

Applied to files:

  • lib/screens/my_page/my_like_list_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly

Applied to files:

  • lib/screens/item_detail_description_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (11)
lib/screens/item_detail_description_screen.dart (1)

356-366: 이전 리뷰 피드백 반영 확인

TextStyle 직접 사용 문제가 해결되었습니다. 이제 CustomTextStyles.h3를 올바르게 사용하고 있습니다.

lib/screens/my_page/my_like_list_screen.dart (1)

437-457: LGTM!

_LikedItem 클래스가 적절하게 정의되어 있으며, 필요한 모든 필드를 포함하고 있습니다.

lib/widgets/user_profile_circular_avatar.dart (1)

93-103: 이전 리뷰 피드백 반영 확인

이전에 지적된 문제가 해결되었습니다. 이제 _kDefaultProfileAsset을 별도로 처리하여 Image.network로 로컬 에셋을 로드하려는 시도를 방지하고, SVG는 SvgPicture.asset으로 올바르게 렌더링합니다.

lib/screens/my_page/my_profile_edit_screen.dart (2)

45-50: 이전 리뷰 피드백 반영 확인

TextEditingControllerFocusNodedispose()가 올바르게 추가되었습니다. 메모리 누수 문제가 해결되었습니다.


309-431: 닉네임 편집 UI 구현 확인

포커스 상태에 따른 조건부 렌더링과 아이콘 위치 계산이 잘 구현되어 있습니다. _measureTextWidth를 사용하여 동적으로 편집 아이콘 위치를 계산하는 접근 방식이 적절합니다.

lib/icons/config.json (6)

23-36: 새 아이콘 추가 확인됨.

profilelikecount 아이콘이 올바르게 추가되었습니다. 구조와 필드가 다른 아이콘 항목들과 일관성을 유지하고 있습니다.


262-274: 새 아이콘 추가 확인됨.

camera 아이콘이 올바르게 추가되었습니다. 프로필 이미지 업로드 등의 기능에 사용될 것으로 보입니다.


289-302: 새 아이콘 추가 확인됨.

edit 아이콘이 올바르게 추가되었습니다. 프로필 편집 등의 기능에 사용될 것으로 보입니다.


276-288: detailView 아이콘 정리 완료 확인.

사용되지 않는 detailview 아이콘(uid: eb85802a6428dc9a61f6a3d9639b80cb)이 제거되었으며, 현재는 활성화된 detailView 아이콘(uid: aab12ac34d786019442b42d61a477f36)만 남아 있습니다.


220-232: information 아이콘 uid 변경은 기존 코드에 영향을 주지 않습니다.

아이콘은 코드포인트(codepoint) 값(0xe803)으로 참조되며, uid는 아이콘 생성 도구의 메타데이터일 뿐입니다. 설정 파일의 "code": 59395 값이 유지되므로 Dart 코드의 AppIcons.information 참조는 정상 작동합니다.


234-246: 검색어 명명 규칙 일관성 확인 필요: arrow_upward를 다른 아이콘과 동일한 형식으로 정렬하세요.

현재 이 아이콘의 검색어는 "arrow_upward" (snake_case with underscore)로 표기되어 있으며, 이는 코드베이스의 다른 대부분 아이콘과 다릅니다. 다른 아이콘들은 언더스코어 없이 소문자만 사용하거나 (예: "mypagedisable", "registerdisable") 또는 camelCase를 따릅니다. 이 아이콘의 검색어를 일관된 명명 규칙으로 수정하세요.

Likely an incorrect or invalid review comment.

Comment on lines 391 to 396
GestureDetector(
onTap: () {
setState(
() => item.isLiked = !item.isLiked,
);
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

좋아요 토글이 서버와 동기화되지 않음

현재 좋아요 버튼을 누르면 로컬 상태만 변경되고 서버 API 호출이 없습니다. 이로 인해 사용자가 좋아요를 취소했다고 생각하지만 실제로는 서버에 반영되지 않는 데이터 불일치가 발생할 수 있습니다.

ItemDetailDescriptionScreen_toggleLike() 메서드처럼 API 호출과 롤백 로직을 추가하는 것이 좋습니다.

🤖 Prompt for AI Agents
In lib/screens/my_page/my_like_list_screen.dart around lines 391 to 396, the
GestureDetector onTap currently flips item.isLiked only in local state without
calling the server; change this to perform an optimistic update, call the
like/unlike API (awaiting the response), and rollback the local state if the API
fails. Implement the same pattern as ItemDetailDescriptionScreen._toggleLike():
store the previous value, setState to the optimistic value, call the async API,
on success keep the value, on failure revert to the previous value inside
setState and show/log the error. Ensure the onTap handler is async, awaits the
network call, and handles exceptions to avoid leaving UI in an inconsistent
state.

@github-actions
Copy link
Contributor

❌ 프로젝트 빌드 실패

실패한 단계: 알 수 없는 단계

빌드 로그를 확인하여 문제를 해결해주세요.

@github-actions
Copy link
Contributor

❌ 프로젝트 빌드 실패

실패한 단계: 알 수 없는 단계

빌드 로그를 확인하여 문제를 해결해주세요.

@github-actions
Copy link
Contributor

❌ 프로젝트 빌드 실패

실패한 단계: 알 수 없는 단계

빌드 로그를 확인하여 문제를 해결해주세요.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/utils/common_utils.dart (1)

133-175: 시간 관련 헬퍼 함수 리팩토링 확인

isSameMinute, formatMessageTime, getLastActivityTime 함수들이 재작성되었습니다. 로직은 동일해 보이지만, UTC/로컬 시간 처리 및 포맷 변경사항을 확인해주세요.

특히:

  • formatMessageTime의 12시간제 변환 로직 (h12 계산)
  • UTC → 로컬 변환 처리

기존 동작과 일치하는지 확인하려면 단위 테스트 추가를 고려하세요:

// 예시 테스트 케이스
void main() {
  test('formatMessageTime handles midnight correctly', () {
    final midnight = DateTime(2025, 1, 1, 0, 5);
    expect(formatMessageTime(midnight), '오전 12:05');
  });
  
  test('formatMessageTime handles noon correctly', () {
    final noon = DateTime(2025, 1, 1, 12, 30);
    expect(formatMessageTime(noon), '오후 12:30');
  });
  
  test('formatMessageTime handles afternoon correctly', () {
    final afternoon = DateTime(2025, 1, 1, 13, 0);
    expect(formatMessageTime(afternoon), '오후 1:00');
  });
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 817db55 and 933cd97.

📒 Files selected for processing (3)
  • lib/screens/chat_room_screen.dart (1 hunks)
  • lib/screens/item_detail_description_screen.dart (7 hunks)
  • lib/utils/common_utils.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/screens/chat_room_screen.dart
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension method context.navigateTo() for screen navigation to support iOS swipe gesture handling

Files:

  • lib/utils/common_utils.dart
  • lib/screens/item_detail_description_screen.dart
🧠 Learnings (2)
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use the team's common extension method `context.navigateTo()` for screen navigation to support iOS swipe gesture handling

Applied to files:

  • lib/utils/common_utils.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly

Applied to files:

  • lib/screens/item_detail_description_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (2)
lib/screens/item_detail_description_screen.dart (1)

357-365: 코딩 가이드라인 준수 - 개선 확인

TextStyle을 직접 사용하는 대신 CustomTextStyles.h3를 사용하도록 수정되었습니다. 이전 리뷰 코멘트가 적절히 반영되었습니다.

lib/utils/common_utils.dart (1)

13-55: 코드 검증 결과: 이슈 없음

context.navigateTo() 확장 함수는 이미 lib/utils/common_utils.dartNavigationExtension에서 올바르게 구현되어 있으며, iOS 스와이프 백 제스처를 지원하도록 CupertinoPageRoute를 사용하고 있습니다. 모든 호출 지점에서 Future<T?> 반환 타입을 올바르게 처리하고 있으며, 팀의 코딩 가이드라인을 준수합니다.

Likely an incorrect or invalid review comment.

Comment on lines +801 to +808
onTap: () async {
// 뒤로갈 때 좋아요가 취소된 상태면 목록에 반영되도록 정보 반환
if (isLikedVN.value == false) {
Navigator.of(context).pop(widget.itemId);
} else {
Navigator.of(context).pop();
}
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all ItemDetailDescriptionScreen navigation calls
echo "=== Searching for ItemDetailDescriptionScreen navigation calls ==="
rg -nP --type=dart 'ItemDetailDescriptionScreen|\.navigateTo.*ItemDetailDescription|Navigator.*ItemDetailDescription' -B2 -A2 -g '!**/item_detail_description_screen.dart'

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 4355


🏁 Script executed:

#!/bin/bash
# Search for navigateTo usage pattern in the codebase
echo "=== Checking for context.navigateTo implementation ==="
rg -nP --type=dart 'extension.*navigateTo|fun.*navigateTo' -A5

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 119


🏁 Script executed:

#!/bin/bash
# Find the navigateTo extension implementation
echo "=== Finding context.navigateTo() implementation ==="
rg -nP --type=dart 'extension.*on.*BuildContext|extension.*on.*Context' -A10 | grep -A10 'navigateTo'

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 877


🏁 Script executed:

#!/bin/bash
# Check each calling site for return value handling
echo "=== Checking home_feed_item_widget.dart line 130 ==="
sed -n '125,140p' lib/widgets/home_feed_item_widget.dart

echo -e "\n=== Checking chat_room_screen.dart lines 450-460 ==="
sed -n '450,465p' lib/screens/chat_room_screen.dart

echo -e "\n=== Checking home_tab_screen.dart lines 115-130 ==="
sed -n '115,130p' lib/screens/home_tab_screen.dart

echo -e "\n=== Checking register_tab_screen.dart lines 658-670 ==="
sed -n '658,670p' lib/screens/register_tab_screen.dart

echo -e "\n=== Checking my_like_list_screen.dart lines 205-220 ==="
sed -n '205,220p' lib/screens/my_page/my_like_list_screen.dart

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 3557


🏁 Script executed:

#!/bin/bash
# Check what happens after navigation in each calling location

echo "=== home_feed_item_widget.dart: what happens after Navigator.push (line 130) ==="
sed -n '127,155p' lib/widgets/home_feed_item_widget.dart

echo -e "\n=== chat_room_screen.dart: what happens after context.navigateTo (line 453) ==="
sed -n '450,475p' lib/screens/chat_room_screen.dart

echo -e "\n=== home_tab_screen.dart: what happens after context.navigateTo (line 120) ==="
sed -n '119,135p' lib/screens/home_tab_screen.dart

echo -e "\n=== request_management_tab_screen.dart: what happens after context.navigateTo (line 356) ==="
sed -n '354,375p' lib/screens/request_management_tab_screen.dart

echo -e "\n=== Check if result is used in register_tab_screen.dart (line 660) ==="
sed -n '658,685p' lib/screens/register_tab_screen.dart

echo -e "\n=== Check if result is used in my_like_list_screen.dart (line 209) ==="
sed -n '207,235p' lib/screens/my_page/my_like_list_screen.dart

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 6656


🏁 Script executed:

#!/bin/bash
# Check the full context.navigateTo() implementation
echo "=== Full context.navigateTo() implementation ==="
sed -n '13,80p' lib/utils/common_utils.dart

echo -e "\n=== Check ContextExtension definition ==="
sed -n '81,90p' lib/utils/common_utils.dart

Repository: TEAM-ROMROM/RomRom-FE

Length of output: 2396


조건부 데이터 반환 처리 및 코딩 가이드라인 준수 필요

좋아요가 취소된 상태(isLikedVN.value == false)일 때 itemId를 반환하는 조건부 pop 동작은 일부 호출처에서만 제대로 처리되고 있습니다.

현황:

  • Navigator.push()를 직접 사용하는 3곳(home_feed_item_widget, register_tab_screen, my_like_list_screen)에서는 결과값을 awaiting하여 처리 중
  • context.navigateTo()를 사용하는 3곳(chat_room_screen, home_tab_screen, request_management_tab_screen)에서는 결과값을 awaiting하지 않아 반환값을 받지 못함
  • 코딩 가이드라인에 따라 iOS 스와이프 제스처 지원을 위해 모든 화면 네비게이션에서 context.navigateTo() 사용 필요

대응 방법:

  • chat_room_screen.dart (line 453), home_tab_screen.dart (line 120), request_management_tab_screen.dart (line 356, 622, 828)의 context.navigateTo() 호출에서 결과값을 await하여 처리
  • home_feed_item_widget.dart (line 130), register_tab_screen.dart (line 660), my_like_list_screen.dart (line 209)의 직접 Navigator.push() 사용을 context.navigateTo()로 변경
🤖 Prompt for AI Agents
In lib/screens/item_detail_description_screen.dart around lines 801-808 the pop
call conditionally returns widget.itemId when isLikedVN.value == false; callers
must await this value and use context.navigateTo() per guideline. Update the
following callers to await the navigation result and handle a returned itemId:
modify chat_room_screen.dart (line 453), home_tab_screen.dart (line 120), and
request_management_tab_screen.dart (lines 356, 622, 828) to await
context.navigateTo(...) and process the result; replace direct
Navigator.push(...) with context.navigateTo(...) in home_feed_item_widget.dart
(line 130), register_tab_screen.dart (line 660), and my_like_list_screen.dart
(line 209) so all navigations are uniform and return values are awaited.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
lib/screens/my_page/my_profile_edit_screen.dart (1)

45-50: dispose() 메서드가 올바르게 추가되었습니다!

이전 리뷰에서 지적된 메모리 누수 문제가 해결되었습니다. nicknameControllernicknameFocusNode가 적절히 해제됩니다.

🧹 Nitpick comments (3)
lib/screens/my_page/my_profile_edit_screen.dart (3)

177-200: async/await와 then/catchError 패턴이 혼용되어 있습니다.

async 함수 내에서 .then().catchError() 대신 try-catch를 사용하면 코드가 더 일관되고 가독성이 좋아집니다.

🔎 제안된 수정
                onTap: () async {
                  if (_isProfileEdited && (_nickname.isNotEmpty)) {
-                   await MemberApi()
-                       .updateMemberProfile(nicknameController.text, imageUrl)
-                       .then((_) {
-                         if (context.mounted) {
-                           CommonSnackBar.show(
-                             context: context,
-                             message: '프로필이 성공적으로 업데이트되었습니다.',
-                             type: SnackBarType.success,
-                           );
-                           Navigator.of(context).pop(true);
-                         }
-                       })
-                       .catchError((e) {
-                         if (context.mounted) {
-                           CommonSnackBar.show(
-                             context: context,
-                             message: '프로필 업데이트에 실패했습니다: $e',
-                             type: SnackBarType.error,
-                           );
-                         }
-                       });
+                   try {
+                     await MemberApi().updateMemberProfile(
+                       nicknameController.text,
+                       imageUrl,
+                     );
+                     if (context.mounted) {
+                       CommonSnackBar.show(
+                         context: context,
+                         message: '프로필이 성공적으로 업데이트되었습니다.',
+                         type: SnackBarType.success,
+                       );
+                       Navigator.of(context).pop(true);
+                     }
+                   } catch (e) {
+                     if (context.mounted) {
+                       CommonSnackBar.show(
+                         context: context,
+                         message: '프로필 업데이트에 실패했습니다: $e',
+                         type: SnackBarType.error,
+                       );
+                     }
+                   }
                  }
                },

366-366: 화면 크기에 따른 스케일링이 적용되지 않은 값이 있습니다.

contentPadding에서 EdgeInsets.all(8) 대신 EdgeInsets.all(8.w)를 사용하여 다른 값들과 일관성을 유지하세요.

🔎 제안된 수정
-                           contentPadding: const EdgeInsets.all(8),
+                           contentPadding: EdgeInsets.all(8.w),

434-434: 하드코딩된 오프셋 값에 스케일링이 누락되었습니다.

-14.0 값에도 .h 스케일링을 적용하여 다양한 화면 크기에서 일관된 레이아웃을 유지하세요.

🔎 제안된 수정
-             offset: Offset(iconOffsetX, -14.0),
+             offset: Offset(iconOffsetX, -14.0.h),
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 933cd97 and 5edc440.

📒 Files selected for processing (1)
  • lib/screens/my_page/my_profile_edit_screen.dart (7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension method context.navigateTo() for screen navigation to support iOS swipe gesture handling

Files:

  • lib/screens/my_page/my_profile_edit_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (2)
lib/screens/my_page/my_profile_edit_screen.dart (2)

246-293: 프로필 이미지 섹션 구현이 잘 되었습니다!

GestureDetector로 탭 이벤트 처리, 업로드 중 로딩 인디케이터 표시, UserProfileCircularAvatar 사용 등이 적절하게 구현되었습니다.


460-527: 정보 섹션과 좋아요 섹션이 잘 구현되었습니다!

CustomTextStylesAppColors를 적절히 사용하고 있으며, 레이아웃이 일관되게 구성되어 있습니다.

Comment on lines +94 to +98
// 사용자가 취소했거나 선택 없음
if (picked == null) {
debugPrint('프로필 이미지 변경 취소');
return;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

사용자가 이미지 선택을 취소할 때 상태가 초기화되지 않습니다.

사용자가 갤러리에서 이미지를 선택하지 않고 취소하면, _hasImageBeenTouched_showProfileSaveButtontrue로 남아있게 됩니다. 이로 인해 UI 상태가 불일치할 수 있습니다.

🔎 제안된 수정
      // 사용자가 취소했거나 선택 없음
      if (picked == null) {
        debugPrint('프로필 이미지 변경 취소');
+       setState(() {
+         _hasImageBeenTouched = false;
+         // _showProfileSaveButton은 다른 편집 여부에 따라 결정 필요
+         if (!_isProfileEdited) {
+           _showProfileSaveButton = false;
+         }
+       });
        return;
      }
🤖 Prompt for AI Agents
In lib/screens/my_page/my_profile_edit_screen.dart around lines 94 to 98, when
image picking returns null the code returns early but does not reset
_hasImageBeenTouched and _showProfileSaveButton; update the null branch to set
those booleans to false (inside setState if within a StatefulWidget) before
returning so the UI reflects no change and the save button is hidden.

Comment on lines +325 to +328
child: nicknameFocusNode.hasFocus || _nickname.isEmpty
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

FocusNode 상태 변경 시 UI가 자동으로 업데이트되지 않을 수 있습니다.

nicknameFocusNode.hasFocus는 포커스 변경 시 자동으로 위젯을 다시 빌드하지 않습니다. onTaponTapOutside에서 setState를 호출하고 있지만, 다른 방식으로 포커스가 변경되면 (예: 시스템 이벤트, 다른 위젯 터치) UI가 업데이트되지 않을 수 있습니다.

🔎 제안된 수정 - initState에 리스너 추가
  @override
  void initState() {
    super.initState();
    _loadUserInfo();
+   nicknameFocusNode.addListener(_onFocusChange);
  }

+ void _onFocusChange() {
+   setState(() {});
+ }

  @override
  void dispose() {
+   nicknameFocusNode.removeListener(_onFocusChange);
    nicknameController.dispose();
    nicknameFocusNode.dispose();
    super.dispose();
  }
🤖 Prompt for AI Agents
In lib/screens/my_page/my_profile_edit_screen.dart around lines 325-328, using
nicknameFocusNode.hasFocus directly in build can miss updates when focus changes
outside onTap handlers; add a FocusNode listener in initState that calls
setState() when the focus changes and remove that listener in dispose (or call
nicknameFocusNode.removeListener) so the UI rebuilds reliably whenever focus
changes.

@github-actions
Copy link
Contributor

✅ 프로젝트 빌드 성공

APK 빌드가 완료되었습니다.

@SeoHyun1024 SeoHyun1024 merged commit 3ba8cfe into main Dec 19, 2025
2 checks passed
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.

2 participants