Skip to content

Conversation

@dmp100
Copy link
Contributor

@dmp100 dmp100 commented Nov 11, 2025

ISSUE

❗ WORK DESCRIPTIONAdd commentMore actions

  • 한성인찾기 API연결 및 이메일보내기까지 완료했습니다
  • 이메일은 등록된 이메일 기반으로 크롬으로넘어가도록 간단하게했습니다

📸 SCREENSHOT

2025-11-11.18.10.04.mov
image

서버에 데이터가없어서 안나오는거같긴한데 Preview로 보면 다 나오게되어있습니다.

📢 TO REVIEWERS

  • 리뷰어에게 전달해야 하는 말

Summary by CodeRabbit

  • New Features
    • Added mentor search functionality with pagination to discover mentors
    • Added mentor profile views displaying detailed information including career history and contact links

@coderabbitai
Copy link

coderabbitai bot commented Nov 11, 2025

Walkthrough

Introduces mentor search and profile viewing features across all architectural layers. Adds SearchDataSource, SearchRepository, and corresponding DTOs for API integration. Implements SearchScreen and ProfileScreen UI components with navigation. Refactors SocialLogin DTOs. Includes DI bindings, Retrofit service endpoints, and reactive state management via ViewModels and flows.

Changes

Cohort / File(s) Summary
Dependency Injection
app/src/main/java/com/hsLink/hslink/data/di/DataSourceModule.kt, RepositoryModule.kt, ServiceModule.kt
Added Dagger/Hilt bindings: bindsSearchDataSource for SearchDataSourceImpl, bindsSearchRepository for SearchRepositoryImpl, and provideSearchService singleton provider for SearchService via Retrofit.
DTO Request/Response
app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt, response/SocialLoginResponseDto.kt, response/SearchResponseDto.kt, response/UserProfileResponseDto.kt
Renamed SocialLogin DTOs (SocialLoginRequest → SocialLoginRequestDto, SocialLoginResponse → SocialLoginResponseDto). Added SearchResponseDto with MentorListResponseDto, MentorListResultDto, and MentorItemDto. Added UserProfileResponseDto with UserProfileDto, CareerItemDto, and LinkItemDto.
Remote Data Layer
app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt, datasourceimpl/SearchDataSourceImpl.kt, service/search/SearchService.kt, service/login/AuthService.kt
Introduced SearchDataSource interface and SearchDataSourceImpl with getMentors and getUserProfile methods. Added SearchService Retrofit interface with GET endpoints. Updated AuthService to use renamed SocialLogin DTOs.
Domain Models
app/src/main/java/com/hsLink/hslink/domain/model/search/SearchEntity.kt, UserProfileEntity.kt
Added MentorListEntity and MentorEntity for mentor data. Added UserProfileEntity, CareerEntity, and LinkEntity for profile data.
Repository Layer
app/src/main/java/com/hsLink/hslink/domain/repository/search/SearchRepository.kt, repositoryimpl/search/SearchRepositoryImpl.kt, repositoryimpl/AuthRepositoryImpl.kt
Introduced SearchRepository interface with getMentors and getUserProfile methods. Implemented SearchRepositoryImpl with DTO-to-Entity mapping and error handling. Updated AuthRepositoryImpl to use renamed SocialLogin DTOs.
Navigation
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt, MainNavigator.kt, MainTab.kt, search/navigation/SearchNavigation.kt, search/navigation/ProfileNavigation.kt
Updated MainNavHost to wire search and profile navigation graphs. Added MainNavigator.navigateToProfile. Moved search navigation from home to search package. Added ProfileNavigation with navigateToProfile and profileNavGraph builder. Updated SearchNavigation to pass onNavigateToProfile callback.
UI Components
app/src/main/java/com/hsLink/hslink/presentation/search/component/CareerSection.kt, HsLinkIconActionButton.kt, LinkCard.kt, ProfileCard.kt, SearchUserItems.kt
Added CareerCard composable for displaying career items. Added HsLinkIconActionButton with size-based styling and enabled state. Added LinkCard for displaying profile links with type mapping. Added ProfileCard with header and status display. Added SearchUserItems card for mentor list display.
Screens
app/src/main/java/com/hsLink/hslink/presentation/search/screen/SearchScreen.kt, ProfileScreen.kt
Created SearchScreen with mentor list, pagination, loading states, and navigation intents. Created ProfileScreen with profile display, career/link sections, and email action button. Both screens integrate ViewModel state and side effects. Deleted old placeholder SearchScreen.kt.
State Management
app/src/main/java/com/hsLink/hslink/presentation/search/state/SearchUiState.kt, ProfileContract.kt, viewmodel/SearchViewModel.kt, ProfileViewModel.kt
Added SearchUiState with pagination fields, SearchIntent (LoadMentors, LoadMoreMentors, NavigateToProfile, ClearError), and SearchSideEffect. Added ProfileUiState, ProfileIntent, and ProfileSideEffect. Implemented SearchViewModel with pagination and error handling. Implemented ProfileViewModel with profile loading and navigation.
Resources
app/src/main/res/drawable/ic_search_mail.xml
Added vector drawable for mail icon (24x24 dp, white stroke, two-path mailbox design).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant SearchScreen
    participant SearchVM as SearchViewModel
    participant SearchRepo as SearchRepository
    participant SearchDS as SearchDataSource
    participant API as SearchService API

    User->>SearchScreen: Navigate to Search
    activate SearchScreen
    SearchScreen->>SearchVM: LoadMentors intent
    activate SearchVM
    SearchVM->>SearchRepo: getMentors(page=0, size=15)
    activate SearchRepo
    SearchRepo->>SearchDS: getMentors(page, size)
    activate SearchDS
    SearchDS->>API: GET /users/mentors?page=0&size=15
    API-->>SearchDS: MentorListResponseDto
    deactivate SearchDS
    SearchRepo->>SearchRepo: Map DTO to MentorListEntity
    SearchRepo-->>SearchVM: Result<MentorListEntity>
    deactivate SearchRepo
    SearchVM->>SearchVM: Update uiState with mentors
    deactivate SearchVM
    SearchScreen->>SearchScreen: Display mentor list
    deactivate SearchScreen

    User->>SearchScreen: Tap mentor item
    activate SearchScreen
    SearchScreen->>SearchVM: NavigateToProfile(userId)
    activate SearchVM
    SearchVM->>SearchVM: Emit NavigateToProfile side effect
    deactivate SearchVM
    SearchScreen->>SearchScreen: Navigate to ProfileScreen
    deactivate SearchScreen

    User->>ProfileScreen: ProfileScreen loaded
    activate ProfileScreen
    ProfileScreen->>SearchVM: LoadProfile(userId)
    activate SearchVM
    SearchVM->>SearchRepo: getUserProfile(userId)
    activate SearchRepo
    SearchRepo->>SearchDS: getUserProfile(userId)
    activate SearchDS
    SearchDS->>API: GET /users/profiles/{userId}
    API-->>SearchDS: UserProfileResponseDto
    deactivate SearchDS
    SearchRepo->>SearchRepo: Map DTO to UserProfileEntity
    SearchRepo-->>SearchVM: Result<UserProfileEntity>
    deactivate SearchRepo
    SearchVM->>SearchVM: Update uiState with profile
    deactivate SearchVM
    ProfileScreen->>ProfileScreen: Display profile details
    deactivate ProfileScreen
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Areas requiring extra attention:

  • SearchViewModel & ProfileViewModel pagination logic: Verify LoadMoreMentors correctly manages page state and append behavior without duplication or off-by-one errors.
  • DTO-to-Entity mapping in SearchRepositoryImpl: Confirm all field mappings are correct and no data loss occurs during transformation.
  • Navigation state management: Validate ProfileNavigation route serialization and parameter passing between SearchScreen and ProfileScreen.
  • SearchScreen UI state rendering: Review loading/error state handling, empty list scenarios, and LazyColumn pagination trigger logic.

