Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
Expand All @@ -25,6 +26,9 @@ object NetworkModule {
@Singleton
fun provideOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(authInterceptor)
.addInterceptor(
HttpLoggingInterceptor().apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.hsLink.hslink.data.dto.response.community

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CommunityDetailResponseDto(
@SerialName("id")
val id: Int,
@SerialName("author")
val author: String,
@SerialName("studentId")
val studentId: String,
@SerialName("authorStatus")
val authorStatus: String,
@SerialName("title")
val title: String,
@SerialName("body")
val body: String,
@SerialName("mine")
val mine: Boolean,
@SerialName("comments")
val comments: List<CommentDto>,
)

@Serializable
data class CommentDto(
@SerialName("id")
val id: Int,
@SerialName("commenter")
val commenter: String,
@SerialName("commenterStatus")
val commenterStatus: String,
@SerialName("content")
val content: String,
@SerialName("mine")
val mine: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.hsLink.hslink.data.dto.request.community.PostRequestDto
import com.hsLink.hslink.data.dto.response.community.CommunityDetailResponseDto
import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
import com.hsLink.hslink.data.paging.CommunityPagingSource
import com.hsLink.hslink.data.service.commuunity.CommunityPostService
Expand Down Expand Up @@ -31,4 +32,14 @@ class CommunityRepositoryImpl @Inject constructor(
pagingSourceFactory = { CommunityPagingSource(communityPostService, type) }
).flow
}

override suspend fun getCommunityDetail(postId: Int): Result<CommunityDetailResponseDto> =
runCatching {
val response = communityPostService.getCommunityDetail(postId)
if (response.isSuccess) {
response.result
} else {
throw Exception(response.message)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package com.hsLink.hslink.data.service.commuunity

import com.hsLink.hslink.core.network.BaseResponse
import com.hsLink.hslink.data.dto.request.community.PostRequestDto
import com.hsLink.hslink.data.dto.response.community.CommunityDetailResponseDto
import com.hsLink.hslink.data.dto.response.community.CommunityListResponseDto
import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query

interface CommunityPostService {
Expand All @@ -20,4 +22,9 @@ interface CommunityPostService {
@Query("type") type: String,
@Query("page") page: Int,
): BaseResponse<CommunityListResponseDto>

@GET("posts/{postId}")
suspend fun getCommunityDetail(
@Path("postId") postId: Int,
): BaseResponse<CommunityDetailResponseDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.hsLink.hslink.domain.repository.community

import androidx.paging.PagingData
import com.hsLink.hslink.data.dto.request.community.PostRequestDto
import com.hsLink.hslink.data.dto.response.community.CommunityDetailResponseDto
import com.hsLink.hslink.data.dto.response.community.CommunityPostResponseDto
import com.hsLink.hslink.domain.model.community.CommunityPost
import kotlinx.coroutines.flow.Flow
Expand All @@ -10,4 +11,6 @@ interface CommunityRepository {
suspend fun createCommunityPost(communityRequestDto: PostRequestDto): Result<CommunityPostResponseDto>

fun getCommunityPosts(type: String): Flow<PagingData<CommunityPost>>

suspend fun getCommunityDetail(postId: Int): Result<CommunityDetailResponseDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.hsLink.hslink.presentation.community.screen.post.CommunityPostRoute
import kotlinx.serialization.Serializable

fun NavController.navigateToCommunityPost(
postId: String,
postId: Int,
navOptions: NavOptions? = null
) {
navigate(CommunityPost(postId), navOptions)
Expand All @@ -20,13 +20,17 @@ fun NavGraphBuilder.communityPostNavGraph(
padding: PaddingValues,
navigateUp: () -> Unit,
) {
composable<CommunityPost> {
composable<CommunityPost> { backStackEntry ->

val postId = backStackEntry.arguments?.getInt("postId") ?: -1

CommunityPostRoute(
postId = postId,
paddingValues = padding,
navigateUp = navigateUp
)
Comment on lines +23 to 31
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

🧩 Analysis chain

Verify error handling for invalid postId fallback.

The code extracts postId from backStackEntry.arguments with a fallback value of -1 (Line 25). If the navigation arguments are missing or malformed, -1 will be passed to CommunityPostRoute. Ensure that downstream code (particularly the API call and UI) properly handles this invalid ID case, or consider throwing an error/navigating back if the postId is missing.

Run the following script to check how -1 (or invalid postId) is handled in the detail flow:


🏁 Script executed:

#!/bin/bash
# Description: Check error handling for invalid postId values in the detail flow

# Search for postId validation or error handling in CommunityPostRoute and related components
ast-grep --pattern $'fun getPostDetail(postId: Int) {
  $$$
}'

# Search for API error handling when postId is invalid
rg -n -A 5 -B 2 "getCommunityDetail|getPostDetail" --type kotlin

Length of output: 6512


Let me verify how the invalid postId is handled in the UI and whether there are guards in place:

Now let me check the CommunityPostScreen to see if there's any UI handling for invalid postId:

Let me search for the actual CommunityPostScreen implementation to see the full picture:

Based on the script output and web research, I can now make a definitive assessment. The review comment raises a valid concern about error handling for invalid postId values. Let me verify the complete error handling flow by examining how the error state is handled in the screen:

Analysis Summary:

From the script execution results, I found:

  1. Navigation layer (CommunityPostNavigation.kt:25): Extracts postId with fallback value -1
  2. ViewModel (CommunityViewModel.kt:58-69): Calls repository.getCommunityDetail(postId) with .onFailure error handler
  3. Repository (CommunityRepositoryImpl.kt:36-43): Wraps API call in runCatching for exception handling
  4. API Service (CommunityPostService.kt:27): Makes HTTP GET request with postId as path parameter
  5. Screen (CommunityPostScreen.kt:63): Calls viewModel.getPostDetail(postId) in LaunchedEffect(postId)

Current State: Error handling exists downstream (ViewModel's .onFailure handler), but there is no early validation for the invalid -1 postId before making the API call. Kotlin best practices recommend throwing exceptions for logic errors with conditions like invalid IDs, and Jetpack Compose documentation notes that providing default values and covering edge cases makes code safer.

Issues identified:

  • API call is made with -1 (wasteful network request for known invalid ID)
  • No explicit validation guard in CommunityPostScreen using LaunchedEffect condition
  • Missing early error check before ViewModel invocation

Recommendation: Add validation for postId != -1 before calling viewModel.getPostDetail() to prevent unnecessary API calls and provide faster user feedback.


Add postId validation in CommunityPostScreen before API call.

Include a guard condition in LaunchedEffect to check if postId is valid (-1 indicates missing argument), and navigate back or show error immediately without making an unnecessary API request:

LaunchedEffect(postId) {
    if (postId == -1) {
        navigateUp()  // or show error UI
        return@LaunchedEffect
    }
    viewModel.getPostDetail(postId)
}

This prevents wasteful API calls and provides faster error feedback when navigation arguments are missing.

🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/presentation/community/navigation/post/CommunityPostNavigation.kt
lines 23-31 the navigation extracts postId with a fallback of -1 but downstream
screens immediately call the ViewModel which triggers unnecessary API calls for
an invalid ID; update the CommunityPostScreen to validate postId before invoking
viewModel.getPostDetail by adding a guard in the LaunchedEffect (if postId == -1
then call navigateUp() or set an error state and return) so you avoid wasteful
network requests and provide immediate user feedback.

}
}

@Serializable
data class CommunityPost(val postId: String) : Route
data class CommunityPost(val postId: Int) : Route
Loading