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
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package com.flint.android.core.common.extension

import android.content.Context
import android.graphics.BlurMaskFilter
import android.graphics.Rect
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect as ComposeRect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.PathFillType
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.addOutline
import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import com.flint.android.core.designsystem.interaction.flintNoFeedbackClickable

/**
Expand Down Expand Up @@ -80,16 +82,66 @@ fun Modifier.dropShadow(
}
}

fun Modifier.draw9Patch(
context: Context,
@DrawableRes ninePatchRes: Int,
) = this.drawBehind {
drawIntoCanvas {
ContextCompat.getDrawable(context, ninePatchRes)?.let { ninePatch ->
ninePatch.run {
bounds = Rect(0, 0, size.width.toInt(), size.height.toInt())
draw(it.nativeCanvas)
@Composable
fun Modifier.innerShadow(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

draw9Patch(90번 줄)가 이제 사용처 없는 죽은 코드가 되었습니다.

git grep draw9Patch 결과 선언 하나만 남았습니다 — 유일한 호출부였던 ProfileKeywordChip이 glassTagBackground로 교체되었습니다.

함께 app/src/main/res/drawable/ 의 9-patch 6개도 참조가 사라져 APK에만 남습니다:
bg_tag_blue.9.png, bg_tag_gray.9.png, bg_tag_green.9.png, bg_tag_orange.9.png, bg_tag_pink.9.png, bg_tag_yellow.9.png

isMinifyEnabled = false이고 리소스 축소도 꺼져 있어 자동 정리되지 않습니다. 이번 PR에서 draw9Patch와 9-patch 리소스를 같이 삭제해 주세요.

shape: Shape,
color: Color = Color.Black,
blur: Dp = 4.dp,
offsetX: Dp = 0.dp,
offsetY: Dp = 0.dp,
) = composed {
val density = LocalDensity.current

// dropShadow와 동일하게 Paint/BlurMaskFilter를 remember (매 프레임 재할당 방지)
val shadowPaint = remember(color, blur) {
Paint().apply {
this.color = color
val blurPx = with(density) { blur.toPx() }
if (blurPx > 0f) {
this.asFrameworkPaint().maskFilter =
BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL)
}
}
}

val maskPaint = remember {
Paint().apply {
this.color = Color.Black
blendMode = BlendMode.DstIn
}
}

// drawBehind는 콘텐츠보다 먼저 그려져서, 그림자가 텍스트를 덮지 않는다
drawBehind {
if (size.minDimension <= 0f) return@drawBehind

val outline = shape.createOutline(size, layoutDirection, this)

val holePath = Path().apply {
fillType = PathFillType.EvenOdd
addRect(
ComposeRect(
-size.width,
-size.height,
size.width * 2f,
size.height * 2f,
),
)
addOutline(outline)
}

drawIntoCanvas { canvas ->
canvas.saveLayer(ComposeRect(Offset.Zero, size), Paint())

val dx = offsetX.toPx()
val dy = offsetY.toPx()
canvas.translate(dx, dy)
canvas.drawPath(holePath, shadowPaint)
canvas.translate(-dx, -dy)

canvas.drawOutline(outline, maskPaint)

canvas.restore()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.flint.android.domain.type

import androidx.annotation.DrawableRes
import androidx.compose.ui.graphics.Color
import com.flint.android.R
import com.flint.android.core.designsystem.theme.FlintColors

sealed class KeywordType {
Expand All @@ -15,26 +13,10 @@ sealed class KeywordType {

enum class PreferenceType(
val color: Color,
@DrawableRes val backgroundRes: Int,
) {
GREEN(
color = FlintColors.green,
backgroundRes = R.drawable.bg_tag_green,
),
ORANGE(
color = FlintColors.orange,
backgroundRes = R.drawable.bg_tag_orange,
),
YELLOW(
color = FlintColors.yellow,
backgroundRes = R.drawable.bg_tag_yellow,
),
BLUE(
color = FlintColors.blue,
backgroundRes = R.drawable.bg_tag_blue,
),
PINK(
color = FlintColors.pink,
backgroundRes = R.drawable.bg_tag_pink,
),
GREEN(color = FlintColors.green),
ORANGE(color = FlintColors.orange),
YELLOW(color = FlintColors.yellow),
BLUE(color = FlintColors.blue),
PINK(color = FlintColors.pink),
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package com.flint.android.presentation.login

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
Expand All @@ -31,11 +31,12 @@ import com.flint.android.domain.type.ProviderType
import com.flint.android.presentation.login.component.KakaoLoginButton
import com.flint.android.presentation.login.event.LoginNavigationEvent
import com.flint.android.presentation.login.data.VerifyStatusData
import com.flint.android.presentation.splash.SplashLogoMetrics
import timber.log.Timber
import kotlin.math.max

@Composable
fun LoginRoute(
paddingValues: PaddingValues,
navigateToOnBoarding: (tempToken: String) -> Unit,
navigateToHome: () -> Unit,
viewModel: LoginViewModel = hiltViewModel(),
Expand All @@ -61,6 +62,8 @@ fun LoginRoute(
}
}

// Splash -> Login 전환 시 로고 위치가 그대로 이어지도록, Splash와 동일하게
// 화면 전체(엣지투엣지)를 기준으로 배치
LoginScreen(
onKakaoLoginClick = {
kakaoLoginManager.login(context) { result ->
Expand All @@ -76,7 +79,6 @@ fun LoginRoute(
}
}
},
modifier = Modifier.padding(paddingValues)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LoginRoute의 paddingValues 파라미터가 죽은 인자가 되었습니다.

유일한 사용처였던 modifier = Modifier.padding(paddingValues)가 이 PR에서 제거되면서, LoginRoute(paddingValues = ...)와 loginNavGraph(paddingValues = ...) 배선만 남았습니다. 컴파일 경고가 뜨고, 나중에 읽는 사람이 "인셋은 여기서 처리되는구나"라고 오해하기 쉽습니다.

엣지투엣지가 의도라면 LoginRoute/loginNavGraph의 paddingValues 파라미터를 아예 제거하고 MainNavHost의 호출부도 같이 정리해 주세요.

}

Expand All @@ -85,7 +87,7 @@ fun LoginScreen(
onKakaoLoginClick: () -> Unit,
modifier: Modifier = Modifier
) {
Box(
BoxWithConstraints(
modifier =
modifier
.fillMaxSize()
Expand All @@ -99,22 +101,35 @@ fun LoginScreen(
spread = 0.dp,
),
) {
// 스플래시-> 1440x3120 캔버스를 ContentScale.Crop + 중앙 정렬로 그림
// 같은 계산을 그대로 재현해서 로고의 크기와 위치를 맞추면, 기기 화면 크기와 무관하게
// 스플래시가 끝난 그 자리에 로그인 화면의 로고가 정확히 겹침
val splashScale = max(
maxWidth.value / SplashLogoMetrics.CANVAS_WIDTH,
maxHeight.value / SplashLogoMetrics.CANVAS_HEIGHT,
)
val splashCanvasLeft = (maxWidth.value - SplashLogoMetrics.CANVAS_WIDTH * splashScale) / 2f
val splashCanvasTop = (maxHeight.value - SplashLogoMetrics.CANVAS_HEIGHT * splashScale) / 2f

Image(
painter = painterResource(R.drawable.img_flint_title),
contentDescription = null,
modifier =
Modifier
.align(Alignment.Center)
.width(160.dp)
.height(56.dp)
.offset(y = (-32).dp),
.absoluteOffset(
x = (splashCanvasLeft + SplashLogoMetrics.LOGO_LEFT * splashScale).dp,
y = (splashCanvasTop + SplashLogoMetrics.LOGO_TOP * splashScale).dp,
)
.width((SplashLogoMetrics.LOGO_WIDTH * splashScale).dp)
.height((SplashLogoMetrics.LOGO_HEIGHT * splashScale).dp),
)

KakaoLoginButton(
modifier =
Modifier
.fillMaxWidth()
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(bottom = 60.dp)
.padding(horizontal = 20.dp),
onClick = {
Expand All @@ -124,6 +139,7 @@ fun LoginScreen(
}
}


@Preview
@Composable
private fun PreviewLoginScreen() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.flint.android.presentation.login.navigation

import androidx.compose.foundation.layout.PaddingValues
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
Expand All @@ -13,13 +12,11 @@ fun NavController.navigateToLogin(navOptions: NavOptions? = null) {
}

fun NavGraphBuilder.loginNavGraph(
paddingValues: PaddingValues,
navigateToOnBoarding: (tempToken: String) -> Unit,
navigateToHome: () -> Unit,
) {
composable<Route.Login> {
LoginRoute(
paddingValues = paddingValues,
navigateToOnBoarding = navigateToOnBoarding,
navigateToHome = navigateToHome,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,11 @@ fun MainNavHost(
popExitTransition = { flintPopExitTransition() },
) {
splashNavGraph(
paddingValues = paddingValues,
navigateToLogin = navigator::navigateToLogin,
navigateToHome = navigator::navigateToHome,
)

loginNavGraph(
paddingValues = paddingValues,
navigateToOnBoarding = navigator::navigateToOnBoarding,
navigateToHome = navigator::navigateToHome,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,23 @@ class OnboardingViewModel

// ---------- onboarding content ----------
fun updateSearchKeyword(keyword: String) {
val previousKeyword = _contentUiState.value.searchKeyword
_contentUiState.update { currentState ->
currentState.copy(searchKeyword = keyword)
}

if (keyword.isEmpty() && previousKeyword.isNotEmpty()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

X 버튼 한 번에 검색 요청이 두 번 나갑니다.

FlintSearchTextField의 clear 아이콘은 두 콜백을 연달아 호출합니다:

// FlintSearchTextField.kt:57-60
if (value.isNotEmpty()) {
    onValueChanged("")   // -> updateSearchKeyword("")
    onClearAction()      // -> clearSearchKeyword()
}

이번에 updateSearchKeyword에 추가된 분기 때문에 onValueChanged("")가 이미 getSearchContentList(null, genres)를 호출하고, 뒤이어 clearSearchKeyword()가 한 번 더 호출합니다.

viewModelScope는 Dispatchers.Main.immediate라 첫 launch 블록이 동기적으로 실행되어 UiState.Loading 방출 + Retrofit 호출까지 실제로 시작된 뒤, 두 번째 호출의 searchJob?.cancel()로 취소됩니다. 즉 X 탭 1회 = 불필요한 네트워크 요청 1건 + Loading 상태 2회 플립.

FlintSearchTextField에서 onValueChanged("")만 남기고 onClearAction을 걷어내거나, 이 분기에 previousKeyword가 아니라 "직전에 실제로 검색을 날린 키워드" 기준을 두는 편이 안전합니다.

val genres = _contentUiState.value.selectedGenres
getSearchContentList(keyword = null, genres = genres)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

clearSearchKeyword()가 이제 완전한 중복입니다.

fun clearSearchKeyword() {
    _contentUiState.update { it.copy(searchKeyword = "") }
    val genres = _contentUiState.value.selectedGenres
    getSearchContentList(keyword = null, genres = genres)
}

새로 추가된 분기가 하는 일과 정확히 같습니다. 둘 중 하나만 남기고(예: clearSearchKeyword()가 updateSearchKeyword("")를 위임) 정리하면 위에서 지적한 이중 호출 문제도 같이 사라집니다.

}
}

fun loadInitialContents() {
getSearchContentList(keyword = null, genres = emptySet())
}

fun clearSearchKeyword() {
_contentUiState.update { it.copy(searchKeyword = "") }
val genres = _contentUiState.value.selectedGenres
getSearchContentList(keyword = null, genres = genres)
updateSearchKeyword("")
}

fun searchContents() {
Expand Down
Loading
Loading