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
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
}

Expand All @@ -78,6 +79,12 @@ dependencies {
implementation(libs.retrofit)
implementation(libs.converter.gson)

// OKHttp
implementation(libs.logging.interceptor)

// DataStore
implementation(libs.androidx.datastore.preferences)

implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.material)
implementation(libs.androidx.material.icons.core)
Expand Down
21 changes: 19 additions & 2 deletions app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.sampoom.android.R
import com.sampoom.android.feature.auth.ui.LoginScreen
import com.sampoom.android.feature.auth.ui.SignUpScreen
import com.sampoom.android.feature.part.ui.PartScreen

const val ROUTE_LOGIN = "login"
const val ROUTE_SIGNUP = "signup"
const val ROUTE_HOME = "home"

// Main Screen
Expand Down Expand Up @@ -51,7 +53,7 @@ fun AppNavHost() {
val navController = rememberNavController()

// TODO: 임시 로그인 상태 확인 -> AuthRepository에서 확인하도록 변경
val isLoggedIn = true
val isLoggedIn = false

NavHost(
navController = navController,
Expand All @@ -62,7 +64,22 @@ fun AppNavHost() {
navController.navigate(ROUTE_HOME) {
popUpTo(ROUTE_LOGIN) { inclusive = true } // 로그인 화면 스택 제거
}
})
},
onNavigateSignUp = {
navController.navigate(ROUTE_SIGNUP)
})
}
composable(ROUTE_SIGNUP) {
SignUpScreen(
onSuccess = {
navController.navigate(ROUTE_HOME) {
popUpTo(ROUTE_LOGIN) { inclusive = true }
}
Comment thread
Sangyoon98 marked this conversation as resolved.
},
onNavigateBack = {
navController.navigateUp()
}
)
}
composable(ROUTE_HOME) { MainScreen(navController) }
composable(ROUTE_PARTS) { PartScreen() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.sampoom.android.core.network

import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import retrofit2.HttpException

data class ApiErrorResponse(
val code: Int? = null,
val message: String? = null
)

fun Throwable.serverMessageOrNull(): String? {
if (this is HttpException) {
val errorBody = response()?.errorBody()?.string() ?: return null
return try {
Gson().fromJson(errorBody, ApiErrorResponse::class.java).message
} catch (_: JsonSyntaxException) {
null
} catch (_: Exception) {
null
}
}
return null
}


Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,37 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
import com.google.gson.GsonBuilder
import com.google.gson.FieldNamingPolicy
import com.sampoom.android.BuildConfig
import okhttp3.logging.HttpLoggingInterceptor
import java.util.concurrent.TimeUnit

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides @Singleton fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder().build()
@Provides @Singleton fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG)
HttpLoggingInterceptor.Level.BODY
else
HttpLoggingInterceptor.Level.NONE
})
// TODO: 로그인 기능 연동 후 인증 인터셉터 추가 필요
.build()

@Provides @Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit =
Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080/api/")
fun provideRetrofit(client: OkHttpClient): Retrofit {
val gson = GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
return Retrofit.Builder()
.baseUrl("https://sampoom.store/api/")
Comment thread
Sangyoon98 marked this conversation as resolved.
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.sampoom.android.core.ui.component

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.FilledTonalButton
Expand All @@ -18,6 +18,10 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.sampoom.android.core.ui.theme.Main500
import com.sampoom.android.core.ui.theme.White
import com.sampoom.android.core.ui.theme.disableColor
import com.sampoom.android.core.ui.theme.textSecondaryColor

/**
* Sampoom common button with multiple visual variants.
Expand All @@ -40,15 +44,14 @@ import androidx.compose.ui.unit.dp
*/
@Composable
fun CommonButton(
text: String,
modifier: Modifier = Modifier,
enabled: Boolean = true,
variant: ButtonVariant = ButtonVariant.Primary,
size: ButtonSize = ButtonSize.Large,
leadingIcon: (@Composable (() -> Unit))? = null,
onClick: () -> Unit
onClick: () -> Unit,
content: @Composable RowScope.() -> Unit
) {
Comment thread
Sangyoon98 marked this conversation as resolved.
val cs = MaterialTheme.colorScheme
val shape = MaterialTheme.shapes.large
val height = when (size) {
ButtonSize.Large -> 56.dp
Expand All @@ -64,21 +67,14 @@ fun CommonButton(
shape = shape,
modifier = modifier.height(height),
colors = ButtonDefaults.buttonColors(
containerColor = cs.primary,
contentColor = cs.onPrimary,
disabledContainerColor = cs.onSurface.copy(alpha = 0.12f),
disabledContentColor = cs.onSurface.copy(alpha = 0.38f),
containerColor = Main500,
contentColor = White,
disabledContainerColor = disableColor(),
disabledContentColor = textSecondaryColor()
)
) {
if (leadingIcon != null) {
leadingIcon()
}
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(start = if (leadingIcon != null) 8.dp else 0.dp)
)
if (leadingIcon != null) leadingIcon()
content()
}
}

Expand All @@ -90,21 +86,14 @@ fun CommonButton(
shape = shape,
modifier = modifier.height(height),
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = cs.secondaryContainer,
contentColor = cs.onSecondaryContainer,
disabledContainerColor = cs.onSurface.copy(alpha = 0.08f),
disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
containerColor = Main500,
contentColor = White,
disabledContainerColor = disableColor(),
disabledContentColor = textSecondaryColor()
)
) {
if (leadingIcon != null) {
leadingIcon()
}
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(start = if (leadingIcon != null) 8.dp else 0.dp)
)
if (leadingIcon != null) leadingIcon()
content()
}
}

