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/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/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/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/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/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/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/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/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/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..d8c5685b 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 @@ -333,7 +333,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..33765868 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,5 +1,6 @@ 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 @@ -14,6 +15,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 @@ -69,6 +71,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 +93,8 @@ class LedgerViewModel @Inject constructor( page = 0, limit = 100 ).onSuccess { + Log.d("fetchLedgerTransactionList${state.existAgency}",it.toString() ) + reduce { state.copy( ledgerTransaction = it, @@ -97,11 +102,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 +124,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 +157,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) } } } @@ -216,4 +240,13 @@ class LedgerViewModel @Inject constructor( 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..be6ce479 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, + changeAgency: (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, + changeAgency, + 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/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 aa236dd5..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 @@ -4,7 +4,8 @@ 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 @@ -21,12 +22,13 @@ 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 { val body = UnivRequest(universityName, grade) - univUseCase.createUniv(body) + createUniversityUseCase(body) .onSuccess { storeSchoolInfoProvided(true) reduce { @@ -46,7 +48,7 @@ class SignUpViewModel @Inject constructor( } fun searchUniv(searchQuery: String) = intent { - univUseCase.searchUniv(searchQuery) + searchUniversityUseCase(searchQuery) .onSuccess { reduce { state.copy( @@ -151,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, ) @@ -168,8 +170,8 @@ class SignUpViewModel @Inject constructor( } - fun isButtonVisibleChanged(isButtonVisible : Boolean) = intent{ - reduce{ + fun isButtonVisibleChanged(isButtonVisible: Boolean) = intent { + reduce { state.copy( isButtonVisible = isButtonVisible )