Possibly related PRs

  • #19: Adds profile/mypage navigation graphs and wiring to MainNavHost, overlapping with profile navigation setup in this PR.
  • #25: Refactors SocialLogin authentication layer (AuthService, DTOs, AuthRepositoryImpl), directly related to this PR's DTO renaming and AuthRepositoryImpl updates.
  • #21: Extends DI bindings in ServiceModule, RepositoryModule, and DataSourceModule; shares the same infrastructure being used here for SearchService and SearchRepository.

Suggested labels

feature ✨, 규현 🐻

Poem

🐰 A rabbit hops through mentor halls,
Searching for connections in digital walls,
Profiles bloom like springtime flowers,
New journeys found with search superpowers,
Finding heroes, linking dreams—
Nothing's ever quite as it seems! ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes a rename of SocialLoginRequest/Response DTOs to append 'Dto' suffix. While this is a naming convention improvement, it extends beyond the stated #26 objective of implementing search view and API. Consider moving the DTO renaming (SocialLoginRequest/Response → SocialLoginRequestDto/ResponseDto) to a separate refactoring PR to keep this PR focused on the search feature implementation.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat#26/search view and api' directly addresses the main changeset, which implements search view UI and API integration as stated in the linked issue #26.
Description check ✅ Passed The PR description follows the template with ISSUE, WORK DESCRIPTION, SCREENSHOT, and TO REVIEWERS sections. It clearly explains the changes (API connection and email feature) with supporting screenshots.
Linked Issues check ✅ Passed The PR implements the core objective from issue #26 (search view and API): creates SearchService, SearchDataSource/impl, SearchRepository/impl, domain entities, UI screens (SearchScreen, ProfileScreen), and navigation components.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat#26/searchViewandAPI

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.