Expand All @@ -115,17 +104,14 @@ fun CommonButton(
enabled = enabled,
shape = shape,
modifier = modifier.height(height),
border = BorderStroke(1.dp, cs.primary),
border = BorderStroke(1.dp, Main500),
colors = ButtonDefaults.outlinedButtonColors(
contentColor = cs.primary,
disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
contentColor = Main500,
disabledContentColor = textSecondaryColor()
)
) {
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold
)
if (leadingIcon != null) leadingIcon()
content()
}
}

Expand All @@ -137,15 +123,12 @@ fun CommonButton(
shape = shape,
modifier = modifier.height(height),
colors = ButtonDefaults.textButtonColors(
contentColor = cs.onSurface,
disabledContentColor = cs.onSurface.copy(alpha = 0.38f)
contentColor = textSecondaryColor(),
disabledContentColor = textSecondaryColor()
)
) {
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold
)
if (leadingIcon != null) leadingIcon()
content()
}
}

Expand All @@ -159,15 +142,12 @@ fun CommonButton(
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF000000),
contentColor = Color.White,
disabledContainerColor = cs.onSurface.copy(alpha = 0.12f),
disabledContentColor = cs.onSurface.copy(alpha = 0.38f),
disabledContainerColor = disableColor(),
disabledContentColor = textSecondaryColor()
)
) {
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.SemiBold
)
if (leadingIcon != null) leadingIcon()
content()
}
}
}
Expand Down Expand Up @@ -197,7 +177,7 @@ enum class ButtonSize { Large, Medium, Small }
private fun CommonButtonPreview_All() {
// Primary
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Primary,
onClick = {}
)
Expand All @@ -207,7 +187,7 @@ private fun CommonButtonPreview_All() {
@Composable
private fun CommonButtonPreview_Primary_WithIcon() {
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Primary,
leadingIcon = { Icon(painterResource(android.R.drawable.ic_menu_call), contentDescription = null) },
onClick = {}
Expand All @@ -218,7 +198,7 @@ private fun CommonButtonPreview_Primary_WithIcon() {
@Composable
private fun CommonButtonPreview_Tonal() {
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Secondary,
onClick = {}
)
Expand All @@ -228,7 +208,7 @@ private fun CommonButtonPreview_Tonal() {
@Composable
private fun CommonButtonPreview_Outlined() {
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Outlined,
onClick = {}
)
Expand All @@ -238,7 +218,7 @@ private fun CommonButtonPreview_Outlined() {
@Composable
private fun CommonButtonPreview_Ghost() {
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Ghost,
onClick = {}
)
Expand All @@ -248,7 +228,7 @@ private fun CommonButtonPreview_Ghost() {
@Composable
private fun CommonButtonPreview_Neutral_Disabled() {
CommonButton(
text = "Button",
content = { Text("Button", fontWeight = FontWeight.Bold) },
variant = ButtonVariant.Neutral,
enabled = false,
onClick = {}
Expand Down
Loading