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
4 changes: 3 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ trim_trailing_whitespace = true
insert_final_newline = true
tab_width = 4
ij_kotlin_allow_trailing_comma = true
ktlint_function_naming_ignore_when_annotated_with=Composable
ij_kotlin_allow_trailing_comma_on_call_site = true
ktlint_function_naming_ignore_when_annotated_with=Composable
ktlint_standard_annotation = disabled
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,39 @@ import java.io.OutputStream
import java.util.Base64
import javax.inject.Inject

internal class AuthTokenSerializer
@Inject
constructor(
private val crypto: Crypto,
) : TokenSerializer {
override val defaultValue: AuthToken
get() = AuthToken()
internal class AuthTokenSerializer @Inject constructor(
private val crypto: Crypto,
) : TokenSerializer {
override val defaultValue: AuthToken
get() = AuthToken()

override suspend fun readFrom(input: InputStream): AuthToken {
return try {
val encryptedBytes =
withContext(Dispatchers.IO) {
input.use { it.readBytes() }
}
val decodedBytes = Base64.getDecoder().decode(encryptedBytes)
val decryptedBytes = crypto.decrypt(decodedBytes)
val decodedJsonString = decryptedBytes.decodeToString()
Json.decodeFromString(decodedJsonString)
} catch (e: Exception) {
AuthToken()
}
override suspend fun readFrom(input: InputStream): AuthToken {
return try {
val encryptedBytes =
withContext(Dispatchers.IO) {
input.use { it.readBytes() }
}
val decodedBytes = Base64.getDecoder().decode(encryptedBytes)
val decryptedBytes = crypto.decrypt(decodedBytes)
val decodedJsonString = decryptedBytes.decodeToString()
Json.decodeFromString(decodedJsonString)
} catch (e: Exception) {
AuthToken()
}
}

override suspend fun writeTo(
t: AuthToken,
output: OutputStream,
) {
val json = Json.encodeToString(t)
val bytes = json.toByteArray()
val encryptedBytes = crypto.encrypt(bytes)
val encryptedBytesBase64 = Base64.getEncoder().encode(encryptedBytes)
withContext(Dispatchers.IO) {
output.use {
it.write(encryptedBytesBase64)
}
override suspend fun writeTo(
t: AuthToken,
output: OutputStream,
) {
val json = Json.encodeToString(t)
val bytes = json.toByteArray()
val encryptedBytes = crypto.encrypt(bytes)
val encryptedBytesBase64 = Base64.getEncoder().encode(encryptedBytes)
withContext(Dispatchers.IO) {
output.use {
it.write(encryptedBytesBase64)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,62 +6,60 @@ import com.threegap.bitnagil.datastore.model.AuthToken
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

internal class AuthTokenDataStoreImpl
@Inject
constructor(
private val dataStore: DataStore<AuthToken>,
) : AuthTokenDataStore {
override val tokenFlow: Flow<AuthToken> = dataStore.data
internal class AuthTokenDataStoreImpl @Inject constructor(
private val dataStore: DataStore<AuthToken>,
) : AuthTokenDataStore {
override val tokenFlow: Flow<AuthToken> = dataStore.data

override suspend fun updateAuthToken(authToken: AuthToken): AuthToken =
runCatching {
dataStore.updateData { authToken }
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateAuthToken failed:", it)
throw it
},
)
override suspend fun updateAuthToken(authToken: AuthToken): AuthToken =
runCatching {
dataStore.updateData { authToken }
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateAuthToken failed:", it)
throw it
},
)

override suspend fun updateAccessToken(accessToken: String): AuthToken =
runCatching {
dataStore.updateData { authToken ->
authToken.copy(accessToken = accessToken)
}
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateAccessToken failed:", it)
throw it
},
)
override suspend fun updateAccessToken(accessToken: String): AuthToken =
runCatching {
dataStore.updateData { authToken ->
authToken.copy(accessToken = accessToken)
}
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateAccessToken failed:", it)
throw it
},
)

override suspend fun updateRefreshToken(refreshToken: String): AuthToken =
runCatching {
dataStore.updateData { authToken ->
authToken.copy(refreshToken = refreshToken)
}
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateRefreshToken failed:", it)
throw it
},
)
override suspend fun updateRefreshToken(refreshToken: String): AuthToken =
runCatching {
dataStore.updateData { authToken ->
authToken.copy(refreshToken = refreshToken)
}
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "updateRefreshToken failed:", it)
throw it
},
)

override suspend fun clearAuthToken(): AuthToken =
runCatching {
dataStore.updateData { AuthToken() }
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "clearAuthToken failed:", it)
throw it
},
)
override suspend fun clearAuthToken(): AuthToken =
runCatching {
dataStore.updateData { AuthToken() }
}.fold(
onSuccess = { it },
onFailure = {
Log.e(TAG, "clearAuthToken failed:", it)
throw it
},
)

companion object {
private const val TAG = "AuthTokenDataStore"
}
companion object {
private const val TAG = "AuthTokenDataStore"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography =
Typography(
bodyLarge =
TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp,
),
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp,
),
)
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ versionName = "1.0"
androidGradlePlugin = "8.10.1"

