Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

전역 네트워크 로딩, 타임아웃 다이얼로그 구현 #87

Merged
merged 3 commits into from
Oct 7, 2024
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
29 changes: 27 additions & 2 deletions app/src/main/java/com/whyranoid/walkie/KoinModules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ import com.whyranoid.presentation.screens.mypage.editprofile.EditProfileViewMode
import com.whyranoid.presentation.screens.mypage.following.FollowingViewModel
import com.whyranoid.presentation.screens.setting.SettingViewModel
import com.whyranoid.presentation.viewmodel.AddPostViewModel
import com.whyranoid.presentation.util.ApiResponseDialog
import com.whyranoid.presentation.viewmodel.CommunityScreenViewModel
import com.whyranoid.presentation.viewmodel.RunningEditViewModel
import com.whyranoid.presentation.viewmodel.RunningViewModel
Expand All @@ -104,6 +105,7 @@ import com.whyranoid.presentation.viewmodel.challenge.ChallengeDetailViewModel
import com.whyranoid.presentation.viewmodel.challenge.ChallengeExitViewModel
import com.whyranoid.presentation.viewmodel.challenge.ChallengeMainViewModel
import com.whyranoid.walkie.walkiedialog.DialogViewModel
import com.whyranoid.walkie.walkiedialog.NetworkInterceptor
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
Expand All @@ -120,7 +122,19 @@ val viewModelModule =
viewModel { ChallengeMainViewModel(get(), get(), get(), get(), get()) }
viewModel { ChallengeDetailViewModel(get(), get()) }
viewModel { ChallengeExitViewModel(get(), get()) }
viewModel { UserPageViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
viewModel {
UserPageViewModel(
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get(),
get()
)
}
viewModel { RunningViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { RunningEditViewModel() }
viewModel { SplashViewModel(get()) }
Expand Down Expand Up @@ -200,7 +214,7 @@ val useCaseModule =
single { GetMyFollowingUseCase(get(), get()) }
single { SendCommentUseCase(get(), get()) }
single { GetUserUseCase(get()) }
single { ChangeChallengeStatusUseCase(get(), get())}
single { ChangeChallengeStatusUseCase(get(), get()) }
single { GetUserPostsUseCase(get(), get()) }
}

Expand Down Expand Up @@ -242,6 +256,17 @@ val networkModule =
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
},
).addInterceptor(
NetworkInterceptor(
onRequest = { ApiResponseDialog.startLoading() },
onResponse = { ApiResponseDialog.finishLoad(it) },
excludedUrls = listOf(
Regex("/api/follow/(\\d+)/following"), // polling 방식 업데이트
Regex("/api/follow/(\\d+)/walking-followings"), // polling 방식 업데이트
Regex("/api/community/listup-post"), // 커뮤니티 탭, 자체 로딩바 있음
Regex("/api/community/upload-post") // 게시글 업로드, 자체 로딩바 있음
)
)
)
.build()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.whyranoid.walkie.walkiedialog

import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response

