diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7633f067..a211eae0 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -19,6 +19,7 @@ dependencies {
implementation(project(":core:design-system"))
implementation(project(":core:ui"))
implementation(project(":core:model"))
+ implementation(project(":core:common"))
implementation(project(":feature:login"))
implementation(project(":feature:main"))
@@ -27,5 +28,7 @@ dependencies {
implementation(project(":feature:score-mission"))
implementation(project(":feature:shop"))
implementation(libs.junit)
+ implementation(libs.google.services)
+ implementation(libs.play.services.auth)
androidTestImplementation(libs.androidx.test.ext)
}
\ No newline at end of file
diff --git a/app/google-services.json b/app/google-services.json
new file mode 100644
index 00000000..6219a2d1
--- /dev/null
+++ b/app/google-services.json
@@ -0,0 +1,29 @@
+{
+ "project_info": {
+ "project_number": "322299804217",
+ "project_id": "stack-knowledge-v2",
+ "storage_bucket": "stack-knowledge-v2.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:322299804217:android:e15d7c6ad9b47cc65e2be8",
+ "android_client_info": {
+ "package_name": "com.kdn.stack_knowledge"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyBxMjD7Mg9vzfBiuZVBzyR12Ubr-uEZ_hQ"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9ee0d221..693f6fc3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,7 +5,7 @@
+
@@ -26,6 +27,7 @@
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/kdn/stack_knowledge/MainActivity.kt b/app/src/main/java/com/kdn/stack_knowledge/MainActivity.kt
new file mode 100644
index 00000000..7633a287
--- /dev/null
+++ b/app/src/main/java/com/kdn/stack_knowledge/MainActivity.kt
@@ -0,0 +1,121 @@
+package com.kdn.stack_knowledge
+
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.compose.setContent
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
+import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
+import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
+import com.example.common.toast.makeToast
+import com.google.android.gms.auth.api.signin.GoogleSignIn
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount
+import com.google.android.gms.auth.api.signin.GoogleSignInClient
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.common.api.Scope
+import com.google.android.gms.tasks.Task
+import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
+import com.stackknowledge.login.viewmodel.AuthViewModel
+import com.kdn.stack_knowledge.ui.StackKnowledgeApp
+import com.stackknowledge.user.R
+import dagger.hilt.android.AndroidEntryPoint
+import remote.request.auth.LoginRequestModel
+import javax.inject.Inject
+import javax.inject.Named
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+
+ @Inject
+ @Named("GOOGLE_CLIENT_ID")
+ lateinit var googleClientId: String
+
+ @Inject
+ @Named("SCOPE")
+ lateinit var scope: String
+
+ private val viewModel by viewModels()
+
+ private val googleSignInClient: GoogleSignInClient by lazy { getGoogleClient() }
+
+ private val googleSignInLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ val task: Task =
+ GoogleSignIn.getSignedInAccountFromIntent(result.data)
+ handleGoogleSignInResult(task)
+ }
+
+ private var doubleBackToExitPressedOnce = false
+
+ private var backPressedTimestamp = 0L
+
+ private val onBackPressedCallback = object : OnBackPressedCallback(true) {
+ override fun handleOnBackPressed() {
+ controlTheStackWhenBackPressed()
+ }
+ }
+
+ @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ this.onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
+ setContent {
+ CompositionLocalProvider(LocalViewModelStoreOwner provides this) {
+ StackKnowledgeAndroidTheme { _, _ ->
+ StackKnowledgeApp(
+ windowSizeClass = calculateWindowSizeClass(this@MainActivity),
+ onLoginButtonClick = {
+ googleSocialLogin()
+ }
+ )
+ }
+ }
+ }
+ }
+
+ private fun controlTheStackWhenBackPressed() {
+ val currentTime = System.currentTimeMillis()
+ if (doubleBackToExitPressedOnce && currentTime - backPressedTimestamp <= 2000) {
+ finishAffinity()
+ } else {
+ doubleBackToExitPressedOnce = true
+ backPressedTimestamp = currentTime
+ makeToast(this, getString(R.string.close_app), Toast.LENGTH_SHORT)
+ }
+ }
+
+ private fun googleSocialLogin() {
+ googleSignInClient.signOut().addOnCompleteListener {
+ val signInIntent = googleSignInClient.signInIntent
+ googleSignInLauncher.launch(signInIntent)
+ }
+ }
+
+ private fun getGoogleClient(): GoogleSignInClient {
+ val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestScopes(Scope(scope))
+ .requestServerAuthCode(googleClientId)
+ .requestEmail()
+ .build()
+
+ return GoogleSignIn.getClient(this@MainActivity, googleSignInOptions)
+ }
+
+ private fun handleGoogleSignInResult(task: Task) {
+ val account = task.getResult(ApiException::class.java)
+
+ with(viewModel) {
+ if (isStudent.value) {
+ loginStudent(body = LoginRequestModel(code = account.serverAuthCode.toString()))
+ } else {
+ loginTeacher(body = LoginRequestModel(code = account.serverAuthCode.toString()))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stackknowledge/StackKnowledgeApplication.kt b/app/src/main/java/com/kdn/stack_knowledge/StackKnowledgeApplication.kt
similarity index 53%
rename from app/src/main/java/com/stackknowledge/StackKnowledgeApplication.kt
rename to app/src/main/java/com/kdn/stack_knowledge/StackKnowledgeApplication.kt
index a5af556c..21f9b8e6 100644
--- a/app/src/main/java/com/stackknowledge/StackKnowledgeApplication.kt
+++ b/app/src/main/java/com/kdn/stack_knowledge/StackKnowledgeApplication.kt
@@ -1,7 +1,7 @@
-package com.stackknowledge
+package com.kdn.stack_knowledge
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
-class StackKnowledgeApplication : Application()
+class StackKnowledgeApplication : Application()
\ No newline at end of file
diff --git a/app/src/main/java/com/stackknowledge/navigation/StackKnowledgeNavHost.kt b/app/src/main/java/com/kdn/stack_knowledge/navigation/StackKnowledgeNavHost.kt
similarity index 90%
rename from app/src/main/java/com/stackknowledge/navigation/StackKnowledgeNavHost.kt
rename to app/src/main/java/com/kdn/stack_knowledge/navigation/StackKnowledgeNavHost.kt
index 8685bd7f..85ef2c28 100644
--- a/app/src/main/java/com/stackknowledge/navigation/StackKnowledgeNavHost.kt
+++ b/app/src/main/java/com/kdn/stack_knowledge/navigation/StackKnowledgeNavHost.kt
@@ -1,4 +1,4 @@
-package com.stackknowledge.navigation
+package com.kdn.stack_knowledge.navigation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -8,10 +8,9 @@ import com.stackknowledge.login.navigation.loginScreen
import com.stackknowledge.login.navigation.navigateToLogin
import com.stackknowledge.login.navigation.roleCheckRoute
import com.stackknowledge.login.navigation.roleCheckScreen
-import com.stackknowledge.main.navigation.mainPageRoute
import com.stackknowledge.main.navigation.mainScreen
import com.stackknowledge.main.navigation.navigateToMain
-import com.stackknowledge.navigation.util.bottomNavigationNavigate
+import com.kdn.stack_knowledge.navigation.util.bottomNavigationNavigate
import com.stackknowledge.ranking.navigation.navigateToRanking
import com.stackknowledge.ranking.navigation.navigateToTeacherRanking
import com.stackknowledge.ranking.navigation.rankingScreen
@@ -24,21 +23,20 @@ import com.stackknowledge.shop.navigation.shopRoute
import com.stackknowledge.shop.navigation.shopScreen
import com.stackknowledge.shop.navigation.teacherShopRoute
import com.stackknowledge.shop.navigation.teacherShopScreen
-import com.stackknowledge.ui.StackKnowledgeAppState
-import com.stackkowledge.mission.navigation.createMissionRoute
+import com.kdn.stack_knowledge.ui.StackKnowledgeAppState
import com.stackkowledge.mission.navigation.createMissionScreen
-import com.stackkowledge.mission.navigation.entireMissionRoute
import com.stackkowledge.mission.navigation.entireMissionScreen
import com.stackkowledge.mission.navigation.navigateToEntireMission
import com.stackkowledge.mission.navigation.navigateToResolveMission
import com.stackkowledge.mission.navigation.resolveMissionScreen
-import enumdatatype.Authority
+import enumdata.Authority
@Composable
fun StackKnowledgeNavHost(
appState: StackKnowledgeAppState,
- modifier: Modifier = Modifier,
startDestination: String = roleCheckRoute,
+ modifier: Modifier = Modifier,
+ onLoginButtonClick: () -> Unit = {},
) {
val navController = appState.navController
@@ -47,9 +45,12 @@ fun StackKnowledgeNavHost(
startDestination = startDestination,
modifier = modifier
) {
- loginScreen()
+ loginScreen(
+ onSuccess = navController::navigateToMain,
+ onLoginButtonClick = onLoginButtonClick
+ )
roleCheckScreen(
- onRoleClick = navController::navigateToLogin
+ onRoleButtonClick = navController::navigateToLogin
)
mainScreen(
onNavigate = { role, navType, index ->
diff --git a/app/src/main/java/com/kdn/stack_knowledge/navigation/TopLevelDestination.kt b/app/src/main/java/com/kdn/stack_knowledge/navigation/TopLevelDestination.kt
new file mode 100644
index 00000000..03b87dc0
--- /dev/null
+++ b/app/src/main/java/com/kdn/stack_knowledge/navigation/TopLevelDestination.kt
@@ -0,0 +1,5 @@
+package com.kdn.stack_knowledge.navigation
+
+enum class TopLevelDestination {
+ ROLE_CHECK,
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/stackknowledge/navigation/util/BottomNavigationNavigate.kt b/app/src/main/java/com/kdn/stack_knowledge/navigation/util/BottomNavigationNavigate.kt
similarity index 96%
rename from app/src/main/java/com/stackknowledge/navigation/util/BottomNavigationNavigate.kt
rename to app/src/main/java/com/kdn/stack_knowledge/navigation/util/BottomNavigationNavigate.kt
index 77adbbf6..cf47d712 100644
--- a/app/src/main/java/com/stackknowledge/navigation/util/BottomNavigationNavigate.kt
+++ b/app/src/main/java/com/kdn/stack_knowledge/navigation/util/BottomNavigationNavigate.kt
@@ -1,4 +1,4 @@
-package com.stackknowledge.navigation.util
+package com.kdn.stack_knowledge.navigation.util
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
@@ -12,7 +12,7 @@ import com.stackknowledge.shop.navigation.navigateToShop
import com.stackknowledge.shop.navigation.navigateToTeacherShop
import com.stackkowledge.mission.navigation.navigateToCreateMission
import com.stackkowledge.mission.navigation.navigateToEntireMission
-import enumdatatype.Authority
+import enumdata.Authority
fun bottomNavigationNavigate(
role: Authority,
diff --git a/app/src/main/java/com/stackknowledge/ui/StackKnowledgeApp.kt b/app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeApp.kt
similarity index 61%
rename from app/src/main/java/com/stackknowledge/ui/StackKnowledgeApp.kt
rename to app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeApp.kt
index d9497105..ee988f04 100644
--- a/app/src/main/java/com/stackknowledge/ui/StackKnowledgeApp.kt
+++ b/app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeApp.kt
@@ -1,9 +1,9 @@
-package com.stackknowledge.ui
+package com.kdn.stack_knowledge.ui
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
-import com.stackknowledge.navigation.StackKnowledgeNavHost
+import com.kdn.stack_knowledge.navigation.StackKnowledgeNavHost
@Composable
fun StackKnowledgeApp(
@@ -11,11 +11,12 @@ fun StackKnowledgeApp(
appState: StackKnowledgeAppState = rememberStackKnowledgeAppState(
windowSizeClass = windowSizeClass
),
+ onLoginButtonClick: () -> Unit = {},
) {
StackKnowledgeAndroidTheme { _, _ ->
- StackKnowledgeNavHost(
- appState = appState,
- //startDestination = "" <- auth 작업후에 추가
- )
+ StackKnowledgeNavHost(
+ appState = appState,
+ onLoginButtonClick = onLoginButtonClick
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/stackknowledge/ui/StackKnowledgeAppState.kt b/app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeAppState.kt
similarity index 89%
rename from app/src/main/java/com/stackknowledge/ui/StackKnowledgeAppState.kt
rename to app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeAppState.kt
index 6c2f57d2..59fe2488 100644
--- a/app/src/main/java/com/stackknowledge/ui/StackKnowledgeAppState.kt
+++ b/app/src/main/java/com/kdn/stack_knowledge/ui/StackKnowledgeAppState.kt
@@ -1,4 +1,4 @@
-package com.stackknowledge.ui
+package com.kdn.stack_knowledge.ui
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
@@ -6,12 +6,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
-import com.stackknowledge.navigation.TopLevelDestination
+import com.kdn.stack_knowledge.navigation.TopLevelDestination
+import com.stackknowledge.login.navigation.roleCheckRoute
import kotlinx.coroutines.CoroutineScope
@Composable
@@ -45,7 +45,7 @@ class StackKnowledgeAppState(
val currentTopLevelDestination: TopLevelDestination?
@Composable get() = when(currentDestination?.route) {
- // loginRoute 작성
+ roleCheckRoute -> TopLevelDestination.ROLE_CHECK
else -> null
}
diff --git a/app/src/main/java/com/stackknowledge/MainActivity.kt b/app/src/main/java/com/stackknowledge/MainActivity.kt
deleted file mode 100644
index dd3c51b3..00000000
--- a/app/src/main/java/com/stackknowledge/MainActivity.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.stackknowledge
-
-import android.os.Bundle
-import android.widget.Toast
-import androidx.activity.ComponentActivity
-import androidx.activity.OnBackPressedCallback
-import androidx.activity.compose.setContent
-import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
-import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
-import androidx.compose.runtime.CompositionLocalProvider
-import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
-import com.stackknowledge.ui.StackKnowledgeApp
-import com.stackknowledge.user.R
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class MainActivity : ComponentActivity() {
- private var doubleBackToExitPressedOnce = false
- private var backPressedTimestamp = 0L
- private val onBackPressedCallback = object : OnBackPressedCallback(true) {
- override fun handleOnBackPressed() {
- controlTheStackWhenBackPressed()
- }
- }
- @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- this.onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
- setContent {
- CompositionLocalProvider {
- StackKnowledgeAndroidTheme { _, _ ->
- StackKnowledgeApp(windowSizeClass = calculateWindowSizeClass(this@MainActivity))
- }
- }
- }
- }
- private fun controlTheStackWhenBackPressed() {
- val currentTime = System.currentTimeMillis()
- if (doubleBackToExitPressedOnce && currentTime - backPressedTimestamp <= 2000) {
- finishAffinity()
- } else {
- doubleBackToExitPressedOnce = true
- backPressedTimestamp = currentTime
- Toast.makeText(this, getString(R.string.close_app),Toast.LENGTH_SHORT).show()
- }
- }
-}
diff --git a/app/src/main/java/com/stackknowledge/navigation/TopLevelDestination.kt b/app/src/main/java/com/stackknowledge/navigation/TopLevelDestination.kt
deleted file mode 100644
index 174d6cae..00000000
--- a/app/src/main/java/com/stackknowledge/navigation/TopLevelDestination.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.stackknowledge.navigation
-
-enum class TopLevelDestination {
- LOGIN,
-}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/java/com/convention/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/java/com/convention/AndroidApplicationConventionPlugin.kt
index 93e799d1..ae4e535b 100644
--- a/build-logic/convention/src/main/java/com/convention/AndroidApplicationConventionPlugin.kt
+++ b/build-logic/convention/src/main/java/com/convention/AndroidApplicationConventionPlugin.kt
@@ -8,7 +8,6 @@ import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
-
class AndroidApplicationConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
@@ -20,7 +19,7 @@ class AndroidApplicationConventionPlugin : Plugin {
extensions.configure {
configureKotlinAndroid(this)
defaultConfig {
- applicationId = "com.stackknowledge_android"
+ applicationId = "com.kdn.stack_knowledge"
minSdk = 26
targetSdk = 34
versionCode = 1
@@ -48,8 +47,6 @@ class AndroidApplicationConventionPlugin : Plugin {
add("implementation", libs.findBundle("compose").get())
}
}
-
}
-
}
}
\ No newline at end of file
diff --git a/core/common/consumer-rules.pro b/core/common/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/common/proguard-rules.pro b/core/common/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/common/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts
index b957e4a5..d54c565f 100644
--- a/core/data/build.gradle.kts
+++ b/core/data/build.gradle.kts
@@ -16,4 +16,5 @@ dependencies {
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.moshi.converter)
+ implementation(libs.retrofit.moshi.codegen)
}
\ No newline at end of file
diff --git a/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt
index d44e5622..fcd84876 100644
--- a/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt
+++ b/core/data/src/main/kotlin/com/stackknowledge/di/RepositoryModule.kt
@@ -1,5 +1,7 @@
package com.stackknowledge.di
+import com.stackknowledge.repository.auth.AuthRepository
+import com.stackknowledge.repository.auth.AuthRepositoryImpl
import com.stackknowledge.repository.item.ItemRepository
import com.stackknowledge.repository.item.ItemRepositoryImpl
import com.stackknowledge.repository.mission.MissionRepository
@@ -20,6 +22,11 @@ import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
+ @Binds
+ abstract fun bindAuthRepository(
+ missionRepositoryImpl: AuthRepositoryImpl
+ ): AuthRepository
+
@Binds
abstract fun bindMissionRepository(
missionRepositoryImpl: MissionRepositoryImpl
diff --git a/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepository.kt b/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepository.kt
new file mode 100644
index 00000000..16cbf5fc
--- /dev/null
+++ b/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepository.kt
@@ -0,0 +1,15 @@
+package com.stackknowledge.repository.auth
+
+import kotlinx.coroutines.flow.Flow
+import remote.request.auth.LoginRequestModel
+import remote.response.auth.LoginResponseModel
+
+interface AuthRepository {
+ fun loginStudent(body: LoginRequestModel): Flow
+
+ fun loginTeacher(body: LoginRequestModel): Flow
+
+ suspend fun saveToken(token: LoginResponseModel)
+
+ fun logout(): Flow
+}
\ No newline at end of file
diff --git a/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepositoryImpl.kt b/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepositoryImpl.kt
new file mode 100644
index 00000000..f599741e
--- /dev/null
+++ b/core/data/src/main/kotlin/com/stackknowledge/repository/auth/AuthRepositoryImpl.kt
@@ -0,0 +1,38 @@
+package com.stackknowledge.repository.auth
+
+import com.stackknowledge.datasource.auth.AuthDataSource
+import com.stackknowledge.datastore.LocalAuthDataSource
+import com.stackknowledge.mapper.request.auth.toDto
+import com.stackknowledge.mapper.response.auth.toModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import remote.request.auth.LoginRequestModel
+import remote.response.auth.LoginResponseModel
+import javax.inject.Inject
+
+class AuthRepositoryImpl @Inject constructor(
+ private val authDataSource: AuthDataSource,
+ private val localDataSource: LocalAuthDataSource
+): AuthRepository {
+ override fun loginStudent(body: LoginRequestModel): Flow {
+ return authDataSource.loginStudent(body = body.toDto()).map { it.toModel() }
+ }
+
+ override fun loginTeacher(body: LoginRequestModel): Flow {
+ return authDataSource.loginTeacher(body = body.toDto()).map { it.toModel() }
+ }
+
+ override suspend fun saveToken(token: LoginResponseModel) {
+ token.let {
+ localDataSource.setAccessToken(it.accessToken)
+ localDataSource.setAccessTime(it.expiredAt)
+ localDataSource.setRefreshToken(it.refreshToken)
+ localDataSource.setRefreshTime(it.expiredAt)
+ localDataSource.setAuthorityInfo(it.authority.toString())
+ }
+ }
+
+ override fun logout(): Flow {
+ return authDataSource.logout()
+ }
+}
\ No newline at end of file
diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts
index bb63c8e6..56e258cc 100644
--- a/core/datastore/build.gradle.kts
+++ b/core/datastore/build.gradle.kts
@@ -2,34 +2,15 @@
plugins {
id("stackknowledge.android.core")
id("stackknowledge.android.hilt")
- alias(libs.plugins.protobuf)
}
android {
namespace = "com.stackknowledge.datastore"
}
-protobuf {
- protoc {
- artifact = libs.protobuf.protoc.get().toString()
- }
- generateProtoTasks {
- all().forEach { task ->
- task.builtins {
- register("java") {
- option("lite")
- }
- register("kotlin") {
- option("lite")
- }
- }
- }
- }
-}
-
dependencies {
implementation(project(":core:model"))
implementation(libs.androidx.dataStore.core)
- implementation(libs.protobuf.kotlin.lite)
+ implementation(libs.androidx.dataStore.preferences)
}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSource.kt b/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSource.kt
new file mode 100644
index 00000000..44d3cef6
--- /dev/null
+++ b/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSource.kt
@@ -0,0 +1,38 @@
+package com.stackknowledge.datastore
+
+import kotlinx.coroutines.flow.Flow
+
+interface LocalAuthDataSource {
+ // AccessToken
+ suspend fun getAccessToken(): Flow
+
+ suspend fun setAccessToken(accessToken: String)
+
+ suspend fun removeAccessToken()
+
+ // AccessTime
+ suspend fun getAccessTime(): Flow
+
+ suspend fun setAccessTime(accessTime: String)
+
+ suspend fun removeAccessTime()
+
+ // RefreshToken
+ suspend fun getRefreshToken(): Flow
+
+ suspend fun setRefreshToken(refreshToken: String)
+
+ suspend fun removeRefreshToken()
+
+ // RefreshTime
+ suspend fun getRefreshTime(): Flow
+
+ suspend fun setRefreshTime(refreshTime: String)
+
+ suspend fun removeRefreshTime()
+
+ // Authority
+ suspend fun setAuthorityInfo(authority: String)
+
+ suspend fun getAuthorityInfo(): Flow
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSourceImpl.kt b/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSourceImpl.kt
new file mode 100644
index 00000000..c4af7226
--- /dev/null
+++ b/core/datastore/src/main/java/com/stackknowledge/datastore/LocalAuthDataSourceImpl.kt
@@ -0,0 +1,87 @@
+package com.stackknowledge.datastore
+
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.edit
+import com.stackknowledge.datastore.key.AuthPreferenceKey
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+class LocalAuthDataSourceImpl @Inject constructor(
+ private val dataStore: DataStore
+): LocalAuthDataSource {
+ override suspend fun getAccessToken(): Flow = dataStore.data.map {
+ it[AuthPreferenceKey.ACCESS_TOKEN] ?: ""
+ }
+
+ override suspend fun setAccessToken(accessToken: String) {
+ dataStore.edit {
+ it[AuthPreferenceKey.ACCESS_TOKEN] = accessToken
+ }
+ }
+
+ override suspend fun removeAccessToken() {
+ dataStore.edit {
+ it.remove(AuthPreferenceKey.ACCESS_TOKEN)
+ }
+ }
+
+ override suspend fun getAccessTime(): Flow = dataStore.data.map {
+ it[AuthPreferenceKey.ACCESS_TIME] ?: ""
+ }
+
+ override suspend fun setAccessTime(accessTime: String) {
+ dataStore.edit {
+ it[AuthPreferenceKey.ACCESS_TIME] = accessTime
+ }
+ }
+
+ override suspend fun removeAccessTime() {
+ dataStore.edit {
+ it.remove(AuthPreferenceKey.ACCESS_TIME)
+ }
+ }
+
+ override suspend fun getRefreshToken(): Flow = dataStore.data.map {
+ it[AuthPreferenceKey.REFRESH_TOKEN] ?: ""
+ }
+
+ override suspend fun setRefreshToken(refreshToken: String) {
+ dataStore.edit {
+ it[AuthPreferenceKey.REFRESH_TOKEN] = refreshToken
+ }
+ }
+
+ override suspend fun removeRefreshToken() {
+ dataStore.edit {
+ it.remove(AuthPreferenceKey.REFRESH_TOKEN)
+ }
+ }
+
+ override suspend fun getRefreshTime(): Flow = dataStore.data.map {
+ it[AuthPreferenceKey.REFRESH_TIME] ?: ""
+ }
+
+ override suspend fun setRefreshTime(refreshTime: String) {
+ dataStore.edit {
+ it[AuthPreferenceKey.REFRESH_TIME] = refreshTime
+ }
+ }
+
+ override suspend fun removeRefreshTime() {
+ dataStore.edit {
+ it.remove(AuthPreferenceKey.REFRESH_TIME)
+ }
+ }
+
+ override suspend fun getAuthorityInfo(): Flow = dataStore.data.map {
+ it[AuthPreferenceKey.AUTHORITY] ?: ""
+ }
+
+ override suspend fun setAuthorityInfo(authority: String) {
+ dataStore.edit {
+ it[AuthPreferenceKey.AUTHORITY] = authority
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/stackknowledge/datastore/di/DataStoreModule.kt
new file mode 100644
index 00000000..983998c7
--- /dev/null
+++ b/core/datastore/src/main/java/com/stackknowledge/datastore/di/DataStoreModule.kt
@@ -0,0 +1,30 @@
+package com.stackknowledge.datastore.di
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.preferences.core.PreferenceDataStoreFactory
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.emptyPreferences
+import androidx.datastore.preferences.preferencesDataStoreFile
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object DataStoreModule {
+ @Provides
+ @Singleton
+ fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore {
+ return PreferenceDataStoreFactory.create(
+ corruptionHandler = ReplaceFileCorruptionHandler(
+ produceNewData = { emptyPreferences() }
+ ),
+ produceFile = { context.preferencesDataStoreFile("autoDataStore") }
+ )
+ }
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/datastore/di/LocalDataSourceModule.kt b/core/datastore/src/main/java/com/stackknowledge/datastore/di/LocalDataSourceModule.kt
new file mode 100644
index 00000000..73a42149
--- /dev/null
+++ b/core/datastore/src/main/java/com/stackknowledge/datastore/di/LocalDataSourceModule.kt
@@ -0,0 +1,18 @@
+package com.stackknowledge.datastore.di
+
+import com.stackknowledge.datastore.LocalAuthDataSource
+import com.stackknowledge.datastore.LocalAuthDataSourceImpl
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class LocalDataSourceModule {
+ @Binds
+ abstract fun provideLocalAuthDataSource(
+ localAuthDataSourceImpl: LocalAuthDataSourceImpl
+ ): LocalAuthDataSource
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/datastore/key/AuthPreferenceKey.kt b/core/datastore/src/main/java/com/stackknowledge/datastore/key/AuthPreferenceKey.kt
new file mode 100644
index 00000000..d6c9d522
--- /dev/null
+++ b/core/datastore/src/main/java/com/stackknowledge/datastore/key/AuthPreferenceKey.kt
@@ -0,0 +1,16 @@
+package com.stackknowledge.datastore.key
+
+import androidx.datastore.preferences.core.stringPreferencesKey
+
+object AuthPreferenceKey {
+
+ val ACCESS_TOKEN = stringPreferencesKey("access_token")
+
+ val ACCESS_TIME = stringPreferencesKey("access_time")
+
+ val REFRESH_TOKEN = stringPreferencesKey("refresh_token")
+
+ val REFRESH_TIME = stringPreferencesKey("refresh_time")
+
+ val AUTHORITY = stringPreferencesKey("authority")
+}
\ No newline at end of file
diff --git a/core/datastore/src/main/java/com/stackknowledge/proto/authToken.proto b/core/datastore/src/main/java/com/stackknowledge/proto/authToken.proto
deleted file mode 100644
index 8978ca71..00000000
--- a/core/datastore/src/main/java/com/stackknowledge/proto/authToken.proto
+++ /dev/null
@@ -1,12 +0,0 @@
- syntax = "proto3";
-
- option java_package = "com.msg.datastore";
- option java_multiple_files = true;
-
- message AuthToken {
- string accessToken = 1;
- string accessExp = 2;
- string refreshToken = 3;
- string refreshExp = 4;
- string authority = 5;
- }
\ No newline at end of file
diff --git a/core/design-system/src/main/java/com/stackknowledge/design_system/component/button/GoogleButton.kt b/core/design-system/src/main/java/com/stackknowledge/design_system/component/button/GoogleButton.kt
index a71d3dbc..5c1eb76e 100644
--- a/core/design-system/src/main/java/com/stackknowledge/design_system/component/button/GoogleButton.kt
+++ b/core/design-system/src/main/java/com/stackknowledge/design_system/component/button/GoogleButton.kt
@@ -24,11 +24,12 @@ import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
@Composable
fun GoogleButton(
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {}
) {
StackKnowledgeAndroidTheme { colors, typography ->
Button(
- onClick = {},
+ onClick = onClick,
modifier = modifier
.fillMaxWidth()
.clip(shape = RoundedCornerShape(10.dp))
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginStudentUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginStudentUseCase.kt
new file mode 100644
index 00000000..cf896537
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginStudentUseCase.kt
@@ -0,0 +1,14 @@
+package com.stackknowledge.usecase.auth
+
+import com.stackknowledge.repository.auth.AuthRepository
+import kotlinx.coroutines.flow.Flow
+import remote.request.auth.LoginRequestModel
+import remote.response.auth.LoginResponseModel
+import javax.inject.Inject
+
+class LoginStudentUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ operator fun invoke(body: LoginRequestModel): Flow =
+ authRepository.loginStudent(body = body)
+}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginTeacherUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginTeacherUseCase.kt
new file mode 100644
index 00000000..fabdf4f4
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LoginTeacherUseCase.kt
@@ -0,0 +1,15 @@
+package com.stackknowledge.usecase.auth
+
+import com.stackknowledge.repository.auth.AuthRepository
+import kotlinx.coroutines.flow.Flow
+import remote.request.auth.LoginRequestModel
+import remote.response.auth.LoginResponseModel
+import javax.inject.Inject
+
+class LoginTeacherUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ operator fun invoke(body: LoginRequestModel): Flow =
+ authRepository.loginTeacher(body = body)
+
+}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LogoutUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LogoutUseCase.kt
new file mode 100644
index 00000000..a098b988
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/LogoutUseCase.kt
@@ -0,0 +1,12 @@
+package com.stackknowledge.usecase.auth
+
+import com.stackknowledge.repository.auth.AuthRepository
+import javax.inject.Inject
+
+class LogoutUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ operator fun invoke() = kotlin.runCatching {
+ authRepository.logout()
+ }
+}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/SaveTokenUseCase.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/SaveTokenUseCase.kt
new file mode 100644
index 00000000..5d809ee7
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/auth/SaveTokenUseCase.kt
@@ -0,0 +1,13 @@
+package com.stackknowledge.usecase.auth
+
+import com.stackknowledge.repository.auth.AuthRepository
+import remote.response.auth.LoginResponseModel
+import javax.inject.Inject
+
+class SaveTokenUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ suspend operator fun invoke(token: LoginResponseModel) = kotlin.runCatching {
+ authRepository.saveToken(token = token)
+ }
+}
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/HttpException.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/HttpException.kt
new file mode 100644
index 00000000..b9fd36b5
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/HttpException.kt
@@ -0,0 +1,42 @@
+package com.stackknowledge.usecase.exception
+
+class BadRequestException(
+ override val message: String?
+) : RuntimeException()
+
+class UnauthorizedException(
+ override val message: String?
+) : RuntimeException()
+
+class ForBiddenException(
+ override val message: String?
+) : RuntimeException()
+
+class NotFoundException(
+ override val message: String?
+) : RuntimeException()
+
+class NotAcceptableException(
+ override val message: String?
+) : RuntimeException()
+
+class ConflictException(
+ override val message: String?
+) : RuntimeException()
+
+class TimeOutException(
+ override val message: String?
+) : RuntimeException()
+
+class ServerException(
+ override val message: String?
+) : RuntimeException()
+
+class OtherException(
+ override val message: String?,
+ val code: Int
+) : RuntimeException()
+
+class UnknownException(
+ override val message: String?
+) : RuntimeException()
\ No newline at end of file
diff --git a/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/NeedLoginException.kt b/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/NeedLoginException.kt
new file mode 100644
index 00000000..fbeb5280
--- /dev/null
+++ b/core/domain/src/main/kotlin/com/stackknowledge/usecase/exception/NeedLoginException.kt
@@ -0,0 +1,8 @@
+package com.stackknowledge.usecase.exception
+
+import java.io.IOException
+
+class NeedLoginException : IOException() {
+ override val message: String
+ get() = "토큰이 만료되었습니다. 다시 로그인 해 주세요"
+}
\ No newline at end of file
diff --git a/core/model/src/main/kotlin/enumdatatype/Authority.kt b/core/model/src/main/kotlin/enumdata/Authority.kt
similarity index 53%
rename from core/model/src/main/kotlin/enumdatatype/Authority.kt
rename to core/model/src/main/kotlin/enumdata/Authority.kt
index 4049a8d7..64c32a62 100644
--- a/core/model/src/main/kotlin/enumdatatype/Authority.kt
+++ b/core/model/src/main/kotlin/enumdata/Authority.kt
@@ -1,6 +1,6 @@
-package enumdatatype
+package enumdata
enum class Authority {
+ ROLE_TEACHER,
ROLE_STUDENT,
- ROLE_TEACHER
}
\ No newline at end of file
diff --git a/core/model/src/main/kotlin/enumdatatype/MissionStatus.kt b/core/model/src/main/kotlin/enumdata/MissionStatus.kt
similarity index 77%
rename from core/model/src/main/kotlin/enumdatatype/MissionStatus.kt
rename to core/model/src/main/kotlin/enumdata/MissionStatus.kt
index 842feaed..1a45c91f 100644
--- a/core/model/src/main/kotlin/enumdatatype/MissionStatus.kt
+++ b/core/model/src/main/kotlin/enumdata/MissionStatus.kt
@@ -1,4 +1,4 @@
-package enumdatatype
+package enumdata
enum class MissionStatus {
CLOSED,
diff --git a/core/model/src/main/kotlin/remote/request/auth/LoginRequestModel.kt b/core/model/src/main/kotlin/remote/request/auth/LoginRequestModel.kt
new file mode 100644
index 00000000..e002a47c
--- /dev/null
+++ b/core/model/src/main/kotlin/remote/request/auth/LoginRequestModel.kt
@@ -0,0 +1,5 @@
+package remote.request.auth
+
+data class LoginRequestModel(
+ val code: String,
+)
diff --git a/core/model/src/main/kotlin/remote/response/auth/LoginResponseModel.kt b/core/model/src/main/kotlin/remote/response/auth/LoginResponseModel.kt
new file mode 100644
index 00000000..2517da7e
--- /dev/null
+++ b/core/model/src/main/kotlin/remote/response/auth/LoginResponseModel.kt
@@ -0,0 +1,10 @@
+package remote.response.auth
+
+import enumdata.Authority
+
+data class LoginResponseModel(
+ val accessToken: String,
+ val refreshToken: String,
+ val expiredAt: String,
+ val authority: Authority,
+)
\ No newline at end of file
diff --git a/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt b/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt
index 01241ca0..a5441003 100644
--- a/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt
+++ b/core/model/src/main/kotlin/remote/response/mission/MissionResponseModel.kt
@@ -1,8 +1,6 @@
package remote.response.mission
-import enumdatatype.MissionStatus
import remote.user.UserModel
-import java.util.UUID
data class MissionResponseModel(
val id: String,
diff --git a/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt b/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt
index e650be7f..9800d859 100644
--- a/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt
+++ b/core/model/src/main/kotlin/remote/response/mission/MissionsModel.kt
@@ -1,6 +1,6 @@
package remote.response.mission
-import enumdatatype.MissionStatus
+import enumdata.MissionStatus
import remote.user.UserModel
import java.util.UUID
diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts
index 32ef8df8..a3754ee5 100644
--- a/core/network/build.gradle.kts
+++ b/core/network/build.gradle.kts
@@ -1,4 +1,5 @@
import java.io.FileInputStream
+import java.io.FileNotFoundException
import java.util.Properties
@Suppress("DSL_SCOPE_VIOLATION")
@@ -14,9 +15,11 @@ android {
defaultConfig {
buildConfigField("String", "BASE_URL", getApiKey("BASE_URL"))
+ buildConfigField("String", "GOOGLE_CLIENT_ID", getApiKey("GOOGLE_CLIENT_ID"))
+ buildConfigField("String", "SCOPE", getApiKey("SCOPE"))
}
- namespace = "com.msg.network"
+ namespace = "com.stackknowledge.network"
}
dependencies {
@@ -33,12 +36,13 @@ dependencies {
implementation(libs.retrofit.kotlin.serialization)
implementation(libs.retrofit.moshi.converter)
implementation(libs.moshi)
- ksp(libs.retrofit.moshi.codegen)
+ implementation(libs.retrofit.moshi.codegen)
+ implementation(libs.moshi.kotlin)
}
fun getApiKey(propertyKey: String): String {
val propFile = rootProject.file("./local.properties")
val properties = Properties()
properties.load(FileInputStream(propFile))
- return properties.getProperty(propertyKey)
+ return properties.getProperty(propertyKey) ?: throw IllegalArgumentException("Property $propertyKey not found in local.properties file")
}
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/api/AuthAPI.kt b/core/network/src/main/kotlin/com/stackknowledge/api/AuthAPI.kt
new file mode 100644
index 00000000..9e6b3179
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/api/AuthAPI.kt
@@ -0,0 +1,22 @@
+package com.stackknowledge.api
+
+import com.stackknowledge.dto.request.auth.LoginRequest
+import com.stackknowledge.dto.response.auth.LoginResponse
+import retrofit2.http.Body
+import retrofit2.http.DELETE
+import retrofit2.http.POST
+
+interface AuthAPI {
+ @POST("/auth/student")
+ suspend fun loginStudent(
+ @Body body: LoginRequest,
+ ): LoginResponse
+
+ @POST("/auth/teacher")
+ suspend fun loginTeacher(
+ @Body body: LoginRequest,
+ ): LoginResponse
+
+ @DELETE("/auth")
+ suspend fun logout()
+}
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSource.kt b/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSource.kt
new file mode 100644
index 00000000..efb39c65
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSource.kt
@@ -0,0 +1,11 @@
+package com.stackknowledge.datasource.auth
+
+import com.stackknowledge.dto.request.auth.LoginRequest
+import com.stackknowledge.dto.response.auth.LoginResponse
+import kotlinx.coroutines.flow.Flow
+
+interface AuthDataSource {
+ fun loginStudent(body: LoginRequest): Flow
+ fun loginTeacher(body: LoginRequest): Flow
+ fun logout(): Flow
+}
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSourceImpl.kt b/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSourceImpl.kt
new file mode 100644
index 00000000..ba24fa16
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/datasource/auth/AuthDataSourceImpl.kt
@@ -0,0 +1,40 @@
+package com.stackknowledge.datasource.auth
+
+import android.util.Log
+import com.stackknowledge.api.AuthAPI
+import com.stackknowledge.dto.request.auth.LoginRequest
+import com.stackknowledge.dto.response.auth.LoginResponse
+import com.stackknowledge.util.StackKnowledgeApiHandler
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import javax.inject.Inject
+
+class AuthDataSourceImpl @Inject constructor(
+ private val authAPI: AuthAPI
+) : AuthDataSource {
+ override fun loginStudent(body: LoginRequest): Flow = flow {
+ emit(
+ StackKnowledgeApiHandler()
+ .httpRequest { authAPI.loginStudent(body = body) }
+ .sendRequest()
+ )
+ }.flowOn(Dispatchers.IO)
+
+ override fun loginTeacher(body: LoginRequest): Flow = flow {
+ emit(
+ StackKnowledgeApiHandler()
+ .httpRequest { authAPI.loginTeacher(body = body) }
+ .sendRequest()
+ )
+ }.flowOn(Dispatchers.IO)
+
+ override fun logout(): Flow = flow {
+ emit(
+ StackKnowledgeApiHandler()
+ .httpRequest { authAPI.logout() }
+ .sendRequest()
+ )
+ }.flowOn(Dispatchers.IO)
+}
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/di/AppConfigModule.kt b/core/network/src/main/kotlin/com/stackknowledge/di/AppConfigModule.kt
new file mode 100644
index 00000000..39f1adc3
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/di/AppConfigModule.kt
@@ -0,0 +1,20 @@
+package com.stackknowledge.di
+
+import com.stackknowledge.network.BuildConfig
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Named
+
+@Module
+@InstallIn(SingletonComponent::class)
+object AppConfigModule {
+ @Provides
+ @Named("GOOGLE_CLIENT_ID")
+ fun provideGoogleClientId(): String = BuildConfig.GOOGLE_CLIENT_ID
+
+ @Provides
+ @Named("SCOPE")
+ fun provideScope(): String = BuildConfig.SCOPE
+}
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt b/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt
index a1469c19..1ca37f29 100644
--- a/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt
+++ b/core/network/src/main/kotlin/com/stackknowledge/di/NetworkModule.kt
@@ -1,19 +1,21 @@
package com.stackknowledge.di
-import com.msg.network.BuildConfig
+import android.util.Log
import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import com.stackknowledge.api.AuthAPI
import com.stackknowledge.api.ItemAPI
import com.stackknowledge.api.MissionAPI
import com.stackknowledge.api.OrderAPI
import com.stackknowledge.api.SolveAPI
import com.stackknowledge.api.StudentAPI
import com.stackknowledge.api.UserAPI
+import com.stackknowledge.network.BuildConfig
import com.stackknowledge.util.AuthInterceptor
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
-import okhttp3.CookieJar
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
@@ -24,6 +26,10 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
+ @Provides
+ fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor =
+ HttpLoggingInterceptor { message -> Log.v("HTTP", message) }
+ .setLevel(HttpLoggingInterceptor.Level.BODY)
@Provides
@Singleton
@@ -32,7 +38,6 @@ object NetworkModule {
authInterceptor: AuthInterceptor,
): OkHttpClient {
return OkHttpClient.Builder()
- .cookieJar(CookieJar.NO_COOKIES)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
@@ -43,21 +48,15 @@ object NetworkModule {
@Provides
@Singleton
- fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
- return HttpLoggingInterceptor().apply {
- level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
- }
- }
-
- @Provides
- @Singleton
- fun provideMoshiInstance(): Moshi {
- return Moshi.Builder().build()
+ fun provideMoshi(): Moshi {
+ return Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
}
@Provides
@Singleton
- fun provideConverterFactory(moshi: Moshi): MoshiConverterFactory {
+ fun provideMoshiConverterFactory(moshi: Moshi): MoshiConverterFactory {
return MoshiConverterFactory.create(moshi)
}
@@ -73,6 +72,12 @@ object NetworkModule {
.addConverterFactory(moshiConverterFactory)
.build()
}
+
+ @Provides
+ @Singleton
+ fun provideAuthAPI(retrofit: Retrofit): AuthAPI =
+ retrofit.create(AuthAPI::class.java)
+
@Provides
@Singleton
fun provideMissionAPI(retrofit: Retrofit): MissionAPI =
diff --git a/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt b/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt
index 536698fa..7b553e36 100644
--- a/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt
+++ b/core/network/src/main/kotlin/com/stackknowledge/di/RemoteDataSourceModule.kt
@@ -1,5 +1,7 @@
package com.stackknowledge.di
+import com.stackknowledge.datasource.auth.AuthDataSource
+import com.stackknowledge.datasource.auth.AuthDataSourceImpl
import com.stackknowledge.datasource.item.ItemDataSource
import com.stackknowledge.datasource.item.ItemDataSourceImpl
import com.stackknowledge.datasource.mission.MissionDataSource
@@ -21,6 +23,12 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
abstract class RemoteDataSourceModule {
+ @Binds
+ @Singleton
+ abstract fun bindAuthDataSource(
+ authDataSourceImpl: AuthDataSourceImpl
+ ): AuthDataSource
+
@Binds
@Singleton
abstract fun bindMissionDataSource(
diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/request/auth/LoginRequest.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/request/auth/LoginRequest.kt
new file mode 100644
index 00000000..f6a75c2f
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/dto/request/auth/LoginRequest.kt
@@ -0,0 +1,9 @@
+package com.stackknowledge.dto.request.auth
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class LoginRequest(
+ @Json(name = "code") val code: String,
+)
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/auth/LoginResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/auth/LoginResponse.kt
new file mode 100644
index 00000000..c3a91ab6
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/auth/LoginResponse.kt
@@ -0,0 +1,13 @@
+package com.stackknowledge.dto.response.auth
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import enumdata.Authority
+
+@JsonClass(generateAdapter = true)
+data class LoginResponse(
+ @Json(name = "accessToken") val accessToken: String,
+ @Json(name = "refreshToken") val refreshToken: String,
+ @Json(name = "expiredAt") val expiredAt: String,
+ @Json(name = "authority") val authority: Authority,
+)
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt
new file mode 100644
index 00000000..73b90378
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/GetMissionListResponse.kt
@@ -0,0 +1,18 @@
+package com.stackknowledge.dto.response.mission
+
+import enumdata.MissionStatus
+import java.util.UUID
+
+data class GetMissionListResponse(
+ val id: UUID,
+ val title: String,
+ val point: Int,
+ val missionStatus: MissionStatus,
+ val user: User,
+) {
+ data class User(
+ val id: UUID,
+ val name: String,
+ val profileImage: String?,
+ )
+}
diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt
index dbdacef4..623ecbb4 100644
--- a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt
+++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/MissionResponse.kt
@@ -3,9 +3,6 @@ package com.stackknowledge.dto.response.mission
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.stackknowledge.dto.user.User
-import enumdatatype.MissionStatus
-import remote.user.UserModel
-import java.util.UUID
@JsonClass(generateAdapter = true)
data class MissionResponse(
diff --git a/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt
new file mode 100644
index 00000000..8f387747
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/dto/response/mission/Missions.kt
@@ -0,0 +1,16 @@
+package com.stackknowledge.dto.response.mission
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import enumdata.MissionStatus
+import remote.user.UserModel
+import java.util.UUID
+
+@JsonClass(generateAdapter = true)
+data class Missions(
+ @Json(name = "id") val id: UUID,
+ @Json(name = "title") val title: String,
+ @Json(name = "point") val point: Int,
+ @Json(name = "missionStatus") val missionStatus: MissionStatus,
+ @Json(name = "user") val user: UserModel,
+)
diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/request/auth/LoginRequestMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/auth/LoginRequestMapper.kt
new file mode 100644
index 00000000..bfda8797
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/request/auth/LoginRequestMapper.kt
@@ -0,0 +1,8 @@
+package com.stackknowledge.mapper.request.auth
+
+import com.stackknowledge.dto.request.auth.LoginRequest
+import remote.request.auth.LoginRequestModel
+
+fun LoginRequestModel.toDto(): LoginRequest = LoginRequest(
+ code = this.code,
+)
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/mapper/response/auth/LoginResponseMapper.kt b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/auth/LoginResponseMapper.kt
new file mode 100644
index 00000000..75458b0f
--- /dev/null
+++ b/core/network/src/main/kotlin/com/stackknowledge/mapper/response/auth/LoginResponseMapper.kt
@@ -0,0 +1,11 @@
+package com.stackknowledge.mapper.response.auth
+
+import com.stackknowledge.dto.response.auth.LoginResponse
+import remote.response.auth.LoginResponseModel
+
+fun LoginResponse.toModel(): LoginResponseModel = LoginResponseModel(
+ accessToken = this.accessToken,
+ refreshToken = this.refreshToken,
+ expiredAt = this.expiredAt,
+ authority = this.authority,
+)
\ No newline at end of file
diff --git a/core/network/src/main/kotlin/com/stackknowledge/util/AuthInterceptor.kt b/core/network/src/main/kotlin/com/stackknowledge/util/AuthInterceptor.kt
index 99d2616f..0911f817 100644
--- a/core/network/src/main/kotlin/com/stackknowledge/util/AuthInterceptor.kt
+++ b/core/network/src/main/kotlin/com/stackknowledge/util/AuthInterceptor.kt
@@ -1,19 +1,90 @@
package com.stackknowledge.util
+import com.example.common.exception.NeedLoginException
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import com.stackknowledge.network.BuildConfig
+import com.stackknowledge.datastore.LocalAuthDataSource
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody
import okhttp3.Response
-import util.ResourceKeys
import javax.inject.Inject
-class AuthInterceptor @Inject constructor(): Interceptor {
+class AuthInterceptor @Inject constructor(
+ private val dataSource: LocalAuthDataSource
+): Interceptor {
+
+ private val moshi: Moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+ data class TokenResponse(
+ val accessToken: String,
+ val refreshToken: String,
+ val expiredAt: String
+ )
+
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val builder = request.newBuilder()
+ val currentTime = System.currentTimeMillis().toLocalDateTime()
+ val ignorePath = listOf("/auth")
+ val ignoreMethod = listOf("POST")
+ val path = request.url.encodedPath
+ val method = request.method
+
+ ignorePath.forEachIndexed { index, s ->
+ if (path.contains(s) && ignoreMethod[index] == method)
+ return chain.proceed(request)
+ }
runBlocking {
- builder.addHeader("Authorization", "${ResourceKeys.BEARER} ")
+ val refreshTime = dataSource.getRefreshTime().first().replace("\"", "")
+ val accessTime = dataSource.getAccessTime().first().replace("\"", "")
+
+ if (refreshTime == "") {
+ return@runBlocking
+ }
+
+ if (currentTime != null) {
+ if (currentTime.isAfter(refreshTime.toLocalDateTime())) {
+ throw NeedLoginException()
+ }
+ }
+
+ // access 토큰 재 발급
+ if (currentTime != null) {
+ if (currentTime.isAfter(accessTime.toLocalDateTime())) {
+ val client = OkHttpClient()
+ val refreshRequest = Request.Builder()
+ .url(BuildConfig.BASE_URL + "/auth")
+ .patch(chain.request().body ?: RequestBody.create(null, byteArrayOf()))
+ .addHeader(
+ "Refresh-Token",
+ dataSource.getRefreshToken().first().replace("\"", "")
+ )
+ .build()
+ val response = client.newCall(refreshRequest).execute()
+ if (response.isSuccessful) {
+ val adapter = moshi.adapter(TokenResponse::class.java)
+ val tokenResponse = adapter.fromJson(response.body!!.string())
+
+ tokenResponse?.let {
+ dataSource.setAccessToken(it.accessToken)
+ dataSource.setRefreshToken(it.refreshToken)
+ dataSource.setAccessTime(it.expiredAt)
+ dataSource.setRefreshTime(it.expiredAt)
+ } ?: throw NeedLoginException()
+ } else throw NeedLoginException()
+ }
+ }
+ val accessToken = dataSource.getAccessToken().first().replace("\"", "")
+ builder.addHeader("Authorization", "Bearer $accessToken")
}
return chain.proceed(builder.build())
}
-}
\ No newline at end of file
+}
diff --git a/core/ui/src/main/java/com/minstone/ui/navigation/StackKnowledgeBottomNavigation.kt b/core/ui/src/main/java/com/minstone/ui/navigation/StackKnowledgeBottomNavigation.kt
index cfb414dc..96d5b25e 100644
--- a/core/ui/src/main/java/com/minstone/ui/navigation/StackKnowledgeBottomNavigation.kt
+++ b/core/ui/src/main/java/com/minstone/ui/navigation/StackKnowledgeBottomNavigation.kt
@@ -1,7 +1,5 @@
package com.minstone.ui.navigation
-import android.icu.text.TimeZoneNames.NameType
-import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -21,7 +19,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.stackknowledge.design_system.R
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
-import enumdatatype.Authority
+import enumdata.Authority
enum class NavigateType(val value: String) {
HOME("home"),
diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts
index 65db4e8a..537d78c1 100644
--- a/feature/login/build.gradle.kts
+++ b/feature/login/build.gradle.kts
@@ -6,4 +6,9 @@ plugins {
android {
namespace = "com.stackknowledge.login"
+}
+
+dependencies {
+ implementation(libs.google.services)
+ implementation(libs.play.services.auth)
}
\ No newline at end of file
diff --git a/feature/login/src/androidTest/java/com/stackknowledge/login/ExampleInstrumentedTest.kt b/feature/login/src/androidTest/java/com/stackknowledge/login/ExampleInstrumentedTest.kt
deleted file mode 100644
index 6ee0b701..00000000
--- a/feature/login/src/androidTest/java/com/stackknowledge/login/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.stackknowledge.login
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.teamgrapefruit.login.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/feature/login/src/main/java/com/stackknowledge/login/LoginScreen.kt b/feature/login/src/main/java/com/stackknowledge/login/LoginScreen.kt
index 98150860..6e18116d 100644
--- a/feature/login/src/main/java/com/stackknowledge/login/LoginScreen.kt
+++ b/feature/login/src/main/java/com/stackknowledge/login/LoginScreen.kt
@@ -1,5 +1,7 @@
package com.stackknowledge.login
+import android.util.Log
+import androidx.activity.ComponentActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -12,38 +14,61 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.stackknowledge.design_system.R
import com.stackknowledge.design_system.component.button.GoogleButton
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.login.background.LoginBackground
+import com.stackknowledge.login.viewmodel.AuthViewModel
+import com.stackknowledge.login.viewmodel.uistate.LoginUiState
@Composable
-internal fun LoginScreenRoute() {
- LoginScreen()
+internal fun LoginRoute(
+ onSuccess: () -> Unit,
+ onGoogleLoginButtonClicked: () -> Unit,
+ viewModel: AuthViewModel = hiltViewModel(LocalContext.current as ComponentActivity),
+) {
+ val loginUiState by viewModel.loginUiState.collectAsStateWithLifecycle()
+
+ LoginScreen(
+ onGoogleLoginButtonClicked = onGoogleLoginButtonClicked,
+ loginUiState = loginUiState,
+ viewModel = viewModel,
+ onLoginSuccess = onSuccess
+ )
}
@Composable
private fun LoginScreen(
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
+ viewModel: AuthViewModel = hiltViewModel(),
+ onGoogleLoginButtonClicked: () -> Unit = {},
+ loginUiState: LoginUiState,
+ onLoginSuccess: () -> Unit = {},
) {
StackKnowledgeAndroidTheme { colors, typography ->
Surface {
Column(
modifier = modifier.fillMaxSize()
) {
- Box() {
+ Box {
LoginBackground()
+
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = modifier.height(157.dp))
+
Image(
painter = painterResource(R.drawable.stack_knowledge_logo),
contentDescription = "Stack Knowledge Logo",
@@ -51,31 +76,59 @@ private fun LoginScreen(
.width(50.dp)
.height(50.dp)
)
+
Spacer(modifier = modifier.height(20.dp))
+
Text(
text = stringResource(R.string.app_title),
style = typography.titleLarge,
color = colors.BLACK
)
+
Spacer(modifier = modifier.height(360.dp))
+
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
GoogleButton(
- modifier = modifier.height(60.dp)
+ modifier = modifier.height(60.dp),
+ onClick = {
+ onGoogleLoginButtonClicked()
+ }
)
}
}
}
+
+ when(loginUiState) {
+ is LoginUiState.Success -> {
+ val tokenResponse = loginUiState.loginResponseModel
+
+ viewModel.saveToken(tokenResponse)
+ onLoginSuccess()
+ }
+ is LoginUiState.Error -> {
+ // Login 실패 처리 (임의)
+ Log.e("Login Error", "Login Error")
+ }
+ LoginUiState.Loading -> {
+ // Loading 처리 (임의)
+ Log.e("Login Loading", "Login Loading")
+ }
+ }
}
}
}
}
+
@Preview
@Composable
fun LoginScreenPre() {
- LoginScreen()
+ LoginScreen(
+ onGoogleLoginButtonClicked = {},
+ loginUiState = LoginUiState.Loading
+ )
}
\ No newline at end of file
diff --git a/feature/login/src/main/java/com/stackknowledge/login/RoleCheckScreen.kt b/feature/login/src/main/java/com/stackknowledge/login/RoleCheckScreen.kt
index 37da72b2..cab874a0 100644
--- a/feature/login/src/main/java/com/stackknowledge/login/RoleCheckScreen.kt
+++ b/feature/login/src/main/java/com/stackknowledge/login/RoleCheckScreen.kt
@@ -1,5 +1,7 @@
package com.stackknowledge.login
+
+import androidx.activity.ComponentActivity
import androidx.compose.foundation.background
import com.stackknowledge.design_system.R
import androidx.compose.foundation.layout.Box
@@ -15,28 +17,41 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
import com.stackknowledge.design_system.component.button.StackKnowledgeButton
import com.stackknowledge.design_system.component.button.enumclass.ButtonState
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.login.background.LoginBackground
+import com.stackknowledge.login.viewmodel.AuthViewModel
@Composable
-internal fun RoleCheckScreenRoute(
- onRoleClick: () -> Unit,
+internal fun RoleCheckRoute(
+ onRoleButtonClick: () -> Unit,
+authViewModel: AuthViewModel = hiltViewModel(LocalContext.current as ComponentActivity)
) {
- RoleCheckScreen(
- onRoleClick = onRoleClick
- )
+ with(authViewModel) {
+ RoleCheckScreen(
+ onTeacherButtonClick = { teacherRoleBoolean ->
+ onRoleButtonClick()
+ isTeacher.value = teacherRoleBoolean
+ },
+ onStudentButtonClick = { studentRoleBoolean ->
+ onRoleButtonClick()
+ isStudent.value = studentRoleBoolean
+ }
+ )
+ }
}
-
@Composable
private fun RoleCheckScreen(
modifier: Modifier = Modifier,
- onRoleClick: () -> Unit,
+ onTeacherButtonClick: (Boolean) -> Unit,
+ onStudentButtonClick: (Boolean) -> Unit,
) {
StackKnowledgeAndroidTheme { colors, typography ->
Box(
@@ -69,7 +84,9 @@ private fun RoleCheckScreen(
modifier = modifier
.height(60.dp)
.weight(1f),
- onClick = onRoleClick
+ onClick = {
+ onStudentButtonClick(true)
+ }
)
Spacer(modifier = modifier.width(8.dp))
@@ -80,7 +97,9 @@ private fun RoleCheckScreen(
modifier = modifier
.height(60.dp)
.weight(1f),
- onClick = onRoleClick
+ onClick = {
+ onTeacherButtonClick(true)
+ }
)
}
@@ -94,6 +113,7 @@ private fun RoleCheckScreen(
@Composable
fun RoleCheckScreenPre() {
RoleCheckScreen(
- onRoleClick = {}
+ onTeacherButtonClick = {},
+ onStudentButtonClick = {},
)
}
\ No newline at end of file
diff --git a/feature/login/src/main/java/com/stackknowledge/login/navigation/LoginNavigation.kt b/feature/login/src/main/java/com/stackknowledge/login/navigation/LoginNavigation.kt
index 9d03e5e8..6e1ece0c 100644
--- a/feature/login/src/main/java/com/stackknowledge/login/navigation/LoginNavigation.kt
+++ b/feature/login/src/main/java/com/stackknowledge/login/navigation/LoginNavigation.kt
@@ -4,8 +4,8 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
-import com.stackknowledge.login.LoginScreenRoute
-import com.stackknowledge.login.RoleCheckScreenRoute
+import com.stackknowledge.login.LoginRoute
+import com.stackknowledge.login.RoleCheckRoute
const val loginRoute = "login_route"
const val roleCheckRoute = "role_check_route"
@@ -14,9 +14,15 @@ fun NavController.navigateToLogin(navOptions: NavOptions? = null) {
this.navigate(loginRoute, navOptions)
}
-fun NavGraphBuilder.loginScreen() {
+fun NavGraphBuilder.loginScreen(
+ onSuccess: () -> Unit = {},
+ onLoginButtonClick: () -> Unit = {},
+) {
composable(route = loginRoute) {
- LoginScreenRoute()
+ LoginRoute(
+ onSuccess = onSuccess,
+ onGoogleLoginButtonClicked = onLoginButtonClick
+ )
}
}
@@ -25,11 +31,11 @@ fun NavController.navigateToRoleCheck(navOptions: NavOptions? = null) {
}
fun NavGraphBuilder.roleCheckScreen(
- onRoleClick: () -> Unit,
+ onRoleButtonClick: () -> Unit,
) {
composable(route = roleCheckRoute) {
- RoleCheckScreenRoute(
- onRoleClick = onRoleClick
+ RoleCheckRoute(
+ onRoleButtonClick = onRoleButtonClick
)
}
}
\ No newline at end of file
diff --git a/feature/login/src/main/java/com/stackknowledge/login/viewmodel/AuthViewModel.kt b/feature/login/src/main/java/com/stackknowledge/login/viewmodel/AuthViewModel.kt
new file mode 100644
index 00000000..dc86fa6e
--- /dev/null
+++ b/feature/login/src/main/java/com/stackknowledge/login/viewmodel/AuthViewModel.kt
@@ -0,0 +1,75 @@
+package com.stackknowledge.login.viewmodel
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.example.common.result.Result
+import com.example.common.result.asResult
+import com.example.common.util.Event
+import com.example.common.util.errorHandling
+import com.stackknowledge.login.viewmodel.uistate.LoginUiState
+import com.stackknowledge.usecase.auth.SaveTokenUseCase
+import com.stackknowledge.usecase.auth.LoginStudentUseCase
+import com.stackknowledge.usecase.auth.LoginTeacherUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import remote.request.auth.LoginRequestModel
+import remote.response.auth.LoginResponseModel
+import javax.inject.Inject
+
+@HiltViewModel
+class AuthViewModel @Inject constructor(
+ private val loginStudentUseCase: LoginStudentUseCase,
+ private val loginTeacherUseCase: LoginTeacherUseCase,
+ private val saveTokenUseCase: SaveTokenUseCase,
+) : ViewModel() {
+ private val _saveTokenRequest = MutableStateFlow>(Event.Loading)
+ internal val saveTokenRequest = _saveTokenRequest.asStateFlow()
+
+ private val _loginUiState = MutableStateFlow(LoginUiState.Loading)
+ internal val loginUiState = _loginUiState.asStateFlow()
+
+ var isTeacher = mutableStateOf(false)
+ private set
+
+ var isStudent = mutableStateOf(false)
+ private set
+
+ fun loginStudent(body: LoginRequestModel) = viewModelScope.launch {
+ loginStudentUseCase(body = body)
+ .asResult()
+ .collectLatest { result ->
+ when (result) {
+ is Result.Loading -> _loginUiState.value = LoginUiState.Loading
+ is Result.Success -> _loginUiState.value = LoginUiState.Success(result.data)
+ is Result.Error -> _loginUiState.value = LoginUiState.Error(result.exception)
+ }
+ }
+ }
+
+ fun loginTeacher(body: LoginRequestModel) = viewModelScope.launch {
+ loginTeacherUseCase(body = body)
+ .asResult()
+ .collectLatest { result ->
+ when (result) {
+ is Result.Loading -> _loginUiState.value = LoginUiState.Loading
+ is Result.Success -> _loginUiState.value = LoginUiState.Success(result.data)
+ is Result.Error -> _loginUiState.value = LoginUiState.Error(result.exception)
+ }
+
+ }
+ }
+
+ internal fun saveToken(token: LoginResponseModel) = viewModelScope.launch {
+ saveTokenUseCase(
+ token = token
+ ).onSuccess {
+ _saveTokenRequest.value = Event.Success()
+ }.onFailure {
+ _saveTokenRequest.value = it.errorHandling()
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/login/src/main/java/com/stackknowledge/login/viewmodel/uistate/LoginUiState.kt b/feature/login/src/main/java/com/stackknowledge/login/viewmodel/uistate/LoginUiState.kt
new file mode 100644
index 00000000..89f1a33f
--- /dev/null
+++ b/feature/login/src/main/java/com/stackknowledge/login/viewmodel/uistate/LoginUiState.kt
@@ -0,0 +1,9 @@
+package com.stackknowledge.login.viewmodel.uistate
+
+import remote.response.auth.LoginResponseModel
+
+sealed interface LoginUiState {
+ object Loading : LoginUiState
+ data class Success(val loginResponseModel: LoginResponseModel) : LoginUiState
+ data class Error(val exception: Throwable) : LoginUiState
+}
\ No newline at end of file
diff --git a/feature/login/src/test/java/com/stackknowledge/login/ExampleUnitTest.kt b/feature/login/src/test/java/com/stackknowledge/login/ExampleUnitTest.kt
deleted file mode 100644
index 54deaa69..00000000
--- a/feature/login/src/test/java/com/stackknowledge/login/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.stackknowledge.login
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/feature/main/src/main/java/com/stackknowledge/main/MainPageScreen.kt b/feature/main/src/main/java/com/stackknowledge/main/MainPageScreen.kt
index e713bb2e..19b0b9eb 100644
--- a/feature/main/src/main/java/com/stackknowledge/main/MainPageScreen.kt
+++ b/feature/main/src/main/java/com/stackknowledge/main/MainPageScreen.kt
@@ -34,7 +34,7 @@ import com.stackknowledge.main.component.StackKnowledgePager
import com.stackknowledge.main.viewModel.MainViewModel
import com.stackknowledge.main.viewModel.uistate.GetMissionUiState
import com.stackknowledge.main.viewModel.uistate.GetRankingUiState
-import enumdatatype.Authority
+import enumdata.Authority
@Composable
internal fun MainPageRoute(
diff --git a/feature/main/src/main/java/com/stackknowledge/main/component/RankingList.kt b/feature/main/src/main/java/com/stackknowledge/main/component/RankingList.kt
index 80dfee42..50e75f6e 100644
--- a/feature/main/src/main/java/com/stackknowledge/main/component/RankingList.kt
+++ b/feature/main/src/main/java/com/stackknowledge/main/component/RankingList.kt
@@ -15,12 +15,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
-import com.stackknowledge.main.viewModel.uistate.GetMissionUiState
import com.stackknowledge.main.viewModel.uistate.GetRankingUiState
-import enumdatatype.Authority
@Composable
fun RankingList(
diff --git a/feature/main/src/main/java/com/stackknowledge/main/navigation/MainNavigation.kt b/feature/main/src/main/java/com/stackknowledge/main/navigation/MainNavigation.kt
index 7f71a6ff..53326e28 100644
--- a/feature/main/src/main/java/com/stackknowledge/main/navigation/MainNavigation.kt
+++ b/feature/main/src/main/java/com/stackknowledge/main/navigation/MainNavigation.kt
@@ -5,7 +5,7 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.stackknowledge.main.MainPageRoute
-import enumdatatype.Authority
+import enumdata.Authority
const val mainPageRoute = "main_page_route"
diff --git a/feature/mission/src/main/java/com/stackkowledge/mission/CreateMissionScreen.kt b/feature/mission/src/main/java/com/stackkowledge/mission/CreateMissionScreen.kt
index 7d100c54..f19c5e5b 100644
--- a/feature/mission/src/main/java/com/stackkowledge/mission/CreateMissionScreen.kt
+++ b/feature/mission/src/main/java/com/stackkowledge/mission/CreateMissionScreen.kt
@@ -1,7 +1,5 @@
package com.stackkowledge.mission
-import android.util.Log
-import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -32,10 +30,9 @@ import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackkowledge.mission.component.CreateMissionTimer
import com.stackkowledge.mission.component.InputMission
import com.stackkowledge.mission.component.InputTitle
-import com.stackkowledge.mission.viewmodel.uistate.CreateMissionUiState
import com.stackkowledge.mission.util.isValidNumber
import com.stackkowledge.mission.viewmodel.MissionViewModel
-import enumdatatype.Authority
+import enumdata.Authority
import remote.request.mission.CreateMissionRequestModel
@Composable
diff --git a/feature/mission/src/main/java/com/stackkowledge/mission/EntireMissionScreen.kt b/feature/mission/src/main/java/com/stackkowledge/mission/EntireMissionScreen.kt
index e8c5f583..63608ec4 100644
--- a/feature/mission/src/main/java/com/stackkowledge/mission/EntireMissionScreen.kt
+++ b/feature/mission/src/main/java/com/stackkowledge/mission/EntireMissionScreen.kt
@@ -21,8 +21,8 @@ import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackkowledge.mission.component.EntireMissionList
import com.stackkowledge.mission.viewmodel.MissionViewModel
+import enumdata.Authority
import com.stackkowledge.mission.viewmodel.uistate.GetMissionUiState
-import enumdatatype.Authority
@Composable
internal fun EntireMissionRoute(
diff --git a/feature/mission/src/main/java/com/stackkowledge/mission/ResolveMissionScreen.kt b/feature/mission/src/main/java/com/stackkowledge/mission/ResolveMissionScreen.kt
index a4b50da5..18e64128 100644
--- a/feature/mission/src/main/java/com/stackkowledge/mission/ResolveMissionScreen.kt
+++ b/feature/mission/src/main/java/com/stackkowledge/mission/ResolveMissionScreen.kt
@@ -30,7 +30,6 @@ import com.minstone.ui.navigation.StackKnowledgeBottomNavigation
import com.stackknowledge.design_system.component.dialog.StackKnowledgeDialog
import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
-import enumdatatype.Authority
import com.stackknowledge.design_system.R
import com.stackknowledge.design_system.component.dialog.EmptyButtonDialog
import com.stackknowledge.design_system.component.toast.SuccessToastMessage
@@ -40,9 +39,8 @@ import com.stackkowledge.mission.component.MissionTimer
import com.stackkowledge.mission.viewmodel.MissionViewModel
import com.stackkowledge.mission.viewmodel.SolveMissionViewModel
import com.stackkowledge.mission.viewmodel.uistate.DetailMissionUiState
-import com.stackkowledge.mission.viewmodel.uistate.SolveMissionUiState
+import enumdata.Authority
import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
import remote.request.solve.SolveRequestModel
@Composable
diff --git a/feature/mission/src/main/java/com/stackkowledge/mission/navigation/MissionNavigation.kt b/feature/mission/src/main/java/com/stackkowledge/mission/navigation/MissionNavigation.kt
index 7ed7681f..39bcd841 100644
--- a/feature/mission/src/main/java/com/stackkowledge/mission/navigation/MissionNavigation.kt
+++ b/feature/mission/src/main/java/com/stackkowledge/mission/navigation/MissionNavigation.kt
@@ -7,7 +7,7 @@ import androidx.navigation.compose.composable
import com.stackkowledge.mission.ResolveMissionRoute
import com.stackkowledge.mission.CreateMissionRoute
import com.stackkowledge.mission.EntireMissionRoute
-import enumdatatype.Authority
+import enumdata.Authority
const val createMissionRoute = "create_mission_route"
const val entireMissionRoute = "entire_mission_route"
diff --git a/feature/ranking/src/main/java/com/stackknowledge/ranking/RankingScreen.kt b/feature/ranking/src/main/java/com/stackknowledge/ranking/RankingScreen.kt
index b897c806..fc88c306 100644
--- a/feature/ranking/src/main/java/com/stackknowledge/ranking/RankingScreen.kt
+++ b/feature/ranking/src/main/java/com/stackknowledge/ranking/RankingScreen.kt
@@ -22,10 +22,10 @@ import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.ranking.component.RankingList
import com.stackknowledge.ranking.component.RankingProfile
+import enumdata.Authority
import com.stackknowledge.ranking.viewmodel.RankingViewModel
import com.stackknowledge.ranking.viewmodel.uistate.GetMyInformationUiState
import com.stackknowledge.ranking.viewmodel.uistate.GetRankingUiState
-import enumdatatype.Authority
@Composable
internal fun RankingRoute(
diff --git a/feature/ranking/src/main/java/com/stackknowledge/ranking/TeacherRankingScreen.kt b/feature/ranking/src/main/java/com/stackknowledge/ranking/TeacherRankingScreen.kt
index 0ac86808..4f4a723e 100644
--- a/feature/ranking/src/main/java/com/stackknowledge/ranking/TeacherRankingScreen.kt
+++ b/feature/ranking/src/main/java/com/stackknowledge/ranking/TeacherRankingScreen.kt
@@ -21,9 +21,9 @@ import com.minstone.ui.navigation.StackKnowledgeBottomNavigation
import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.ranking.component.RankingList
+import enumdata.Authority
import com.stackknowledge.ranking.viewmodel.RankingViewModel
import com.stackknowledge.ranking.viewmodel.uistate.GetRankingUiState
-import enumdatatype.Authority
@Composable
internal fun TeacherRankingRoute(
diff --git a/feature/ranking/src/main/java/com/stackknowledge/ranking/navigation/RankingNavigation.kt b/feature/ranking/src/main/java/com/stackknowledge/ranking/navigation/RankingNavigation.kt
index bd91d7c0..b1e662cb 100644
--- a/feature/ranking/src/main/java/com/stackknowledge/ranking/navigation/RankingNavigation.kt
+++ b/feature/ranking/src/main/java/com/stackknowledge/ranking/navigation/RankingNavigation.kt
@@ -6,7 +6,7 @@ import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.stackknowledge.ranking.RankingRoute
import com.stackknowledge.ranking.TeacherRankingRoute
-import enumdatatype.Authority
+import enumdata.Authority
const val rankingRoute = "ranking_route"
const val teacherRankingRoute = "teacher_ranking_route"
diff --git a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/GradingAnswerScreen.kt b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/GradingAnswerScreen.kt
index 97bdddde..daa39e8f 100644
--- a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/GradingAnswerScreen.kt
+++ b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/GradingAnswerScreen.kt
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -19,7 +18,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -35,8 +33,7 @@ import com.stackknowledge.score_mission.component.SolvedMissionAnswer
import com.stackknowledge.score_mission.component.SolvedMissionTitle
import com.stackknowledge.score_mission.viewmodel.ScoreMissionViewModel
import com.stackknowledge.score_mission.viewmodel.uistate.DetailScoreMissionUiState
-import com.stackknowledge.score_mission.viewmodel.uistate.ScoreMissionUiState
-import enumdatatype.Authority
+import enumdata.Authority
import remote.request.user.ScoreRequestModel
@Composable
diff --git a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/SolvedMissionScreen.kt b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/SolvedMissionScreen.kt
index 17f18ee6..577736fd 100644
--- a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/SolvedMissionScreen.kt
+++ b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/SolvedMissionScreen.kt
@@ -22,7 +22,7 @@ import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.score_mission.component.SolvedMissionList
import com.stackknowledge.score_mission.viewmodel.ScoreMissionViewModel
import com.stackknowledge.score_mission.viewmodel.uistate.GetScoreMissionListUiState
-import enumdatatype.Authority
+import enumdata.Authority
@Composable
internal fun SolvedMissionRoute(
diff --git a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/navigation/ScoreMissionNavigation.kt b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/navigation/ScoreMissionNavigation.kt
index c77dfb66..7da44b41 100644
--- a/feature/score-mission/src/main/java/com/stackknowledge/score_mission/navigation/ScoreMissionNavigation.kt
+++ b/feature/score-mission/src/main/java/com/stackknowledge/score_mission/navigation/ScoreMissionNavigation.kt
@@ -6,7 +6,7 @@ import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.stackknowledge.score_mission.GradingAnswerRoute
import com.stackknowledge.score_mission.SolvedMissionRoute
-import enumdatatype.Authority
+import enumdata.Authority
const val gradingAnswerRoute = "grading_answer_route"
const val solvedMissionRoute = "solved_mission_route"
diff --git a/feature/shop/src/main/java/com/stackknowledge/shop/ShopScreen.kt b/feature/shop/src/main/java/com/stackknowledge/shop/ShopScreen.kt
index da5c822e..e148d8b8 100644
--- a/feature/shop/src/main/java/com/stackknowledge/shop/ShopScreen.kt
+++ b/feature/shop/src/main/java/com/stackknowledge/shop/ShopScreen.kt
@@ -27,12 +27,12 @@ import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.shop.component.CurrentMileage
import com.stackknowledge.shop.component.GoodsList
+import enumdata.Authority
import com.stackknowledge.shop.data.SelectedItemData
import com.stackknowledge.shop.viewmodel.ItemViewModel
import com.stackknowledge.shop.viewmodel.OrderViewModel
import com.stackknowledge.shop.viewmodel.uistate.GetItemUiState
import com.stackknowledge.shop.viewmodel.uistate.GetMyInformationUiState
-import enumdatatype.Authority
import remote.request.order.OrderRequestModel
import remote.response.item.GetItemResponseModel
diff --git a/feature/shop/src/main/java/com/stackknowledge/shop/TeacherShopScreen.kt b/feature/shop/src/main/java/com/stackknowledge/shop/TeacherShopScreen.kt
index fef25e80..703b8987 100644
--- a/feature/shop/src/main/java/com/stackknowledge/shop/TeacherShopScreen.kt
+++ b/feature/shop/src/main/java/com/stackknowledge/shop/TeacherShopScreen.kt
@@ -23,9 +23,9 @@ import com.stackknowledge.design_system.component.dialog.StackKnowledgeDialog
import com.stackknowledge.design_system.component.topbar.StackKnowledgeTopBar
import com.stackknowledge.design_system.theme.StackKnowledgeAndroidTheme
import com.stackknowledge.shop.component.OrderedGoodsList
+import enumdata.Authority
import com.stackknowledge.shop.viewmodel.OrderViewModel
import com.stackknowledge.shop.viewmodel.uistate.GetOrderListUiState
-import enumdatatype.Authority
import remote.request.order.ChangeOrderStatusRequestModel
@Composable
diff --git a/feature/shop/src/main/java/com/stackknowledge/shop/navigation/ShopNavigation.kt b/feature/shop/src/main/java/com/stackknowledge/shop/navigation/ShopNavigation.kt
index 49b811d8..158a12db 100644
--- a/feature/shop/src/main/java/com/stackknowledge/shop/navigation/ShopNavigation.kt
+++ b/feature/shop/src/main/java/com/stackknowledge/shop/navigation/ShopNavigation.kt
@@ -6,7 +6,7 @@ import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.stackknowledge.shop.ShopRoute
import com.stackknowledge.shop.TeacherShopRoute
-import enumdatatype.Authority
+import enumdata.Authority
const val shopRoute = "shop_route"
const val teacherShopRoute = "teacher_shop_route"
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d98f8063..55ebeea2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -23,6 +23,7 @@ androidxTestRunner = "1.5.2"
androidx-test-ext-junit = "1.1.5"
androidxWindowManager = "1.2.0"
coil = "2.4.0"
+firebase-auth = "22.0.0"
converter-moshi = "2.9.0"
firebaseBom = "31.2.0"
firebaseCrashlyticsPlugin = "2.9.2"
@@ -39,7 +40,6 @@ kotlinxSerializationJson = "1.5.1"
ksp = "1.8.10-1.0.9"
lint = "31.2.1"
material = "1.2.0-alpha02"
-moshi = "1.15.0"
okhttp = "4.11.0"
protobuf = "3.24.0"
protobufPlugin = "0.9.4"
@@ -48,6 +48,9 @@ retrofitKotlinxSerializableJson = "1.0.0"
room = "2.6.1"
org-jetbrains-kotlin-android = "1.8.10"
lifecycle-runtime-ktx = "2.6.2"
+google-services = "4.4.2"
+play-services-auth = "20.7.0"
+moshi = "1.15.0"
[libraries]
#Define Library
@@ -69,6 +72,7 @@ androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", ve
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" }
androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" }
+androidx-dataStore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "androidxDataStore" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" }
androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" }
@@ -96,6 +100,7 @@ kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime",
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" }
moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "moshi" }
+moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshi" }
okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp"}
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" }
@@ -107,6 +112,10 @@ retrofit-kotlin-serialization = { group = "com.jakewharton.retrofit", name = "re
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
+google-services = { group = "com.google.gms", name = "google-services", version.ref = "google-services" }
+firebase-auth = { group = "com.google", name = "firebase-auth", version.ref = "firebase-auth" }
+firebase-bom = { group = "com.google", name = "firebase-bom", version.ref = "firebaseBom" }
+play-services-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "play-services-auth" }
#Define Dependeicies used in build-logic
android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }