Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.iml
*.jks
Copy link
Contributor

Choose a reason for hiding this comment

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

이거 무슨 폴더인지 μ•Œλ €μ€„ 수 μžˆμ–΄?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ideμ—μ„œ μžλ™μœΌλ‘œ μΆ”κ°€ν•œ ν˜•νƒœμž…λ‹ˆλ‹€.

*.keystore
.gradle
/local.properties
.idea/
Expand Down
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ android {
namespace = "com.example.linku_android"
compileSdk = 35


defaultConfig {
applicationId = "com.example.linku_android"
minSdk = 26
Expand Down
7 changes: 3 additions & 4 deletions app/src/main/java/com/example/linku_android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@ import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.Text
import androidx.core.view.WindowCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.compose.rememberNavController
import com.example.login.auth.AnimatedLoginScreen
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -20,6 +16,7 @@ class MainActivity : ComponentActivity() {


intent?.data?.let { Log.d("DEEPLINK", "onCreate uri = $it") }

WindowCompat.setDecorFitsSystemWindows(window, false)
//enableEdgeToEdge()
setContent {
Expand All @@ -29,5 +26,7 @@ class MainActivity : ComponentActivity() {
}




}
}
107 changes: 56 additions & 51 deletions app/src/main/java/com/example/linku_android/MainApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,21 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import com.example.home.HomeApp
import com.example.curation.ui.CurationDetailScreen
import com.example.curation.ui.CurationScreen
import com.example.login.auth.AnimatedLoginScreen
import com.example.login.auth.EmailVerificationScreen
import com.example.login.auth.ServiceTermsScreen
import com.example.login.auth.PrivacyTermsScreenFixed
import com.example.login.auth.MarketingTermsScreenComposable
import com.example.login.auth.SignUpPasswordScreen
import com.example.login.auth.EmailLoginScreen
import com.example.login.auth.InterestContentScreen
import com.example.login.auth.InterestPurposeScreen
import com.example.login.auth.SignUpGenderScreen
import com.example.login.auth.SignUpNicknameScreen
import com.example.login.auth.SignUpJobScreen
import com.example.login.auth.WelcomeScreen
import com.example.login.auth.ResetPasswordScreen
import com.example.login.auth.SignUpViewModel
import com.example.login.ui.animation.AnimatedLoginScreen
import com.example.login.ui.screen.EmailVerificationScreen
import com.example.login.ui.terms.ServiceTermsScreen
import com.example.login.ui.terms.PrivacyTermsScreenFixed
import com.example.login.ui.terms.MarketingTermsScreenComposable
import com.example.login.ui.screen.SignUpPasswordScreen
import com.example.login.ui.screen.EmailLoginScreen
import com.example.login.ui.screen.InterestContentScreen
import com.example.login.ui.screen.InterestPurposeScreen
import com.example.login.ui.screen.SignUpGenderScreen
import com.example.login.ui.screen.SignUpNicknameScreen
import com.example.login.ui.screen.SignUpJobScreen
import com.example.login.ui.screen.WelcomeScreen
import com.example.login.ui.screen.ResetPasswordScreen
import com.example.login.viewmodel.SignUpViewModel
import java.io.File
import java.io.FileOutputStream

Expand All @@ -82,7 +82,7 @@ import com.example.file.ui.theme.DefaultFont
import com.example.file.ui.theme.Gray600
import com.example.file.viewmodel.folder.state.FolderStateViewModel
import com.example.linku_android.deeplink.DeepLinkHandlerViewModel
import com.example.login.auth.LoginViewModel
import com.example.login.viewmodel.LoginViewModel

import dagger.hilt.android.EntryPointAccessors
import androidx.core.net.toUri
Expand Down Expand Up @@ -260,56 +260,39 @@ fun MainApp(
) {

/* β‘  Login composable */
composable(NavigationRoute.Login.route) { entry ->
val parentEntry = entry
composable(NavigationRoute.Login.route) { parentEntry ->
val signUpVm: SignUpViewModel = hiltViewModel(parentEntry)

val showTermsSheet by parentEntry.savedStateHandle
.getStateFlow("show_terms_sheet", false)
.collectAsStateWithLifecycle()

BackHandler(enabled = showTermsSheet) {
parentEntry.savedStateHandle["show_terms_sheet"] = false
}

// 이메일 μΈμ¦μ—μ„œ λ°±λ²„νŠΌμœΌλ‘œ 갔을 λ•Œ, μ•½κ΄€ νŽ˜μ΄μ§€ λ‚˜μ˜€λŠ”κ²Œ λ§žλŠ”μ§€.
val skipAnimation =
parentEntry.savedStateHandle
Copy link
Contributor

Choose a reason for hiding this comment

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

parentEntryλΌλŠ” λ³€μˆ˜λ₯Ό λ”°λ‘œ νŒŒμ„œ entryλ₯Ό κ·ΈλŒ€λ‘œ μ°Έμ‘°ν•˜κ³  μžˆλŠ”λ°, λ³€μˆ˜ μƒˆλ‘œ νŒŒμ§€ 말고 entry λ³€μˆ˜λͺ…을 parentEntry둜 λ°”κΎΈλ©΄ 쒋을 κ±° κ°™μ•„.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

λ„΅ λ§žμΆ°μ„œ λ³€κ²½ν•˜κ² μŠ΅λ‹ˆλ‹€.

.get<Boolean>("skip_login_animation") == true

// 이메일 μΈμ¦μ—μ„œ λŒμ•„μ˜€λŠ”μ§€ 확인
var cameFromEmail by remember { mutableStateOf(false) }

LaunchedEffect(navigator.currentBackStackEntry) {
if (parentEntry.savedStateHandle.get<Boolean>("from_email_verification") == true) {
cameFromEmail = true
parentEntry.savedStateHandle["show_terms_sheet"] = true

kotlinx.coroutines.delay(120)

cameFromEmail = false
parentEntry.savedStateHandle["from_email_verification"] = false
// 읽은 직후 μ΄ˆκΈ°ν™”
LaunchedEffect(skipAnimation) {
if (skipAnimation) {
parentEntry.savedStateHandle["skip_login_animation"] = false
}
}

// μ•½κ°„μ˜ μ§€μ—° + μž¬λ Œλ”λ§ μœ„ν•΄ 빈 λ°•μŠ€ λ§Œλ“¬.
if (cameFromEmail) {
Box(Modifier.fillMaxSize()) {}
return@composable
}


AnimatedLoginScreen(
navigator = navigator,
skipAnimation = skipAnimation,
onSignUpClick = {
parentEntry.savedStateHandle["show_terms_sheet"] = true
}
)

}

/* β‘‘ Service Terms */
composable("terms/service") { entry ->
val parentEntry = remember(entry) { navigator.getBackStackEntry("auth_graph") }
val vm: SignUpViewModel = hiltViewModel(parentEntry)

//μ‹œμŠ€ν…œ λ°±λ²„νŠΌ 처리
BackHandler {
parentEntry.savedStateHandle["show_terms_sheet"] = true
navigator.popBackStack()
}

ServiceTermsScreen(
onBackClicked = {
parentEntry.savedStateHandle["show_terms_sheet"] = true
Expand Down Expand Up @@ -368,6 +351,15 @@ fun MainApp(


val vm: SignUpViewModel = hiltViewModel(parentEntry)
//λ°±λ²„νŠΌμœΌλ‘œ 온 경우 μ• λ‹ˆλ©”μ΄μ…˜ 적용X
BackHandler {
// 둜그인 ν™”λ©΄(AnimatedLoginScreen)에 μ• λ‹ˆλ©”μ΄μ…˜ μŠ€ν‚΅ ν”Œλž˜κ·Έ 전달함.
parentEntry.savedStateHandle["skip_login_animation"] = true
parentEntry.savedStateHandle["from_email_verification"] = true

navigator.popBackStack()
}
Comment on lines +354 to +361
Copy link

@coderabbitai coderabbitai bot Jan 21, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

email_verification BackHandlerκ°€ ν™”λ©΄ λ‚΄λΆ€ ν•Έλ“€λŸ¬μ— κ°€λ €μ§ˆ 수 있음
feature/login/src/main/java/com/example/login/ui/screen/EmailVerificationScreen.kt 내뢀에도 BackHandlerκ°€ μžˆμ–΄ 이 블둝이 μ‹€ν–‰λ˜μ§€ μ•ŠμœΌλ©΄ skip_login_animation이 μ„ΈνŒ…λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν•œ 곳으둜 ν†΅ν•©ν•˜κ±°λ‚˜ λ‚΄λΆ€ ν•Έλ“€λŸ¬μ—λ„ 동일 ν”Œλž˜κ·Έ μ„ΈνŒ…μ„ μΆ”κ°€ν•΄ μ£Όμ„Έμš”.

πŸ€– Prompt for AI Agents
In `@app/src/main/java/com/example/linku_android/MainApp.kt` around lines 348 -
355, The BackHandler in MainApp.kt may be bypassed by a local BackHandler inside
EmailVerificationScreen, so ensure the skip flags are always set: either
remove/centralize duplicate BackHandler logic or add the same savedStateHandle
assignments inside the internal BackHandler in EmailVerificationScreen (set
parentEntry.savedStateHandle["skip_login_animation"] = true and
["from_email_verification"] = true before popping the navigator). Update the
handling in the EmailVerificationScreen's BackHandler or centralize the
BackHandler to the parent so the skip_login_animation/from_email_verification
flags are reliably written.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이메일 인증 화면을 μ •μƒμ μœΌλ‘œ 거쳐 둜그인 ν™”λ©΄μœΌλ‘œ λ³΅κ·€ν•˜λŠ” κ²½μš°μ—λ§Œ μ‚¬μš©ν•˜κΈ°μ—, μ½”λ“œ μˆ˜μ •μ΄ λΆˆν•„μš”ν•΄λ³΄μž…λ‹ˆλ‹€.

Copy link

Choose a reason for hiding this comment

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

@KateteDeveloper, μ„€λͺ… κ°μ‚¬ν•©λ‹ˆλ‹€! 이메일 인증 ν™”λ©΄μ—μ„œ λ’€λ‘œκ°€κΈ°λ‘œ 둜그인 화면에 볡귀할 λ•Œλ§Œ ν•΄λ‹Ή ν”Œλž˜κ·Έκ°€ ν•„μš”ν•˜λ‹€λŠ” 점 μ΄ν•΄ν–ˆμŠ΅λ‹ˆλ‹€. μ˜λ„λœ λ™μž‘μ΄λΌλ©΄ ν˜„μž¬ κ΅¬ν˜„μ΄ μ μ ˆν•©λ‹ˆλ‹€. πŸ‘


EmailVerificationScreen(
navigator = navigator,
parentEntry = parentEntry, // β¬… μΆ”κ°€
Expand Down Expand Up @@ -433,14 +425,19 @@ fun MainApp(

composable("email_login") {

val parentEntry = remember {
val parentEntry = remember(navigator.currentBackStackEntry) {
navigator.getBackStackEntry("auth_graph")
}

val showTermsSheet by parentEntry.savedStateHandle
.getStateFlow("show_terms_sheet", false)
.collectAsStateWithLifecycle()

//μ•½κ΄€ λ°”ν…€μ‹œνŠΈ λ–  μžˆμ„ λ•Œ λ°±λ²„νŠΌ = μ‹œνŠΈ λ‹«κΈ°
BackHandler(enabled = showTermsSheet) {
parentEntry.savedStateHandle["show_terms_sheet"] = false
}

LaunchedEffect(Unit) { showNavBar = false }

// 둜그인 μƒνƒœ κ΄€μ°°
Expand Down Expand Up @@ -821,12 +818,20 @@ fun MainApp(

// ❺ μ‹€μ œ 둜그인 UI(AnimatedLoginScreen λ“±) λ Œλ”λ§
// AnimatedLoginScreen(navigator = navigator)
AnimatedLoginScreen(navigator = navigator, onSignUpClick = {})
val skipAnimation =
backStackEntry.savedStateHandle
.get<Boolean>("skip_login_animation") == true

AnimatedLoginScreen(
navigator = navigator,
skipAnimation = skipAnimation,
onSignUpClick = {}
)
}



// TODO: 둜그인 λ˜μ–΄ μžˆμ§€ μ•Šμ€ 상황 처리
// TODO: 둜그인 λ˜μ–΄ μžˆμ§€ μ•Šμ€ 상황 처리 ?이게 뭐람
Copy link
Contributor

Choose a reason for hiding this comment

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

λ‚΄κ°€ ν•˜λ˜ 건데 같이 컀밋됐넀;; γ…ˆγ……

Copy link
Contributor Author

Choose a reason for hiding this comment

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

그러면... λ”₯링크 λΉ λ₯΄κ²Œ μˆ˜μ • λ˜λ‚˜μš”..?

// 링크 곡유 앱링크
composable(
route = "open?action={action}&folderId={folderId}",
Expand Down
40 changes: 8 additions & 32 deletions app/src/main/java/com/example/linku_android/Splash.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@ import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import com.example.design.util.PixelScaler
import android.app.Activity
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import android.view.WindowInsets
import com.example.design.util.DesignSystemBars



Expand All @@ -50,31 +44,13 @@ interface SplashDeps {
@Composable
fun Splash(onResult: (Boolean) -> Unit) {

val view = LocalView.current
val isPreview = LocalInspectionMode.current

if (!isPreview) {
val activity = view.context as Activity
val window = activity.window

SideEffect {
// edge-to-edge
WindowCompat.setDecorFitsSystemWindows(window, false)

// μƒνƒœλ°” + λ„€λΉ„κ²Œμ΄μ…˜λ°” μ™„μ „ μˆ¨κΉ€
WindowInsetsControllerCompat(window, view).apply {
hide(
WindowInsets.Type.statusBars() or
WindowInsets.Type.navigationBars()
)

// μŠ€μ™€μ΄ν”„λ‘œ 잠깐 λ‚˜νƒ€λ‚¬λ‹€κ°€ λ‹€μ‹œ μˆ¨κΉ€
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}

//λ°”ν…€λ°” μˆ¨κΉ€
DesignSystemBars(
statusBarColor = Color.Transparent,
navigationBarColor = Color.Transparent,
darkIcons = false,
immersive = true
)
val rotationAnim = remember { Animatable(0f) }
var isGlowPhase by remember { mutableStateOf(false) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass

data class UserInfoDTO(
// @Json(name = "nickname")
// val nickname: String,

// TODO: 톡합
@Json(name = "nickname")
val nickname: String? = null,
// Done 톡합 : 01.13 μ™„λ£Œ ν–ˆμŠ΅λ‹ˆλ‹€. (username 제거)
@Json(name = "nickName")
val nickName: String? = null,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,25 +151,19 @@ class UserRepositoryImpl @Inject constructor(

// λ§ˆμ΄νŽ˜μ΄μ§€ 쑰회
override suspend fun getUserInfo(userId: Long): UserInfo {
// val response = userApi.withAuth(authPreference) { getUserInfo(userId) }
val response = userApi.getUserInfo(userId)

val dto: UserInfoDTO = response.result
val dto = response.result
?: throw IllegalStateException("λ§ˆμ΄νŽ˜μ΄μ§€ 쑰회 μ‹€νŒ¨: ${response.message}")

// DTO -> 도메인 맀핑을 μ—¬κΈ°μ„œ λ°”λ‘œ 처리
val nick = dto.nickname ?: dto.nickName ?: "" // ← Fallback μΆ”κ°€

// μ„œλ²„μ—μ„œ 받은 enum μ½”λ“œ β†’ ν™”λ©΄ ν•œκΈ€ 라벨둜 λ³€ν™˜
// μ„œλ²„ enum β†’ ν•œκΈ€
val displayPurposes = dto.purposes.map { reversePurposeMap[it] ?: it }
val displayInterests = dto.interests.map { reverseInterestMap[it] ?: it }

// DTO -> 도메인 맀핑을 μ—¬κΈ°μ„œ λ°”λ‘œ 처리
return UserInfo(
// nickname = dto.nickname,
nickname = nick,
nickname = dto.nickName.orEmpty(),
email = dto.email,
gender = dto.gender.value, // "MALE" | "FEMALE"
gender = dto.gender.value,
jobId = dto.job.id,
jobName = dto.job.name,
myLinku = dto.myLinku,
Expand Down Expand Up @@ -224,12 +218,11 @@ class UserRepositoryImpl @Inject constructor(
// λ‹‰λ„€μž„ μ „μš© λ©”μ„œλ“œλ‘œ 뢄리
override suspend fun getNickname(userId: Long): String? {
return try {
val res = userApi.getUserInfo(userId) // BaseResponse<UserInfoDTO>
// μ„œλ²„ DTO ν•„λ“œλͺ… λŒ€μ‘ (nickname ν˜Ήμ€ nickName)
val nick = res.result?.nickname ?: res.result?.nickName
val res = userApi.getUserInfo(userId)
val nick = res.result?.nickName
Log.d("UserRepository", "λ‹‰λ„€μž„=$nick")
nick?.takeIf { it.isNotBlank() }
} catch (e: retrofit2.HttpException) {
} catch (e: HttpException) {
if (e.code() == 500) null else throw e
} catch (e: Exception) {
Log.e("UserRepository", "λ‹‰λ„€μž„ κ°€μ Έμ˜€κΈ° μ‹€νŒ¨", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ data object Basic: ThemeColorScheme(
maincolor= Brush.horizontalGradient(
listOf(
Color(0xFF2C6FFF),
Color(0xFFCB59EB)
Color(0xFFC800FF) //μˆ˜μ •ν•¨.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

컬러 μ½”λ“œμ— μ˜€νƒ€κ°€ μžˆμ–΄μ„œ λΆ€λ“μ΄ν•˜κ²Œ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.

)
),
blue = ColorMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ sealed class ThemeColorScheme(
800 to Color(0xFF43454B),
),

// λΉ„ν™œμ„±ν™”μš© κ·ΈλΌλ°μ΄μ…˜ λΈŒλŸ¬μ‹œ μΆ”κ°€ -둜그인, νšŒμ›κ°€μž…μš©
val inactiveColor: Brush = Brush.horizontalGradient(
listOf(Color(0xFFD4E1FF), Color(0xFFF2CCFF))
),

val black: Color = Color(0xFF000208),
val white: Color = Color(0xFFFFFFFF),
val positive: Color = Color(0xFF35DF79),
Expand Down
Loading