diff --git a/.gitignore b/.gitignore index 4960cd83..2aaba2fb 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ keystore.properties # Keystore Files *.jks -*.keystore \ No newline at end of file +*.keystore + +*.json \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index de1dda7b..92d6ee8d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ android { applicationId = "com.moneymong.moneymong" minSdk = 24 targetSdk = 34 - versionCode = 13 - versionName = "1.1.0" + versionCode = 15 + versionName = "1.2.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/moneymong/moneymong/MainActivity.kt b/app/src/main/java/com/moneymong/moneymong/MainActivity.kt index 51b7d080..c9a08ade 100644 --- a/app/src/main/java/com/moneymong/moneymong/MainActivity.kt +++ b/app/src/main/java/com/moneymong/moneymong/MainActivity.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier +import com.moneymong.moneymong.common.event.EventTracker import dagger.hilt.android.AndroidEntryPoint import com.moneymong.moneymong.design_system.theme.MMTheme import com.moneymong.moneymong.domain.repository.TokenRepository @@ -32,8 +33,13 @@ class MainActivity : ComponentActivity() { lateinit var tokenRepository: TokenRepository private val expired = mutableStateOf(false) + @Inject + lateinit var eventTracker: EventTracker + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + eventTracker.initialize() + setContent { MMTheme { MoneyMongApp(expired.value) { diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 8d7caeb2..fba5fbee 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.moneymong.android.library) alias(libs.plugins.moneymong.android.library.compose) + alias(libs.plugins.moneymong.android.hilt) } android { @@ -20,6 +21,10 @@ dependencies { implementation(libs.orbit.viewModel) implementation(libs.okhttp) + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) + testImplementation(libs.junit4) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.test.espresso.core) diff --git a/core/common/src/main/java/com/moneymong/moneymong/common/di/EventModule.kt b/core/common/src/main/java/com/moneymong/moneymong/common/di/EventModule.kt new file mode 100644 index 00000000..d058d534 --- /dev/null +++ b/core/common/src/main/java/com/moneymong/moneymong/common/di/EventModule.kt @@ -0,0 +1,18 @@ +package com.moneymong.moneymong.common.di + +import com.moneymong.moneymong.common.event.EventTracker +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object EventModule { + + @Provides + @Singleton + fun provideEventTracker(): EventTracker = + EventTracker() +} \ No newline at end of file diff --git a/core/common/src/main/java/com/moneymong/moneymong/common/event/Event.kt b/core/common/src/main/java/com/moneymong/moneymong/common/event/Event.kt new file mode 100644 index 00000000..6d0fe619 --- /dev/null +++ b/core/common/src/main/java/com/moneymong/moneymong/common/event/Event.kt @@ -0,0 +1,38 @@ +package com.moneymong.moneymong.common.event + +enum class Event(val eventName: String, val desc: String) { + OPERATION_COST_CLICK( + eventName = "operation_cost_click", + desc = "운영비 등록하기 버튼 클릭" + ), + LEDGER_CLICK( + eventName = "ledger_click", + desc = "소속 장부 확인 버튼 클릭" + ), + OCR_REGISTER_CLICK( + eventName = "ocr_register_click", + desc = "등록하기 버튼 클릭" + ), + OCR_MODIFY_CLICK( + eventName = "ocr_modify_click", + desc = "수정하기 버튼 클릭" + ), + OCR_MODIFY_TO_REGISTER_CLICK( + eventName = "ocr_modify_to_register_click", + desc = "등록하기 버튼 클릭" + ), + PLUS_CLICK( + eventName = "plus_click", + desc = "+ 플로팅 버튼 클릭" + ), + HAND_CLICK( + eventName = "hand_click", + desc = "수동 등록 플로팅 버튼 클릭" + ), + OCR_CLICK( + eventName = "ocr_click", + desc = "OCR 등록 플로팅 버튼 클릭" + ) + ; + +} \ No newline at end of file diff --git a/core/common/src/main/java/com/moneymong/moneymong/common/event/EventTracker.kt b/core/common/src/main/java/com/moneymong/moneymong/common/event/EventTracker.kt new file mode 100644 index 00000000..c6288943 --- /dev/null +++ b/core/common/src/main/java/com/moneymong/moneymong/common/event/EventTracker.kt @@ -0,0 +1,24 @@ +package com.moneymong.moneymong.common.event + +import android.os.Bundle +import com.google.firebase.Firebase +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.analytics +import javax.inject.Inject + +class EventTracker @Inject constructor() { + + private lateinit var firebaseAnalytics: FirebaseAnalytics + private var initialized: Boolean = false + + fun initialize() { + firebaseAnalytics = Firebase.analytics + initialized = true + } + + fun logEvent(event: Event, param: Bundle? = null) { + check(initialized) { "logEvent를 호출하기 전 초기화가 필요합니다." } + + firebaseAnalytics.logEvent(event.eventName, param) + } +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/Selection.kt b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/Selection.kt index 782fbab9..60cae240 100644 --- a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/Selection.kt +++ b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/Selection.kt @@ -6,40 +6,43 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.moneymong.moneymong.design_system.theme.Body3 -import com.moneymong.moneymong.design_system.theme.Gray03 @Composable fun MDSSelection( modifier: Modifier = Modifier, text: String, - isSelected: Boolean, + enabled: Boolean = true, + isSelected: Boolean = false, type: MDSSelectionType = MDSSelectionType.PRIMARY, - onClick: () -> Unit + onClick: () -> Unit = {} ) { - val backgroundColor = - if (isSelected) type.backgroundColor else unSelectedBackgroundColor - val contentColor = - if (isSelected) type.contentColor else unSelectedContentColor - val borderColor = - if (isSelected) type.backgroundColor else Gray03 + val backgroundColor = selectionBackgroundColor(enabled, isSelected, type) + val contentColor = selectionContentColor(enabled, isSelected, type) + val borderColor = selectionBorderColor(enabled, isSelected, type) Box( modifier = modifier .clip(RoundedCornerShape(8.dp)) .background(color = backgroundColor) - .clickable { onClick() } + .clickable(enabled = enabled) { onClick() } .border( width = 1.dp, color = borderColor, @@ -59,28 +62,35 @@ fun MDSSelection( @Preview(showBackground = true) @Composable private fun MDSSelectionPreview() { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(10.dp) + var selectedType by remember { mutableIntStateOf(1) } + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.White) ) { - MDSSelection( - modifier = Modifier.weight(1f), - text = "동아리", - isSelected = true, - onClick = {} - ) - MDSSelection( - modifier = Modifier.weight(1f), - text = "나는 Secondary", - isSelected = true, - type = MDSSelectionType.SECONDARY, - onClick = {} - ) - MDSSelection( - modifier = Modifier.weight(1f), - text = "학생회", - isSelected = false, - onClick = {} - ) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + MDSSelection( + modifier = Modifier.weight(1f), + text = "동아리", + isSelected = selectedType == 1, + onClick = { selectedType = 1 } + ) + MDSSelection( + modifier = Modifier.weight(1f), + text = "나는 Secondary", + isSelected = selectedType == 2, + type = MDSSelectionType.SECONDARY, + onClick = { selectedType = 2 } + ) + MDSSelection( + modifier = Modifier.weight(1f), + text = "나는 disabled", + enabled = false, + ) + } } } \ No newline at end of file diff --git a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionDefaults.kt b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionDefaults.kt new file mode 100644 index 00000000..c310238b --- /dev/null +++ b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionDefaults.kt @@ -0,0 +1,66 @@ +package com.moneymong.moneymong.design_system.component.selection + +import androidx.compose.ui.graphics.Color +import com.moneymong.moneymong.design_system.theme.Blue01 +import com.moneymong.moneymong.design_system.theme.Blue04 +import com.moneymong.moneymong.design_system.theme.Gray03 +import com.moneymong.moneymong.design_system.theme.Gray04 +import com.moneymong.moneymong.design_system.theme.Gray05 +import com.moneymong.moneymong.design_system.theme.White + + +enum class MDSSelectionType( + val backgroundColor: Color, + val contentColor: Color +) { + PRIMARY( + backgroundColor = Blue04, + contentColor = White + ), + SECONDARY( + backgroundColor = Blue01, + contentColor = Blue04 + ) +} + +internal val unSelectedBackgroundColor = White +internal val unSelectedContentColor = Gray05 + +internal val disabledBackgroundColor = Gray03 +internal val disabledContentColor = Gray04 + +internal val selectionBackgroundColor: ( + enabled: Boolean, + isSelected: Boolean, + type: MDSSelectionType +) -> Color = { enabled, isSelected, type -> + when { + enabled.not() -> disabledBackgroundColor + isSelected.not() -> unSelectedBackgroundColor + else -> type.backgroundColor + } +} + +internal val selectionContentColor: ( + enabled: Boolean, + isSelected: Boolean, + type: MDSSelectionType +) -> Color = { enabled, isSelected, type -> + when { + enabled.not() -> disabledContentColor + isSelected.not() -> unSelectedContentColor + else -> type.contentColor + } +} + +internal val selectionBorderColor: ( + enabled: Boolean, + isSelected: Boolean, + type: MDSSelectionType +) -> Color = { enabled, isSelected, type -> + when { + enabled.not() -> disabledBackgroundColor + isSelected.not() -> Gray03 + else -> type.backgroundColor + } +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionType.kt b/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionType.kt deleted file mode 100644 index 6b308d7f..00000000 --- a/core/design-system/src/main/java/com/moneymong/moneymong/design_system/component/selection/SelectionType.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.moneymong.moneymong.design_system.component.selection - -import androidx.compose.ui.graphics.Color -import com.moneymong.moneymong.design_system.theme.Blue01 -import com.moneymong.moneymong.design_system.theme.Blue04 -import com.moneymong.moneymong.design_system.theme.Gray05 -import com.moneymong.moneymong.design_system.theme.White - - -enum class MDSSelectionType( - val backgroundColor: Color, - val contentColor: Color -) { - PRIMARY( - backgroundColor = Blue04, - contentColor = White - ), - SECONDARY( - backgroundColor = Blue01, - contentColor = Blue04 - ), -} - -internal val unSelectedBackgroundColor = White -internal val unSelectedContentColor = Gray05 \ No newline at end of file diff --git a/core/design-system/src/main/res/drawable/ic_agency_delete.png b/core/design-system/src/main/res/drawable/ic_agency_delete.png new file mode 100644 index 00000000..7a3b6b7f Binary files /dev/null and b/core/design-system/src/main/res/drawable/ic_agency_delete.png differ diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/LoginType.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/LoginType.kt new file mode 100644 index 00000000..4d046902 --- /dev/null +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/LoginType.kt @@ -0,0 +1,5 @@ +package com.moneymong.moneymong.model.sign + +enum class LoginType { + KAKAO +} \ No newline at end of file diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenRequest.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenRequest.kt index 298de768..8b48f005 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenRequest.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenRequest.kt @@ -2,5 +2,7 @@ package com.moneymong.moneymong.model.sign data class TokenRequest ( val provider : String, - val accessToken : String + val accessToken : String, + val name: String, + val code: String ) \ No newline at end of file diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenResponse.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenResponse.kt index 470bac3c..a52d62a0 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenResponse.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/TokenResponse.kt @@ -4,5 +4,6 @@ data class TokenResponse( val accessToken : String, val refreshToken : String, val loginSuccess : Boolean, - val schoolInfoExist : Boolean + val schoolInfoExist : Boolean, + val schoolInfoProvided: Boolean ) diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivRequest.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivRequest.kt index 19fb0f42..c11c53de 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivRequest.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivRequest.kt @@ -1,6 +1,6 @@ package com.moneymong.moneymong.model.sign data class UnivRequest( - val universityName: String, - val grade: Int + val universityName: String?, + val grade: Int? ) \ No newline at end of file diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivResponse.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivResponse.kt index 46dbf79f..6a098bd1 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivResponse.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UnivResponse.kt @@ -1,6 +1,6 @@ package com.moneymong.moneymong.model.sign data class UnivResponse( - val universityName: String, + val universityName: String?, val grade: Int ) \ No newline at end of file diff --git a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UserDataStoreInfoResponse.kt b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UserDataStoreInfoResponse.kt index 75a46603..560cdcc8 100644 --- a/core/model/src/main/java/com/moneymong/moneymong/model/sign/UserDataStoreInfoResponse.kt +++ b/core/model/src/main/java/com/moneymong/moneymong/model/sign/UserDataStoreInfoResponse.kt @@ -2,5 +2,5 @@ package com.moneymong.moneymong.model.sign data class UserDataStoreInfoResponse( val accessToken : String, - val schoolInfoExist : Boolean, + val schoolInfoProvided : Boolean, ) diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/api/AccessTokenApi.kt b/core/network/src/main/java/com/moneymong/moneymong/network/api/AccessTokenApi.kt index a625211b..845a39ae 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/api/AccessTokenApi.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/api/AccessTokenApi.kt @@ -1,8 +1,8 @@ package com.moneymong.moneymong.network.api import com.moneymong.moneymong.model.sign.RefreshTokenRequest -import com.moneymong.moneymong.model.sign.TokenRequest import com.moneymong.moneymong.model.sign.RefreshTokenResponse +import com.moneymong.moneymong.model.sign.TokenRequest import com.moneymong.moneymong.model.sign.TokenResponse import retrofit2.http.Body import retrofit2.http.DELETE @@ -10,14 +10,14 @@ import retrofit2.http.POST interface AccessTokenApi { @POST("api/v1/users") - suspend fun accessTokenApi( - @Body body : TokenRequest + suspend fun postAccessToken( + @Body body: TokenRequest ): Result @POST("api/v1/tokens") suspend fun refreshTokenApi( - @Body body : RefreshTokenRequest - ) : Result + @Body body: RefreshTokenRequest + ): Result @DELETE("api/v1/tokens") suspend fun deleteRefreshToken( diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt b/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt index 7407a87e..c03d0c1f 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/api/AgencyApi.kt @@ -8,6 +8,7 @@ import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.agency.RegisterAgencyResponse import com.moneymong.moneymong.model.member.InvitationCodeResponse import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET import retrofit2.http.Header import retrofit2.http.PATCH @@ -49,4 +50,10 @@ interface AgencyApi { suspend fun reInvitationCode( @Path("agencyId") agencyId: Long ): Result + + //DELETE + @DELETE("api/v1/agencies/{agencyId}") + suspend fun deleteAgency( + @Path("agencyId") agencyId: Int + ) : Result } \ No newline at end of file diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/api/UniversityApi.kt b/core/network/src/main/java/com/moneymong/moneymong/network/api/UniversityApi.kt index bc68e2ce..9dcb60b7 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/api/UniversityApi.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/api/UniversityApi.kt @@ -1,6 +1,7 @@ package com.moneymong.moneymong.network.api import com.moneymong.moneymong.model.sign.UnivRequest +import com.moneymong.moneymong.model.sign.UnivResponse import com.moneymong.moneymong.model.sign.UniversitiesResponse import retrofit2.http.Body import retrofit2.http.GET @@ -18,4 +19,7 @@ interface UniversityApi { suspend fun searchUniv( @Query("keyword") searchQuery: String ): Result + + @GET("api/v1/user-university") + suspend fun getMyUniv(): Result } \ No newline at end of file diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/di/NetworkModule.kt b/core/network/src/main/java/com/moneymong/moneymong/network/di/NetworkModule.kt index c56ba0f8..683c179a 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/di/NetworkModule.kt @@ -9,15 +9,15 @@ import com.google.gson.GsonBuilder import com.moneymong.moneymong.network.BuildConfig import com.moneymong.moneymong.network.adapter.ResultCallAdapterFactory import com.moneymong.moneymong.network.api.AccessTokenApi -import com.moneymong.moneymong.network.api.UniversityApi -import com.moneymong.moneymong.network.util.AuthInterceptor -import com.moneymong.moneymong.network.api.MoneyMongApi import com.moneymong.moneymong.network.api.AgencyApi import com.moneymong.moneymong.network.api.ClovaApi import com.moneymong.moneymong.network.api.LedgerApi import com.moneymong.moneymong.network.api.LedgerDetailApi import com.moneymong.moneymong.network.api.MemberApi +import com.moneymong.moneymong.network.api.MoneyMongApi +import com.moneymong.moneymong.network.api.UniversityApi import com.moneymong.moneymong.network.api.UserApi +import com.moneymong.moneymong.network.util.AuthInterceptor import com.moneymong.moneymong.network.util.MoneyMongTokenAuthenticator import dagger.Module import dagger.Provides @@ -97,7 +97,7 @@ object NetworkModule { addConverterFactory(GsonConverterFactory.create(gson)) addCallAdapterFactory(ResultCallAdapterFactory.create()) }.build() - + @Provides @Singleton @ClovaRetrofit @@ -112,7 +112,7 @@ object NetworkModule { @Provides fun provideMoneyMongApi(@MoneyMongRetrofit retrofit: Retrofit): MoneyMongApi = retrofit.create(MoneyMongApi::class.java) - + @Provides fun provideUnivApi(@MoneyMongRetrofit retrofit: Retrofit): UniversityApi = retrofit.create(UniversityApi::class.java) diff --git a/core/network/src/main/java/com/moneymong/moneymong/network/util/AuthInterceptor.kt b/core/network/src/main/java/com/moneymong/moneymong/network/util/AuthInterceptor.kt index 16ceb716..924232e0 100644 --- a/core/network/src/main/java/com/moneymong/moneymong/network/util/AuthInterceptor.kt +++ b/core/network/src/main/java/com/moneymong/moneymong/network/util/AuthInterceptor.kt @@ -1,6 +1,5 @@ package com.moneymong.moneymong.network.util -import com.moneymong.moneymong.domain.repository.LoginRepository import com.moneymong.moneymong.domain.repository.TokenRepository import com.moneymong.moneymong.network.BuildConfig import kotlinx.coroutines.runBlocking diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSource.kt index 70399855..33410314 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSource.kt @@ -5,10 +5,11 @@ import com.moneymong.moneymong.model.sign.UserDataStoreInfoResponse interface LoginLocalDataSource { suspend fun getRefreshToken(): Result suspend fun getAccessToken(): Result - suspend fun getDataStoreInfo() : Result + suspend fun getDataStoreInfo(): Result suspend fun deleteToken() + suspend fun setDataStore(aToken: String, rToken: String, success: Boolean, infoExist: Boolean) suspend fun updateTokens(aToken: String, rToken: String) suspend fun updateAccessToken(aToken: String) suspend fun getSchoolInfo(): Result - suspend fun setSchoolInfoExist(infoExist: Boolean) + suspend fun setSchoolInfoProvided(infoExist: Boolean) } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSourceImpl.kt index 4bcf1195..7cf33267 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginLocalDataSourceImpl.kt @@ -19,7 +19,7 @@ class LoginLocalDataSourceImpl @Inject constructor( private val accessToken = stringPreferencesKey("ACCESS_TOKEN") private val refreshToken = stringPreferencesKey("REFRESH_TOKEN") private val loginSuccess = booleanPreferencesKey("LOGIN_SUCCESS") - private val schoolInfoExist = booleanPreferencesKey("SCHOOL_INFO_EXIST") + private val schoolInfoProvided = booleanPreferencesKey("SCHOOL_INFO_PROVIDED") override suspend fun getRefreshToken(): Result { val preferences = context.dataStore.data.first() @@ -35,7 +35,7 @@ class LoginLocalDataSourceImpl @Inject constructor( override suspend fun getDataStoreInfo(): Result { val preferences = context.dataStore.data.first() - return Result.success(UserDataStoreInfoResponse(preferences[accessToken] ?: "",preferences[schoolInfoExist] ?: false)) + return Result.success(UserDataStoreInfoResponse(preferences[accessToken] ?: "",preferences[schoolInfoProvided] ?: false)) } override suspend fun updateTokens(aToken: String, rToken: String) { @@ -53,8 +53,8 @@ class LoginLocalDataSourceImpl @Inject constructor( override suspend fun getSchoolInfo(): Result { val preferences = context.dataStore.data.first() - return preferences[schoolInfoExist]?.let { Result.success(it) } - ?: Result.failure(Exception("schoolInfoExist is null")) + return preferences[schoolInfoProvided]?.let { Result.success(it) } + ?: Result.failure(Exception("schoolInfoProvided is null")) } override suspend fun deleteToken() { @@ -62,23 +62,27 @@ class LoginLocalDataSourceImpl @Inject constructor( preferences.remove(accessToken) preferences.remove(refreshToken) preferences.remove(loginSuccess) - preferences.remove(schoolInfoExist) + preferences.remove(schoolInfoProvided) } } - suspend fun setDataStore(aToken: String, rToken: String, success: Boolean, infoExist: Boolean) { + override suspend fun setDataStore( + aToken: String, + rToken: String, + success: Boolean, + infoExist: Boolean + ) { context.dataStore.edit { preferences -> preferences[accessToken] = aToken preferences[refreshToken] = rToken preferences[loginSuccess] = success - preferences[schoolInfoExist] = infoExist - Log.d("schoolInfoExist", preferences[schoolInfoExist]!!.toString()) + preferences[schoolInfoProvided] = infoExist } } - override suspend fun setSchoolInfoExist(infoExist: Boolean) { + override suspend fun setSchoolInfoProvided(infoExist: Boolean) { context.dataStore.edit { preferences -> - preferences[schoolInfoExist] = infoExist + preferences[schoolInfoProvided] = infoExist } } diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSource.kt deleted file mode 100644 index 592da4e6..00000000 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSource.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.moneymong.moneymong.data.datasource.login - -import com.moneymong.moneymong.domain.util.LoginCallback - - -interface LoginRemoteDataSource { - suspend fun loginWithKakaoTalk(callback: LoginCallback) - suspend fun loginWithKakaoAccount(callback: LoginCallback) -} \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSourceImpl.kt deleted file mode 100644 index 3ecd5b9a..00000000 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/LoginRemoteDataSourceImpl.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.moneymong.moneymong.data.datasource.login - -import android.content.Context -import android.util.Log -import com.kakao.sdk.common.model.ClientError -import com.kakao.sdk.common.model.ClientErrorCause -import com.kakao.sdk.user.UserApiClient -import com.moneymong.moneymong.data.datasource.user.UserLocalDataSource -import com.moneymong.moneymong.data.util.LoginType -import com.moneymong.moneymong.domain.util.LoginCallback -import com.moneymong.moneymong.network.api.AccessTokenApi -import com.moneymong.moneymong.network.api.UserApi -import com.moneymong.moneymong.model.sign.TokenRequest -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import javax.inject.Inject - - -class LoginRemoteDataSourceImpl @Inject constructor( - @ApplicationContext private val context: Context, - private val accessTokenApi: AccessTokenApi, - private val userApi: UserApi, - private val loginLocalDataSourceImpl: LoginLocalDataSourceImpl, - private val userLocalDataSource: UserLocalDataSource, -) : LoginRemoteDataSource { - - override suspend fun loginWithKakaoTalk(callback: LoginCallback) { - UserApiClient.instance.loginWithKakaoTalk(context) { token, error -> - if (error != null) { - Log.d("this", "카카오톡으로 로그인 실패 : $error") - CoroutineScope(Dispatchers.IO).launch { - callback.onLoginFailure(exception = Exception()) - } - - // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우, - // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기) - if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { - return@loginWithKakaoTalk - } - - // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인 시도 - CoroutineScope(Dispatchers.IO).launch { - loginWithKakaoAccount(callback) - } - } else if (token != null) { - CoroutineScope(Dispatchers.IO).launch { - sendToken(LoginType.KAKAO.type, token.accessToken) - callback.onLoginSuccess() - } - Log.d("this", "카카오톡으로 로그인 성공 ${token.accessToken}") - } - } - } - - - override suspend fun loginWithKakaoAccount(callback: LoginCallback) { - UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> - if (error != null) { - Log.d("this", "카카오계정으로 로그인 실패 : $error") - CoroutineScope(Dispatchers.IO).launch { - callback.onLoginFailure(Exception(error)) - } - } else if (token != null) { - Log.d("this", "카카오계정으로 로그인 성공, accessToken: ${token.accessToken}") - UserApiClient.instance.me { user, error -> - if (error == null) { - CoroutineScope(Dispatchers.IO).launch { - sendToken(LoginType.KAKAO.type, token.accessToken) - callback.onLoginSuccess() - } - } else { - CoroutineScope(Dispatchers.IO).launch { - callback.onLoginFailure(Exception(error)) - } - } - } - } - } - } - - - private suspend fun sendToken(type: String, accessToken: String) { - accessTokenApi.accessTokenApi(TokenRequest(type, accessToken)) - .onSuccess { - Log.d( - "success", "${it.accessToken}\n" + - "${it.refreshToken}\n" + - "${it.loginSuccess}\n" + - "${it.schoolInfoExist}" - ) - loginLocalDataSourceImpl.setDataStore( - it.accessToken, - it.refreshToken, - it.loginSuccess, - it.schoolInfoExist - ) - saveUserInfo() - } - .onFailure { - Log.d("failure", it.message.toString()) - //TODO - } - } - - private suspend fun saveUserInfo() { - userApi.getMyInfo() - .onSuccess { - userLocalDataSource.saveUserId(it.id.toInt()) - userLocalDataSource.saveUserNickName(it.name) - }.onFailure { - userLocalDataSource.saveUserId(0) - userLocalDataSource.saveUserNickName("에러가 발생했습니다.") - } - } -} \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSource.kt index 9d3318dd..43d9a833 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSource.kt @@ -1,9 +1,17 @@ package com.moneymong.moneymong.data.datasource.login +import com.moneymong.moneymong.model.sign.LoginType import com.moneymong.moneymong.model.sign.RefreshTokenRequest import com.moneymong.moneymong.model.sign.RefreshTokenResponse +import com.moneymong.moneymong.model.sign.TokenResponse interface TokenRemoteDataSource { + + suspend fun postAccessToken( + type: LoginType, + accessToken: String + ): Result + suspend fun getUpdateToken(refreshToken: String): Result suspend fun deleteRefreshToken(body: RefreshTokenRequest) } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSourceImpl.kt index 24d2a1f7..bbd096c3 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/login/TokenRemoteDataSourceImpl.kt @@ -1,8 +1,11 @@ package com.moneymong.moneymong.data.datasource.login -import com.moneymong.moneymong.network.api.AccessTokenApi +import com.moneymong.moneymong.model.sign.LoginType import com.moneymong.moneymong.model.sign.RefreshTokenRequest import com.moneymong.moneymong.model.sign.RefreshTokenResponse +import com.moneymong.moneymong.model.sign.TokenRequest +import com.moneymong.moneymong.model.sign.TokenResponse +import com.moneymong.moneymong.network.api.AccessTokenApi import javax.inject.Inject import javax.inject.Provider @@ -11,6 +14,13 @@ class TokenRemoteDataSourceImpl @Inject constructor( ) : TokenRemoteDataSource { private val accessTokenApi by lazy { accessTokenApiProvider.get() } + override suspend fun postAccessToken( + type: LoginType, + accessToken: String + ): Result { + return accessTokenApi.postAccessToken(TokenRequest(type.name, accessToken, "", "")) + } + override suspend fun getUpdateToken(refreshToken: String): Result { return accessTokenApi.refreshTokenApi(RefreshTokenRequest(refreshToken)) } diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt index 8b5713b9..da900da5 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSource.kt @@ -11,4 +11,6 @@ interface MemberRemoteDataSource { suspend fun getMemberLists(agencyId: Long): Result suspend fun updateMemberAuthor(agencyId : Long, data : UpdateAuthorRequest) : Result suspend fun blockMemberAuthor(agencyId: Long, data : MemberBlockRequest) : Result + suspend fun deleteAgency(agencyId: Int) : Result + } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt index 222d5038..0cdcc50c 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/member/MemberRemoteDataSourceImpl.kt @@ -31,4 +31,8 @@ class MemberRemoteDataSourceImpl @Inject constructor( override suspend fun blockMemberAuthor(agencyId: Long, data: MemberBlockRequest): Result { return memberApi.blockMemberAuthor(agencyId, data) } + + override suspend fun deleteAgency(agencyId: Int): Result { + return agencyApi.deleteAgency(agencyId) + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSource.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSource.kt index 3d59f394..915bce95 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSource.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSource.kt @@ -2,10 +2,13 @@ package com.moneymong.moneymong.data.datasource.signup import com.moneymong.moneymong.model.sign.SearchQueryRequest import com.moneymong.moneymong.model.sign.UnivRequest +import com.moneymong.moneymong.model.sign.UnivResponse import com.moneymong.moneymong.model.sign.UniversitiesResponse interface UnivRemoteDataSource { - suspend fun createUniv(body: UnivRequest) : Result + suspend fun createUniv(body: UnivRequest): Result - suspend fun searchUniv(searchQuery : SearchQueryRequest) : Result + suspend fun searchUniv(searchQuery: SearchQueryRequest): Result + + suspend fun getMyUniv(): Result } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSourceImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSourceImpl.kt index 534d35b7..fce6e293 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSourceImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/datasource/signup/UnivRemoteDataSourceImpl.kt @@ -1,12 +1,14 @@ package com.moneymong.moneymong.data.datasource.signup -import com.moneymong.moneymong.network.api.UniversityApi import com.moneymong.moneymong.model.sign.SearchQueryRequest import com.moneymong.moneymong.model.sign.UnivRequest +import com.moneymong.moneymong.model.sign.UnivResponse import com.moneymong.moneymong.model.sign.UniversitiesResponse +import com.moneymong.moneymong.network.api.UniversityApi import javax.inject.Inject -class UnivRemoteDataSourceImpl @Inject constructor(private val universityApi: UniversityApi) : UnivRemoteDataSource { +class UnivRemoteDataSourceImpl @Inject constructor(private val universityApi: UniversityApi) : + UnivRemoteDataSource { override suspend fun createUniv(body: UnivRequest): Result { return universityApi.createUniv(body = body) } @@ -14,4 +16,8 @@ class UnivRemoteDataSourceImpl @Inject constructor(private val universityApi: Un override suspend fun searchUniv(searchQuery: SearchQueryRequest): Result { return universityApi.searchUniv(searchQuery.searchQuery) } + + override suspend fun getMyUniv(): Result { + return universityApi.getMyUniv() + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/di/DataSourceModule.kt b/data/src/main/java/com/moneymong/moneymong/data/di/DataSourceModule.kt index 84502359..65ac8784 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/di/DataSourceModule.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/di/DataSourceModule.kt @@ -2,14 +2,6 @@ package com.moneymong.moneymong.data.di import com.moneymong.moneymong.data.datasource.agency.AgencyLocalDataSource import com.moneymong.moneymong.data.datasource.agency.AgencyLocalDataSourceImpl -import com.moneymong.moneymong.data.datasource.login.LoginLocalDataSource -import com.moneymong.moneymong.data.datasource.login.LoginLocalDataSourceImpl -import com.moneymong.moneymong.data.datasource.login.LoginRemoteDataSource -import com.moneymong.moneymong.data.datasource.login.LoginRemoteDataSourceImpl -import com.moneymong.moneymong.data.datasource.login.TokenRemoteDataSource -import com.moneymong.moneymong.data.datasource.login.TokenRemoteDataSourceImpl -import com.moneymong.moneymong.data.datasource.signup.UnivRemoteDataSource -import com.moneymong.moneymong.data.datasource.signup.UnivRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.agency.AgencyRemoteDataSource import com.moneymong.moneymong.data.datasource.agency.AgencyRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.ledger.LedgerLocalDataSource @@ -18,10 +10,16 @@ import com.moneymong.moneymong.data.datasource.ledger.LedgerRemoteDataSource import com.moneymong.moneymong.data.datasource.ledger.LedgerRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.ledgerdetail.LedgerDetailRemoteDataSource import com.moneymong.moneymong.data.datasource.ledgerdetail.LedgerDetailRemoteDataSourceImpl +import com.moneymong.moneymong.data.datasource.login.LoginLocalDataSource +import com.moneymong.moneymong.data.datasource.login.LoginLocalDataSourceImpl +import com.moneymong.moneymong.data.datasource.login.TokenRemoteDataSource +import com.moneymong.moneymong.data.datasource.login.TokenRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.member.MemberRemoteDataSource import com.moneymong.moneymong.data.datasource.member.MemberRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.ocr.OCRRemoteDataSource import com.moneymong.moneymong.data.datasource.ocr.OCRRemoteDataSourceImpl +import com.moneymong.moneymong.data.datasource.signup.UnivRemoteDataSource +import com.moneymong.moneymong.data.datasource.signup.UnivRemoteDataSourceImpl import com.moneymong.moneymong.data.datasource.user.UserLocalDataSource import com.moneymong.moneymong.data.datasource.user.UserLocalDataSourceImpl import com.moneymong.moneymong.data.datasource.user.UserRemoteDataSource @@ -35,11 +33,6 @@ import dagger.hilt.components.SingletonComponent @InstallIn(SingletonComponent::class) interface DataSourceModule { - @Binds - fun bindLoginDataSource( - loginRemoteDataSourceImpl: LoginRemoteDataSourceImpl - ): LoginRemoteDataSource - @Binds fun bindUnivDataSource( univRemoteDataSourceImpl: UnivRemoteDataSourceImpl diff --git a/data/src/main/java/com/moneymong/moneymong/data/di/RepositoryModule.kt b/data/src/main/java/com/moneymong/moneymong/data/di/RepositoryModule.kt index d231f318..911bffe9 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/di/RepositoryModule.kt @@ -1,18 +1,16 @@ package com.moneymong.moneymong.data.di -import com.moneymong.moneymong.data.repository.login.LoginRepositoryImpl -import com.moneymong.moneymong.data.repository.login.TokenRepositoryImpl -import com.moneymong.moneymong.data.repository.signup.UnivRepositoryImpl -import com.moneymong.moneymong.domain.repository.LoginRepository -import com.moneymong.moneymong.domain.repository.TokenRepository -import com.moneymong.moneymong.domain.repository.UnivRepository import com.moneymong.moneymong.data.repository.agency.AgencyRepositoryImpl -import com.moneymong.moneymong.domain.repository.AgencyRepository import com.moneymong.moneymong.data.repository.ledger.LedgerRepositoryImpl import com.moneymong.moneymong.data.repository.ledgerdetail.LedgerDetailRepositoryImpl import com.moneymong.moneymong.data.repository.member.MemberRepositoryImpl import com.moneymong.moneymong.data.repository.ocr.OCRRepositoryImpl +import com.moneymong.moneymong.data.repository.signup.UnivRepositoryImpl +import com.moneymong.moneymong.data.repository.token.TokenRepositoryImpl import com.moneymong.moneymong.data.repository.user.UserRepositoryImpl +import com.moneymong.moneymong.domain.repository.AgencyRepository +import com.moneymong.moneymong.domain.repository.TokenRepository +import com.moneymong.moneymong.domain.repository.UnivRepository import com.moneymong.moneymong.domain.repository.ledger.LedgerRepository import com.moneymong.moneymong.domain.repository.ledgerdetail.LedgerDetailRepository import com.moneymong.moneymong.domain.repository.member.MemberRepository @@ -28,11 +26,6 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) interface RepositoryModule { - @Binds - fun bindLoginRepository( - loginRepositoryImpl: LoginRepositoryImpl - ): LoginRepository - @Binds fun bindUnivRepository( univRepositoryImpl: UnivRepositoryImpl diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/login/LoginRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/login/LoginRepositoryImpl.kt deleted file mode 100644 index e2c33efe..00000000 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/login/LoginRepositoryImpl.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.moneymong.moneymong.data.repository.login - -import android.content.Context -import com.kakao.sdk.user.UserApiClient -import com.moneymong.moneymong.data.datasource.login.LoginRemoteDataSource -import com.moneymong.moneymong.domain.util.LoginCallback -import com.moneymong.moneymong.domain.repository.LoginRepository -import dagger.hilt.android.qualifiers.ApplicationContext -import javax.inject.Inject - -class LoginRepositoryImpl @Inject constructor( - private val remoteDataSource: LoginRemoteDataSource, - @ApplicationContext private val context: Context -) : LoginRepository { - override suspend fun kakaoLogin(callback: LoginCallback) { -// if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) { -// remoteDataSource.loginWithKakaoTalk(callback) -// } else { - remoteDataSource.loginWithKakaoAccount(callback) -// } - } -} \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt index 50ca2298..8bfc4841 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/member/MemberRepositoryImpl.kt @@ -31,4 +31,8 @@ class MemberRepositoryImpl @Inject constructor( return memberRemoteDataSource.blockMemberAuthor(agencyId, data) } + + override suspend fun deleteAgency(agencyId: Int): Result { + return memberRemoteDataSource.deleteAgency(agencyId) + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/signup/UnivRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/signup/UnivRepositoryImpl.kt index 202c42f8..7c5fb33d 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/signup/UnivRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/signup/UnivRepositoryImpl.kt @@ -4,6 +4,7 @@ import com.moneymong.moneymong.data.datasource.signup.UnivRemoteDataSource import com.moneymong.moneymong.domain.repository.UnivRepository import com.moneymong.moneymong.model.sign.SearchQueryRequest import com.moneymong.moneymong.model.sign.UnivRequest +import com.moneymong.moneymong.model.sign.UnivResponse import com.moneymong.moneymong.model.sign.UniversitiesResponse import javax.inject.Inject @@ -17,4 +18,8 @@ class UnivRepositoryImpl @Inject constructor( override suspend fun searchUniv(searchQuery: SearchQueryRequest): Result { return univRemoteDataSource.searchUniv(searchQuery) } + + override suspend fun getMyUniv(): Result { + return univRemoteDataSource.getMyUniv() + } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/login/TokenRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/token/TokenRepositoryImpl.kt similarity index 55% rename from data/src/main/java/com/moneymong/moneymong/data/repository/login/TokenRepositoryImpl.kt rename to data/src/main/java/com/moneymong/moneymong/data/repository/token/TokenRepositoryImpl.kt index 67eb5d57..2694d89f 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/login/TokenRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/token/TokenRepositoryImpl.kt @@ -1,17 +1,19 @@ -package com.moneymong.moneymong.data.repository.login +package com.moneymong.moneymong.data.repository.token import com.moneymong.moneymong.data.datasource.login.LoginLocalDataSource import com.moneymong.moneymong.data.datasource.login.TokenRemoteDataSource import com.moneymong.moneymong.domain.repository.TokenRepository +import com.moneymong.moneymong.model.sign.LoginType import com.moneymong.moneymong.model.sign.RefreshTokenRequest import com.moneymong.moneymong.model.sign.RefreshTokenResponse +import com.moneymong.moneymong.model.sign.TokenResponse import com.moneymong.moneymong.model.sign.UserDataStoreInfoResponse import kotlinx.coroutines.flow.MutableSharedFlow import javax.inject.Inject class TokenRepositoryImpl @Inject constructor( - private val localDataSource: LoginLocalDataSource, - private val tokenRemoteDataSource: TokenRemoteDataSource + private val loginLocalDataSource: LoginLocalDataSource, + private val tokenRemoteDataSource: TokenRemoteDataSource, ) : TokenRepository { override val tokenUpdateFailed = MutableSharedFlow(replay = 1) @@ -20,15 +22,26 @@ class TokenRepositoryImpl @Inject constructor( } override suspend fun getRefreshToken(): Result { - return localDataSource.getRefreshToken() + return loginLocalDataSource.getRefreshToken() + } + + override suspend fun postAccessToken(type: LoginType, accessToken: String): Result { + return tokenRemoteDataSource.postAccessToken(type = type, accessToken = accessToken).onSuccess { + loginLocalDataSource.setDataStore( + it.accessToken, + it.refreshToken, + it.loginSuccess, + it.schoolInfoProvided + ) + } } override suspend fun getAccessToken(): Result { - return localDataSource.getAccessToken() + return loginLocalDataSource.getAccessToken() } override suspend fun getDataStoreInfo(): Result { - return localDataSource.getDataStoreInfo() + return loginLocalDataSource.getDataStoreInfo() } override suspend fun getUpdateToken(refreshToken: String): Result { @@ -36,15 +49,15 @@ class TokenRepositoryImpl @Inject constructor( } override suspend fun deleteToken() { - localDataSource.deleteToken() + loginLocalDataSource.deleteToken() } override suspend fun updateTokens(aToken: String, rToken: String) { - localDataSource.updateTokens(aToken, rToken) + loginLocalDataSource.updateTokens(aToken, rToken) } override suspend fun updateAccessToken(aToken: String) { - localDataSource.updateAccessToken(aToken) + loginLocalDataSource.updateAccessToken(aToken) } override suspend fun deleteRefreshToken(body: RefreshTokenRequest) { @@ -52,11 +65,11 @@ class TokenRepositoryImpl @Inject constructor( } override suspend fun getSchoolInfo(): Result { - return localDataSource.getSchoolInfo() + return loginLocalDataSource.getSchoolInfo() } - override suspend fun setSchoolInfoExist(infoExist : Boolean) { - return localDataSource.setSchoolInfoExist(infoExist) + override suspend fun setSchoolInfoProvided(infoExist: Boolean) { + return loginLocalDataSource.setSchoolInfoProvided(infoExist) } } \ No newline at end of file diff --git a/data/src/main/java/com/moneymong/moneymong/data/repository/user/UserRepositoryImpl.kt b/data/src/main/java/com/moneymong/moneymong/data/repository/user/UserRepositoryImpl.kt index d1d1e8d8..c95fbc37 100644 --- a/data/src/main/java/com/moneymong/moneymong/data/repository/user/UserRepositoryImpl.kt +++ b/data/src/main/java/com/moneymong/moneymong/data/repository/user/UserRepositoryImpl.kt @@ -40,6 +40,18 @@ class UserRepositoryImpl @Inject constructor( override suspend fun fetchUserId(): Int = userLocalDataSource.fetchUserId() + override suspend fun saveUserInfo() { + userRemoteDataSource.getMyInfo() + .onSuccess { + userLocalDataSource.saveUserId(it.id.toInt()) + userLocalDataSource.saveUserNickName(it.name) + }.onFailure { + userLocalDataSource.saveUserId(0) + userLocalDataSource.saveUserNickName("에러가 발생했습니다.") + } + } + + override suspend fun saveUserNickName(nickname: String) = userLocalDataSource.saveUserNickName(nickname = nickname) diff --git a/data/src/main/java/com/moneymong/moneymong/data/util/LoginType.kt b/data/src/main/java/com/moneymong/moneymong/data/util/LoginType.kt deleted file mode 100644 index 568f14e9..00000000 --- a/data/src/main/java/com/moneymong/moneymong/data/util/LoginType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.moneymong.moneymong.data.util - -enum class LoginType(val type : String) { - KAKAO(type = "KAKAO") -} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/LoginRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/LoginRepository.kt deleted file mode 100644 index 9b40bd68..00000000 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/LoginRepository.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.moneymong.moneymong.domain.repository - -import com.moneymong.moneymong.domain.util.LoginCallback - -interface LoginRepository { - suspend fun kakaoLogin(callback: LoginCallback) -} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/TokenRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/TokenRepository.kt index 7d920735..ca1e7464 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/TokenRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/TokenRepository.kt @@ -1,7 +1,9 @@ package com.moneymong.moneymong.domain.repository +import com.moneymong.moneymong.model.sign.LoginType import com.moneymong.moneymong.model.sign.RefreshTokenRequest import com.moneymong.moneymong.model.sign.RefreshTokenResponse +import com.moneymong.moneymong.model.sign.TokenResponse import com.moneymong.moneymong.model.sign.UserDataStoreInfoResponse import kotlinx.coroutines.flow.MutableSharedFlow @@ -9,13 +11,14 @@ interface TokenRepository { val tokenUpdateFailed: MutableSharedFlow suspend fun notifyTokenUpdateFailed(failed: Boolean) suspend fun getRefreshToken(): Result + suspend fun postAccessToken(type: LoginType, accessToken: String): Result suspend fun getAccessToken(): Result - suspend fun getDataStoreInfo() : Result + suspend fun getDataStoreInfo(): Result suspend fun getUpdateToken(refreshToken: String): Result suspend fun deleteToken() suspend fun updateTokens(aToken: String, rToken: String) suspend fun updateAccessToken(aToken: String) suspend fun deleteRefreshToken(body: RefreshTokenRequest) suspend fun getSchoolInfo(): Result - suspend fun setSchoolInfoExist(infoExist : Boolean) + suspend fun setSchoolInfoProvided(infoExist: Boolean) } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/UnivRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/UnivRepository.kt index 62559313..fadba50c 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/UnivRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/UnivRepository.kt @@ -2,10 +2,13 @@ package com.moneymong.moneymong.domain.repository import com.moneymong.moneymong.model.sign.SearchQueryRequest import com.moneymong.moneymong.model.sign.UnivRequest +import com.moneymong.moneymong.model.sign.UnivResponse import com.moneymong.moneymong.model.sign.UniversitiesResponse interface UnivRepository { - suspend fun createUniv(body: UnivRequest) : Result + suspend fun createUniv(body: UnivRequest): Result - suspend fun searchUniv(searchQuery: SearchQueryRequest) : Result + suspend fun searchUniv(searchQuery: SearchQueryRequest): Result + + suspend fun getMyUniv(): Result } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt index c789f21c..4ff59b80 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/member/MemberRepository.kt @@ -11,4 +11,6 @@ interface MemberRepository { suspend fun getMemberLists(agencyId: Long) : Result suspend fun updateMemberAuthor(agencyId : Long, data : UpdateAuthorRequest) : Result suspend fun blockMemberAuthor(agencyId: Long, data: MemberBlockRequest) : Result + suspend fun deleteAgency(agencyId: Int) :Result + } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/repository/user/UserRepository.kt b/domain/src/main/java/com/moneymong/moneymong/domain/repository/user/UserRepository.kt index b46b4e0f..c2aa4812 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/repository/user/UserRepository.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/repository/user/UserRepository.kt @@ -4,7 +4,6 @@ import com.moneymong.moneymong.model.user.UserResponse interface UserRepository { - suspend fun getMyInfo(): Result @@ -16,6 +15,8 @@ interface UserRepository { suspend fun fetchUserId(): Int + suspend fun saveUserInfo() + suspend fun saveUserNickName(nickname: String) suspend fun fetchUserNickName(): String diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/LoginUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/LoginUseCase.kt deleted file mode 100644 index 012dc699..00000000 --- a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/LoginUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.moneymong.moneymong.domain.usecase.login - -import com.moneymong.moneymong.domain.util.LoginCallback -import com.moneymong.moneymong.domain.repository.LoginRepository -import javax.inject.Inject - -class LoginUseCase @Inject constructor( - private val loginRepository: LoginRepository, -) { - - suspend fun invoke(callback: LoginCallback) { - loginRepository.kakaoLogin(callback) - } - -} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt new file mode 100644 index 00000000..c9a6fc3e --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/member/AgencyDeleteUseCase.kt @@ -0,0 +1,12 @@ +package com.moneymong.moneymong.domain.usecase.member + +import com.moneymong.moneymong.domain.repository.member.MemberRepository +import javax.inject.Inject + +class DeleteAgencyUseCase @Inject constructor( + private val memberRepository: MemberRepository +) { + suspend operator fun invoke(agencyId: Int) : Result { + return memberRepository.deleteAgency(agencyId) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/SchoolInfoUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/SchoolInfoUseCase.kt index 2ad72318..49c0c685 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/SchoolInfoUseCase.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/SchoolInfoUseCase.kt @@ -8,6 +8,6 @@ class SchoolInfoUseCase @Inject constructor( ) { suspend operator fun invoke(infoExist: Boolean){ - tokenRepository.setSchoolInfoExist(infoExist) + tokenRepository.setSchoolInfoProvided(infoExist) } } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/PostAccessTokenUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/PostAccessTokenUseCase.kt new file mode 100644 index 00000000..a0955c18 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/PostAccessTokenUseCase.kt @@ -0,0 +1,18 @@ +package com.moneymong.moneymong.domain.usecase.token + +import com.moneymong.moneymong.domain.repository.TokenRepository +import com.moneymong.moneymong.domain.repository.user.UserRepository +import com.moneymong.moneymong.model.sign.LoginType +import com.moneymong.moneymong.model.sign.TokenResponse +import javax.inject.Inject + +class PostAccessTokenUseCase @Inject constructor( + private val tokenRepository: TokenRepository, + private val userRepository: UserRepository +) { + suspend operator fun invoke(type: LoginType, accessToken: String): Result { + return tokenRepository.postAccessToken(type = type, accessToken = accessToken).onSuccess { + userRepository.saveUserInfo() + } + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/TokenUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/TokenUseCase.kt similarity index 91% rename from domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/TokenUseCase.kt rename to domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/TokenUseCase.kt index d640157a..7aafbe6d 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/login/TokenUseCase.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/token/TokenUseCase.kt @@ -1,4 +1,4 @@ -package com.moneymong.moneymong.domain.usecase.login +package com.moneymong.moneymong.domain.usecase.token import com.moneymong.moneymong.domain.repository.TokenRepository import com.moneymong.moneymong.model.sign.UserDataStoreInfoResponse @@ -18,5 +18,4 @@ class TokenUseCase @Inject constructor( suspend fun getSchoolInfo(): Result { return tokenRepository.getSchoolInfo() } - } \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/CreateUniveristyUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/CreateUniveristyUseCase.kt new file mode 100644 index 00000000..6e474301 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/CreateUniveristyUseCase.kt @@ -0,0 +1,13 @@ +package com.moneymong.moneymong.domain.usecase.university + +import com.moneymong.moneymong.domain.repository.UnivRepository +import com.moneymong.moneymong.model.sign.UnivRequest +import javax.inject.Inject + +class CreateUniversityUseCase @Inject constructor( + private val univRepository: UnivRepository +) { + suspend operator fun invoke(body: UnivRequest): Result { + return univRepository.createUniv(body) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/FetchMyUniversityUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/FetchMyUniversityUseCase.kt new file mode 100644 index 00000000..e4145337 --- /dev/null +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/FetchMyUniversityUseCase.kt @@ -0,0 +1,13 @@ +package com.moneymong.moneymong.domain.usecase.university + +import com.moneymong.moneymong.domain.repository.UnivRepository +import com.moneymong.moneymong.model.sign.UnivResponse +import javax.inject.Inject + +class FetchMyUniversityUseCase @Inject constructor( + private val univRepository: UnivRepository +) { + suspend operator fun invoke(): Result { + return univRepository.getMyUniv() + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/UnivUseCase.kt b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/SearchUniversityUseCase.kt similarity index 50% rename from domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/UnivUseCase.kt rename to domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/SearchUniversityUseCase.kt index bc5143fc..2247bc43 100644 --- a/domain/src/main/java/com/moneymong/moneymong/domain/usecase/signup/UnivUseCase.kt +++ b/domain/src/main/java/com/moneymong/moneymong/domain/usecase/university/SearchUniversityUseCase.kt @@ -1,19 +1,14 @@ -package com.moneymong.moneymong.domain.usecase.signup +package com.moneymong.moneymong.domain.usecase.university import com.moneymong.moneymong.domain.repository.UnivRepository import com.moneymong.moneymong.model.sign.SearchQueryRequest -import com.moneymong.moneymong.model.sign.UnivRequest import com.moneymong.moneymong.model.sign.UniversitiesResponse import javax.inject.Inject -class UnivUseCase @Inject constructor( +class SearchUniversityUseCase @Inject constructor( private val univRepository: UnivRepository ) { - suspend fun createUniv(body : UnivRequest) : Result{ - return univRepository.createUniv(body) - } - - suspend fun searchUniv(searchQuery : String) : Result { + suspend operator fun invoke(searchQuery: String): Result { return univRepository.searchUniv(SearchQueryRequest(searchQuery)) } } \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index e6d5a8c4..3dc6c53b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -23,12 +23,10 @@ platform :android do version_code: options[:new_version_code] ) - git_add( - path: ["./app/build.gradle.kts"] - ) git_commit( path: ["./app/build.gradle.kts"], - message: "INCREMENT VERSION CODE" + message: "INCREMENT VERSION CODE", + allow_nothing_to_commit: true ) push_to_git_remote @@ -97,7 +95,7 @@ platform :android do gradle( task: 'clean assemble', flavor: "tb", - build_type: 'Release' + build_type: 'Debug' ) firebase_app_distribution( service_credentials_file: "#{ENV["FIREBASE_CREDENTIALS"]}", diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyNavigation.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyNavigation.kt index 740c4dc9..b8e29e87 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyNavigation.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyNavigation.kt @@ -19,7 +19,7 @@ fun NavController.navigateAgency( fun NavGraphBuilder.agencyScreen( padding: PaddingValues, - navigateToRegister: () -> Unit, + navigateToRegister: (isUniversityStudent: Boolean) -> Unit, navigateAgencyJoin: (agencyId: Long) -> Unit ) { composable(route = agencyRoute) { diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyRegisterNavigation.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyRegisterNavigation.kt index cbabd184..36edc7b2 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyRegisterNavigation.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/navigation/AgencyRegisterNavigation.kt @@ -3,23 +3,42 @@ package com.moneymong.moneymong.feature.agency.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import androidx.navigation.NavType import androidx.navigation.compose.composable +import androidx.navigation.navArgument import com.moneymong.moneymong.feature.agency.register.AgencyRegisterScreen const val agencyRegisterRoute = "agencyRegister_route" +const val IS_UNIVERSITY_STUDENT = "isUniversityStudent" +const val agencyRegisterRouteWithArgs = "${agencyRegisterRoute}/{${IS_UNIVERSITY_STUDENT}}" -fun NavController.navigateAgencyRegister(navOptions: NavOptions? = null) { - navigate(agencyRegisterRoute, navOptions) +private val arguments = listOf( + navArgument(IS_UNIVERSITY_STUDENT) { + type = NavType.BoolType + defaultValue = false + } +) + +fun NavController.navigateAgencyRegister( + isUniversityStudent: Boolean, + navOptions: NavOptions? = null +) { + navigate("${agencyRegisterRoute}/${isUniversityStudent}", navOptions) } fun NavGraphBuilder.agencyRegisterScreen( navigateToComplete: () -> Unit, navigateUp: () -> Unit ) { - composable(route = agencyRegisterRoute) { + composable( + route = agencyRegisterRouteWithArgs, + arguments = arguments + ) { backStackEntry -> AgencyRegisterScreen( navigateToComplete = navigateToComplete, - navigateUp = navigateUp + navigateUp = navigateUp, + registrableClubOrCouncil = backStackEntry.arguments?.getBoolean(IS_UNIVERSITY_STUDENT) + ?: false ) } } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/AgencyRegisterScreen.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/AgencyRegisterScreen.kt index 10c98b41..8e4a3fda 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/AgencyRegisterScreen.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/AgencyRegisterScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -34,6 +35,7 @@ import com.moneymong.moneymong.design_system.theme.Gray07 import com.moneymong.moneymong.design_system.theme.MMHorizontalSpacing import com.moneymong.moneymong.design_system.theme.White import com.moneymong.moneymong.feature.agency.register.view.AgencyResisterContentView +import com.moneymong.moneymong.feature.agency.search.AgencyType import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -42,7 +44,8 @@ fun AgencyRegisterScreen( modifier: Modifier = Modifier, viewModel: AgencyRegisterViewModel = hiltViewModel(), navigateToComplete: () -> Unit, - navigateUp: () -> Unit + navigateUp: () -> Unit, + registrableClubOrCouncil: Boolean ) { val state by viewModel.collectAsState() val focusManager = LocalFocusManager.current @@ -60,6 +63,12 @@ fun AgencyRegisterScreen( } } + LaunchedEffect(key1 = registrableClubOrCouncil) { + if (registrableClubOrCouncil.not()) { + viewModel.changeAgencyType(AgencyType.GENERAL) + } + } + if (state.visibleOutDialog) { MDSModal( icon = R.drawable.ic_warning_filled, @@ -117,6 +126,7 @@ fun AgencyRegisterScreen( agencyName = state.agencyName, onAgencyNameChange = viewModel::changeAgencyName, changeNameTextFieldIsError = viewModel::changeNameTextFieldIsError, + registrableClubOrCouncil = registrableClubOrCouncil ) val canRegister = state.agencyName.text.isNotEmpty() && state.nameTextFieldIsError.not() @@ -137,6 +147,7 @@ fun AgencyRegisterScreen( fun AgencyRegisterScreenPreview() { AgencyRegisterScreen( navigateToComplete = {}, - navigateUp = {} + navigateUp = {}, + registrableClubOrCouncil = false ) } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/complete/AgencyRegisterCompleteViewModel.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/complete/AgencyRegisterCompleteViewModel.kt index 775e5e65..4efbe5f1 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/complete/AgencyRegisterCompleteViewModel.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/complete/AgencyRegisterCompleteViewModel.kt @@ -1,21 +1,29 @@ package com.moneymong.moneymong.feature.agency.register.complete import com.moneymong.moneymong.common.base.BaseViewModel +import com.moneymong.moneymong.common.event.Event +import com.moneymong.moneymong.common.event.EventTracker import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel -class AgencyRegisterCompleteViewModel @Inject constructor() : +class AgencyRegisterCompleteViewModel @Inject constructor( + private val eventTracker: EventTracker +) : BaseViewModel( AgencyRegisterCompleteState ) { - fun navigateToLedger() = + fun navigateToLedger() { + eventTracker.logEvent(Event.LEDGER_CLICK) eventEmit(sideEffect = AgencyRegisterCompleteSideEffect.NavigateToLedger) + } fun navigateToAgencySearch() = eventEmit(sideEffect = AgencyRegisterCompleteSideEffect.NavigateToAgencySearch) - fun navigateToLedgerManual() = + fun navigateToLedgerManual() { + eventTracker.logEvent(Event.OPERATION_COST_CLICK) eventEmit(sideEffect = AgencyRegisterCompleteSideEffect.NavigateToLedgerManual) + } } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/view/AgencyRegisterContentView.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/view/AgencyRegisterContentView.kt index 4e2af5a7..ef9dce22 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/view/AgencyRegisterContentView.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/register/view/AgencyRegisterContentView.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.moneymong.moneymong.design_system.component.selection.MDSSelection import com.moneymong.moneymong.design_system.component.textfield.MDSTextField @@ -37,14 +38,16 @@ internal fun AgencyResisterContentView( onAgencyTypeChange: (AgencyType) -> Unit, agencyName: TextFieldValue, onAgencyNameChange: (TextFieldValue) -> Unit, - changeNameTextFieldIsError: (Boolean) -> Unit + changeNameTextFieldIsError: (Boolean) -> Unit, + registrableClubOrCouncil: Boolean ) { Column(modifier = modifier) { TitleView() Spacer(modifier = Modifier.height(44.dp)) SelectTypeView( agencyType = agencyType, - onAgencyTypeChange = onAgencyTypeChange + onAgencyTypeChange = onAgencyTypeChange, + registrableClubOrCouncil = registrableClubOrCouncil ) Spacer(modifier = Modifier.height(24.dp)) InputNameView( @@ -59,7 +62,7 @@ internal fun AgencyResisterContentView( @Composable private fun TitleView() { Text( - text = "동아리 or 학생회 등록에\n필요한 항목들을 채워주세요.", + text = "회비 관리가 필요한\n소속 정보를 알려주세요!", color = Gray10, style = Heading2 ) @@ -69,7 +72,8 @@ private fun TitleView() { @Composable private fun SelectTypeView( agencyType: AgencyType?, - onAgencyTypeChange: (AgencyType) -> Unit + onAgencyTypeChange: (AgencyType) -> Unit, + registrableClubOrCouncil: Boolean ) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text( @@ -83,16 +87,24 @@ private fun SelectTypeView( ) { MDSSelection( modifier = Modifier.weight(1f), - text = "동아리", + text = AgencyType.CLUB.text, + enabled = registrableClubOrCouncil, isSelected = agencyType == AgencyType.CLUB, onClick = { onAgencyTypeChange(AgencyType.CLUB) } ) MDSSelection( modifier = Modifier.weight(1f), - text = "학생회", + text = AgencyType.COUNCIL.text, + enabled = registrableClubOrCouncil, isSelected = agencyType == AgencyType.COUNCIL, onClick = { onAgencyTypeChange(AgencyType.COUNCIL) } ) + MDSSelection( + modifier = Modifier.weight(1f), + text = AgencyType.GENERAL.text, + isSelected = agencyType == AgencyType.GENERAL || registrableClubOrCouncil.not(), + onClick = { onAgencyTypeChange(AgencyType.GENERAL) } + ) } } } @@ -144,4 +156,18 @@ private fun InputNameView( focusManager.clearFocus() }) ) +} + + +@Preview +@Composable +private fun AgencyResisterContentViewPreview() { + AgencyResisterContentView( + agencyType = AgencyType.GENERAL, + onAgencyTypeChange = {}, + agencyName = TextFieldValue("동아리"), + onAgencyNameChange = {}, + changeNameTextFieldIsError = {}, + registrableClubOrCouncil = true + ) } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/Agency.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/Agency.kt index 594479fe..97faed69 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/Agency.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/Agency.kt @@ -10,12 +10,25 @@ data class Agency( val memberCount: Int ) +enum class AgencyType(val text: String) { + CLUB(text = "동아리"), + COUNCIL(text = "학생회"), + GENERAL(text = "기타 모임"); + + fun agencyRegisterTypeToString(): String = when (this) { + CLUB -> "IN_SCHOOL_CLUB" + COUNCIL -> "STUDENT_COUNCIL" + GENERAL -> "GENERAL" + } +} + fun AgencyGetResponse.toAgency(): Agency { return Agency( id = this.id, type = when (this.type) { "IN_SCHOOL_CLUB" -> AgencyType.CLUB "STUDENT_COUNCIL" -> AgencyType.COUNCIL + "GENERAL" -> AgencyType.GENERAL else -> throw IllegalArgumentException("Unknown type: $type") }, name = this.name, @@ -29,19 +42,10 @@ fun MyAgencyResponse.toAgency(): Agency { type = when (this.type) { "IN_SCHOOL_CLUB" -> AgencyType.CLUB "STUDENT_COUNCIL" -> AgencyType.COUNCIL + "GENERAL" -> AgencyType.GENERAL else -> throw IllegalArgumentException("Unknown type: $type") }, name = this.name, memberCount = this.headCount ) -} - -enum class AgencyType(val text: String) { - CLUB(text = "동아리"), - COUNCIL(text = "학생회"); - - fun agencyRegisterTypeToString(): String = when (this) { - CLUB -> "IN_SCHOOL_CLUB" - COUNCIL -> "STUDENT_COUNCIL" - } } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchScreen.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchScreen.kt index 12c01cc4..8796ae61 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchScreen.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchScreen.kt @@ -29,13 +29,13 @@ import androidx.paging.compose.itemKey import com.moneymong.moneymong.common.error.MoneyMongError import com.moneymong.moneymong.design_system.R import com.moneymong.moneymong.design_system.component.button.MDSFloatingActionButton +import com.moneymong.moneymong.design_system.component.indicator.LoadingItem +import com.moneymong.moneymong.design_system.component.indicator.LoadingScreen import com.moneymong.moneymong.design_system.component.tooltip.MDSToolTip import com.moneymong.moneymong.design_system.component.tooltip.MDSToolTipPosition import com.moneymong.moneymong.design_system.error.ErrorDialog import com.moneymong.moneymong.design_system.error.ErrorItem import com.moneymong.moneymong.design_system.error.ErrorScreen -import com.moneymong.moneymong.design_system.component.indicator.LoadingItem -import com.moneymong.moneymong.design_system.component.indicator.LoadingScreen import com.moneymong.moneymong.design_system.theme.Body4 import com.moneymong.moneymong.design_system.theme.Gray01 import com.moneymong.moneymong.design_system.theme.Gray08 @@ -50,7 +50,7 @@ import org.orbitmvi.orbit.compose.collectSideEffect fun AgencySearchScreen( modifier: Modifier = Modifier, viewModel: AgencySearchViewModel = hiltViewModel(), - navigateToRegister: () -> Unit, + navigateToRegister: (isUniversityStudent: Boolean) -> Unit, navigateAgencyJoin: (agencyId: Long) -> Unit ) { val state by viewModel.collectAsState() @@ -59,7 +59,7 @@ fun AgencySearchScreen( viewModel.collectSideEffect { when (it) { is AgencySearchSideEffect.NavigateToRegister -> { - navigateToRegister() + navigateToRegister(it.isUniversityStudent) } is AgencySearchSideEffect.NavigateToAgencyJoin -> { @@ -102,7 +102,7 @@ fun AgencySearchScreen( isLoading = state.isLoading, isError = state.isError, errorMessage = state.errorMessage, - fetchMyAgencyList = viewModel::fetchMyAgencyList + onRetry = viewModel::getInitialData, ) } Column( @@ -135,7 +135,7 @@ private fun AgencySearchContentView( isLoading: Boolean, isError: Boolean, errorMessage: String, - fetchMyAgencyList: () -> Unit + onRetry: () -> Unit, ) { val contentLoading = pagingItems.loadState.refresh is LoadState.Loading || isLoading val contentError = pagingItems.loadState.refresh is LoadState.Error || isError @@ -152,7 +152,7 @@ private fun AgencySearchContentView( message = contentErrorMessage, onRetry = { pagingItems.retry() - fetchMyAgencyList() + onRetry() }, ) } else { diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchSideEffect.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchSideEffect.kt index 678b673d..4934e6e8 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchSideEffect.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchSideEffect.kt @@ -3,6 +3,6 @@ package com.moneymong.moneymong.feature.agency.search import com.moneymong.moneymong.common.base.SideEffect sealed interface AgencySearchSideEffect : SideEffect { - data object NavigateToRegister : AgencySearchSideEffect + data class NavigateToRegister(val isUniversityStudent: Boolean) : AgencySearchSideEffect data class NavigateToAgencyJoin(val agencyId: Long) : AgencySearchSideEffect } \ No newline at end of file diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchState.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchState.kt index 8ee355c3..2feee998 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchState.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchState.kt @@ -8,6 +8,7 @@ data class AgencySearchState( val isError: Boolean = false, val errorMessage: String = "", val visibleWarningDialog: Boolean = false, + val isUniversityStudent: Boolean = false ) : State { val joinedAgenciesIds: List diff --git a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchViewModel.kt b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchViewModel.kt index 74869816..c27bcd1e 100644 --- a/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchViewModel.kt +++ b/feature/agency/src/main/java/com/moneymong/moneymong/feature/agency/search/AgencySearchViewModel.kt @@ -7,20 +7,26 @@ import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.common.error.MoneyMongError import com.moneymong.moneymong.domain.usecase.agency.FetchMyAgencyListUseCase import com.moneymong.moneymong.domain.usecase.agency.GetAgenciesUseCase +import com.moneymong.moneymong.domain.usecase.university.FetchMyUniversityUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.map import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import javax.inject.Inject @HiltViewModel class AgencySearchViewModel @Inject constructor( getAgenciesUseCase: GetAgenciesUseCase, - private val fetchMyAgencyListUseCase: FetchMyAgencyListUseCase + private val fetchMyAgencyListUseCase: FetchMyAgencyListUseCase, + private val fetchMyUniversityUseCase: FetchMyUniversityUseCase ) : BaseViewModel(AgencySearchState()) { - fun navigateToRegister() = - eventEmit(sideEffect = AgencySearchSideEffect.NavigateToRegister) + fun navigateToRegister() = intent { + postSideEffect(AgencySearchSideEffect.NavigateToRegister(isUniversityStudent = state.isUniversityStudent)) + } fun navigateToJoin(agencyId: Long) = eventEmit(sideEffect = AgencySearchSideEffect.NavigateToAgencyJoin(agencyId)) @@ -32,37 +38,50 @@ class AgencySearchViewModel @Inject constructor( }.cachedIn(viewModelScope) init { - fetchMyAgencyList() + getInitialData() } - fun fetchMyAgencyList() = intent { + fun getInitialData() = intent { reduce { state.copy( isLoading = true, isError = false ) } - fetchMyAgencyListUseCase() - .also { - reduce { state.copy(isLoading = false) } + coroutineScope { + val fetchMyAgenciesDeferred = async { + fetchMyAgencyListUseCase() + } + val fetchMyUniversityDeferred = async { + fetchMyUniversityUseCase() } - .onSuccess { + + val fetchMyUniversityResult = fetchMyUniversityDeferred.await() + val fetchMyAgenciesResult = fetchMyAgenciesDeferred.await() + + if (fetchMyAgenciesResult.isSuccess && fetchMyUniversityResult.isSuccess) { reduce { state.copy( - joinedAgencies = it.map { myAgencyResponse -> myAgencyResponse.toAgency() } + isLoading = false, + joinedAgencies = fetchMyAgenciesResult.getOrThrow() + .map { myAgencyResponse -> myAgencyResponse.toAgency() }, + isUniversityStudent = fetchMyUniversityResult.getOrThrow().universityName != null, ) } - }.onFailure { + } else { reduce { state.copy( + isLoading = false, isError = true, - errorMessage = it.message ?: MoneyMongError.UnExpectedError.message, + errorMessage = fetchMyAgenciesResult.exceptionOrNull()?.message + ?: fetchMyUniversityResult.exceptionOrNull()?.message + ?: MoneyMongError.UnExpectedError.message ) } } + } } - fun changeVisibleWarningDialog(visible: Boolean) = intent { reduce { state.copy(visibleWarningDialog = visible) diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt index 36d6afaa..318e22ad 100644 --- a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavOptions import com.example.member.MemberScreen +import com.moneymong.moneymong.common.event.Event import com.moneymong.moneymong.common.ui.plus import com.moneymong.moneymong.design_system.R import com.moneymong.moneymong.design_system.component.bottomSheet.MDSBottomSheet @@ -283,7 +284,7 @@ fun LedgerScreen( height = 24.dp ), containerColor = Mint03, - onClick = { viewModel.eventEmit(LedgerSideEffect.LedgerNavigateToOCR) } + onClick = viewModel::onClickLedgerRegisterOCR ) } if (expandableFab) Spacer(modifier = Modifier.height(10.dp)) @@ -307,7 +308,7 @@ fun LedgerScreen( MDSFloatingActionButton( iconResource = R.drawable.ic_pencil, containerColor = Mint03, - onClick = { viewModel.eventEmit(LedgerSideEffect.LedgerNavigateToLedgerManual) } + onClick = viewModel::onClickLedgerRegisterManual ) } if (expandableFab) Spacer(modifier = Modifier.height(10.dp)) @@ -325,6 +326,7 @@ fun LedgerScreen( iconResource = R.drawable.ic_plus_default, containerColor = containerColor, onClick = { + viewModel.eventTracker.logEvent(Event.PLUS_CLICK) expandableFab = !expandableFab } ) @@ -333,7 +335,17 @@ fun LedgerScreen( } } else { Box(modifier = modifier.fillMaxSize()) { - MemberScreen(agencyId = state.agencyId) + MemberScreen( + agencyId = state.agencyId, + agencyList = state.agencyList, + onClickItem = { + viewModel.eventEmit( + LedgerSideEffect.LedgerSelectedAgencyChange( + it + ) + ) + }, + changeAgencyList = { viewModel.changeAgencyList(it) }) } } } diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt index e3aa1497..5c0ec38a 100644 --- a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/LedgerViewModel.kt @@ -1,8 +1,11 @@ package com.moneymong.moneymong.ledger +import android.util.Log import androidx.lifecycle.SavedStateHandle import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.common.error.MoneyMongError +import com.moneymong.moneymong.common.event.Event +import com.moneymong.moneymong.common.event.EventTracker import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase import com.moneymong.moneymong.domain.usecase.agency.FetchMyAgencyListUseCase import com.moneymong.moneymong.domain.usecase.agency.SaveAgencyIdUseCase @@ -14,6 +17,7 @@ import com.moneymong.moneymong.domain.usecase.member.MemberListUseCase import com.moneymong.moneymong.domain.usecase.user.FetchUserIdUseCase import com.moneymong.moneymong.ledger.navigation.LedgerArgs import com.moneymong.moneymong.ledger.view.LedgerTransactionType +import com.moneymong.moneymong.model.agency.MyAgencyResponse import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.collectLatest import org.orbitmvi.orbit.annotation.OrbitExperimental @@ -28,6 +32,7 @@ import javax.inject.Inject @HiltViewModel class LedgerViewModel @Inject constructor( savedStateHandle: SavedStateHandle, + val eventTracker: EventTracker, private val fetchLedgerTransactionListUseCase: FetchLedgerTransactionListUseCase, private val fetchAgencyExistLedgerUseCase: FetchAgencyExistLedgerUseCase, private val fetchMyAgencyListUseCase: FetchMyAgencyListUseCase, @@ -69,6 +74,7 @@ class LedgerViewModel @Inject constructor( reduce { state.copy(isAgencyExistLoading = true) } fetchAgencyExistLedgerUseCase(state.agencyId) .onSuccess { + Log.d("fetchAgencyExistLedger${state.agencyId}",it.toString() ) reduce { state.copy( isExistLedger = it, @@ -90,6 +96,8 @@ class LedgerViewModel @Inject constructor( page = 0, limit = 100 ).onSuccess { + Log.d("fetchLedgerTransactionList${state.existAgency}",it.toString() ) + reduce { state.copy( ledgerTransaction = it, @@ -97,11 +105,19 @@ class LedgerViewModel @Inject constructor( ) } }.onFailure { - reduce { - state.copy( - visibleError = true, - errorMessage = it.message ?: MoneyMongError.UnExpectedError.message - ) + if (it.message.equals("장부가 존재하지 않습니다.")) { + reduce { //TODO - 서버 의논 후 변경 예정 + state.copy( + visibleError = false, + ) + } + } else { + reduce { + state.copy( + visibleError = true, + errorMessage = it.message ?: MoneyMongError.UnExpectedError.message + ) + } } }.also { reduce { state.copy(isLedgerTransactionLoading = false) } } } @@ -111,6 +127,8 @@ class LedgerViewModel @Inject constructor( reduce { state.copy(isMyAgencyLoading = true) } fetchMyAgencyListUseCase() .onSuccess { + Log.d("fetchMyAgencyList${state.existAgency}",it.toString() ) + reduce { state.copy( agencyList = it, @@ -142,11 +160,20 @@ class LedgerViewModel @Inject constructor( ) } }.onFailure { - reduce { - state.copy( - visibleError = true, - errorMessage = it.message ?: MoneyMongError.UnExpectedError.message - ) + if (it.message.equals("소속에 가입 후 장부를 사용할 수 있습니다.")) { //TODO - 서버 의논 후 변경 예정 + reduce { + state.copy( + visibleError = false + ) + } + } else { + reduce { + state.copy( + visibleError = true, + errorMessage = it.message + ?: MoneyMongError.UnExpectedError.message + ) + } } }.also { reduce { state.copy(isAgencyMemberLoading = false) } } } @@ -212,8 +239,27 @@ class LedgerViewModel @Inject constructor( fetchLedgerTransactionList() } + fun onClickLedgerRegisterOCR() = intent { + eventTracker.logEvent(Event.OCR_CLICK) + postSideEffect(LedgerSideEffect.LedgerNavigateToOCR) + } + + fun onClickLedgerRegisterManual() = intent { + eventTracker.logEvent(Event.HAND_CLICK) + postSideEffect(LedgerSideEffect.LedgerNavigateToLedgerManual) + } + fun onDismissOnboarding() = intent { postDisplayedLedgerOnboardingUseCase(onboardingType = state.onboardingType) reduce { state.copy(visibleOnboarding = false) } } -} \ No newline at end of file + + + fun changeAgencyList(filteredAgencyList: List) = intent { + reduce { + state.copy( + agencyList = filteredAgencyList + ) + } + } +} diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/LedgerOnboarding.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/LedgerOnboarding.kt index 47a7ae3b..1beb6833 100644 --- a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/LedgerOnboarding.kt +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/LedgerOnboarding.kt @@ -9,15 +9,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.window.Popup -import androidx.compose.ui.window.PopupPositionProvider import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.moneymong.moneymong.design_system.theme.Gray10 import com.moneymong.moneymong.design_system.theme.White +import com.moneymong.moneymong.ledger.view.onboarding.popup.LedgerOnboardingPopup import java.time.LocalDate @@ -68,9 +64,7 @@ internal fun LedgerOnboarding( } } - Popup( - popupPositionProvider = LedgerPopupPositionProvider() - ) { + LedgerOnboardingPopup { when (currentPage) { LedgerOnboardingPage.DATE -> { LedgerOnboardingDatePage( @@ -101,20 +95,4 @@ internal fun LedgerOnboarding( } } } -} - - -private class LedgerPopupPositionProvider : PopupPositionProvider { - override fun calculatePosition( - anchorBounds: IntRect, - windowSize: IntSize, - layoutDirection: LayoutDirection, - popupContentSize: IntSize - ): IntOffset { - - return IntOffset( - x = 0, - y = 0 - ) - } } \ No newline at end of file diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopup.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopup.kt new file mode 100644 index 00000000..5e6649ec --- /dev/null +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopup.kt @@ -0,0 +1,39 @@ +package com.moneymong.moneymong.ledger.view.onboarding.popup + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCompositionContext +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.platform.LocalView + +@Composable +internal fun LedgerOnboardingPopup( + content: @Composable () -> Unit +) { + val view = LocalView.current + val parentComposition = rememberCompositionContext() + val currentContent by rememberUpdatedState(content) + val popupLayout = remember { + LedgerOnboardingPopupLayout( + composeView = view + ).apply { + setContent(parentComposition) { + currentContent() + } + } + } + + DisposableEffect(key1 = popupLayout) { + popupLayout.show() + + onDispose { + popupLayout.disposeComposition() + popupLayout.dismiss() + } + } +} + + + diff --git a/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopupLayout.kt b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopupLayout.kt new file mode 100644 index 00000000..d490c94c --- /dev/null +++ b/feature/ledger/src/main/java/com/moneymong/moneymong/ledger/view/onboarding/popup/LedgerOnboardingPopupLayout.kt @@ -0,0 +1,95 @@ +package com.moneymong.moneymong.ledger.view.onboarding.popup + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.PixelFormat +import android.view.Gravity +import android.view.View +import android.view.WindowManager +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionContext +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.AbstractComposeView +import androidx.lifecycle.findViewTreeLifecycleOwner +import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.findViewTreeSavedStateRegistryOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner + + +@SuppressLint("ViewConstructor") +internal class LedgerOnboardingPopupLayout( + private val composeView: View, +) : AbstractComposeView(composeView.context) { + + private val windowManager = + composeView.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + private val params = createLayoutParams() + private var content: @Composable () -> Unit by mutableStateOf({}) + + + init { + id = android.R.id.content + setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner()) + setViewTreeViewModelStoreOwner(composeView.findViewTreeViewModelStoreOwner()) + setViewTreeSavedStateRegistryOwner(composeView.findViewTreeSavedStateRegistryOwner()) + } + + @Composable + override fun Content() { + content() + } + + fun setContent(parent: CompositionContext, content: @Composable () -> Unit) { + setParentCompositionContext(parent) + this.content = content + } + + fun show() { + windowManager.addView(this, params) + } + + fun dismiss() { + setViewTreeLifecycleOwner(null) + windowManager.removeViewImmediate(this) + } + + + private fun createLayoutParams(): WindowManager.LayoutParams { + return WindowManager.LayoutParams().apply { + // Start to position the popup in the top left corner, a new position will be calculated + gravity = Gravity.START or Gravity.TOP + + // Flags specific to android.widget.PopupWindow + flags = flags and ( + WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES or + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or + WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + ).inv() + + // Make the popup window not focusable + flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + + // Enables us to intercept outside clicks even when popup is not focusable + flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + + type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL + + // Get the Window token from the parent view + token = composeView.applicationWindowToken + + // Set the popup window to occupy the entire screen + width = WindowManager.LayoutParams.MATCH_PARENT + height = WindowManager.LayoutParams.MATCH_PARENT + + format = PixelFormat.TRANSLUCENT + + // accessibilityTitle is not exposed as a public API therefore we set popup window + // title which is used as a fallback by a11y services + title = "LedgerOnboardingPopup" + } + } +} \ No newline at end of file diff --git a/feature/member/src/main/java/com/example/member/MemberScreen.kt b/feature/member/src/main/java/com/example/member/MemberScreen.kt index e7a4019c..9dedc5fb 100644 --- a/feature/member/src/main/java/com/example/member/MemberScreen.kt +++ b/feature/member/src/main/java/com/example/member/MemberScreen.kt @@ -37,6 +37,7 @@ import com.moneymong.moneymong.design_system.component.bottomSheet.MDSBottomShee import com.moneymong.moneymong.design_system.component.button.MDSButton import com.moneymong.moneymong.design_system.component.button.MDSButtonSize import com.moneymong.moneymong.design_system.component.button.MDSButtonType +import com.moneymong.moneymong.design_system.component.modal.MDSModal import com.moneymong.moneymong.design_system.component.snackbar.MDSSnackbarHost import com.moneymong.moneymong.design_system.error.ErrorDialog import com.moneymong.moneymong.design_system.error.ErrorScreen @@ -50,6 +51,7 @@ import com.moneymong.moneymong.design_system.theme.Gray08 import com.moneymong.moneymong.design_system.theme.MMHorizontalSpacing import com.moneymong.moneymong.design_system.theme.Red03 import com.moneymong.moneymong.design_system.theme.White +import com.moneymong.moneymong.model.agency.MyAgencyResponse import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -59,7 +61,10 @@ import org.orbitmvi.orbit.compose.collectSideEffect @Composable fun MemberScreen( viewModel: MemberViewModel = hiltViewModel(), - agencyId: Int + agencyId: Int, + agencyList: List, + onClickItem: (agencyId: Int) -> Unit, + changeAgencyList: (changeAgencyList: List) -> Unit ) { val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, @@ -196,6 +201,25 @@ fun MemberScreen( ) } + if (state.deleteAgency) { + MDSModal( + icon = R.drawable.ic_warning_filled, + title = "소속을 정말 삭제하시겠어요?", + description = "등록된 회비 내역이 모두 사라져요.", + negativeBtnText = "취소", + positiveBtnText = "확인", + onClickNegative = { viewModel.deleteAgencyBtnClicked(false) }, + onClickPositive = { + viewModel.deleteAgency( + agencyId, + agencyList, + onClickItem, + changeAgencyList + ) + } + ) + } + if (state.visibleBottomSheet) { viewModel.isRoleChanged(false) MDSBottomSheet( @@ -383,6 +407,8 @@ fun MemberScreen( invitationCode = state.invitationCode, isReInvitationCode = { viewModel.eventEmit(MemberSideEffect.GetReInvitationCode(it)) }, //TODO onCopyChange = { onCopyClick -> viewModel.onCopyClickChanged(onCopyClick) }, + deleteAgencyBtnClicked = { onClick -> viewModel.deleteAgencyBtnClicked(onClick)} + ) MemberListView( @@ -428,6 +454,8 @@ fun MemberScreen( invitationCode = state.invitationCode, isReInvitationCode = { viewModel.eventEmit(MemberSideEffect.GetReInvitationCode(it)) }, onCopyChange = { onCopyClick -> viewModel.onCopyClickChanged(onCopyClick) }, + deleteAgencyBtnClicked = { onClick -> viewModel.deleteAgencyBtnClicked(onClick)} + ) MemberListView( diff --git a/feature/member/src/main/java/com/example/member/MemberState.kt b/feature/member/src/main/java/com/example/member/MemberState.kt index cc11fb63..3a4ae76a 100644 --- a/feature/member/src/main/java/com/example/member/MemberState.kt +++ b/feature/member/src/main/java/com/example/member/MemberState.kt @@ -31,4 +31,6 @@ data class MemberState( val isUserAuthor: String = "", val agencyId: Int = 0, val isBlockedUser : Boolean = false, + val deleteAgency : Boolean = false, + ) : State \ No newline at end of file diff --git a/feature/member/src/main/java/com/example/member/MemberViewModel.kt b/feature/member/src/main/java/com/example/member/MemberViewModel.kt index e0283de1..460f31b7 100644 --- a/feature/member/src/main/java/com/example/member/MemberViewModel.kt +++ b/feature/member/src/main/java/com/example/member/MemberViewModel.kt @@ -3,12 +3,14 @@ package com.example.member import android.util.Log import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase +import com.moneymong.moneymong.domain.usecase.member.DeleteAgencyUseCase import com.moneymong.moneymong.domain.usecase.member.MemberBlockUseCase import com.moneymong.moneymong.domain.usecase.member.MemberInvitationCodeUseCase import com.moneymong.moneymong.domain.usecase.member.MemberListUseCase import com.moneymong.moneymong.domain.usecase.member.MemberReInvitationCodeUseCase import com.moneymong.moneymong.domain.usecase.member.UpdateMemberAuthorUseCase import com.moneymong.moneymong.domain.usecase.user.GetMyInfoUseCase +import com.moneymong.moneymong.model.agency.MyAgencyResponse import com.moneymong.moneymong.model.member.AgencyUser import com.moneymong.moneymong.model.member.MemberBlockRequest import com.moneymong.moneymong.model.member.UpdateAuthorRequest @@ -27,7 +29,8 @@ class MemberViewModel @Inject constructor( private val getMyInfoUseCase: GetMyInfoUseCase, private val updateMemberAuthorUseCase: UpdateMemberAuthorUseCase, private val memberBlockUseCase: MemberBlockUseCase, - private val fetchAgencyIdUseCase: FetchAgencyIdUseCase + private val fetchAgencyIdUseCase: FetchAgencyIdUseCase, + private val deleteAgencyUseCase: DeleteAgencyUseCase ) : BaseViewModel(MemberState()) { init { @@ -256,6 +259,7 @@ class MemberViewModel @Inject constructor( } } + fun blockMemberAuthor(agencyId: Long, userId: Long) = intent { memberBlockUseCase(agencyId, MemberBlockRequest(userId)) .onSuccess { @@ -263,7 +267,7 @@ class MemberViewModel @Inject constructor( updateMemberListByBlock(userId) } .onFailure { - reduce{ + reduce { state.copy( visiblePopUpError = true, errorPopUpMessage = it.message.toString() @@ -272,6 +276,49 @@ class MemberViewModel @Inject constructor( } } + + fun deleteAgency( + agencyId: Int, + agencyList: List, + onClickItem: (agencyId: Int) -> Unit, + changeAgencyList: (agencyList: List) -> Unit + ) = intent { + deleteAgencyUseCase.invoke(agencyId) + .onSuccess { + Log.d("deleteAgency${agencyId}", it.toString()) + val filteredList = agencyList.filter { it.id != agencyId } + if (filteredList.isNotEmpty()) { + val randomAgency = filteredList.random() + onClickItem(randomAgency.id) + changeAgencyList(filteredList) + } else { + changeAgencyList(emptyList()) + } + reduce { + state.copy( + deleteAgency = false + ) + } + }.onFailure { + reduce { + state.copy( + deleteAgency = false, + visiblePopUpError = true, + errorPopUpMessage = it.message.toString() + ) + } + } + } + + + fun deleteAgencyBtnClicked(deleteAgencyBtnClicked : Boolean) = intent { + reduce { + state.copy( + deleteAgency = deleteAgencyBtnClicked + ) + } + } + private fun updateFilteredMemberListByBlock(userId: Long) = intent { val currentMemberList = state.filteredMemberList val updateBlockedMemberList = currentMemberList.filterNot { member -> diff --git a/feature/member/src/main/java/com/example/member/component/MemberCardView.kt b/feature/member/src/main/java/com/example/member/component/MemberCardView.kt index 42b2336c..b0ca5ba8 100644 --- a/feature/member/src/main/java/com/example/member/component/MemberCardView.kt +++ b/feature/member/src/main/java/com/example/member/component/MemberCardView.kt @@ -5,6 +5,7 @@ import android.content.ClipboardManager import android.content.Context import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -31,6 +32,7 @@ import com.moneymong.moneymong.design_system.theme.Body4 import com.moneymong.moneymong.design_system.theme.Gray02 import com.moneymong.moneymong.design_system.theme.Gray10 import com.moneymong.moneymong.design_system.theme.Mint03 +import com.moneymong.moneymong.design_system.theme.Red03 import com.moneymong.moneymong.design_system.theme.White import com.moneymong.moneymong.model.member.AgencyUser @@ -45,6 +47,8 @@ fun MemberCardView( invitationCode: String, isReInvitationCode: (Long) -> Unit, onCopyChange: (Boolean) -> Unit, + deleteAgencyBtnClicked : (Boolean) -> Unit + ) { val context = LocalContext.current @@ -92,6 +96,27 @@ fun MemberCardView( contentColor = White, ) + if(memberMyInfo.agencyUserRole != "MEMBER"){ + Row ( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable { deleteAgencyBtnClicked(true) }, + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically + ){ + Text( + text = "소속삭제", + color = Red03, + style = Body3 + ) + Image( + modifier= Modifier.size(18.dp), + painter =painterResource(id = R.drawable.ic_agency_delete), + contentDescription ="" + ) + + } + } } if (memberMyInfo.agencyUserRole == "STAFF") { diff --git a/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/MyMongScreen.kt b/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/MyMongScreen.kt index bf60302a..c4bb3f91 100644 --- a/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/MyMongScreen.kt +++ b/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/MyMongScreen.kt @@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -77,6 +79,7 @@ fun MyMongScreen( modifier = modifier .fillMaxSize() .background(color = Gray01) + .verticalScroll(state = rememberScrollState()) .padding(horizontal = MMHorizontalSpacing), ) { MyMongTopBar(modifier = Modifier.align(Alignment.CenterHorizontally)) diff --git a/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/view/MyMongInfoView.kt b/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/view/MyMongInfoView.kt index 11479c5f..e0e4e7c2 100644 --- a/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/view/MyMongInfoView.kt +++ b/feature/mymong/src/main/java/com/moneymong/moneymong/feature/mymong/main/view/MyMongInfoView.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.moneymong.moneymong.design_system.R -import com.moneymong.moneymong.design_system.error.ErrorItem import com.moneymong.moneymong.design_system.component.indicator.LoadingItem +import com.moneymong.moneymong.design_system.error.ErrorItem import com.moneymong.moneymong.design_system.theme.Blue04 import com.moneymong.moneymong.design_system.theme.Body2 import com.moneymong.moneymong.design_system.theme.Body3 @@ -97,7 +97,7 @@ private fun Profile( Spacer(modifier = Modifier.width(16.dp)) Column { Text( - text = name, + text = name + "님", color = Gray10, style = Heading1 ) @@ -126,6 +126,12 @@ fun UniversityInfo( university: String, grade: Int ) { + val universityInfoText = when { + university.isEmpty() -> "정보 없음" + grade == 5 -> "$university ${grade}학년 이상" + else -> "$university ${grade}학년" + } + Box( modifier = Modifier .fillMaxWidth() @@ -140,7 +146,9 @@ fun UniversityInfo( style = Body3 ) Spacer(modifier = Modifier.height(6.dp)) - Row { + Row( + verticalAlignment = Alignment.CenterVertically + ) { Image( modifier = Modifier.size(24.dp), painter = painterResource(id = R.drawable.img_university), @@ -148,7 +156,7 @@ fun UniversityInfo( ) Spacer(modifier = Modifier.width(8.dp)) Text( - text = "$university $grade" + if (grade == 5) "학년 이상" else "학년", + text = universityInfoText, color = Gray08, style = Body4 ) diff --git a/feature/ocr-detail/src/main/java/com/moneymong/moneymong/ocr_detail/OCRDetailViewModel.kt b/feature/ocr-detail/src/main/java/com/moneymong/moneymong/ocr_detail/OCRDetailViewModel.kt index ad718676..b987fca8 100644 --- a/feature/ocr-detail/src/main/java/com/moneymong/moneymong/ocr_detail/OCRDetailViewModel.kt +++ b/feature/ocr-detail/src/main/java/com/moneymong/moneymong/ocr_detail/OCRDetailViewModel.kt @@ -3,6 +3,8 @@ package com.moneymong.moneymong.ocr_detail import android.content.SharedPreferences import androidx.compose.ui.text.input.TextFieldValue import com.moneymong.moneymong.common.base.BaseViewModel +import com.moneymong.moneymong.common.event.Event +import com.moneymong.moneymong.common.event.EventTracker import com.moneymong.moneymong.common.ext.toMultipart import com.moneymong.moneymong.common.ui.isValidPaymentDate import com.moneymong.moneymong.common.ui.isValidPaymentTime @@ -29,6 +31,7 @@ import javax.inject.Inject @HiltViewModel class OCRDetailViewModel @Inject constructor( + private val eventTracker: EventTracker, private val prefs: SharedPreferences, private val postLedgerTransactionUseCase: PostLedgerTransactionUseCase, private val postFileUploadUseCase: PostFileUploadUseCase, @@ -134,6 +137,7 @@ class OCRDetailViewModel @Inject constructor( } fun onClickPostLedger() = intent { + eventTracker.logEvent(Event.OCR_MODIFY_TO_REGISTER_CLICK) postDocumentImage(imageFile = state.receiptFile, isReceipt = true) } diff --git a/feature/ocr-result/src/main/java/com/moneymong/moneymong/ocr_result/OCRResultViewModel.kt b/feature/ocr-result/src/main/java/com/moneymong/moneymong/ocr_result/OCRResultViewModel.kt index ca5f1fa6..b7e9c027 100644 --- a/feature/ocr-result/src/main/java/com/moneymong/moneymong/ocr_result/OCRResultViewModel.kt +++ b/feature/ocr-result/src/main/java/com/moneymong/moneymong/ocr_result/OCRResultViewModel.kt @@ -2,6 +2,8 @@ package com.moneymong.moneymong.ocr_result import android.content.SharedPreferences import com.moneymong.moneymong.common.base.BaseViewModel +import com.moneymong.moneymong.common.event.Event +import com.moneymong.moneymong.common.event.EventTracker import com.moneymong.moneymong.common.ext.toMultipart import com.moneymong.moneymong.domain.usecase.agency.FetchAgencyIdUseCase import com.moneymong.moneymong.domain.usecase.ledger.PostLedgerTransactionUseCase @@ -21,6 +23,7 @@ import javax.inject.Inject @HiltViewModel class OCRResultViewModel @Inject constructor( + private val eventTracker: EventTracker, private val prefs: SharedPreferences, private val postLedgerTransactionUseCase: PostLedgerTransactionUseCase, private val postFileUploadUseCase: PostFileUploadUseCase, @@ -59,6 +62,8 @@ class OCRResultViewModel @Inject constructor( } fun postReceiptImage() = intent { + eventTracker.logEvent(Event.OCR_REGISTER_CLICK) + state.receiptFile?.let { if (!state.isLoading) { reduce { state.copy(isLoading = true) } @@ -93,6 +98,7 @@ class OCRResultViewModel @Inject constructor( // onClick fun onClickOCREdit() = intent { + eventTracker.logEvent(Event.OCR_MODIFY_CLICK) postSideEffect(OCRResultSideEffect.OCRResultNavigateToOCRDetail(state.document)) } } \ No newline at end of file diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/LoginScreen.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/LoginScreen.kt index a23d3f3c..d80b937b 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/LoginScreen.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/LoginScreen.kt @@ -11,14 +11,16 @@ import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment -import com.moneymong.moneymong.design_system.R import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.moneymong.moneymong.design_system.R import com.moneymong.moneymong.design_system.error.ErrorScreen import com.moneymong.moneymong.design_system.theme.Blue04 import com.moneymong.moneymong.design_system.theme.MMHorizontalSpacing +import com.moneymong.moneymong.feature.sign.util.KakaoLogin import com.moneymong.moneymong.feature.sign.view.KakaoLoginView import com.moneymong.moneymong.feature.sign.view.TitleView import com.moneymong.moneymong.feature.sign.viewmodel.LoginViewModel @@ -29,35 +31,35 @@ fun LoginScreen( navigateToSignup: () -> Unit, navigateToLedger: () -> Unit, navigateToLogin: () -> Unit, - viewModel: LoginViewModel = hiltViewModel(), - - ) { + viewModel: LoginViewModel = hiltViewModel() +) { val state = viewModel.collectAsState().value + val context = LocalContext.current - LaunchedEffect(key1 = state.isSchoolInfoExist) { - if (state.isSchoolInfoExist == true) { + LaunchedEffect(key1 = state.isSchoolInfoProvided) { + if (state.isSchoolInfoProvided == true) { navigateToLedger() - } else if (state.isSchoolInfoExist == false) { + } else if (state.isSchoolInfoProvided == false) { navigateToSignup() - viewModel.isSchoolInfoExistChanged(null) + viewModel.isSchoolInfoProvidedChanged(null) } } LaunchedEffect(key1 = state.isLoginRequired) { - if (state.isLoginRequired == true ) { + if (state.isLoginRequired == true) { navigateToLogin() viewModel.isLoginRequiredChanged(false) } } - if (state.visibleError == true ) { + if (state.visibleError == true) { ErrorScreen( modifier = Modifier.fillMaxSize(), message = state.errorMessage, onRetry = { viewModel.visibleErrorChanged(false) } ) - } else if(state.visibleError == false ){ + } else if (state.visibleError == false) { Column( modifier = Modifier .fillMaxSize() @@ -86,7 +88,13 @@ fun LoginScreen( ) { KakaoLoginView( modifier = Modifier.fillMaxWidth(), - onLoginButtonClicked = { viewModel.onLoginButtonClicked() } + loginByKaKao = { + KakaoLogin( + context = context, + onSuccess = viewModel::onKakaoLoginSuccess, + onFailure = viewModel::onKakaoLoginFailure + ) + } ) } } diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/SignUpScreen.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/SignUpScreen.kt index c4be6bd0..b3e98d26 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/SignUpScreen.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/SignUpScreen.kt @@ -73,7 +73,7 @@ fun SignUpScreen( modifier = Modifier .fillMaxSize() .background(White) - .padding(MMHorizontalSpacing), + .padding(horizontal = MMHorizontalSpacing), topBar = { Row( modifier = Modifier @@ -231,7 +231,6 @@ fun SignUpContent( ) ) }, - storeSchoolInfoExist = { viewModel.storeSchoolInfoExist(it) } ) } } diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/state/LoginState.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/state/LoginState.kt index a4d5a86f..ede8c01d 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/state/LoginState.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/state/LoginState.kt @@ -4,7 +4,7 @@ import com.moneymong.moneymong.common.base.State data class LoginState( val isClickable: Boolean = false, - val isSchoolInfoExist: Boolean? = null, + val isSchoolInfoProvided: Boolean? = null, val isLoginRequired: Boolean? = null, val visibleError : Boolean = false, val errorMessage : String = "" diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/util/KakaoLogin.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/util/KakaoLogin.kt new file mode 100644 index 00000000..23b9728f --- /dev/null +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/util/KakaoLogin.kt @@ -0,0 +1,67 @@ +package com.moneymong.moneymong.feature.sign.util + +import android.content.Context +import com.kakao.sdk.common.model.ClientError +import com.kakao.sdk.common.model.ClientErrorCause +import com.kakao.sdk.user.UserApiClient + +internal object KakaoLogin { + operator fun invoke( + context: Context, + onSuccess: (accessToken: String) -> Unit, + onFailure: (Throwable) -> Unit + ) { + if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) { + loginWithKakaoTalk( + context = context, + onSuccess = onSuccess, + onFailure = onFailure + ) + } else { + loginWithKakaoAccount( + context = context, + onSuccess = onSuccess, + onFailure = onFailure + ) + } + } + + private fun loginWithKakaoTalk( + context: Context, + onSuccess: (accessToken: String) -> Unit, + onFailure: (Throwable) -> Unit + ) { + UserApiClient.instance.loginWithKakaoTalk(context) inner@{ token, error -> + if (error != null) { + // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우, + // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기) + if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { + onFailure(error) + return@inner + } + // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인 시도 + loginWithKakaoAccount( + context = context, + onSuccess = onSuccess, + onFailure = onFailure + ) + } else if (token != null) { + onSuccess(token.accessToken) + } + } + } + + private fun loginWithKakaoAccount( + context: Context, + onSuccess: (accessToken: String) -> Unit, + onFailure: (Throwable) -> Unit + ) { + UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> + if (error != null) { + onFailure(error) + } else if (token != null) { + onSuccess(token.accessToken) + } + } + } +} \ No newline at end of file diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/KakaoLoginView.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/KakaoLoginView.kt index 4021d6b7..79768098 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/KakaoLoginView.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/KakaoLoginView.kt @@ -2,35 +2,24 @@ package com.moneymong.moneymong.feature.sign.view import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp import com.moneymong.moneymong.design_system.R @Composable fun KakaoLoginView( modifier: Modifier = Modifier, - onLoginButtonClicked: () -> Unit + loginByKaKao: () -> Unit ) { - Column( - modifier = modifier - ) { - Image( - modifier = Modifier - .fillMaxWidth() - .clickable { - onLoginButtonClicked() - }, - painter = painterResource(id = R.drawable.img_kakao_login), - contentDescription = null, - contentScale = ContentScale.FillWidth, - ) - } -} + Image( + modifier = modifier.clickable { + loginByKaKao() + }, + painter = painterResource(id = R.drawable.img_kakao_login), + contentDescription = "LoginByKakao", + contentScale = ContentScale.FillWidth, + ) +} \ No newline at end of file diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpButtonView.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpButtonView.kt index 46bc3930..6b83058b 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpButtonView.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpButtonView.kt @@ -4,33 +4,36 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import com.moneymong.moneymong.common.ui.noRippleClickable import com.moneymong.moneymong.design_system.component.button.MDSButton import com.moneymong.moneymong.design_system.component.button.MDSButtonSize import com.moneymong.moneymong.design_system.component.button.MDSButtonType import com.moneymong.moneymong.design_system.error.ErrorDialog +import com.moneymong.moneymong.design_system.theme.Blue04 +import com.moneymong.moneymong.design_system.theme.Body3 @Composable fun SignUpButtonView( - modifier: Modifier = Modifier , - isEnabled : Boolean, - visiblePopUpError : Boolean, - popUpErrorMessage : String, - visiblePopUpErrorChanged : (Boolean) -> Unit, - onCreateUniversity : () -> Unit, - storeSchoolInfoExist : (Boolean) -> Unit + modifier: Modifier = Modifier, + isEnabled: Boolean, + visiblePopUpError: Boolean, + popUpErrorMessage: String, + visiblePopUpErrorChanged: (Boolean) -> Unit, + onCreateUniversity: () -> Unit, ) { - if(visiblePopUpError){ + if (visiblePopUpError) { ErrorDialog( message = popUpErrorMessage, onConfirm = { visiblePopUpErrorChanged(false) } ) - } - else{ + } else { Column( modifier = modifier ) { @@ -38,14 +41,27 @@ fun SignUpButtonView( modifier = Modifier.fillMaxWidth(), onClick = { onCreateUniversity() - storeSchoolInfoExist(true) }, text = "가입하기", type = MDSButtonType.PRIMARY, size = MDSButtonSize.LARGE, enabled = isEnabled ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(16.dp)) + Text( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable { + onCreateUniversity() + }, + textAlign = TextAlign.Center, + text = "입력할 대학 정보가 없어요", + color = Blue04, + style = Body3 + ) + + Spacer(modifier = Modifier.height(28.dp)) + } } diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpTitleView.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpTitleView.kt index 0546e5c9..dad5d7e5 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpTitleView.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/view/SignUpTitleView.kt @@ -21,13 +21,13 @@ fun SignUpTitleView(modifier: Modifier = Modifier, subTitleState: Boolean) { horizontalAlignment = Alignment.Start ) { Text( - text = "회원가입을 진행해주세요", + text = "대학정보를 알려주세요!", style = Heading2, color = Black ) Text( modifier = Modifier.padding(top= 8.dp), - text = "아래 항목들을 정확히 채워주세요", + text = "학교 이름과 학년을 선택해주세요.", style = Body3, color = if (!subTitleState) Gray06.copy(alpha = 0.4f) else Gray06 diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/LoginViewModel.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/LoginViewModel.kt index 2fab88ed..795dd167 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/LoginViewModel.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/LoginViewModel.kt @@ -1,68 +1,72 @@ package com.moneymong.moneymong.feature.sign.viewmodel -import androidx.lifecycle.viewModelScope import com.moneymong.moneymong.common.base.BaseViewModel -import com.moneymong.moneymong.domain.util.LoginCallback -import com.moneymong.moneymong.domain.usecase.login.LoginUseCase -import com.moneymong.moneymong.domain.usecase.login.TokenUseCase -import dagger.hilt.android.lifecycle.HiltViewModel +import com.moneymong.moneymong.domain.usecase.token.PostAccessTokenUseCase +import com.moneymong.moneymong.domain.usecase.token.TokenUseCase import com.moneymong.moneymong.feature.sign.sideeffect.LoginSideEffect import com.moneymong.moneymong.feature.sign.state.LoginState +import com.moneymong.moneymong.model.sign.LoginType import com.moneymong.moneymong.network.util.TokenCallback -import kotlinx.coroutines.launch +import dagger.hilt.android.lifecycle.HiltViewModel import org.orbitmvi.orbit.syntax.simple.intent import org.orbitmvi.orbit.syntax.simple.reduce import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( - private val loginUseCase: LoginUseCase, private val tokenUseCase: TokenUseCase, + private val postAccessTokenUseCase: PostAccessTokenUseCase ) : BaseViewModel(LoginState()), TokenCallback { - fun onLoginButtonClicked() = intent { - viewModelScope.launch { - loginUseCase.invoke(object : LoginCallback { - override suspend fun onLoginSuccess() { - tokenUseCase.getSchoolInfo() - .onSuccess { - if(it) { - reduce { - state.copy( - isSchoolInfoExist = true - ) - } - } - else{ - reduce { - state.copy( - isSchoolInfoExist = false - ) - } - } - }.onFailure { - reduce { - state.copy( - visibleError = true, - errorMessage = "문제가 발생했습니다.\n다시 시도해주세요" - ) - } - } - } + fun onKakaoLoginSuccess(accessToken: String) = intent { + postAccessTokenUseCase(type = LoginType.KAKAO, accessToken = accessToken).onSuccess { + getSchoolInfo() + }.onFailure { + reduce { + state.copy( + visibleError = true, + errorMessage = "문제가 발생했습니다.\n다시 시도해주세요" + ) + } + } + } - override suspend fun onLoginFailure(exception: Exception) { - // 로그인 실패 - reduce { - state.copy( - visibleError = true, - errorMessage = "문제가 발생했습니다.\n다시 시도해주세요" - ) - } + fun onKakaoLoginFailure(throwable: Throwable) = intent { + reduce { + state.copy( + visibleError = true, + errorMessage = "문제가 발생했습니다.\n다시 시도해주세요" + ) + } + } + + + private fun getSchoolInfo() = intent { + tokenUseCase.getSchoolInfo().onSuccess { result -> + if (result) { + reduce { + state.copy( + isSchoolInfoProvided = true + ) + } + } else { + reduce { + state.copy( + isSchoolInfoProvided = false + ) } - }) + } + }.onFailure { + reduce { + state.copy( + visibleError = true, + errorMessage = "문제가 발생했습니다.\n다시 시도해주세요" + ) + } } } + override suspend fun onTokenFailure() { intent { reduce { @@ -81,7 +85,7 @@ class LoginViewModel @Inject constructor( } } - fun visibleErrorChanged(isVisibleError: Boolean) = intent{ + fun visibleErrorChanged(isVisibleError: Boolean) = intent { reduce { state.copy( visibleError = isVisibleError, @@ -89,10 +93,10 @@ class LoginViewModel @Inject constructor( } } - fun isSchoolInfoExistChanged(isSchoolInfoExist : Boolean?) = intent{ + fun isSchoolInfoProvidedChanged(isSchoolInfoProvided: Boolean?) = intent { reduce { state.copy( - isSchoolInfoExist = isSchoolInfoExist + isSchoolInfoProvided = isSchoolInfoProvided ) } } diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SignUpViewModel.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SignUpViewModel.kt index 5ccca470..85fe8a43 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SignUpViewModel.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SignUpViewModel.kt @@ -1,9 +1,11 @@ package com.moneymong.moneymong.feature.sign.viewmodel import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.viewModelScope import com.moneymong.moneymong.common.base.BaseViewModel import com.moneymong.moneymong.domain.usecase.signup.SchoolInfoUseCase -import com.moneymong.moneymong.domain.usecase.signup.UnivUseCase +import com.moneymong.moneymong.domain.usecase.university.CreateUniversityUseCase +import com.moneymong.moneymong.domain.usecase.university.SearchUniversityUseCase import com.moneymong.moneymong.feature.sign.sideeffect.SignUpSideEffect import com.moneymong.moneymong.feature.sign.state.SignUpState import com.moneymong.moneymong.feature.sign.util.Grade @@ -20,13 +22,15 @@ import javax.inject.Inject @HiltViewModel class SignUpViewModel @Inject constructor( - private val univUseCase: UnivUseCase, - private val schoolInfoUseCase : SchoolInfoUseCase, + private val createUniversityUseCase: CreateUniversityUseCase, + private val searchUniversityUseCase: SearchUniversityUseCase, + private val schoolInfoUseCase: SchoolInfoUseCase, ) : BaseViewModel(SignUpState()) { - fun createUniv(universityName: String, grade: Int) = intent { + fun createUniv(universityName: String?, grade: Int?) = intent { val body = UnivRequest(universityName, grade) - univUseCase.createUniv(body) + createUniversityUseCase(body) .onSuccess { + storeSchoolInfoProvided(true) reduce { state.copy( isUnivCreated = true @@ -44,7 +48,7 @@ class SignUpViewModel @Inject constructor( } fun searchUniv(searchQuery: String) = intent { - univUseCase.searchUniv(searchQuery) + searchUniversityUseCase(searchQuery) .onSuccess { reduce { state.copy( @@ -61,8 +65,8 @@ class SignUpViewModel @Inject constructor( } } - fun storeSchoolInfoExist(infoExist : Boolean ){ - CoroutineScope(Dispatchers.IO).launch { + fun storeSchoolInfoProvided(infoExist : Boolean ){ + viewModelScope.launch { schoolInfoUseCase.invoke(infoExist) } } @@ -149,16 +153,16 @@ class SignUpViewModel @Inject constructor( } } - fun visiblePopUpErrorChanged(visiblePopUpError : Boolean) = intent{ - reduce{ + fun visiblePopUpErrorChanged(visiblePopUpError: Boolean) = intent { + reduce { state.copy( visiblePopUpError = visiblePopUpError, ) } } - fun visibleErrorChanged(visibleError : Boolean) = intent{ - reduce{ + fun visibleErrorChanged(visibleError: Boolean) = intent { + reduce { state.copy( visibleError = visibleError, ) @@ -166,8 +170,8 @@ class SignUpViewModel @Inject constructor( } - fun isButtonVisibleChanged(isButtonVisible : Boolean) = intent{ - reduce{ + fun isButtonVisibleChanged(isButtonVisible: Boolean) = intent { + reduce { state.copy( isButtonVisible = isButtonVisible ) diff --git a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SplashViewModel.kt b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SplashViewModel.kt index 0cdc55f1..1c0e0098 100644 --- a/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SplashViewModel.kt +++ b/feature/sign/src/main/java/com/moneymong/moneymong/feature/sign/viewmodel/SplashViewModel.kt @@ -2,7 +2,7 @@ package com.moneymong.moneymong.feature.sign.viewmodel import android.util.Log import com.moneymong.moneymong.common.base.BaseViewModel -import com.moneymong.moneymong.domain.usecase.login.TokenUseCase +import com.moneymong.moneymong.domain.usecase.token.TokenUseCase import com.moneymong.moneymong.feature.sign.sideeffect.SplashSideEffect import com.moneymong.moneymong.feature.sign.state.SplashState import dagger.hilt.android.lifecycle.HiltViewModel @@ -28,8 +28,8 @@ class SplashViewModel @Inject constructor( fun checkTokenValidity() = intent { tokenUseCase.getDataStoreInfo() .onSuccess { - Log.d("Splash", "${it.accessToken}, ${it.schoolInfoExist}") - if (it.accessToken.isNotEmpty() && it.schoolInfoExist) { + Log.d("Splash", "${it.accessToken}, ${it.schoolInfoProvided}") + if (it.accessToken.isNotEmpty() && it.schoolInfoProvided) { reduce { state.copy( isTokenValid = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a5becfeb..4bfe10cf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,6 +39,8 @@ material = "1.10.0" lifecycle-runtime-ktx = "2.6.2" constraintlayout = "2.1.4" paging = "3.2.1" +kakao-sdk = "2.20.3" + [libraries] accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } @@ -98,8 +100,7 @@ ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" } navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" } -kakao-kakaolink = { group = "com.kakao.sdk", name = "kakaolink", version = "1.30.5" } -kakao-v2-user = { group = "com.kakao.sdk", name = "v2-user", version = "2.5.0" } +kakao-v2-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-sdk" } paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" } paging-common = { group = "androidx.paging", name = "paging-common", version.ref = "paging" }