class NetworkInterceptor(
private val onRequest: () -> Unit,
private val onResponse: (isSuccessFul: Boolean) -> Unit,
private val excludedUrls: List<Regex>,
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
Log.d("ju0828", request.url.toString())

Log.d("ju0828", "${request.url} validate = ${excludedUrls.all { request.url.toString().contains(it).not() }}")

// 요청 URL이 제외할 URL과 같지 않으면 콜백 호출
if (excludedUrls.all { request.url.toString().contains(it).not() }) {
onRequest() // 요청 전 콜백 호출
Log.d("ju0828", "${request.url} request called")

}

return try {
val response = chain.proceed(request)

// 응답 후 콜백 호출
if (excludedUrls.all { request.url.toString().contains(it).not() }) {
onResponse(response.isSuccessful) // 응답 후 콜백 호출
}
response
} catch (e: Exception) {
// 예외가 발생한 경우 (타임아웃 포함)
if (e is java.net.SocketTimeoutException) {
onResponse(false)
}
throw e // 예외를 다시 던져서 호출자에게 알림
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.whyranoid.presentation.reusable

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -17,6 +18,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.whyranoid.presentation.theme.WalkieColor
Expand All @@ -28,16 +30,31 @@ fun CircleProgressWithText(
modifier: Modifier = Modifier,
text: String,
) {
Box(modifier = modifier.fillMaxSize().alpha(0.5f).background(WalkieColor.GrayDefault))
Box(
modifier = modifier
.fillMaxSize()
.alpha(0.5f)
.background(WalkieColor.GrayDefault)
.pointerInput(Unit) { // 터치 이벤트 소비
detectTapGestures(onPress = {
// 아무것도 하지 않음, 터치 이벤트 소비
})
})
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = modifier.width(200.dp).height(160.dp).align(Alignment.Center)
.clip(RoundedCornerShape(20.dp)).background(Color.White),
modifier = modifier
.width(200.dp)
.height(160.dp)
.align(Alignment.Center)
.clip(RoundedCornerShape(20.dp))
.background(Color.White),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(text, style = WalkieTypography.SubTitle)
Spacer(modifier = Modifier.height(20.dp))
if (text.isNotEmpty()) {
Text(text, style = WalkieTypography.SubTitle)
Spacer(modifier = Modifier.height(20.dp))
}
CircularProgressIndicator()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Icon
Expand All @@ -13,6 +14,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
Expand All @@ -27,6 +29,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.whyranoid.domain.model.post.Post
import com.whyranoid.presentation.reusable.CircleProgressWithText
import com.whyranoid.presentation.screens.Screen.Companion.bottomNavigationItems
import com.whyranoid.presentation.screens.challenge.ChallengeCompleteScreen
import com.whyranoid.presentation.screens.challenge.ChallengeDetailScreen
Expand All @@ -47,6 +50,8 @@ import com.whyranoid.presentation.screens.signin.SignInScreen
import com.whyranoid.presentation.screens.splash.SplashScreen
import com.whyranoid.presentation.theme.WalkieColor
import com.whyranoid.presentation.theme.WalkieTypography
import com.whyranoid.presentation.util.CustomDialog
import com.whyranoid.presentation.util.ApiResponseDialog
import com.whyranoid.presentation.viewmodel.SplashState
import com.whyranoid.presentation.viewmodel.SplashViewModel
import org.koin.androidx.compose.koinViewModel
Expand Down Expand Up @@ -249,5 +254,18 @@ fun AppScreenContent(
)
}
}

val isLoading = ApiResponseDialog.isLoading.collectAsStateWithLifecycle()
val isError = ApiResponseDialog.isShowError.collectAsStateWithLifecycle()
if (isLoading.value) {
CircleProgressWithText(text = "")
} else if (isError.value) {
CustomDialog(
title = "네트워크 연결 실패",
description = "네트워크 연결이 끊겼거나 속도가 느립니다.\n다시 시도해주세요.",
onAction = { ApiResponseDialog.closeErrorDialog() },
Modifier.clip(RoundedCornerShape(20.dp))
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -52,7 +53,7 @@ fun SignInUserNameScreen(onSuccess: () -> Unit) {
val userNameState = signInState.value as SignInState.UserNameState

Surface(
modifier = Modifier.background(Color.White).padding(20.dp),
modifier = Modifier.background(Color.White).padding(20.dp).systemBarsPadding(),
) {
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.Start) {
Spacer(modifier = Modifier.height(68.dp))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.whyranoid.presentation.util


import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.util.concurrent.atomic.AtomicInteger

object ApiResponseDialog {
private val loadingCount = AtomicInteger(0)

private val _isLaoding = MutableStateFlow(false)
val isLoading get() = _isLaoding.asStateFlow()

private val _isShowError = MutableStateFlow(false)

val isShowError get() = _isShowError.asStateFlow()

fun startLoading() {
if (loadingCount.incrementAndGet() > 0) _isLaoding.value = true
}

fun finishLoad(isSuccessFul: Boolean) {
if (loadingCount.decrementAndGet() == 0) _isLaoding.value = false
if (isSuccessFul.not()) {
_isLaoding.value = false
_isShowError.value = true
}
}

fun closeErrorDialog() {
_isShowError.value = false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.whyranoid.presentation.util

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.whyranoid.presentation.theme.WalkieColor
import com.whyranoid.presentation.theme.WalkieTypography

@Composable
fun CustomDialog(
title: String,
description: String,
onAction: () -> Unit,
modifier: Modifier = Modifier,
) {
AlertDialog(
onDismissRequest = onAction,
buttons = {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
text = "확인",
style = WalkieTypography.SubTitle.copy(color = WalkieColor.Primary),
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
) {
onAction()
}
.padding(bottom = 20.dp)
.padding(horizontal = 20.dp),
)
}
},
title = {
Text(
modifier = Modifier.fillMaxWidth(),
text = title, style = WalkieTypography.SubTitle,
textAlign = TextAlign.Center,
)
},
text = {
Text(
modifier = Modifier.fillMaxWidth(),
text = description,
style = WalkieTypography.Body1_Normal,
textAlign = TextAlign.Center,
)
},
modifier = modifier,
)
}
Loading