Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal fun Project.configureBuildConfig(
val naverClientSecret = properties.getProperty("NAVER_CLIENT_SECRET") ?: ""
val appName = properties.getProperty("APP_NAME") ?: "SPOT"

val weatherUrl = properties.getProperty("WEATHER_BASE_URL") ?: ""
val weatherToken = properties.getProperty("WEATHER_TOKEN") ?: ""

commonExtension.apply {
defaultConfig {
Expand Down Expand Up @@ -48,6 +50,18 @@ internal fun Project.configureBuildConfig(
"APP_NAME",
"\"$appName\""
)

buildConfigField(
"String",
"WEATHER_BASE_URL",
"\"$weatherUrl\""
)

buildConfigField(
"String",
"WEATHER_TOKEN",
"\"$weatherToken\""
)
}

buildFeatures {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.umcspot.spot.buildconfig.di

import com.umcspot.spot.buildconfig.impl.WeatherConfigFieldsProviderImpl
import com.umcspot.spot.common.BuildConfigFieldProvider
import com.umcspot.spot.common.WeatherConfigFieldProvider
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
@Module
object WeatherConfigModule {
@Provides
@Singleton
fun provideWeatherConfigFieldsProvider(
weatherConfigFieldProvider: WeatherConfigFieldsProviderImpl
): WeatherConfigFieldProvider = weatherConfigFieldProvider
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.umcspot.spot.buildconfig.impl

import com.umcspot.spot.buildconfig.BuildConfig.WEATHER_BASE_URL
import com.umcspot.spot.buildconfig.BuildConfig.WEATHER_TOKEN
import com.umcspot.spot.common.WeatherConfigFieldProvider
import com.umcspot.spot.common.WeatherConfigFields
import javax.inject.Inject

class WeatherConfigFieldsProviderImpl @Inject constructor() : WeatherConfigFieldProvider {
override fun getWeather(): WeatherConfigFields =
WeatherConfigFields(
weatherUrl = WEATHER_BASE_URL,
token = WEATHER_TOKEN
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.umcspot.spot.common

interface WeatherConfigFieldProvider {
fun getWeather(): WeatherConfigFields
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.umcspot.spot.common

data class WeatherConfigFields(
val weatherUrl: String,
val token: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import com.umcspot.spot.designsystem.theme.B400
import com.umcspot.spot.designsystem.theme.Black
import com.umcspot.spot.designsystem.theme.SpotTheme
import com.umcspot.spot.designsystem.theme.White
import com.umcspot.spot.ui.extension.screenWidthDp

data class ImageButtonColors(
val bg: Color,
Expand Down Expand Up @@ -74,7 +75,27 @@ enum class ImageButtonState(
bg = B100,
icon = B400
)
),

XOUTLINETransparentState(
normal = ImageButtonColors(
bg = Color.Transparent,
icon = Black
),
disabled = ImageButtonColors(
bg = White,
icon = B400
),
pressed = ImageButtonColors(
bg = B200,
icon = B400
),
selected = ImageButtonColors(
bg = B100,
icon = B400
)
)

}

fun ImageButtonState.resolveColors(
Expand All @@ -83,33 +104,20 @@ fun ImageButtonState.resolveColors(
checked: Boolean
): ImageButtonColors = when {
!enabled -> disabled
checked -> selected
checked -> selected
isPressed -> pressed
else -> normal
}

/** 이미지 전용 버튼 크기 토큰 */
enum class ImageButtonSize(
val side: Dp, // 버튼 한 변(정사각형)
val icon: Dp // 아이콘/이미지 한 변
) {
XL(56.dp, 28.dp),
L (52.dp, 24.dp),
M (48.dp, 22.dp),
S (44.dp, 20.dp),
XS(40.dp, 18.dp);
}

@Composable
fun BlankButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
size: Dp,
enabled: Boolean = true,
checked: Boolean = false,
state: ImageButtonState = ImageButtonState.XOUTLINEState,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape : Shape = SpotShapes.Hard,
shape: Shape = SpotShapes.Hard,
content: @Composable BoxScope.() -> Unit = {}

) {
Expand All @@ -118,7 +126,6 @@ fun BlankButton(

Box(
modifier = modifier
.defaultMinSize(size, size)
.semantics { role = Role.Button }
.clip(shape) // 클릭 영역과 모서리 일치
.clickable(
Expand Down Expand Up @@ -150,126 +157,27 @@ private fun preview() {
SpotTheme {
BlankButton(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 13.dp, vertical = 8.dp),
size = 71.dp,
.padding(screenWidthDp(13.dp))
.size(screenWidthDp(71.dp)),
onClick = {}
) {
Column (
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(R.drawable.prefer_location),
contentDescription = null,
tint = Color.Unspecified,
modifier = Modifier.size(32.dp)
modifier = Modifier.size(screenWidthDp(31.dp))
)
Spacer(Modifier.height(6.dp))
Spacer(Modifier.height(screenWidthDp(7.dp)))
Text(
text = "내 지역",
style = SpotTheme.typography.small_500,
fontSize = 14.sp,
style = SpotTheme.typography.regular_500,
color = Black,
maxLines = 1
)
}
}
}
}

@Composable
fun ImageButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
size: ImageButtonSize = ImageButtonSize.M,
enabled: Boolean = true,
checked: Boolean = false,
state: ImageButtonState = ImageButtonState.XOUTLINEState,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },

painter: Painter? = null,

useTint: Boolean = true,
contentDescription: String? = null,
) {
val isPressed by interactionSource.collectIsPressedAsState()
val colors = state.resolveColors(enabled = enabled, isPressed = isPressed, checked = checked)

Box(
modifier = modifier
.size(size.side)
.semantics { role = Role.Button }
.clickable(
enabled = enabled,
interactionSource = interactionSource,
indication = null,
onClick = onClick
),
contentAlignment = Alignment.Center
) {
// ✅ 보더 없이 배경만: ShapeBox 재사용
ShapeBox(
shape = SpotShapes.Hard,
color = colors.bg, // 상태별 배경색
borderWidth = 0.dp, // 테두리 없음
borderColor = null, // 안전하게 보더 비활성화
modifier = Modifier.size(size.side)
)

painter?.let { p ->
if (useTint) {
Icon(
painter = p,
contentDescription = contentDescription,
tint = colors.icon,
modifier = Modifier.size(size.icon)
)
} else {
Image(
painter = p,
contentDescription = contentDescription,
modifier = Modifier.size(size.icon)
)
}
}
}
}


// --- 편의 함수: Painter 버전
@Composable
fun ImageButtonPainterM(
painter: Painter,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
state: ImageButtonState = ImageButtonState.XOUTLINEState,
useTint: Boolean = true,
checked: Boolean = false,
contentDescription: String? = null
) = ImageButton(
onClick = onClick,
modifier = modifier,
size = ImageButtonSize.M,
enabled = enabled,
state = state,
painter = painter,
useTint = useTint,
checked = checked,
contentDescription = contentDescription
)

@Preview(showBackground = true)
@Composable
fun ImageButtonMPreview() {
SpotTheme {
ImageButtonPainterM(
modifier = Modifier.padding(10.dp),
painter = painterResource(R.drawable.search),
onClick = {},
checked = false,
useTint = true,
contentDescription = "검색"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import com.umcspot.spot.designsystem.R
import com.umcspot.spot.designsystem.shapes.SpotShapes
import com.umcspot.spot.designsystem.theme.SpotTheme
import com.umcspot.spot.model.WeatherType
import com.umcspot.spot.ui.extension.screenHeightDp
import com.umcspot.spot.ui.extension.screenWidthDp
import java.time.LocalTime

@Composable
Expand All @@ -36,34 +38,30 @@ fun WeatherCard(
modifier: Modifier = Modifier,
) {
// 1) 배경 이미지 결정
val isDay = currentTime?.hour in 6..17
val isDay = currentTime?.hour in 7..18
val backgroundRes = when {
!isDay -> R.drawable.night_background
weatherType == WeatherType.RAIN -> R.drawable.rainy_background
weatherType == WeatherType.HEAVYRAIN -> R.drawable.rainy_background
else -> R.drawable.default_background
}

// 2) 날씨별 아이콘, 그라데이션 오버레이, 메시지
val (iconRes, message) = when (weatherType) {
WeatherType.HEAVYRAIN -> R.drawable.heavy_rain to "실내에서 집중! 목표는 선명히!"
WeatherType.RAIN -> R.drawable.light_rain to "우산 챙기고 오늘도 파이팅!"
WeatherType.SNOW -> R.drawable.heavy_snow to "눈길 조심! 한 걸음씩 나아가요!"
WeatherType.RAIN -> R.drawable.light_rain to "실내에서 집중! 목표는 선명히!"
WeatherType.SNOW -> R.drawable.heavy_snow to "눈길 조심! 한 걸음씩 나아가요"
WeatherType.WIND -> R.drawable.gale to "바람 조심! 흔들려도 전진!"
WeatherType.COLD -> R.drawable.cold to "따뜻하게! 오늘도 열정 가득!"
WeatherType.COLD -> R.drawable.cold to "날씨가 추워요! 실내 공부 추천"
WeatherType.HOT -> R.drawable.hot to "수분 보충! 더위도 이겨내요!"
WeatherType.SUNNY -> R.drawable.sunny to "좋은 날! 목표 향해 달려요!"
WeatherType.SUNNY -> R.drawable.sunny to "좋은 날! 목표 향해 달려요!"
null -> R.drawable.spot_logo to "날씨를 불러오는 중 입니다."
}



Card(
shape = SpotShapes.Hard, // ✅ 프로젝트 정의 모서리
shape = SpotShapes.Hard,
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
modifier = modifier
.width(190.dp)
.height(80.dp)
.width(screenWidthDp(156.dp))
.height(screenHeightDp(79.dp))
) {
Box(Modifier.fillMaxSize()) {
// 배경 이미지
Expand All @@ -77,30 +75,32 @@ fun WeatherCard(
Box(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 12.dp, vertical = 8.dp)
.padding(horizontal = screenWidthDp(10.dp), vertical = screenHeightDp(7.dp))
) {
Column(
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxSize()
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom
) {
Image(
painter = painterResource(iconRes),
contentDescription = null,
modifier = Modifier.size(40.dp)
modifier = Modifier.size(screenWidthDp(40.dp))
)
Spacer(Modifier.width(8.dp))
Spacer(Modifier.width(screenWidthDp(13.dp)))
Text(
text = "${"%.1f".format(temperature?.toFloat())} °C",
style = SpotTheme.typography.h1,
fontSize = 25.sp,
color = SpotTheme.colors.white
color = SpotTheme.colors.white,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
Text(
text = message,
style = SpotTheme.typography.small_500,
fontSize = 14.sp,
style = SpotTheme.typography.regular_500,
color = SpotTheme.colors.white
)
}
Expand Down
Loading