# Formatting Plugin
ktlint = "12.3.0"
ktlint = "11.3.2"

# AndroidX
androidxCore = "1.16.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ fun HomeScreen(modifier: Modifier = Modifier) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
modifier
.fillMaxSize()
.background(Color.White),
modifier = modifier
.fillMaxSize()
.background(Color.White),
) {
Text(text = "여긴 홈 화면")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,9 @@ private fun LoginScreen(
modifier: Modifier = Modifier,
) {
Box(
modifier =
modifier
.fillMaxSize()
.background(Color.White),
modifier = modifier
.fillMaxSize()
.background(Color.White),
) {
Text(
text = "빛나길 로고",
Expand All @@ -68,10 +67,9 @@ private fun LoginScreen(

Button(
onClick = onKakaoLoginClick,
modifier =
Modifier
.align(Alignment.BottomCenter)
.padding(20.dp),
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(20.dp),
) {
Text(
text = "카카오 로그인버튼",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,55 +14,53 @@ import org.orbitmvi.orbit.syntax.simple.SimpleSyntax
import javax.inject.Inject

@HiltViewModel
class LoginViewModel
@Inject
constructor(
private val savedStateHandle: SavedStateHandle,
) : MviViewModel<LoginState, LoginSideEffect, LoginIntent>(
initState = LoginState(),
savedStateHandle = savedStateHandle,
) {
override suspend fun SimpleSyntax<LoginState, LoginSideEffect>.reduceState(
intent: LoginIntent,
state: LoginState,
): LoginState? =
when (intent) {
is LoginIntent.OnKakaoLoginClick -> {
if (!intent.onKakaoTalkLoginAvailable) {
sendSideEffect(LoginSideEffect.RequestKakaoAccountLogin)
} else {
sendSideEffect(LoginSideEffect.RequestKakaoTalkLogin)
}
null
class LoginViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
) : MviViewModel<LoginState, LoginSideEffect, LoginIntent>(
initState = LoginState(),
savedStateHandle = savedStateHandle,
) {
override suspend fun SimpleSyntax<LoginState, LoginSideEffect>.reduceState(
intent: LoginIntent,
state: LoginState,
): LoginState? =
when (intent) {
is LoginIntent.OnKakaoLoginClick -> {
if (!intent.onKakaoTalkLoginAvailable) {
sendSideEffect(LoginSideEffect.RequestKakaoAccountLogin)
} else {
sendSideEffect(LoginSideEffect.RequestKakaoTalkLogin)
}
null
}

is LoginIntent.OnKakaoLoginResult -> {
when {
intent.token != null -> {
Log.i("KakaoLogin", "로그인 성공 ${intent.token.accessToken}")
UserApiClient.instance.me { user, error ->
if (error != null) {
Log.e("KakaoLogin", "사용자 정보 요청 실패", error)
} else if (user != null) {
Log.i(
"KakaoLogin",
"사용자 정보 요청 성공" +
"\n이메일: ${user.kakaoAccount?.email}" +
"\n닉네임: ${user.kakaoAccount?.profile?.nickname}",
)
}
is LoginIntent.OnKakaoLoginResult -> {
when {
intent.token != null -> {
Log.i("KakaoLogin", "로그인 성공 ${intent.token.accessToken}")
UserApiClient.instance.me { user, error ->
if (error != null) {
Log.e("KakaoLogin", "사용자 정보 요청 실패", error)
} else if (user != null) {
Log.i(
"KakaoLogin",
"사용자 정보 요청 성공" +
"\n이메일: ${user.kakaoAccount?.email}" +
"\n닉네임: ${user.kakaoAccount?.profile?.nickname}",
)
}
}
}

intent.error is ClientError && intent.error.reason == ClientErrorCause.Cancelled -> {
Log.e("KakaoLogin", "로그인 취소", intent.error)
}
intent.error is ClientError && intent.error.reason == ClientErrorCause.Cancelled -> {
Log.e("KakaoLogin", "로그인 취소", intent.error)
}

intent.error != null -> {
Log.e("KakaoLogin", "로그인 실패", intent.error)
}
intent.error != null -> {
Log.e("KakaoLogin", "로그인 실패", intent.error)
}
null
}
null
}
}
}
}