From f76107fe86350d960cffb54be954e73e9c01e53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 8 Sep 2022 23:06:58 +0900 Subject: [PATCH 01/12] =?UTF-8?q?#51=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=9A=94=EC=B2=AD=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20BottomSheetDialog=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cheocharm/data/repository/GroupRepositoryImpl.kt | 3 ++- .../cheocharm/data/source/GroupRemoteDataSource.kt | 3 ++- .../main/java/com/cheocharm/domain/model/GroupJoin.kt | 6 ++++++ .../cheocharm/domain/repository/GroupRepository.kt | 3 ++- .../domain/usecase/group/JoinGroupUseCase.kt | 3 ++- .../presentation/ui/search/SearchViewModel.kt | 5 +++-- .../main/java/com/cheocharm/remote/api/GroupApi.kt | 3 ++- .../java/com/cheocharm/remote/mapper/GroupMapper.kt | 7 +++++++ .../remote/model/response/group/GroupJoinResponse.kt | 6 ++++++ .../remote/source/GroupRemoteDataSourceImpl.kt | 11 ++++++++--- 10 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 domain/src/main/java/com/cheocharm/domain/model/GroupJoin.kt create mode 100644 remote/src/main/java/com/cheocharm/remote/model/response/group/GroupJoinResponse.kt diff --git a/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt b/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt index 4cfac9eb..c2380b53 100644 --- a/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt @@ -5,6 +5,7 @@ import com.cheocharm.data.error.ErrorData import com.cheocharm.data.error.toDomain import com.cheocharm.data.source.GroupRemoteDataSource import com.cheocharm.domain.model.Group +import com.cheocharm.domain.model.GroupJoin import com.cheocharm.domain.repository.GroupRepository import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -17,7 +18,7 @@ class GroupRepositoryImpl @Inject constructor( return groupRemoteDataSource.fetchGroupSearchList(searchGroupName) } - override suspend fun joinGroup(groupName: String): Result { + override suspend fun joinGroup(groupName: String): Result { val result = groupRemoteDataSource.joinGroup(groupName) return when (val exception = result.exceptionOrNull()) { is ErrorData -> Result.failure(exception.toDomain()) diff --git a/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt b/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt index 61718f52..6b750189 100644 --- a/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt +++ b/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt @@ -2,11 +2,12 @@ package com.cheocharm.data.source import androidx.paging.PagingData import com.cheocharm.domain.model.Group +import com.cheocharm.domain.model.GroupJoin import kotlinx.coroutines.flow.Flow interface GroupRemoteDataSource { fun fetchGroupSearchList(searchGroupName: String): Flow> - suspend fun joinGroup(groupName: String): Result + suspend fun joinGroup(groupName: String): Result } diff --git a/domain/src/main/java/com/cheocharm/domain/model/GroupJoin.kt b/domain/src/main/java/com/cheocharm/domain/model/GroupJoin.kt new file mode 100644 index 00000000..cc683fb9 --- /dev/null +++ b/domain/src/main/java/com/cheocharm/domain/model/GroupJoin.kt @@ -0,0 +1,6 @@ +package com.cheocharm.domain.model + +data class GroupJoin( + val alreadyJoin: Boolean, + val status: String +) diff --git a/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt b/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt index dfef6d25..5f219f08 100644 --- a/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt +++ b/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt @@ -2,11 +2,12 @@ package com.cheocharm.domain.repository import androidx.paging.PagingData import com.cheocharm.domain.model.Group +import com.cheocharm.domain.model.GroupJoin import kotlinx.coroutines.flow.Flow interface GroupRepository { fun searchGroup(searchGroupName: String): Flow> - suspend fun joinGroup(groupName: String): Result + suspend fun joinGroup(groupName: String): Result } diff --git a/domain/src/main/java/com/cheocharm/domain/usecase/group/JoinGroupUseCase.kt b/domain/src/main/java/com/cheocharm/domain/usecase/group/JoinGroupUseCase.kt index 9f0bf413..651f9251 100644 --- a/domain/src/main/java/com/cheocharm/domain/usecase/group/JoinGroupUseCase.kt +++ b/domain/src/main/java/com/cheocharm/domain/usecase/group/JoinGroupUseCase.kt @@ -1,12 +1,13 @@ package com.cheocharm.domain.usecase.group +import com.cheocharm.domain.model.GroupJoin import com.cheocharm.domain.repository.GroupRepository import javax.inject.Inject class JoinGroupUseCase @Inject constructor( private val groupRepository: GroupRepository ) { - suspend operator fun invoke(groupName: String): Result { + suspend operator fun invoke(groupName: String): Result { return groupRepository.joinGroup(groupName) } } diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt index 12d0ff5c..65761374 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt @@ -51,8 +51,9 @@ class SearchViewModel @Inject constructor( viewModelScope.launch { selectedGroup.value?.name?.let { joinGroupUseCase.invoke(it) - .onSuccess { - _searchGroupJoinBottom.value = Event(Unit) + .onSuccess { groupJoin -> + if (groupJoin.alreadyJoin.not()) _searchGroupJoinBottom.value = Event(Unit) + else setToastMessage("그룹 가입 요청 대기중입니다.") }.onFailure { throwable -> when (throwable) { is Error.JoinGroupUnavailable -> setToastMessage(throwable.message) diff --git a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt index 997cd516..48faa010 100644 --- a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt +++ b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt @@ -1,6 +1,7 @@ package com.cheocharm.remote.api import com.cheocharm.remote.model.BaseResponse +import com.cheocharm.remote.model.response.group.GroupJoinResponse import com.cheocharm.remote.model.response.group.GroupSearchResponse import retrofit2.http.Body import retrofit2.http.GET @@ -16,5 +17,5 @@ interface GroupApi { ): BaseResponse @POST("group/join") - suspend fun joinGroup(@Body body: HashMap): BaseResponse + suspend fun joinGroup(@Body body: HashMap): BaseResponse } diff --git a/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt b/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt index d1fcdc01..38a176ef 100644 --- a/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt +++ b/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt @@ -1,8 +1,10 @@ package com.cheocharm.remote.mapper import com.cheocharm.domain.model.Group +import com.cheocharm.domain.model.GroupJoin import com.cheocharm.domain.model.GroupMember import com.cheocharm.domain.model.GroupSearch +import com.cheocharm.remote.model.response.group.GroupJoinResponse import com.cheocharm.remote.model.response.group.GroupSearchResponse // remote -> domain @@ -15,3 +17,8 @@ internal fun GroupSearchResponse.toDomain(): GroupSearch { } return GroupSearch(hasNextPage, groupResultList) } + +// remote -> domain +internal fun GroupJoinResponse.toDomain(): GroupJoin { + return GroupJoin(alreadyJoin, status) +} diff --git a/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupJoinResponse.kt b/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupJoinResponse.kt new file mode 100644 index 00000000..f5c14181 --- /dev/null +++ b/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupJoinResponse.kt @@ -0,0 +1,6 @@ +package com.cheocharm.remote.model.response.group + +data class GroupJoinResponse( + val alreadyJoin: Boolean, + val status: String +) diff --git a/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt b/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt index d1acb7ba..b8b20db8 100644 --- a/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt +++ b/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt @@ -6,7 +6,9 @@ import androidx.paging.PagingData import com.cheocharm.data.error.ErrorData import com.cheocharm.data.source.GroupRemoteDataSource import com.cheocharm.domain.model.Group +import com.cheocharm.domain.model.GroupJoin import com.cheocharm.remote.api.GroupApi +import com.cheocharm.remote.mapper.toDomain import kotlinx.coroutines.flow.Flow import java.net.UnknownHostException import javax.inject.Inject @@ -21,15 +23,18 @@ class GroupRemoteDataSourceImpl @Inject constructor( }.flow } - override suspend fun joinGroup(groupName: String): Result { + override suspend fun joinGroup(groupName: String): Result { val result = runCatching { groupApi.joinGroup(hashMapOf("groupName" to groupName)) } return when (val exception = result.exceptionOrNull()) { null -> { val response = result.getOrNull() ?: return Result.failure(Throwable(NullPointerException())) - if (response.statusCode == 200) Result.success(Unit) - else Result.failure(ErrorData.JoinGroupUnavailable(response.message)) + Result.success( + response.data?.toDomain() ?: return Result.failure( + ErrorData.JoinGroupUnavailable(response.message) + ) + ) } is UnknownHostException -> Result.failure(ErrorData.NetworkUnavailable) else -> Result.failure(exception) From 971e1243077734888fdde9705d8598ae902e42fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 8 Sep 2022 23:08:20 +0900 Subject: [PATCH 02/12] =?UTF-8?q?fix:=20=EC=84=9C=EB=B2=84=EC=9D=98=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EA=B2=80=EC=83=89=20API=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20query=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=9D=B4=EB=A6=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt index 48faa010..a9e41ee7 100644 --- a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt +++ b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt @@ -13,7 +13,7 @@ interface GroupApi { @GET("group") suspend fun fetchGroupSearchList( @Query("page") page: Int, - @Query("searchName") searchName: String + @Query("groupName") searchName: String ): BaseResponse @POST("group/join") From 389d7f83156d2c9c5a0b3fe6416c83c90f702d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 8 Sep 2022 23:41:09 +0900 Subject: [PATCH 03/12] =?UTF-8?q?#51=20fix:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=9A=94=EC=B2=AD=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cheocharm/presentation/ui/search/SearchViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt index 65761374..967a465d 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchViewModel.kt @@ -53,7 +53,7 @@ class SearchViewModel @Inject constructor( joinGroupUseCase.invoke(it) .onSuccess { groupJoin -> if (groupJoin.alreadyJoin.not()) _searchGroupJoinBottom.value = Event(Unit) - else setToastMessage("그룹 가입 요청 대기중입니다.") + else setToastMessage(groupJoin.status) }.onFailure { throwable -> when (throwable) { is Error.JoinGroupUnavailable -> setToastMessage(throwable.message) From 21fc015322960c6ef77017942045963595c88176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Fri, 9 Sep 2022 12:13:16 +0900 Subject: [PATCH 04/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=ED=99=94=EB=A9=B4=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20ui=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 4 + .../ui/group/GroupCreateActivity.kt | 69 +++++++++++ .../ui/group/GroupCreateViewModel.kt | 12 ++ .../presentation/ui/search/SearchFragment.kt | 10 ++ .../src/main/res/drawable/bg_cursor.xml | 2 +- .../res/drawable/bg_outlined_radius_10dp.xml | 7 ++ .../main/res/layout/activity_group_create.xml | 107 ++++++++++++++++++ presentation/src/main/res/values/dimens.xml | 3 + presentation/src/main/res/values/strings.xml | 7 ++ 9 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt create mode 100644 presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt create mode 100644 presentation/src/main/res/drawable/bg_outlined_radius_10dp.xml create mode 100644 presentation/src/main/res/layout/activity_group_create.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d306d60..dbdfe748 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,6 +32,10 @@ + + diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt new file mode 100644 index 00000000..250cf37a --- /dev/null +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt @@ -0,0 +1,69 @@ +package com.cheocharm.presentation.ui.group + +import android.content.Context +import android.graphics.Rect +import android.os.Bundle +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.MotionEvent +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import com.cheocharm.presentation.R +import com.cheocharm.presentation.base.BaseActivity +import com.cheocharm.presentation.databinding.ActivityGroupCreateBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class GroupCreateActivity : + BaseActivity(R.layout.activity_group_create) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + with(binding.toolbarGroupCreate) { + setSupportActionBar(this) + setNavigationIcon(R.drawable.ic_back) + setNavigationOnClickListener { + finish() + } + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_location, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.location_confirm -> { + // TODO: 저 버튼을 누르면 어떻게 되나..? + Log.d("GroupCreateActivity", "confirm clicked") + true + } + else -> return super.onOptionsItemSelected(item) + } + } + + override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { + hideKeyboardWhenOutsideTouched(ev) + return super.dispatchTouchEvent(ev) + } + + private fun hideKeyboardWhenOutsideTouched(ev: MotionEvent?) { + if (ev?.action == MotionEvent.ACTION_DOWN) { + val v = currentFocus + if (v is EditText) { + val rect = Rect() + v.getGlobalVisibleRect(rect) + if (!rect.contains(ev.rawX.toInt(), ev.rawY.toInt())) { + v.clearFocus() + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(v.windowToken, 0) + } + } + } + } +} diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt new file mode 100644 index 00000000..147b34f1 --- /dev/null +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt @@ -0,0 +1,12 @@ +package com.cheocharm.presentation.ui.group + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class GroupCreateViewModel @Inject constructor( + +) : ViewModel() { + +} diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchFragment.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchFragment.kt index 821c8623..9df89905 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchFragment.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/search/SearchFragment.kt @@ -1,5 +1,6 @@ package com.cheocharm.presentation.ui.search +import android.content.Intent import android.os.Bundle import android.view.View import android.view.inputmethod.EditorInfo @@ -11,6 +12,7 @@ import com.cheocharm.presentation.R import com.cheocharm.presentation.databinding.ActivityMainBinding import com.cheocharm.presentation.databinding.FragmentSearchBinding import com.cheocharm.presentation.ui.MainActivity +import com.cheocharm.presentation.ui.group.GroupCreateActivity import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -27,6 +29,7 @@ class SearchFragment : BaseFragment(R.layout.fragment_sea mainActivityBinding = (activity as MainActivity).getBinding() initEditTexts() + initButtons() initObservers() } @@ -49,6 +52,13 @@ class SearchFragment : BaseFragment(R.layout.fragment_sea } } + private fun initButtons() { + binding.btnSearchGroupCreate.setOnClickListener { + val intent = Intent(requireActivity(), GroupCreateActivity::class.java) + startActivity(intent) + } + } + private fun initObservers() { } diff --git a/presentation/src/main/res/drawable/bg_cursor.xml b/presentation/src/main/res/drawable/bg_cursor.xml index 01a99f38..4956f243 100644 --- a/presentation/src/main/res/drawable/bg_cursor.xml +++ b/presentation/src/main/res/drawable/bg_cursor.xml @@ -1,5 +1,5 @@ - + diff --git a/presentation/src/main/res/drawable/bg_outlined_radius_10dp.xml b/presentation/src/main/res/drawable/bg_outlined_radius_10dp.xml new file mode 100644 index 00000000..1cb93815 --- /dev/null +++ b/presentation/src/main/res/drawable/bg_outlined_radius_10dp.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/presentation/src/main/res/layout/activity_group_create.xml b/presentation/src/main/res/layout/activity_group_create.xml new file mode 100644 index 00000000..36c43cc9 --- /dev/null +++ b/presentation/src/main/res/layout/activity_group_create.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/values/dimens.xml b/presentation/src/main/res/values/dimens.xml index bc9960e9..17ab9bc1 100644 --- a/presentation/src/main/res/values/dimens.xml +++ b/presentation/src/main/res/values/dimens.xml @@ -45,4 +45,7 @@ 120dp 10dp 15dp + + 37dp + 2dp diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 3edb8562..bc23f9ab 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -63,4 +63,11 @@ 그룹 가입하기 %s의 그룹장에게\n가입요청이 전달되었습니다. 가입이 승인되면 홈에서\n가입된 그룹을 볼 수 있어요! + + 그룹 만들기 + 그룹 이름 입력 + 그룹의 소개글을 간략하게 적어주세요. + 검색 비허용 + 그룹이 검색결과로 뜨지 않습니다.\n오직 그룹장의 초대로 멤버가 들어올 수 있습니다.진 + 사진 추가 From 5dd10b727e3b8e55a6f55e933d6cae3d83d7ad59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Tue, 24 Jan 2023 21:02:58 +0900 Subject: [PATCH 05/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20Activity=20=EB=B0=8F=20navigation=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/GroupCreateFragment.kt | 23 ++++ .../main/res/layout/activity_group_create.xml | 89 ++------------- .../main/res/layout/fragment_group_create.xml | 103 ++++++++++++++++++ .../src/main/res/navigation/nav_group.xml | 13 +++ 4 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt create mode 100644 presentation/src/main/res/layout/fragment_group_create.xml create mode 100644 presentation/src/main/res/navigation/nav_group.xml diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt new file mode 100644 index 00000000..72ac2992 --- /dev/null +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt @@ -0,0 +1,23 @@ +package com.cheocharm.presentation.ui.group + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.activityViewModels +import com.cheocharm.presentation.R +import com.cheocharm.presentation.base.BaseFragment +import com.cheocharm.presentation.databinding.FragmentGroupCreateBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class GroupCreateFragment : + BaseFragment(R.layout.fragment_group_create) { + + private val groupCreateViewModel: GroupCreateViewModel by activityViewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.viewmodel = groupCreateViewModel + + } +} diff --git a/presentation/src/main/res/layout/activity_group_create.xml b/presentation/src/main/res/layout/activity_group_create.xml index 36c43cc9..79207308 100644 --- a/presentation/src/main/res/layout/activity_group_create.xml +++ b/presentation/src/main/res/layout/activity_group_create.xml @@ -19,89 +19,14 @@ app:menu="@menu/menu_location" app:title="@string/group_create_title" /> - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@id/toolbar_group_create" + app:navGraph="@navigation/nav_group" /> diff --git a/presentation/src/main/res/layout/fragment_group_create.xml b/presentation/src/main/res/layout/fragment_group_create.xml new file mode 100644 index 00000000..c8df3a89 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_group_create.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/navigation/nav_group.xml b/presentation/src/main/res/navigation/nav_group.xml new file mode 100644 index 00000000..3e6b5833 --- /dev/null +++ b/presentation/src/main/res/navigation/nav_group.xml @@ -0,0 +1,13 @@ + + + + + From 0b0c1d1864966b8577ec7e80174a41c692255fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Sat, 4 Feb 2023 10:51:11 +0900 Subject: [PATCH 06/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cheocharm/remote/api/GroupApi.kt | 35 ++++++++++++++++--- .../response/group/GroupMemberResponse.kt | 8 +++++ .../remote/source/GroupSearchPagingSource.kt | 2 +- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 remote/src/main/java/com/cheocharm/remote/model/response/group/GroupMemberResponse.kt diff --git a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt index a9e41ee7..35bfbd6d 100644 --- a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt +++ b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt @@ -2,20 +2,45 @@ package com.cheocharm.remote.api import com.cheocharm.remote.model.BaseResponse import com.cheocharm.remote.model.response.group.GroupJoinResponse +import com.cheocharm.remote.model.response.group.GroupMemberResponse +import com.cheocharm.remote.model.response.group.GroupResponse import com.cheocharm.remote.model.response.group.GroupSearchResponse -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.POST -import retrofit2.http.Query +import okhttp3.RequestBody +import retrofit2.http.* interface GroupApi { @GET("group") suspend fun fetchGroupSearchList( @Query("page") page: Int, - @Query("groupName") searchName: String + @Query("groupName") searchName: String, + @Query("cursorId") cursorId: Int ): BaseResponse + @POST("group") + suspend fun createGroup(@Body body: RequestBody): BaseResponse + @POST("group/join") suspend fun joinGroup(@Body body: HashMap): BaseResponse + + @PATCH("group/join") + suspend fun modifyJoinGroup(@Body body: RequestBody): BaseResponse + + @POST("group/invite") + suspend fun inviteGroup(@Body body: RequestBody): BaseResponse + + @PATCH("group/status") + suspend fun modifyGroupStatus(@Body body: RequestBody): BaseResponse + + @PATCH("group/exit") + suspend fun exitGroup(@Body body: RequestBody): BaseResponse + + @PATCH("group/chief") + suspend fun modifyChief(@Body body: RequestBody): BaseResponse + + @GET("group/mygroup") + suspend fun fetchMyGroup(): BaseResponse> + + @GET("group/member") + suspend fun fetchGroupMember(@Query("groupId") groupId: Int): BaseResponse> } diff --git a/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupMemberResponse.kt b/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupMemberResponse.kt new file mode 100644 index 00000000..5ef1cd13 --- /dev/null +++ b/remote/src/main/java/com/cheocharm/remote/model/response/group/GroupMemberResponse.kt @@ -0,0 +1,8 @@ +package com.cheocharm.remote.model.response.group + +data class GroupMemberResponse( + val userName: String, + val userImageUrl: String, + val userId: Int, + val invitationStatus: String +) diff --git a/remote/src/main/java/com/cheocharm/remote/source/GroupSearchPagingSource.kt b/remote/src/main/java/com/cheocharm/remote/source/GroupSearchPagingSource.kt index 63ef4c69..1a7fa5a5 100644 --- a/remote/src/main/java/com/cheocharm/remote/source/GroupSearchPagingSource.kt +++ b/remote/src/main/java/com/cheocharm/remote/source/GroupSearchPagingSource.kt @@ -22,7 +22,7 @@ class GroupSearchPagingSource( override suspend fun load(params: LoadParams): LoadResult { val start = params.key ?: 0 - val result = runCatching { groupApi.fetchGroupSearchList(start, groupSearchName) } + val result = runCatching { groupApi.fetchGroupSearchList(start, groupSearchName, 0) } return when (val exception = result.exceptionOrNull()) { null -> { From c36def454fb7d68f709cb149b7b0026e3dc3baaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 23 Feb 2023 21:13:30 +0900 Subject: [PATCH 07/12] =?UTF-8?q?chore:=20circle=20image=20view=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/presentation/build.gradle b/presentation/build.gradle index 2c49d378..8b7cbceb 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -80,4 +80,7 @@ dependencies { // paging implementation "androidx.paging:paging-runtime:$pagingVersion" + + // circle imageview + implementation 'de.hdodenhof:circleimageview:3.1.0' } From 0650150316ae2ad18a06a6dc71bd9792720de5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 23 Feb 2023 21:15:55 +0900 Subject: [PATCH 08/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=9B=84=20=EC=9D=B4=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=B0=BE=EA=B8=B0=20=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/GroupCreateSearchFragment.kt | 15 +++++++++++++++ .../res/layout/fragment_group_create_search.xml | 16 ++++++++++++++++ .../src/main/res/navigation/nav_group.xml | 11 ++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateSearchFragment.kt create mode 100644 presentation/src/main/res/layout/fragment_group_create_search.xml diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateSearchFragment.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateSearchFragment.kt new file mode 100644 index 00000000..f75acacf --- /dev/null +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateSearchFragment.kt @@ -0,0 +1,15 @@ +package com.cheocharm.presentation.ui.group + +import androidx.fragment.app.activityViewModels +import com.cheocharm.presentation.R +import com.cheocharm.presentation.base.BaseFragment +import com.cheocharm.presentation.databinding.FragmentGroupCreateSearchBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class GroupCreateSearchFragment : + BaseFragment(R.layout.fragment_group_create_search) { + + private val groupCreateViewModel by activityViewModels() + +} diff --git a/presentation/src/main/res/layout/fragment_group_create_search.xml b/presentation/src/main/res/layout/fragment_group_create_search.xml new file mode 100644 index 00000000..e3fe9390 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_group_create_search.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/presentation/src/main/res/navigation/nav_group.xml b/presentation/src/main/res/navigation/nav_group.xml index 3e6b5833..83d6be40 100644 --- a/presentation/src/main/res/navigation/nav_group.xml +++ b/presentation/src/main/res/navigation/nav_group.xml @@ -9,5 +9,14 @@ android:id="@+id/groupCreateFragment" android:name="com.cheocharm.presentation.ui.group.GroupCreateFragment" android:label="GroupCreateFragment" - tools:layout="@layout/fragment_group_create" /> + tools:layout="@layout/fragment_group_create"> + + + From 74e7c3821e0906db20dcfe9b6c5d6f1a9e7db1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 23 Feb 2023 21:16:59 +0900 Subject: [PATCH 09/12] =?UTF-8?q?#53=20feat:=20=EB=A7=B5=EC=A7=80=20?= =?UTF-8?q?=EC=8A=A4=EC=9C=84=EC=B9=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- presentation/src/main/res/color/switch_track.xml | 5 +++++ presentation/src/main/res/values/themes.xml | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 presentation/src/main/res/color/switch_track.xml diff --git a/presentation/src/main/res/color/switch_track.xml b/presentation/src/main/res/color/switch_track.xml new file mode 100644 index 00000000..8dfcf497 --- /dev/null +++ b/presentation/src/main/res/color/switch_track.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/presentation/src/main/res/values/themes.xml b/presentation/src/main/res/values/themes.xml index a9730e03..29f37866 100644 --- a/presentation/src/main/res/values/themes.xml +++ b/presentation/src/main/res/values/themes.xml @@ -36,4 +36,9 @@ @color/grey_dark @drawable/bg_cursor + + From 69883cf5d96f2aab542272df4e20dcf6b0fc23d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 23 Feb 2023 21:19:05 +0900 Subject: [PATCH 10/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=99=94=EB=A9=B4=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/group/GroupCreateActivity.kt | 33 ------- .../ui/group/GroupCreateFragment.kt | 87 +++++++++++++++++++ .../ui/group/GroupCreateViewModel.kt | 45 ++++++++++ .../main/res/layout/activity_group_create.xml | 13 +-- .../main/res/layout/fragment_group_create.xml | 60 +++++++++---- presentation/src/main/res/values/dimens.xml | 1 + presentation/src/main/res/values/strings.xml | 2 +- 7 files changed, 180 insertions(+), 61 deletions(-) diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt index 250cf37a..bc90d971 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateActivity.kt @@ -2,10 +2,6 @@ package com.cheocharm.presentation.ui.group import android.content.Context import android.graphics.Rect -import android.os.Bundle -import android.util.Log -import android.view.Menu -import android.view.MenuItem import android.view.MotionEvent import android.view.inputmethod.InputMethodManager import android.widget.EditText @@ -18,35 +14,6 @@ import dagger.hilt.android.AndroidEntryPoint class GroupCreateActivity : BaseActivity(R.layout.activity_group_create) { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - with(binding.toolbarGroupCreate) { - setSupportActionBar(this) - setNavigationIcon(R.drawable.ic_back) - setNavigationOnClickListener { - finish() - } - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - val inflater = menuInflater - inflater.inflate(R.menu.menu_location, menu) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.location_confirm -> { - // TODO: 저 버튼을 누르면 어떻게 되나..? - Log.d("GroupCreateActivity", "confirm clicked") - true - } - else -> return super.onOptionsItemSelected(item) - } - } - override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { hideKeyboardWhenOutsideTouched(ev) return super.dispatchTouchEvent(ev) diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt index 72ac2992..39829714 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt @@ -1,11 +1,24 @@ package com.cheocharm.presentation.ui.group +import android.app.Activity +import android.content.Intent +import android.graphics.ImageDecoder +import android.os.Build import android.os.Bundle +import android.provider.MediaStore +import android.util.Log import android.view.View +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.view.isVisible +import androidx.core.widget.doOnTextChanged import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController import com.cheocharm.presentation.R import com.cheocharm.presentation.base.BaseFragment import com.cheocharm.presentation.databinding.FragmentGroupCreateBinding +import com.cheocharm.presentation.util.UriUtil import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -13,11 +26,85 @@ class GroupCreateFragment : BaseFragment(R.layout.fragment_group_create) { private val groupCreateViewModel: GroupCreateViewModel by activityViewModels() + private lateinit var galleryImageLauncher: ActivityResultLauncher override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.viewmodel = groupCreateViewModel + initButtons() + initGalleryLauncher() + } + + private fun initButtons() { + binding.toolbarGroupCreate.setOnMenuItemClickListener { + when (it.itemId) { + R.id.location_confirm -> { + if (groupCreateViewModel.isGroupEnabled.value == true) findNavController().navigate( + R.id.action_groupCreateFragment_to_groupCreateSearchFragment + ) + true + } + else -> { + false + } + } + } + binding.toolbarGroupCreate.setNavigationOnClickListener { + requireActivity().finish() + } + binding.ivGroupCreateGroup.setOnClickListener { + selectGroupImage() + } + binding.etGroupCreateName.doOnTextChanged { text, start, before, count -> + groupCreateViewModel.setGroupName(text.toString()) + groupCreateViewModel.checkGroupNameVerified() + groupCreateViewModel.checkGroupEnabled() + } + binding.etGroupCreateBio.doOnTextChanged { text, start, before, count -> + groupCreateViewModel.setGroupBio(text.toString()) + groupCreateViewModel.checkGroupEnabled() + } + } + + private fun initGalleryLauncher() { + galleryImageLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK && activityResult.data != null) { + val imageUri = activityResult.data?.data + runCatching { + imageUri?.let { uri -> + val bitmap = if (Build.VERSION.SDK_INT < 28) { + MediaStore.Images.Media.getBitmap( + requireActivity().contentResolver, uri + ) + } else { + val source = ImageDecoder.createSource( + requireActivity().contentResolver, + uri + ) + ImageDecoder.decodeBitmap(source) + } + val file = UriUtil.getFileFromUri(requireActivity(), uri) + groupCreateViewModel.setGroupImage(file) + binding.ivGroupCreateGroup.setImageBitmap(bitmap) + binding.tvGroupCreateImage.isVisible = false + binding.ivGroupCreateImage.isVisible = false + } + }.onFailure { + Log.e("SignUpProfileFragment", "${it.message}") + } + } else if (activityResult.resultCode == Activity.RESULT_CANCELED) { + Toast.makeText(requireActivity(), "사진 선택 취소", Toast.LENGTH_SHORT).show() + } + } + } + + private fun selectGroupImage() { + val intent = Intent(Intent.ACTION_PICK).apply { + type = "image/*" + } + galleryImageLauncher.launch(intent) } } diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt index 147b34f1..5b0cf7bd 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt @@ -1,7 +1,11 @@ package com.cheocharm.presentation.ui.group +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.File +import java.util.regex.Pattern import javax.inject.Inject @HiltViewModel @@ -9,4 +13,45 @@ class GroupCreateViewModel @Inject constructor( ) : ViewModel() { + private val _groupImage = MutableLiveData() + val groupImage: LiveData + get() = _groupImage + + private val _groupName = MutableLiveData() + val groupName: LiveData + get() = _groupName + + private val _groupBio = MutableLiveData() + val groupBio: LiveData + get() = _groupBio + + private var isGroupNameVerified = false + + val isSearchEnabled = MutableLiveData() + + private val _isGroupEnabled = MutableLiveData(false) + val isGroupEnabled: LiveData + get() = _isGroupEnabled + + fun setGroupImage(groupImage: File) { + _groupImage.value = groupImage + } + + fun setGroupName(name: String) { + _groupName.value = name + } + + fun setGroupBio(bio: String) { + _groupBio.value = bio + } + + fun checkGroupNameVerified() { + val pattern = Pattern.compile("^[가-힣a-zA-Z0-9]{2,12}$") + isGroupNameVerified = pattern.matcher(groupName.value ?: return).find() == true + } + + fun checkGroupEnabled() { + _isGroupEnabled.value = groupImage.value != null && groupName.value.isNullOrEmpty() + .not() && groupBio.value.isNullOrEmpty().not() && isGroupNameVerified + } } diff --git a/presentation/src/main/res/layout/activity_group_create.xml b/presentation/src/main/res/layout/activity_group_create.xml index 79207308..649a1913 100644 --- a/presentation/src/main/res/layout/activity_group_create.xml +++ b/presentation/src/main/res/layout/activity_group_create.xml @@ -10,22 +10,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - diff --git a/presentation/src/main/res/layout/fragment_group_create.xml b/presentation/src/main/res/layout/fragment_group_create.xml index c8df3a89..91dc4362 100644 --- a/presentation/src/main/res/layout/fragment_group_create.xml +++ b/presentation/src/main/res/layout/fragment_group_create.xml @@ -16,46 +16,74 @@ android:layout_height="match_parent" tools:context=".ui.group.GroupCreateFragment"> - + + + + + + + app:layout_constraintTop_toBottomOf="@id/iv_group_create_group" /> @@ -93,7 +123,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/space_xx_large" - android:text="@string/search_group_join_request_description" + android:text="@string/group_create_search_block_description" android:textColor="@color/grey_04" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="@id/tv_group_create_search_block" diff --git a/presentation/src/main/res/values/dimens.xml b/presentation/src/main/res/values/dimens.xml index 17ab9bc1..fb54fd19 100644 --- a/presentation/src/main/res/values/dimens.xml +++ b/presentation/src/main/res/values/dimens.xml @@ -48,4 +48,5 @@ 37dp 2dp + 120dp diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index bc23f9ab..a5e863fd 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -68,6 +68,6 @@ 그룹 이름 입력 그룹의 소개글을 간략하게 적어주세요. 검색 비허용 - 그룹이 검색결과로 뜨지 않습니다.\n오직 그룹장의 초대로 멤버가 들어올 수 있습니다.진 + 그룹이 검색결과로 뜨지 않습니다.\n오직 그룹장의 초대로 멤버가 들어올 수 있습니다. 사진 추가 From cd64b6a9479ecde33d4a07619622403f02b4ab40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Thu, 23 Feb 2023 21:56:27 +0900 Subject: [PATCH 11/12] =?UTF-8?q?#53=20ui:=20=EA=B7=B8=EB=A3=B9=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EA=B2=80=EC=83=89=ED=99=94=EB=A9=B4=20=EB=94=94?= =?UTF-8?q?=EC=9E=90=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layout/fragment_group_create_search.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/presentation/src/main/res/layout/fragment_group_create_search.xml b/presentation/src/main/res/layout/fragment_group_create_search.xml index e3fe9390..7d480fa6 100644 --- a/presentation/src/main/res/layout/fragment_group_create_search.xml +++ b/presentation/src/main/res/layout/fragment_group_create_search.xml @@ -12,5 +12,26 @@ android:layout_height="match_parent" tools:context=".ui.group.GroupCreateSearchFragment"> + + + + From 5d88177f86de0312e3cc002e425a9c7cf413a78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EB=AF=BC=EC=A0=95?= Date: Sun, 19 Mar 2023 14:36:19 +0900 Subject: [PATCH 12/12] =?UTF-8?q?#53=20feat:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cheocharm/data/error/ErrorData.kt | 1 + .../com/cheocharm/data/error/ErrorMapper.kt | 1 + .../data/repository/GroupRepositoryImpl.kt | 10 ++++ .../data/source/GroupRemoteDataSource.kt | 3 ++ .../java/com/cheocharm/domain/model/Error.kt | 1 + .../domain/model/group/GroupCreateRequest.kt | 10 ++++ .../domain/repository/GroupRepository.kt | 3 ++ .../usecase/group/GroupCreateUseCase.kt | 13 +++++ .../ui/group/GroupCreateFragment.kt | 14 ++++-- .../ui/group/GroupCreateViewModel.kt | 48 ++++++++++++++++++- .../src/main/res/navigation/nav_group.xml | 8 ++++ presentation/src/main/res/values/dimens.xml | 1 + .../java/com/cheocharm/remote/api/GroupApi.kt | 8 +++- .../cheocharm/remote/mapper/GroupMapper.kt | 7 +++ .../remote/model/request/GroupCreateDto.kt | 7 +++ .../source/GroupRemoteDataSourceImpl.kt | 29 +++++++++++ 16 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 domain/src/main/java/com/cheocharm/domain/model/group/GroupCreateRequest.kt create mode 100644 domain/src/main/java/com/cheocharm/domain/usecase/group/GroupCreateUseCase.kt create mode 100644 remote/src/main/java/com/cheocharm/remote/model/request/GroupCreateDto.kt diff --git a/data/src/main/java/com/cheocharm/data/error/ErrorData.kt b/data/src/main/java/com/cheocharm/data/error/ErrorData.kt index afda20cc..0ddd28cc 100644 --- a/data/src/main/java/com/cheocharm/data/error/ErrorData.kt +++ b/data/src/main/java/com/cheocharm/data/error/ErrorData.kt @@ -14,4 +14,5 @@ sealed class ErrorData( data class RefreshAccessTokenUnavailable(override val message: String) : ErrorData(message) data class SearchGroupUnavailable(override val message: String) : ErrorData(message) data class JoinGroupUnavailable(override val message: String) : ErrorData(message) + data class GroupCreateUnavailable(override val message: String) : ErrorData(message) } diff --git a/data/src/main/java/com/cheocharm/data/error/ErrorMapper.kt b/data/src/main/java/com/cheocharm/data/error/ErrorMapper.kt index 18a50230..25156247 100644 --- a/data/src/main/java/com/cheocharm/data/error/ErrorMapper.kt +++ b/data/src/main/java/com/cheocharm/data/error/ErrorMapper.kt @@ -12,4 +12,5 @@ internal fun ErrorData.toDomain(): Error = when (this) { is ErrorData.RefreshAccessTokenUnavailable -> Error.RefreshAccessTokenUnavailable(message) is ErrorData.SearchGroupUnavailable -> Error.SearchGroupUnavailable(message) is ErrorData.JoinGroupUnavailable -> Error.JoinGroupUnavailable(message) + is ErrorData.GroupCreateUnavailable -> Error.GroupCreateUnavailable(message) } diff --git a/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt b/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt index c2380b53..1bccd00c 100644 --- a/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt +++ b/data/src/main/java/com/cheocharm/data/repository/GroupRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.cheocharm.data.error.toDomain import com.cheocharm.data.source.GroupRemoteDataSource import com.cheocharm.domain.model.Group import com.cheocharm.domain.model.GroupJoin +import com.cheocharm.domain.model.group.GroupCreateRequest import com.cheocharm.domain.repository.GroupRepository import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -26,4 +27,13 @@ class GroupRepositoryImpl @Inject constructor( else -> Result.failure(exception) } } + + override suspend fun createGroup(groupCreateRequest: GroupCreateRequest): Result { + val result = groupRemoteDataSource.createGroup(groupCreateRequest) + return when (val exception = result.exceptionOrNull()) { + is ErrorData -> Result.failure(exception.toDomain()) + null -> result + else -> Result.failure(exception) + } + } } diff --git a/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt b/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt index 6b750189..ff1c9dc0 100644 --- a/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt +++ b/data/src/main/java/com/cheocharm/data/source/GroupRemoteDataSource.kt @@ -3,6 +3,7 @@ package com.cheocharm.data.source import androidx.paging.PagingData import com.cheocharm.domain.model.Group import com.cheocharm.domain.model.GroupJoin +import com.cheocharm.domain.model.group.GroupCreateRequest import kotlinx.coroutines.flow.Flow interface GroupRemoteDataSource { @@ -10,4 +11,6 @@ interface GroupRemoteDataSource { fun fetchGroupSearchList(searchGroupName: String): Flow> suspend fun joinGroup(groupName: String): Result + + suspend fun createGroup(groupCreateRequest: GroupCreateRequest): Result } diff --git a/domain/src/main/java/com/cheocharm/domain/model/Error.kt b/domain/src/main/java/com/cheocharm/domain/model/Error.kt index 74d19b83..231adccc 100644 --- a/domain/src/main/java/com/cheocharm/domain/model/Error.kt +++ b/domain/src/main/java/com/cheocharm/domain/model/Error.kt @@ -14,4 +14,5 @@ sealed class Error( data class RefreshAccessTokenUnavailable(override val message: String) : Error() data class SearchGroupUnavailable(override val message: String) : Error() data class JoinGroupUnavailable(override val message: String) : Error() + data class GroupCreateUnavailable(override val message: String) : Error() } diff --git a/domain/src/main/java/com/cheocharm/domain/model/group/GroupCreateRequest.kt b/domain/src/main/java/com/cheocharm/domain/model/group/GroupCreateRequest.kt new file mode 100644 index 00000000..32ef9795 --- /dev/null +++ b/domain/src/main/java/com/cheocharm/domain/model/group/GroupCreateRequest.kt @@ -0,0 +1,10 @@ +package com.cheocharm.domain.model.group + +import java.io.File + +data class GroupCreateRequest( + val groupImage: File, + val groupName: String, + val groupBio: String, + val changeStatus: Boolean +) diff --git a/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt b/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt index 5f219f08..56fcc8b6 100644 --- a/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt +++ b/domain/src/main/java/com/cheocharm/domain/repository/GroupRepository.kt @@ -3,6 +3,7 @@ package com.cheocharm.domain.repository import androidx.paging.PagingData import com.cheocharm.domain.model.Group import com.cheocharm.domain.model.GroupJoin +import com.cheocharm.domain.model.group.GroupCreateRequest import kotlinx.coroutines.flow.Flow interface GroupRepository { @@ -10,4 +11,6 @@ interface GroupRepository { fun searchGroup(searchGroupName: String): Flow> suspend fun joinGroup(groupName: String): Result + + suspend fun createGroup(groupCreateRequest: GroupCreateRequest): Result } diff --git a/domain/src/main/java/com/cheocharm/domain/usecase/group/GroupCreateUseCase.kt b/domain/src/main/java/com/cheocharm/domain/usecase/group/GroupCreateUseCase.kt new file mode 100644 index 00000000..eee1f9ed --- /dev/null +++ b/domain/src/main/java/com/cheocharm/domain/usecase/group/GroupCreateUseCase.kt @@ -0,0 +1,13 @@ +package com.cheocharm.domain.usecase.group + +import com.cheocharm.domain.model.group.GroupCreateRequest +import com.cheocharm.domain.repository.GroupRepository +import javax.inject.Inject + +class GroupCreateUseCase @Inject constructor( + private val groupRepository: GroupRepository +) { + suspend operator fun invoke(groupCreateRequest: GroupCreateRequest): Result { + return groupRepository.createGroup(groupCreateRequest) + } +} diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt index 39829714..9d5a9d1a 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateFragment.kt @@ -17,6 +17,7 @@ import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import com.cheocharm.presentation.R import com.cheocharm.presentation.base.BaseFragment +import com.cheocharm.presentation.common.EventObserver import com.cheocharm.presentation.databinding.FragmentGroupCreateBinding import com.cheocharm.presentation.util.UriUtil import dagger.hilt.android.AndroidEntryPoint @@ -34,6 +35,7 @@ class GroupCreateFragment : binding.viewmodel = groupCreateViewModel initButtons() + initObservers() initGalleryLauncher() } @@ -41,9 +43,8 @@ class GroupCreateFragment : binding.toolbarGroupCreate.setOnMenuItemClickListener { when (it.itemId) { R.id.location_confirm -> { - if (groupCreateViewModel.isGroupEnabled.value == true) findNavController().navigate( - R.id.action_groupCreateFragment_to_groupCreateSearchFragment - ) + if (groupCreateViewModel.isGroupEnabled.value == true) + groupCreateViewModel.requestGroupCreate() true } else -> { @@ -68,6 +69,13 @@ class GroupCreateFragment : } } + private fun initObservers() { + groupCreateViewModel.isGoToSearchEnabled.observe(viewLifecycleOwner, EventObserver { + //TODO: 검색화면으로 넘어가기 +// findNavController().navigate(R.id.action_groupCreateFragment_to_groupCreateSearchFragment) + }) + } + private fun initGalleryLauncher() { galleryImageLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult -> diff --git a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt index 5b0cf7bd..34af4077 100644 --- a/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt +++ b/presentation/src/main/java/com/cheocharm/presentation/ui/group/GroupCreateViewModel.kt @@ -3,14 +3,20 @@ package com.cheocharm.presentation.ui.group import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.cheocharm.domain.model.group.GroupCreateRequest +import com.cheocharm.domain.model.group.GroupCreateSearchResult +import com.cheocharm.domain.usecase.group.GroupCreateUseCase +import com.cheocharm.presentation.common.Event import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import java.io.File import java.util.regex.Pattern import javax.inject.Inject @HiltViewModel class GroupCreateViewModel @Inject constructor( - + private val groupCreateUseCase: GroupCreateUseCase ) : ViewModel() { private val _groupImage = MutableLiveData() @@ -33,6 +39,23 @@ class GroupCreateViewModel @Inject constructor( val isGroupEnabled: LiveData get() = _isGroupEnabled + private val _isGoToSearchEnabled = MutableLiveData>() + val isGoToSearchEnabled: LiveData> + get() = _isGoToSearchEnabled + + // search + private val _searchMemberName = MutableLiveData() + val searchMemberName: LiveData + get() = _searchMemberName + + private val _searchResultList = MutableLiveData>() + val searchResultList: LiveData> + get() = _searchResultList + + private val _inviteMemberList = MutableLiveData>() + val inviteMemberList: LiveData> + get() = _inviteMemberList + fun setGroupImage(groupImage: File) { _groupImage.value = groupImage } @@ -54,4 +77,27 @@ class GroupCreateViewModel @Inject constructor( _isGroupEnabled.value = groupImage.value != null && groupName.value.isNullOrEmpty() .not() && groupBio.value.isNullOrEmpty().not() && isGroupNameVerified } + + fun requestGroupCreate() { + if (isGroupEnabled.value != true) return + + val image = groupImage.value ?: return + val name = groupName.value ?: return + val bio = groupBio.value ?: return + + viewModelScope.launch { + groupCreateUseCase.invoke( + GroupCreateRequest( + image, + name, + bio, + isSearchEnabled.value ?: false + ) + ) + .onSuccess { _isGoToSearchEnabled.value = Event(Unit) } + .onFailure { + // TODO: 그룹 셍성 실패 토스트 띄우기 + } + } + } } diff --git a/presentation/src/main/res/navigation/nav_group.xml b/presentation/src/main/res/navigation/nav_group.xml index 83d6be40..de3cf92e 100644 --- a/presentation/src/main/res/navigation/nav_group.xml +++ b/presentation/src/main/res/navigation/nav_group.xml @@ -13,10 +13,18 @@ + + diff --git a/presentation/src/main/res/values/dimens.xml b/presentation/src/main/res/values/dimens.xml index fb54fd19..843fd90a 100644 --- a/presentation/src/main/res/values/dimens.xml +++ b/presentation/src/main/res/values/dimens.xml @@ -3,6 +3,7 @@ 5dp 8dp 12dp + 14dp 16dp 18dp 20dp diff --git a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt index 35bfbd6d..ce9db3f3 100644 --- a/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt +++ b/remote/src/main/java/com/cheocharm/remote/api/GroupApi.kt @@ -1,10 +1,12 @@ package com.cheocharm.remote.api import com.cheocharm.remote.model.BaseResponse +import com.cheocharm.remote.model.request.GroupCreateDto import com.cheocharm.remote.model.response.group.GroupJoinResponse import com.cheocharm.remote.model.response.group.GroupMemberResponse import com.cheocharm.remote.model.response.group.GroupResponse import com.cheocharm.remote.model.response.group.GroupSearchResponse +import okhttp3.MultipartBody import okhttp3.RequestBody import retrofit2.http.* @@ -17,8 +19,12 @@ interface GroupApi { @Query("cursorId") cursorId: Int ): BaseResponse + @Multipart @POST("group") - suspend fun createGroup(@Body body: RequestBody): BaseResponse + suspend fun createGroup( + @Part("dto") dto: GroupCreateDto, + @Part file: MultipartBody.Part + ): BaseResponse @POST("group/join") suspend fun joinGroup(@Body body: HashMap): BaseResponse diff --git a/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt b/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt index 38a176ef..ab5aff89 100644 --- a/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt +++ b/remote/src/main/java/com/cheocharm/remote/mapper/GroupMapper.kt @@ -4,6 +4,8 @@ import com.cheocharm.domain.model.Group import com.cheocharm.domain.model.GroupJoin import com.cheocharm.domain.model.GroupMember import com.cheocharm.domain.model.GroupSearch +import com.cheocharm.domain.model.group.GroupCreateRequest +import com.cheocharm.remote.model.request.GroupCreateDto import com.cheocharm.remote.model.response.group.GroupJoinResponse import com.cheocharm.remote.model.response.group.GroupSearchResponse @@ -22,3 +24,8 @@ internal fun GroupSearchResponse.toDomain(): GroupSearch { internal fun GroupJoinResponse.toDomain(): GroupJoin { return GroupJoin(alreadyJoin, status) } + +// domain -> remote +internal fun GroupCreateRequest.toDto(): GroupCreateDto { + return GroupCreateDto(groupName, groupBio, changeStatus) +} diff --git a/remote/src/main/java/com/cheocharm/remote/model/request/GroupCreateDto.kt b/remote/src/main/java/com/cheocharm/remote/model/request/GroupCreateDto.kt new file mode 100644 index 00000000..ebc55f76 --- /dev/null +++ b/remote/src/main/java/com/cheocharm/remote/model/request/GroupCreateDto.kt @@ -0,0 +1,7 @@ +package com.cheocharm.remote.model.request + +data class GroupCreateDto( + val groupName: String, + val bio: String, + val changeStatus: Boolean +) diff --git a/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt b/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt index b8b20db8..2e0b212b 100644 --- a/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt +++ b/remote/src/main/java/com/cheocharm/remote/source/GroupRemoteDataSourceImpl.kt @@ -7,9 +7,14 @@ import com.cheocharm.data.error.ErrorData import com.cheocharm.data.source.GroupRemoteDataSource import com.cheocharm.domain.model.Group import com.cheocharm.domain.model.GroupJoin +import com.cheocharm.domain.model.group.GroupCreateRequest import com.cheocharm.remote.api.GroupApi import com.cheocharm.remote.mapper.toDomain +import com.cheocharm.remote.mapper.toDto import kotlinx.coroutines.flow.Flow +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody import java.net.UnknownHostException import javax.inject.Inject @@ -40,4 +45,28 @@ class GroupRemoteDataSourceImpl @Inject constructor( else -> Result.failure(exception) } } + + override suspend fun createGroup(groupCreateRequest: GroupCreateRequest): Result { + val groupCreateDto = groupCreateRequest.toDto() + val fileRequestBody = MultipartBody.Part.createFormData( + "file", + groupCreateRequest.groupImage.name, + groupCreateRequest.groupImage.asRequestBody("image/*".toMediaType()) + ) + + val result = runCatching { groupApi.createGroup(groupCreateDto, fileRequestBody) } + return when (val exception = result.exceptionOrNull()) { + null -> { + val response = + result.getOrNull() ?: return Result.failure(Throwable(NullPointerException())) + Result.success( + if (response.statusCode != 200) return Result.failure( + ErrorData.GroupCreateUnavailable(response.message) + ) else response.data ?: "" + ) + } + is UnknownHostException -> Result.failure(ErrorData.NetworkUnavailable) + else -> Result.failure(exception) + } + } }