@dmp100 dmp100 merged commit b9040ef into develop Nov 11, 2025
2 of 3 checks passed
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: 3

🧹 Nitpick comments (13)
app/src/main/java/com/hsLink/hslink/presentation/search/component/HsLinkIconActionButton.kt (1)

35-56: Simplify the duplicated textColor logic.

The textColor assignment is identical in both the enabled and disabled branches (lines 39-43 vs. 50-54), making the conditional unnecessary for text color. Only the backgroundColor actually changes based on the isEnabled state.

Additionally, it's unusual that disabled buttons retain the same text color as enabled ones—typically, disabled states have reduced contrast for better UX signaling.

Apply this diff to eliminate the duplication:

     val textStyle = size.getTextStyle()
 
-    val textColor: Color
+    val textColor = when(size){
+        HsLinkActionButtonSize.Large -> HsLinkTheme.colors.Common
+        HsLinkActionButtonSize.Medium -> HsLinkTheme.colors.Common
+        HsLinkActionButtonSize.Small -> HsLinkTheme.colors.Grey500
+    }
+    
     val backgroundColor: Color
 
     if (isEnabled) {
-        textColor = when(size){
-            HsLinkActionButtonSize.Large -> HsLinkTheme.colors.Common
-            HsLinkActionButtonSize.Medium -> HsLinkTheme.colors.Common
-            HsLinkActionButtonSize.Small -> HsLinkTheme.colors.Grey500
-        }
         backgroundColor = when (size) {
             HsLinkActionButtonSize.Large -> HsLinkTheme.colors.DeepBlue500
             HsLinkActionButtonSize.Medium -> HsLinkTheme.colors.SkyBlue400
             HsLinkActionButtonSize.Small -> HsLinkTheme.colors.Grey100
         }
     } else {
-        textColor = when (size) {
-            HsLinkActionButtonSize.Large -> HsLinkTheme.colors.Common
-            HsLinkActionButtonSize.Medium -> HsLinkTheme.colors.Common
-            HsLinkActionButtonSize.Small -> HsLinkTheme.colors.Grey500
-        }
         backgroundColor = HsLinkTheme.colors.Grey200
     }

If the text color is intentionally the same for both states, this refactor preserves that behavior while improving maintainability. If disabled buttons should have reduced contrast, consider using a different color (e.g., Grey400 or Grey300) for the disabled state.

app/src/main/java/com/hsLink/hslink/presentation/search/component/CareerSection.kt (1)

20-59: Consider adding empty state handling.

The component renders the title and card structure even when the careers list is empty. Consider conditionally rendering the entire component or showing an appropriate empty state message.

Apply this diff to conditionally render only when careers are present:

 @Composable
 fun CareerCard(
     careers: List<CareerEntity>,
     modifier: Modifier = Modifier
 ) {
+    if (careers.isEmpty()) return
+    
     Column(modifier = modifier) {

Alternatively, add an empty state message inside the card when the list is empty.

app/src/main/java/com/hsLink/hslink/presentation/search/component/LinkCard.kt (2)

20-56: Consider empty state handling and clickable links.

Similar to CareerCard, this component renders the title and card structure even when the links list is empty. Additionally, the URLs are displayed as plain text rather than clickable links.

Consider these improvements:

  1. Conditionally render when links are present:
 @Composable
 fun LinkCard(
     links: List<LinkEntity>,
     modifier: Modifier = Modifier
 ) {
+    if (links.isEmpty()) return
+    
     Column(modifier = modifier) {
  1. Make URLs clickable using ClickableText or wrapping in a clickable modifier with platform-specific URL opening logic.

80-88: Consider extracting link type constants.

The link type strings are hardcoded magic strings. Consider defining them as constants (in a companion object or separate file) to improve maintainability and reduce typos.

Example:

private object LinkType {
    const val LINKEDIN = "LINKEDIN"
    const val GITHUB = "GITHUB"
    const val INSTAGRAM = "INSTAGRAM"
    const val BLOG = "BLOG"
}

private fun getLinkTypeText(type: String): String {
    return when (type) {
        LinkType.LINKEDIN -> "LinkedIn"
        LinkType.GITHUB -> "GitHub"
        LinkType.INSTAGRAM -> "Instagram"
        LinkType.BLOG -> "블로그"
        else -> "기타"
    }
}
app/src/main/java/com/hsLink/hslink/presentation/search/component/SearchUserItems.kt (3)

24-26: Consider renaming the preview function to match the component.

The preview function is named MyPageDetailItemContentPreview but it previews SearchUserItems. This naming mismatch could cause confusion.

Apply this diff:

-private fun MyPageDetailItemContentPreview() {
+private fun SearchUserItemsPreview() {

37-43: Remove or integrate the unused data class.

SearchUserItemsData is defined but never used by the SearchUserItems composable or elsewhere in the file. If this was intended for future use, consider removing it until needed to avoid dead code.


76-98: Prefer theme colors over hardcoded Black.

The component uses HsLinkTheme.colors for some colors (e.g., Grey400, Common) but hardcodes Black directly in multiple places. For consistency and to support potential theming changes, consider using a theme color instead.

If HsLinkTheme.colors provides a text color property, apply this diff:

             Text(
                 text = name,
-                color = Black,
+                color = HsLinkTheme.colors.TextPrimary, // or appropriate theme color
                 style = HsLinkTheme.typography.title_20Strong
             )

             Text(
                 text = title,
-                color = Black,
+                color = HsLinkTheme.colors.TextPrimary, // or appropriate theme color
                 style = HsLinkTheme.typography.body_14Normal
             )
         Icon(
             imageVector = ImageVector.vectorResource(id = R.drawable.ic_mypage_item_lefrarrow),
             contentDescription = null,
-            tint = Black
+            tint = HsLinkTheme.colors.IconPrimary // or appropriate theme color
         )
app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt (1)

21-25: Consider using expression body for consistency.

The existing providesDummyService uses an expression body (=), while the new provideSearchService uses a block body with an explicit return. For consistency within the module, consider using an expression body.

Apply this diff:

     @Provides
     @Singleton
-    fun provideSearchService(retrofit: Retrofit): SearchService {
-        return retrofit.create(SearchService::class.java)
-    }
+    fun provideSearchService(retrofit: Retrofit): SearchService =
+        retrofit.create(SearchService::class.java)
app/src/main/java/com/hsLink/hslink/data/dto/response/SearchResponseDto.kt (1)

5-28: Consider nullable fields for API resilience.

All DTO fields are non-nullable, which assumes the backend will always provide every field. If the API contract guarantees non-null values, this is fine. However, if there's any chance of missing fields in the response, consider making optional fields nullable to prevent deserialization failures.

Additionally, totalMentorCount uses Long which supports values up to ~9 quintillion. Unless you expect more than 2 billion mentors, Int would suffice and reduce memory footprint slightly.

app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/ProfileViewModel.kt (2)

38-64: Good error handling with proper fallback.

The loadProfile implementation correctly handles both success and failure cases. The fallback to "Unknown error" on Line 61 when exception.message is null is a good defensive practice.

Similar to the repository layer, consider:

  1. Using English for log messages for better team readability
  2. Evaluating log levels for production builds

70-74: Minor: Unnecessary coroutine launch.

The navigateBack() function launches a coroutine just to call postSideEffect(). Since postSideEffect() is a suspend function and side effects are typically emitted synchronously, you could make navigateBack() itself a suspend function or handle the launch at the call site in handleIntent(). However, the current approach is consistent with how side effects are managed elsewhere in the ViewModel.

If you want to simplify:

private fun navigateBack() {
    viewModelScope.launch {
        postSideEffect(ProfileSideEffect.NavigateBack)
    }
}

is fine, or you could refactor to:

private fun navigateBack() {
    viewModelScope.launch {
        _sideEffect.emit(ProfileSideEffect.NavigateBack)
    }
}
app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/SearchViewModel.kt (1)

68-88: Guard loadMore while the first page is in flight

loadMoreMentors() only checks isLoadingMore. If the UI fires a load-more intent before the initial loadMentors() finishes, the page‑1 result can overwrite the page‑0 payload (and reset currentPage), so users lose mentors. Add a guard (if (currentState.isLoading || currentState.isLoadingMore) return) or otherwise serialize these jobs so pagination can’t race the initial fetch.

app/src/main/java/com/hsLink/hslink/data/repositoryimpl/search/SearchRepositoryImpl.kt (1)

53-92: Add logging for consistency and debugging.

The getUserProfile function is implemented correctly, but it lacks the logging statements present in getMentors. Adding similar logs (request parameters, API response status, errors) would improve debugging consistency across the repository.

Consider adding logs similar to the getMentors pattern:

 override suspend fun getUserProfile(userId: Long): Result<UserProfileEntity> {
+    Log.d("SearchRepository", "getUserProfile 호출: userId=$userId")
     return try {
         val response = searchDataSource.getUserProfile(userId)
+        Log.d("SearchRepository", "API 응답: isSuccess=${response.isSuccess}, message=${response.message}")
         if (response.isSuccess) {
             val userProfileEntity = UserProfileEntity(
                 // ... mapping code ...
             )
+            Log.d("SearchRepository", "프로필 변환 완료")
             Result.success(userProfileEntity)
         } else {
+            Log.e("SearchRepository", "API 실패: ${response.message}")
             Result.failure(Exception(response.message))
         }
     } catch (e: Exception) {
+        Log.e("SearchRepository", "예외 발생: ${e.message}", e)
         Result.failure(e)
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 980091f and 20d90b5.

📒 Files selected for processing (35)
  • app/src/main/java/com/hsLink/hslink/data/di/DataSourceModule.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/di/ServiceModule.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/dto/response/SearchResponseDto.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponseDto.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/dto/response/UserProfileResponseDto.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/data/repositoryimpl/search/SearchRepositoryImpl.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/domain/model/search/SearchEntity.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/domain/model/search/UserProfileEntity.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/domain/repository/search/SearchRepository.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (2 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/main/MainTab.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/SearchScreen.kt (0 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/component/CareerSection.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/component/HsLinkIconActionButton.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/component/LinkCard.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/component/ProfileCard.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/component/SearchUserItems.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/navigation/ProfileNavigation.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/navigation/SearchNavigation.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/screen/ProfileScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/screen/SearchScreen.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/state/ProfileContract.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/state/SearchUiState.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/ProfileViewModel.kt (1 hunks)
  • app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/SearchViewModel.kt (1 hunks)
  • app/src/main/res/drawable/ic_search_mail.xml (1 hunks)
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/hsLink/hslink/presentation/search/SearchScreen.kt
🧰 Additional context used
🧬 Code graph analysis (8)
app/src/main/java/com/hsLink/hslink/presentation/search/screen/ProfileScreen.kt (5)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTopBar.kt (1)
  • HsLinkTopBar (22-68)
app/src/main/java/com/hsLink/hslink/presentation/search/component/ProfileCard.kt (1)
  • ProfileCard (91-180)
app/src/main/java/com/hsLink/hslink/presentation/search/component/CareerSection.kt (1)
  • CareerCard (19-59)
app/src/main/java/com/hsLink/hslink/presentation/search/component/LinkCard.kt (1)
  • LinkCard (19-56)
app/src/main/java/com/hsLink/hslink/presentation/search/component/HsLinkIconActionButton.kt (1)
  • HsLinkIconActionButton (24-91)
app/src/main/java/com/hsLink/hslink/presentation/search/navigation/ProfileNavigation.kt (1)
app/src/main/java/com/hsLink/hslink/presentation/search/screen/ProfileScreen.kt (1)
  • ProfileRoute (61-92)
app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/SearchViewModel.kt (1)
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1)
  • navigateToProfile (76-78)
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (2)
app/src/main/java/com/hsLink/hslink/presentation/search/navigation/SearchNavigation.kt (1)
  • searchNavGraph (16-26)
app/src/main/java/com/hsLink/hslink/presentation/search/navigation/ProfileNavigation.kt (1)
  • profileNavGraph (20-32)
app/src/main/java/com/hsLink/hslink/presentation/search/screen/SearchScreen.kt (3)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/core/designsystem/component/HsLinkTopBar.kt (1)
  • HsLinkTopBar (22-68)
app/src/main/java/com/hsLink/hslink/presentation/search/component/SearchUserItems.kt (1)
  • SearchUserItems (45-101)
app/src/main/java/com/hsLink/hslink/presentation/search/component/ProfileCard.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
app/src/main/java/com/hsLink/hslink/presentation/search/navigation/SearchNavigation.kt (2)
app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1)
  • navigate (36-54)
app/src/main/java/com/hsLink/hslink/presentation/search/screen/SearchScreen.kt (1)
  • SearchRoute (83-114)
app/src/main/java/com/hsLink/hslink/presentation/search/component/SearchUserItems.kt (1)
app/src/main/java/com/hsLink/hslink/core/designsystem/theme/Theme.kt (1)
  • HsLinkTheme (37-57)
⏰ 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: build-and-test
🔇 Additional comments (31)
app/src/main/res/drawable/ic_search_mail.xml (1)

1-20: Well-formed vector drawable with appropriate styling.

The icon is properly structured with valid XML namespace, correct dimensions (24×24 dp), and consistent stroke styling across both paths. The geometry is sound: the first path creates a clean mailbox outline with rounded corners, and the second path forms the envelope flap line. Stroke width (1.5) and line styling (round joins/caps) are appropriate for a minimal 24 dp icon.

app/src/main/java/com/hsLink/hslink/presentation/search/component/HsLinkIconActionButton.kt (1)

24-91: Well-structured composable implementation.

The component follows Compose best practices with proper parameter defaults, modifier chaining, and layout composition. The click handling correctly respects the isEnabled state, and the UI elements are properly aligned and styled.

app/src/main/java/com/hsLink/hslink/data/dto/request/SocialLoginRequestDto.kt (1)

6-9: LGTM! Clean DTO naming convention.

The rename from SocialLoginRequest to SocialLoginRequestDto follows consistent DTO naming patterns and all consuming code has been updated accordingly.

app/src/main/java/com/hsLink/hslink/domain/repository/AuthRepository.kt (1)

3-9: LGTM! Return type updated consistently.

The import and return type are correctly updated to SocialLoginResponseDto, maintaining consistency with the DTO rename across the authentication flow.

app/src/main/java/com/hsLink/hslink/data/service/login/AuthService.kt (1)

3-11: LGTM! Service interface updated consistently.

The imports and method signature are correctly updated to use SocialLoginRequestDto and SocialLoginResponseDto, maintaining type safety and consistency across the Retrofit service layer.

app/src/main/java/com/hsLink/hslink/data/repositoryimpl/AuthRepositoryImpl.kt (1)

3-31: LGTM! Implementation updated consistently.

The repository implementation correctly uses the renamed DTO types:

  • Line 18: Request constructed with SocialLoginRequestDto
  • Line 16: Return type updated to Result<SocialLoginResponseDto>
  • Error handling and null safety maintained
app/src/main/java/com/hsLink/hslink/data/dto/response/SocialLoginResponseDto.kt (1)

6-11: The needsOnboarding field is properly integrated and handled throughout the codebase.

Verification confirms the new field flows correctly from the API response through the ViewModel, into the LoginState, and is used in KaKaoLoginScreen for conditional navigation (directing users to onboarding or home based on this flag). All consuming code properly accesses and handles the field.

Note: The AI summary was incorrect in stating "No behavioral changes or additional fields introduced"—the new field does represent a behavioral change to support onboarding logic, but it is properly implemented end-to-end.

app/src/main/java/com/hsLink/hslink/presentation/main/MainTab.kt (1)

10-10: LGTM: Clean package refactoring.

The import update correctly reflects the relocation of Search navigation to its dedicated package.

app/src/main/java/com/hsLink/hslink/data/dto/response/UserProfileResponseDto.kt (3)

5-11: LGTM: Standard API response wrapper.

The DTO follows a consistent pattern for API responses with success indicators and error handling fields.


12-24: LGTM: Well-structured profile DTO.

The DTO comprehensively captures user profile data with appropriate types. Empty lists for careers and links are preferable to nullable lists.


25-34: LGTM: Nullable endYm correctly models ongoing employment.

The nullable endYm field appropriately represents current positions where the end date is not yet determined.

app/src/main/java/com/hsLink/hslink/presentation/search/component/CareerSection.kt (1)

83-87: LGTM: Period formatting handles current employment correctly.

The logic appropriately displays "현재" for ongoing positions. Note that this assumes startYm and endYm are pre-formatted strings from the API.

app/src/main/java/com/hsLink/hslink/domain/repository/search/SearchRepository.kt (1)

6-9: LGTM: Clean repository interface.

The interface follows best practices with suspend functions and Result types for proper async operations and error handling.

app/src/main/java/com/hsLink/hslink/domain/model/search/UserProfileEntity.kt (1)

3-30: LGTM: Well-structured domain entities.

The domain models are immutable and properly separated from DTOs, following clean architecture principles. The nullable endYm field in CareerEntity correctly models ongoing employment.

app/src/main/java/com/hsLink/hslink/domain/model/search/SearchEntity.kt (1)

3-18: LGTM: Clean domain models with pagination support.

The entities properly model paginated mentor search results with appropriate fields for list display.

app/src/main/java/com/hsLink/hslink/presentation/search/navigation/SearchNavigation.kt (2)

1-1: LGTM: Package reorganization.

Moving search navigation to its dedicated package improves code organization and modularity.


16-26: LGTM: Enhanced navigation signature.

The updated function signature adds profile navigation support while maintaining clean parameter naming. The default empty lambda for onNavigateToProfile provides flexibility for callers.

app/src/main/java/com/hsLink/hslink/presentation/search/state/SearchUiState.kt (1)

5-24: LGTM!

Clean MVI pattern implementation with immutable state, well-defined intents, and appropriate side effects. The use of sealed classes for intents and side effects provides type safety, and the state defaults are sensible.

app/src/main/java/com/hsLink/hslink/data/di/RepositoryModule.kt (1)

44-47: Verify the scope requirement for SearchRepository.

The binding follows the module's pattern (most repositories are not @Singleton, except AuthRepository). Please confirm that SearchRepository doesn't need to be a singleton or maintain state across the application lifecycle.

app/src/main/java/com/hsLink/hslink/data/di/DataSourceModule.kt (1)

27-30: LGTM!

The data source binding follows the established pattern in the module and correctly maps SearchDataSourceImpl to SearchDataSource.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavHost.kt (1)

39-49: LGTM with a minor observation on parameter naming.

The navigation wiring correctly integrates the profile navigation flow. The search graph now properly passes the onNavigateToProfile callback to enable navigation from search results to profile details.

Minor note: The parameter name paddingValues in the search and profile nav graphs differs from padding used in other nav graphs (e.g., homeNavGraph, communityNavGraph). While not an issue, consistent parameter naming across all nav graphs would improve readability.

app/src/main/java/com/hsLink/hslink/presentation/main/MainNavigator.kt (1)

76-78: LGTM!

The navigateToProfile function follows the established pattern in the navigator and correctly delegates to the extension function with appropriate parameters.

app/src/main/java/com/hsLink/hslink/data/remote/datasource/SearchDataSource.kt (1)

6-9: LGTM!

Clean data source interface with appropriate suspend functions for async operations and pagination support for mentor listing.

app/src/main/java/com/hsLink/hslink/presentation/search/component/SearchUserItems.kt (1)

95-95: Code correctly references existing drawable resource.

The resource ic_mypage_item_lefrarrow exists as a valid drawable (app/src/main/res/drawable/ic_mypage_item_lefrarrow.xml). The code at line 95 correctly references this resource. While the resource name may contain a spelling variation ("lefr" vs "left"), the reference is valid and will not cause runtime issues.

app/src/main/java/com/hsLink/hslink/data/service/search/SearchService.kt (1)

9-21: LGTM!

The service interface is well-structured with appropriate default pagination parameters and proper use of Retrofit annotations.

app/src/main/java/com/hsLink/hslink/data/remote/datasourceimpl/SearchDataSourceImpl.kt (1)

9-20: LGTM!

Clean data source implementation that properly delegates to the service layer. The dependency injection setup is correct.

app/src/main/java/com/hsLink/hslink/presentation/search/state/ProfileContract.kt (1)

5-20: LGTM!

Well-structured state management following the MVI pattern. The separation of UI state, intents, and side effects is clean and appropriate. Nullable types are correctly used for optional state fields.

app/src/main/java/com/hsLink/hslink/presentation/search/navigation/ProfileNavigation.kt (1)

13-35: LGTM!

Excellent use of type-safe navigation with Kotlin Serialization. The navigation setup correctly extracts the userId parameter and passes it through to the ProfileRoute composable.

app/src/main/java/com/hsLink/hslink/presentation/search/viewmodel/ProfileViewModel.kt (1)

23-27: LGTM!

Proper use of StateFlow for UI state and SharedFlow for one-time side effects. The flow setup correctly exposes immutable views of the internal mutable flows.

app/src/main/java/com/hsLink/hslink/data/repositoryimpl/search/SearchRepositoryImpl.kt (2)

1-15: LGTM! Clean architecture and DI setup.

The class follows proper clean architecture principles with dependency injection. The repository implementation correctly depends on the data source abstraction, and all necessary imports are present.


17-50: Implementation looks solid.

The getMentors function properly handles API calls, error cases, and DTO-to-entity mapping. The extensive logging will be helpful for debugging.

Comment on lines +157 to +176
StatusItem(
title = if (profile.jobSeeking) "구직 중" else "구직 안함",
subtitle = "구직 여부"
)
VerticalDivider(
modifier = Modifier.height(40.dp),
color = HsLinkTheme.colors.Grey200
)
StatusItem(
title = if (profile.employed) "재직 중" else "미재직",
subtitle = "재직 여부"
)
VerticalDivider(
modifier = Modifier.height(40.dp),
color = HsLinkTheme.colors.Grey200
)
StatusItem(
title = getAcademicStatusText(profile.academicStatus),
subtitle = "재학 여부"
)
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

Externalize hard-coded strings to string resources.

The status labels are hard-coded in Korean ("구직 중", "구직 안함", "재직 중", "미재직", "구직 여부", "재직 여부", "재학 여부"). These should be moved to string resources for proper localization support and easier maintenance.

Apply this pattern to externalize strings:

+// In strings.xml
+<string name="job_seeking_yes">구직 중</string>
+<string name="job_seeking_no">구직 안함</string>
+<string name="job_seeking_label">구직 여부</string>
+// ... other strings

 StatusItem(
-    title = if (profile.jobSeeking) "구직 중" else "구직 안함",
-    subtitle = "구직 여부"
+    title = stringResource(if (profile.jobSeeking) R.string.job_seeking_yes else R.string.job_seeking_no),
+    subtitle = stringResource(R.string.job_seeking_label)
 )

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +183 to +190
private fun getAcademicStatusText(status: String): String {
return when (status) {
"ENROLLED" -> "재학"
"GRADUATED" -> "졸업"
"ON_LEAVE" -> "휴학"
else -> "기타"
}
}
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

Externalize hard-coded strings and consider logging unknown statuses.

Like the status labels above, these academic status strings should be externalized. Additionally, consider logging when an unknown status is encountered to help identify data issues or missing mappings.

 private fun getAcademicStatusText(status: String): String {
     return when (status) {
-        "ENROLLED" -> "재학"
-        "GRADUATED" -> "졸업"
-        "ON_LEAVE" -> "휴학"
-        else -> "기타"
+        "ENROLLED" -> stringResource(R.string.academic_status_enrolled)
+        "GRADUATED" -> stringResource(R.string.academic_status_graduated)
+        "ON_LEAVE" -> stringResource(R.string.academic_status_on_leave)
+        else -> {
+            Log.w("ProfileCard", "Unknown academic status: $status")
+            stringResource(R.string.academic_status_other)
+        }
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/search/component/ProfileCard.kt
around lines 183 to 190, the academic status labels are hard-coded and unknown
statuses are not logged; move the Korean labels ("재학", "졸업", "휴학", "기타") into
string resources (res/values/strings.xml) and reference them via
context.getString(...) or Composable stringResource; update
getAcademicStatusText to return those resource-backed strings (or keys resolved
by the caller) and add a logging call (e.g., Log.w or your logger) in the else
branch to record unknown status values for diagnostics.

Comment on lines +169 to +188
Spacer(modifier = Modifier.height(32.dp))
val context = LocalContext.current
HsLinkIconActionButton(
label = "멘토링 메일 보내기",
iconRes = R.drawable.ic_search_mail,
onClick = {
val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:${profile.email}") // 프로필의 이메일 주소 사용
putExtra(Intent.EXTRA_SUBJECT, "멘토링 문의드립니다.")
putExtra(Intent.EXTRA_TEXT, "안녕하세요, ${profile.name}님.\n\n멘토링 관련하여 문의드립니다.")
}
context.startActivity(intent)
},
size = HsLinkActionButtonSize.Large,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 32.dp)
)
}
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

Handle the mail intent defensively

Starting the ACTION_SENDTO intent without checking for a matching email client will crash the app (ActivityNotFoundException) on devices that lack one. Please gate the launch with resolveActivity (or wrap it in runCatching and surface an error side effect) and optionally use Intent.createChooser so the flow degrades gracefully instead of killing the screen.

-                HsLinkIconActionButton(
+                HsLinkIconActionButton(
                     label = "멘토링 매일 보내기",
                     iconRes = R.drawable.ic_search_mail,
                     onClick = {
-                        val intent = Intent(Intent.ACTION_SENDTO).apply {
-                            data = Uri.parse("mailto:${profile.email}")
-                            putExtra(Intent.EXTRA_SUBJECT, "멘토링 문의드립니다.")
-                            putExtra(Intent.EXTRA_TEXT, "안녕하세요, ${profile.name}님.\n\n멘토링 관련하여 문의드립니다.")
-                        }
-                        context.startActivity(intent)
+                        val intent = Intent(Intent.ACTION_SENDTO).apply {
+                            data = Uri.parse("mailto:${profile.email}")
+                            putExtra(Intent.EXTRA_SUBJECT, "멘토링 문의드립니다.")
+                            putExtra(Intent.EXTRA_TEXT, "안녕하세요, ${profile.name}님.\n\n멘토링 관련하여 문의드립니다.")
+                        }
+                        intent.resolveActivity(context.packageManager)?.let {
+                            context.startActivity(Intent.createChooser(intent, "멘토링 메일 보내기"))
+                        } ?: run {
+                            // TODO: show a toast/snackbar informing the user that no email app is available.
+                        }
                     },
🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/search/screen/ProfileScreen.kt
around lines 169 to 188, the mail ACTION_SENDTO intent is launched unguarded
which can throw ActivityNotFoundException on devices without an email client;
fix by creating a chooser with Intent.createChooser(intent, "Send email") and
only call context.startActivity(chooser) after verifying there is a handler
(e.g., intent.resolveActivity(context.packageManager) != null) or wrap the start
in runCatching and surface an error side-effect (Toast/Snackbar) when no
activity is available so the UI fails gracefully.

@vvan2 vvan2 added feature ✨ 기능 구현 규현 🐻 규현 전용 라벨 labels Nov 12, 2025
@vvan2 vvan2 changed the title Feat#26/search view and api [feat#26] search view and api Nov 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature ✨ 기능 구현 규현 🐻 규현 전용 라벨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 한성인찾기 View+API

3 participants