From 272abd9e36ba3baf25de3993247b912f2178e06b Mon Sep 17 00:00:00 2001 From: Sohyun Date: Wed, 20 Nov 2024 19:09:01 +0900 Subject: [PATCH 01/28] #106 [FEAT] : save isAdmin value to datastore --- .../com/teamwable/data/mapper/toModel/AuthMapper.kt | 3 ++- .../teamwable/data/repository/UserInfoRepository.kt | 4 ++++ .../repositoryimpl/DefaultUserInfoRepository.kt | 7 +++++++ .../datasource/DefaultWablePreferenceDatasource.kt | 13 +++++++++++++ .../datasource/WablePreferencesDataSource.kt | 3 +++ .../main/java/com/teamwable/model/auth/UserModel.kt | 1 + .../dto/response/auth/ResponseSocialLoginDto.kt | 2 ++ .../main/java/com/teamwable/auth/LoginViewModel.kt | 7 +++++++ 8 files changed, 39 insertions(+), 1 deletion(-) diff --git a/core/data/src/main/java/com/teamwable/data/mapper/toModel/AuthMapper.kt b/core/data/src/main/java/com/teamwable/data/mapper/toModel/AuthMapper.kt index 58fb464e..bb478b22 100644 --- a/core/data/src/main/java/com/teamwable/data/mapper/toModel/AuthMapper.kt +++ b/core/data/src/main/java/com/teamwable/data/mapper/toModel/AuthMapper.kt @@ -14,5 +14,6 @@ internal fun ResponseSocialLoginDto.toUserModel(): UserModel = isPushAlarmAllowed = isPushAlarmAllowed, memberFanTeam = memberFanTeam, memberLckYears = memberLckYears, - memberLevel = memberLevel + memberLevel = memberLevel, + isAdmin = isAdmin, ) diff --git a/core/data/src/main/java/com/teamwable/data/repository/UserInfoRepository.kt b/core/data/src/main/java/com/teamwable/data/repository/UserInfoRepository.kt index 4bbfdb2e..f1df55bd 100644 --- a/core/data/src/main/java/com/teamwable/data/repository/UserInfoRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repository/UserInfoRepository.kt @@ -17,6 +17,8 @@ interface UserInfoRepository { fun getIsPushAlarmAllowed(): Flow + fun getIsAdmin(): Flow + suspend fun saveAccessToken(accessToken: String) suspend fun saveRefreshToken(refreshToken: String) @@ -31,6 +33,8 @@ interface UserInfoRepository { suspend fun saveIsPushAlarmAllowed(isPushAlarmAllowed: Boolean) + suspend fun saveIsAdmin(isAdmin: Boolean) + suspend fun clearAll() suspend fun clearForRefreshToken() diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultUserInfoRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultUserInfoRepository.kt index 9c14d267..9b38325d 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultUserInfoRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultUserInfoRepository.kt @@ -29,6 +29,9 @@ internal class DefaultUserInfoRepository @Inject constructor( override fun getIsPushAlarmAllowed(): Flow = wablePreferencesDataSource.isPushAlarmAllowed + override fun getIsAdmin(): Flow = + wablePreferencesDataSource.isAdmin + override suspend fun saveAccessToken(accessToken: String) { wablePreferencesDataSource.updateAccessToken(accessToken) } @@ -57,6 +60,10 @@ internal class DefaultUserInfoRepository @Inject constructor( wablePreferencesDataSource.updateIsPushAlarmAllowed(isPushAlarmAllowed) } + override suspend fun saveIsAdmin(isAdmin: Boolean) { + wablePreferencesDataSource.updateIsAdmin(isAdmin) + } + override suspend fun clearAll() { wablePreferencesDataSource.clear() } diff --git a/core/datastore/src/main/java/com/teamwable/datastore/datasource/DefaultWablePreferenceDatasource.kt b/core/datastore/src/main/java/com/teamwable/datastore/datasource/DefaultWablePreferenceDatasource.kt index f603ec5c..a09a956f 100644 --- a/core/datastore/src/main/java/com/teamwable/datastore/datasource/DefaultWablePreferenceDatasource.kt +++ b/core/datastore/src/main/java/com/teamwable/datastore/datasource/DefaultWablePreferenceDatasource.kt @@ -25,6 +25,7 @@ class DefaultWablePreferenceDatasource @Inject constructor( val MemberId = intPreferencesKey("memberId") val MemberProfileUrl = stringPreferencesKey("memberProfileUrl") val IsPushAlarmAllowed = booleanPreferencesKey("isPushAlarmAllowed") + val IsAdmin = booleanPreferencesKey("isAdmin") } override val accessToken: Flow = dataStore.data @@ -69,6 +70,12 @@ class DefaultWablePreferenceDatasource @Inject constructor( preferences[PreferencesKeys.IsPushAlarmAllowed] ?: false } + override var isAdmin: Flow = dataStore.data + .catch { handleError(it) } + .map { preferences -> + preferences[PreferencesKeys.IsAdmin] ?: false + } + override suspend fun updateAccessToken(accessToken: String) { dataStore.edit { preferences -> preferences[PreferencesKeys.AccessToken] = accessToken @@ -111,6 +118,12 @@ class DefaultWablePreferenceDatasource @Inject constructor( } } + override suspend fun updateIsAdmin(isAdmin: Boolean) { + dataStore.edit { preferences -> + preferences[PreferencesKeys.IsAdmin] = isAdmin + } + } + override suspend fun clear() { dataStore.edit { preferences -> preferences.clear() diff --git a/core/datastore/src/main/java/com/teamwable/datastore/datasource/WablePreferencesDataSource.kt b/core/datastore/src/main/java/com/teamwable/datastore/datasource/WablePreferencesDataSource.kt index b4c1ca84..18801467 100644 --- a/core/datastore/src/main/java/com/teamwable/datastore/datasource/WablePreferencesDataSource.kt +++ b/core/datastore/src/main/java/com/teamwable/datastore/datasource/WablePreferencesDataSource.kt @@ -10,6 +10,7 @@ interface WablePreferencesDataSource { val memberId: Flow val memberProfileUrl: Flow val isPushAlarmAllowed: Flow + val isAdmin: Flow suspend fun updateAccessToken(accessToken: String) @@ -25,6 +26,8 @@ interface WablePreferencesDataSource { suspend fun updateIsPushAlarmAllowed(isPushAlarmAllowed: Boolean) + suspend fun updateIsAdmin(isAdmin: Boolean) + suspend fun clear() suspend fun clearForRefreshToken() diff --git a/core/model/src/main/java/com/teamwable/model/auth/UserModel.kt b/core/model/src/main/java/com/teamwable/model/auth/UserModel.kt index b5550c0d..d9339882 100644 --- a/core/model/src/main/java/com/teamwable/model/auth/UserModel.kt +++ b/core/model/src/main/java/com/teamwable/model/auth/UserModel.kt @@ -11,4 +11,5 @@ data class UserModel( val memberFanTeam: String, val memberLckYears: Int, val memberLevel: Int, + val isAdmin: Boolean, ) diff --git a/core/network/src/main/java/com/teamwable/network/dto/response/auth/ResponseSocialLoginDto.kt b/core/network/src/main/java/com/teamwable/network/dto/response/auth/ResponseSocialLoginDto.kt index afd07e66..8e69ede3 100644 --- a/core/network/src/main/java/com/teamwable/network/dto/response/auth/ResponseSocialLoginDto.kt +++ b/core/network/src/main/java/com/teamwable/network/dto/response/auth/ResponseSocialLoginDto.kt @@ -25,4 +25,6 @@ data class ResponseSocialLoginDto( val memberLckYears: Int, @SerialName("memberLevel") val memberLevel: Int, + @SerialName("isAdmin") + val isAdmin: Boolean, ) diff --git a/feature/auth/src/main/java/com/teamwable/auth/LoginViewModel.kt b/feature/auth/src/main/java/com/teamwable/auth/LoginViewModel.kt index c3e40b63..85872eec 100644 --- a/feature/auth/src/main/java/com/teamwable/auth/LoginViewModel.kt +++ b/feature/auth/src/main/java/com/teamwable/auth/LoginViewModel.kt @@ -88,6 +88,7 @@ class LoginViewModel @Inject constructor( saveRefreshToken(response.refreshToken) saveMemberId(response.memberId) checkIsNewUser(response.nickName.isBlank()) + saveIsAdmin(response.isAdmin) }.onFailure { _loginSideEffect.emit(LoginSideEffect.ShowSnackBar(it)) } @@ -129,6 +130,12 @@ class LoginViewModel @Inject constructor( } } + private fun saveIsAdmin(isAdmin: Boolean) { + viewModelScope.launch { + userInfoRepository.saveIsAdmin(isAdmin) + } + } + companion object { private const val BEARER = "Bearer " private const val KAKAO = "KAKAO" From fa40ab076523ed67c57189c35b7b52facdb68578 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 15:35:24 +0900 Subject: [PATCH 02/28] #106 [FEAT] : set up ban api --- .../teamwable/data/mapper/toData/BanModelMapper.kt | 6 ++++++ .../teamwable/data/repository/ProfileRepository.kt | 2 ++ .../data/repositoryimpl/DefaultProfileRepository.kt | 8 ++++++++ .../teamwable/network/datasource/ProfileService.kt | 6 ++++++ .../teamwable/network/dto/request/RequestBanDto.kt | 11 +++++++++++ 5 files changed, 33 insertions(+) create mode 100644 core/data/src/main/java/com/teamwable/data/mapper/toData/BanModelMapper.kt create mode 100644 core/network/src/main/java/com/teamwable/network/dto/request/RequestBanDto.kt diff --git a/core/data/src/main/java/com/teamwable/data/mapper/toData/BanModelMapper.kt b/core/data/src/main/java/com/teamwable/data/mapper/toData/BanModelMapper.kt new file mode 100644 index 00000000..5102aadd --- /dev/null +++ b/core/data/src/main/java/com/teamwable/data/mapper/toData/BanModelMapper.kt @@ -0,0 +1,6 @@ +package com.teamwable.data.mapper.toData + +import com.teamwable.network.dto.request.RequestBanDto + +internal fun Triple.toBanDto(): RequestBanDto = + RequestBanDto(first, second, third) diff --git a/core/data/src/main/java/com/teamwable/data/repository/ProfileRepository.kt b/core/data/src/main/java/com/teamwable/data/repository/ProfileRepository.kt index ff94b74d..86631c65 100644 --- a/core/data/src/main/java/com/teamwable/data/repository/ProfileRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repository/ProfileRepository.kt @@ -16,4 +16,6 @@ interface ProfileRepository { suspend fun getNickNameDoubleCheck(nickname: String): Result suspend fun postReport(nickname: String, relateText: String): Result + + suspend fun postBan(banInfo: Triple): Result } diff --git a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt index d3a571cf..f653aea8 100644 --- a/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt +++ b/core/data/src/main/java/com/teamwable/data/repositoryimpl/DefaultProfileRepository.kt @@ -1,6 +1,7 @@ package com.teamwable.data.repositoryimpl import android.content.ContentResolver +import com.teamwable.data.mapper.toData.toBanDto import com.teamwable.data.mapper.toData.toReportDto import com.teamwable.data.mapper.toModel.toMemberDataModel import com.teamwable.data.mapper.toModel.toProfile @@ -76,6 +77,13 @@ internal class DefaultProfileRepository @Inject constructor( return it.handleThrowable() } + override suspend fun postBan(banInfo: Triple): Result = runCatching { + apiService.postBan(banInfo.toBanDto()) + Unit + }.onFailure { + return it.handleThrowable() + } + companion object { private const val FILE_NAME = "file" } diff --git a/core/network/src/main/java/com/teamwable/network/datasource/ProfileService.kt b/core/network/src/main/java/com/teamwable/network/datasource/ProfileService.kt index d54dfdf4..8cd65e6c 100644 --- a/core/network/src/main/java/com/teamwable/network/datasource/ProfileService.kt +++ b/core/network/src/main/java/com/teamwable/network/datasource/ProfileService.kt @@ -1,5 +1,6 @@ package com.teamwable.network.datasource +import com.teamwable.network.dto.request.RequestBanDto import com.teamwable.network.dto.request.RequestReportDto import com.teamwable.network.dto.request.RequestWithdrawalDto import com.teamwable.network.dto.response.ResponseProfileInfoDto @@ -45,4 +46,9 @@ interface ProfileService { suspend fun postReport( @Body request: RequestReportDto, ): BaseUnitResponse + + @POST("api/v1/report/ban") + suspend fun postBan( + @Body request: RequestBanDto, + ): BaseUnitResponse } diff --git a/core/network/src/main/java/com/teamwable/network/dto/request/RequestBanDto.kt b/core/network/src/main/java/com/teamwable/network/dto/request/RequestBanDto.kt new file mode 100644 index 00000000..7ccbf5ce --- /dev/null +++ b/core/network/src/main/java/com/teamwable/network/dto/request/RequestBanDto.kt @@ -0,0 +1,11 @@ +package com.teamwable.network.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestBanDto( + @SerialName("memberId") val memberId: Long, + @SerialName("triggerType") val triggerType: String, + @SerialName("triggerId") val triggerId: Long, +) From ad59b5e2ce5fcfb613b33e0a5423f46a60892daa Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 15:55:11 +0900 Subject: [PATCH 03/28] #106 [FEAT] : add isBlind value at feed dto --- .../teamwable/data/mapper/toModel/ResponseFeedDtoMapper.kt | 1 + core/model/src/main/java/com/teamwable/model/Feed.kt | 1 + .../java/com/teamwable/network/datasource/FeedService.kt | 6 +++--- .../com/teamwable/network/dto/response/ResponseFeedDto.kt | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseFeedDtoMapper.kt b/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseFeedDtoMapper.kt index 29055996..5e98ff76 100644 --- a/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseFeedDtoMapper.kt +++ b/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseFeedDtoMapper.kt @@ -19,4 +19,5 @@ internal fun ResponseFeedDto.toFeed(): Feed = this.commentNumber.toString(), this.contentImageUrl, this.memberFanTeam, + isBlind = this.isBlind, ) diff --git a/core/model/src/main/java/com/teamwable/model/Feed.kt b/core/model/src/main/java/com/teamwable/model/Feed.kt index 228a5621..8bd744a7 100644 --- a/core/model/src/main/java/com/teamwable/model/Feed.kt +++ b/core/model/src/main/java/com/teamwable/model/Feed.kt @@ -21,6 +21,7 @@ data class Feed( val postAuthorTeamTag: String = "", val ghostColor: String = GhostColor.DEFAULT_0, val isAuth: Boolean = false, + val isBlind: Boolean = false, ) : Parcelable object GhostColor { diff --git a/core/network/src/main/java/com/teamwable/network/datasource/FeedService.kt b/core/network/src/main/java/com/teamwable/network/datasource/FeedService.kt index 64d2f795..ca8c456c 100644 --- a/core/network/src/main/java/com/teamwable/network/datasource/FeedService.kt +++ b/core/network/src/main/java/com/teamwable/network/datasource/FeedService.kt @@ -13,12 +13,12 @@ import retrofit2.http.Path import retrofit2.http.Query interface FeedService { - @GET("api/v2/contents") + @GET("api/v3/contents") suspend fun getHomeFeeds( @Query(value = "cursor") contentId: Long = -1, ): BaseResponse> - @GET("api/v2/member/{memberId}/contents") + @GET("api/v3/member/{memberId}/contents") suspend fun getProfileFeeds( @Path("memberId") userId: Long, @Query(value = "cursor") contentId: Long = -1, @@ -29,7 +29,7 @@ interface FeedService { @Path(value = "contentId") contentId: Long, ): BaseUnitResponse - @GET("api/v2/content/{contentId}") + @GET("api/v3/content/{contentId}") suspend fun getHomeDetail( @Path(value = "contentId") contentId: Long, ): BaseResponse diff --git a/core/network/src/main/java/com/teamwable/network/dto/response/ResponseFeedDto.kt b/core/network/src/main/java/com/teamwable/network/dto/response/ResponseFeedDto.kt index ad56a551..c5a1c838 100644 --- a/core/network/src/main/java/com/teamwable/network/dto/response/ResponseFeedDto.kt +++ b/core/network/src/main/java/com/teamwable/network/dto/response/ResponseFeedDto.kt @@ -20,4 +20,5 @@ data class ResponseFeedDto( @SerialName("isDeleted") val isDeleted: Boolean? = null, @SerialName("contentImageUrl") val contentImageUrl: String, @SerialName("memberFanTeam") val memberFanTeam: String, + @SerialName("isBlind") val isBlind: Boolean, ) From 301f8b4ab9dbb5de06dac5b63bfa332f2931dfab Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:07:21 +0900 Subject: [PATCH 04/28] #106 [UI] : set up feed blind ui --- .../src/main/res/drawable/ic_share_blind.xml | 10 +++++ core/ui/src/main/res/layout/item_feed.xml | 45 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 core/common/src/main/res/drawable/ic_share_blind.xml diff --git a/core/common/src/main/res/drawable/ic_share_blind.xml b/core/common/src/main/res/drawable/ic_share_blind.xml new file mode 100644 index 00000000..9383d1e1 --- /dev/null +++ b/core/common/src/main/res/drawable/ic_share_blind.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/layout/item_feed.xml b/core/ui/src/main/res/layout/item_feed.xml index 31c18f62..1e3a23fe 100644 --- a/core/ui/src/main/res/layout/item_feed.xml +++ b/core/ui/src/main/res/layout/item_feed.xml @@ -90,7 +90,7 @@ android:textColorLink="@color/info" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" - app:layout_constraintTop_toBottomOf="@id/tv_feed_ghost_level" + app:layout_constraintTop_toBottomOf="@id/view_feed_blind_bg" tools:text="@string/dummy" /> + + + + + + + + From fbe4e84d572de7e4d222689bed5c7254cdae338c Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:08:41 +0900 Subject: [PATCH 05/28] #106 [UI] : set up two label bottomsheet --- .../main/res/layout/bottomsheet_two_label.xml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 core/ui/src/main/res/layout/bottomsheet_two_label.xml diff --git a/core/ui/src/main/res/layout/bottomsheet_two_label.xml b/core/ui/src/main/res/layout/bottomsheet_two_label.xml new file mode 100644 index 00000000..3f9a5f2d --- /dev/null +++ b/core/ui/src/main/res/layout/bottomsheet_two_label.xml @@ -0,0 +1,43 @@ + + + + + + + + + From 80e9adcf4470f91a6fe8b6aa776c8a50b8292e36 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:10:10 +0900 Subject: [PATCH 06/28] #106 [ADD] : add BAN type at bottomsheet, dialog, snackbar --- .../src/main/java/com/teamwable/ui/type/BottomSheetType.kt | 1 + core/ui/src/main/java/com/teamwable/ui/type/DialogType.kt | 6 ++++++ core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt | 1 + 3 files changed, 8 insertions(+) diff --git a/core/ui/src/main/java/com/teamwable/ui/type/BottomSheetType.kt b/core/ui/src/main/java/com/teamwable/ui/type/BottomSheetType.kt index 8866e3f7..1914b618 100644 --- a/core/ui/src/main/java/com/teamwable/ui/type/BottomSheetType.kt +++ b/core/ui/src/main/java/com/teamwable/ui/type/BottomSheetType.kt @@ -8,5 +8,6 @@ enum class BottomSheetType( ) { REPORT(R.string.label_dialog_report_yes), DELETE_FEED(R.string.label_dialog_delete_yes), + BAN(R.string.label_dialog_ban_yes), EMPTY(), } diff --git a/core/ui/src/main/java/com/teamwable/ui/type/DialogType.kt b/core/ui/src/main/java/com/teamwable/ui/type/DialogType.kt index 3b525297..ba23e8a4 100644 --- a/core/ui/src/main/java/com/teamwable/ui/type/DialogType.kt +++ b/core/ui/src/main/java/com/teamwable/ui/type/DialogType.kt @@ -17,6 +17,12 @@ enum class DialogType( yesLabel = R.string.label_dialog_report_yes, titleTypo = R.style.TextAppearance_Wable_Head1, ), + BAN( + title = R.string.label_dialog_ban_title, + description = R.string.label_dialog_ban_description, + yesLabel = R.string.label_dialog_ban_yes, + titleTypo = R.style.TextAppearance_Wable_Head1, + ), DELETE_FEED( title = R.string.label_dialog_delete_feed_title, description = R.string.label_dialog_delete_feed_description, diff --git a/core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt b/core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt index 192f58df..00c8fcd4 100644 --- a/core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt +++ b/core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt @@ -13,4 +13,5 @@ enum class SnackbarType( COMMENT_ING(R.string.label_snack_bar_comment_ing), COMMENT_COMPLETE(R.string.label_snack_bar_comment_complete), REPORT(R.string.label_snack_bar_report), + BAN(R.string.label_snack_bar_ban), } From 24129d89e2f31cde25a9fec337400469129984a9 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:10:51 +0900 Subject: [PATCH 07/28] #106 [ADD] : add ban trigger type and admin type --- .../src/main/java/com/teamwable/ui/type/BanTriggerType.kt | 6 ++++++ .../src/main/java/com/teamwable/ui/type/ProfileUserType.kt | 1 + 2 files changed, 7 insertions(+) create mode 100644 core/ui/src/main/java/com/teamwable/ui/type/BanTriggerType.kt diff --git a/core/ui/src/main/java/com/teamwable/ui/type/BanTriggerType.kt b/core/ui/src/main/java/com/teamwable/ui/type/BanTriggerType.kt new file mode 100644 index 00000000..d59b77d0 --- /dev/null +++ b/core/ui/src/main/java/com/teamwable/ui/type/BanTriggerType.kt @@ -0,0 +1,6 @@ +package com.teamwable.ui.type + +enum class BanTriggerType() { + COMMENT(), + CONTENT(), +} diff --git a/core/ui/src/main/java/com/teamwable/ui/type/ProfileUserType.kt b/core/ui/src/main/java/com/teamwable/ui/type/ProfileUserType.kt index 33f3cbc2..08ef3945 100644 --- a/core/ui/src/main/java/com/teamwable/ui/type/ProfileUserType.kt +++ b/core/ui/src/main/java/com/teamwable/ui/type/ProfileUserType.kt @@ -3,5 +3,6 @@ package com.teamwable.ui.type enum class ProfileUserType { AUTH(), MEMBER(), + ADMIN(), EMPTY(), } From 5642d90b6de00b5ab947cf80fc768adb12f1af53 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:12:18 +0900 Subject: [PATCH 08/28] #106 [FEAT] : set up navgation of two label bottomsheet --- .../ui/component/TwoLabelBottomSheet.kt | 71 +++++++++++++++++++ .../teamwable/ui/extensions/NavigationExt.kt | 2 + .../java/com/teamwable/ui/util/Constants.kt | 2 + core/ui/src/main/res/navigation/graph_ui.xml | 16 ++++- core/ui/src/main/res/values/deeplink.xml | 1 + 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 core/ui/src/main/java/com/teamwable/ui/component/TwoLabelBottomSheet.kt diff --git a/core/ui/src/main/java/com/teamwable/ui/component/TwoLabelBottomSheet.kt b/core/ui/src/main/java/com/teamwable/ui/component/TwoLabelBottomSheet.kt new file mode 100644 index 00000000..f86791c1 --- /dev/null +++ b/core/ui/src/main/java/com/teamwable/ui/component/TwoLabelBottomSheet.kt @@ -0,0 +1,71 @@ +package com.teamwable.ui.component + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.setFragmentResult +import androidx.navigation.NavController +import com.teamwable.ui.base.BindingBottomSheetFragment +import com.teamwable.ui.databinding.BottomsheetTwoLabelBinding +import com.teamwable.ui.extensions.DeepLinkDestination +import com.teamwable.ui.extensions.deepLinkNavigateTo +import com.teamwable.ui.extensions.stringOf +import com.teamwable.ui.type.BottomSheetType +import com.teamwable.ui.util.Arg.BOTTOM_SHEET_FIRST_TYPE +import com.teamwable.ui.util.Arg.BOTTOM_SHEET_RESULT +import com.teamwable.ui.util.Arg.BOTTOM_SHEET_SECOND_TYPE +import com.teamwable.ui.util.Arg.BOTTOM_SHEET_TYPE + +class TwoLabelBottomSheet() : BindingBottomSheetFragment(BottomsheetTwoLabelBinding::inflate) { + private lateinit var firstLabelType: BottomSheetType + private lateinit var secondLabelType: BottomSheetType + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + firstLabelType = BottomSheetType.valueOf(arguments?.getString(BOTTOM_SHEET_FIRST_TYPE) ?: return) + secondLabelType = BottomSheetType.valueOf(arguments?.getString(BOTTOM_SHEET_SECOND_TYPE) ?: return) + } + + override fun initView() { + if (this::firstLabelType.isInitialized && this::secondLabelType.isInitialized) { + initLayout() + initLabelClickListener() + } + } + + private fun initLayout() { + binding.tvBottomsheetFirstLabel.text = stringOf(firstLabelType.title) + binding.tvBottomsheetSecondLabel.text = stringOf(secondLabelType.title) + } + + private fun initLabelClickListener() { + binding.tvBottomsheetFirstLabel.setOnClickListener { + setBottomSheetResult(firstLabelType.name) + } + + binding.tvBottomsheetSecondLabel.setOnClickListener { + setBottomSheetResult(secondLabelType.name) + } + } + + private fun setBottomSheetResult(type: String) { + val result = Bundle().apply { putString(BOTTOM_SHEET_TYPE, type) } + setFragmentResult(BOTTOM_SHEET_RESULT, result) + } + + companion object { + fun show( + context: Context, + navController: NavController, + bottomSheetFirstType: BottomSheetType, + bottomSheetSecondType: BottomSheetType, + ) = TwoLabelBottomSheet().apply { + navigateToBottomSheet(context, navController, bottomSheetFirstType, bottomSheetSecondType) + } + + private fun navigateToBottomSheet(context: Context, navController: NavController, bottomSheetFirstType: BottomSheetType, bottomSheetSecondType: BottomSheetType) = + navController.deepLinkNavigateTo( + context, DeepLinkDestination.TwoLabelBottomSheet, + mapOf(BOTTOM_SHEET_FIRST_TYPE to bottomSheetFirstType.name, BOTTOM_SHEET_SECOND_TYPE to bottomSheetSecondType.name), + ) + } +} diff --git a/core/ui/src/main/java/com/teamwable/ui/extensions/NavigationExt.kt b/core/ui/src/main/java/com/teamwable/ui/extensions/NavigationExt.kt index 66f7dd97..0e9a8934 100644 --- a/core/ui/src/main/java/com/teamwable/ui/extensions/NavigationExt.kt +++ b/core/ui/src/main/java/com/teamwable/ui/extensions/NavigationExt.kt @@ -12,6 +12,8 @@ sealed class DeepLinkDestination(val addressRes: Int) { data object BottomSheet : DeepLinkDestination(R.string.deeplink_url_bottomsheet) + data object TwoLabelBottomSheet : DeepLinkDestination(R.string.deeplink_url_two_label_bottomsheet) + data object Posting : DeepLinkDestination(R.string.deeplink_url_posting) data object Profile : DeepLinkDestination(R.string.deeplink_url_profile) diff --git a/core/ui/src/main/java/com/teamwable/ui/util/Constants.kt b/core/ui/src/main/java/com/teamwable/ui/util/Constants.kt index ecae28be..02a29301 100644 --- a/core/ui/src/main/java/com/teamwable/ui/util/Constants.kt +++ b/core/ui/src/main/java/com/teamwable/ui/util/Constants.kt @@ -15,6 +15,8 @@ object Arg { const val FEED_ID = "feed_id" const val CONTENT = "content" const val PROFILE_EDIT_RESULT = "profile_edit_result" + const val BOTTOM_SHEET_FIRST_TYPE = "bottom_sheet_first_type" + const val BOTTOM_SHEET_SECOND_TYPE = "bottom_sheet_second_type" } object BundleKey { diff --git a/core/ui/src/main/res/navigation/graph_ui.xml b/core/ui/src/main/res/navigation/graph_ui.xml index 995a7e2d..d15bec59 100644 --- a/core/ui/src/main/res/navigation/graph_ui.xml +++ b/core/ui/src/main/res/navigation/graph_ui.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/graph_ui" - app:startDestination="@id/navigation_two_button_dialog"> + app:startDestination="@id/navigation_two_label_bottomsheet"> + + + + diff --git a/core/ui/src/main/res/values/deeplink.xml b/core/ui/src/main/res/values/deeplink.xml index a3f07ed2..6be5f856 100644 --- a/core/ui/src/main/res/values/deeplink.xml +++ b/core/ui/src/main/res/values/deeplink.xml @@ -2,6 +2,7 @@ wable://common:ui/dialog/{dialog_type} wable://common:ui/bottomsheet/{bottom_sheet_type} + wable://common:ui/bottomsheet/{bottom_sheet_first_type}/{bottom_sheet_second_type} wable://feature:posting/posting wable://feature:profile/profile/{user_id} wable://common:ui/img/{img_url} From 9cf5dd674ebeac7f3c252eaa21fb4ec86eedea95 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Tue, 19 Nov 2024 21:08:22 +0900 Subject: [PATCH 09/28] #106 [UI] : update comment ui --- .../drawable/ic_home_detail_child_comment.xml | 11 ++++++ core/ui/src/main/res/layout/item_comment.xml | 35 ++++++++++++++++--- core/ui/src/main/res/values/strings.xml | 9 +++-- 3 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 core/common/src/main/res/drawable/ic_home_detail_child_comment.xml diff --git a/core/common/src/main/res/drawable/ic_home_detail_child_comment.xml b/core/common/src/main/res/drawable/ic_home_detail_child_comment.xml new file mode 100644 index 00000000..590e260c --- /dev/null +++ b/core/common/src/main/res/drawable/ic_home_detail_child_comment.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/ui/src/main/res/layout/item_comment.xml b/core/ui/src/main/res/layout/item_comment.xml index 136eedb8..38a894c7 100644 --- a/core/ui/src/main/res/layout/item_comment.xml +++ b/core/ui/src/main/res/layout/item_comment.xml @@ -136,21 +136,48 @@ android:id="@+id/tv_comment_like_count" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="4dp" android:textAppearance="@style/TextAppearance.Wable.Caption1" + android:textColor="@color/gray_600" app:layout_constraintBottom_toBottomOf="@id/btn_comment_like" - app:layout_constraintEnd_toStartOf="@id/spacer_comment" + app:layout_constraintStart_toEndOf="@id/btn_comment_like" app:layout_constraintTop_toTopOf="@id/btn_comment_like" tools:text="00" /> + + + + diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 4043b441..9f10ab2e 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -32,9 +32,14 @@ 투명도 %s%% · %s + + 답글쓰기 + - 답글 남기는 중 - 답글을 남겼어요 + 댓글 남기는 중 + 댓글을 남겼어요 + 답글 남기는 중 + 답글을 남겼어요 덕분에 와블이 더 온화해지고 있어요! 신고 접수가 완료되었어요.\n24시간 내에 조치할 예정이에요. From 07f5ed6eebf4f5147535ebeb839ac720c78eed44 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:57:00 +0900 Subject: [PATCH 10/28] #106 [FEAT] : add ban action at feedactionhandler --- .../com/teamwable/ui/component/Snackbar.kt | 2 +- .../ui/shareAdapter/FeedViewHolder.kt | 10 ++++++-- .../teamwable/ui/util/FeedActionHandler.kt | 23 ++++++++++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/core/ui/src/main/java/com/teamwable/ui/component/Snackbar.kt b/core/ui/src/main/java/com/teamwable/ui/component/Snackbar.kt index f83d9ee5..96352dd2 100644 --- a/core/ui/src/main/java/com/teamwable/ui/component/Snackbar.kt +++ b/core/ui/src/main/java/com/teamwable/ui/component/Snackbar.kt @@ -54,7 +54,7 @@ class Snackbar(private val view: View, private val type: SnackbarType) { fun show() { snackbar.show() - if (type in listOf(SnackbarType.GHOST, SnackbarType.REPORT)) dismissSnackbar(2000) + if (type in listOf(SnackbarType.GHOST, SnackbarType.REPORT, SnackbarType.BAN)) dismissSnackbar(2000) } private fun dismissSnackbar(duration: Long) { diff --git a/core/ui/src/main/java/com/teamwable/ui/shareAdapter/FeedViewHolder.kt b/core/ui/src/main/java/com/teamwable/ui/shareAdapter/FeedViewHolder.kt index 1c330e44..9a3b97ec 100644 --- a/core/ui/src/main/java/com/teamwable/ui/shareAdapter/FeedViewHolder.kt +++ b/core/ui/src/main/java/com/teamwable/ui/shareAdapter/FeedViewHolder.kt @@ -37,8 +37,8 @@ class FeedViewHolder private constructor( fun bind(feed: Feed?) { item = feed ?: return - val isImageInclude = feed.image.isNotBlank() - val isContentBlank = feed.content.isBlank() + val isImageInclude = feed.image.isNotBlank() && !feed.isBlind + val isContentBlank = feed.content.isBlank() || feed.isBlind with(binding) { ivFeedProfileImg.load(feed.postAuthorProfile) tvFeedNickname.text = feed.postAuthorNickname @@ -60,9 +60,15 @@ class FeedViewHolder private constructor( btnFeedGhost.isEnabled = !feed.isPostAuthorGhost viewFeedTransparentBg.setBackgroundColor(Color.parseColor(feed.ghostColor)) btnFeedGhost.visible(!feed.isAuth) + setBlindVisible(feed.isBlind) } } + private fun setBlindVisible(isBlind: Boolean) = with(binding) { + groupFeedBlind.visible(isBlind) + tvFeedTitle.visible(!isBlind) + } + companion object { fun from(parent: ViewGroup, feedClickListener: FeedClickListener): FeedViewHolder = FeedViewHolder( diff --git a/core/ui/src/main/java/com/teamwable/ui/util/FeedActionHandler.kt b/core/ui/src/main/java/com/teamwable/ui/util/FeedActionHandler.kt index 38016e1c..5722f916 100644 --- a/core/ui/src/main/java/com/teamwable/ui/util/FeedActionHandler.kt +++ b/core/ui/src/main/java/com/teamwable/ui/util/FeedActionHandler.kt @@ -13,7 +13,9 @@ import com.teamwable.model.LikeState import com.teamwable.ui.component.BottomSheet import com.teamwable.ui.component.FeedImageDialog import com.teamwable.ui.component.TwoButtonDialog +import com.teamwable.ui.component.TwoLabelBottomSheet import com.teamwable.ui.shareAdapter.FeedViewHolder +import com.teamwable.ui.type.BanTriggerType import com.teamwable.ui.type.BottomSheetType import com.teamwable.ui.type.DialogType import com.teamwable.ui.type.ProfileUserType @@ -29,10 +31,17 @@ class FeedActionHandler( private val fragmentManager: FragmentManager, private val lifecycleOwner: LifecycleOwner, ) { - fun onKebabBtnClick(feed: Feed, fetchUserType: (Long) -> ProfileUserType, removeFeed: (Long) -> Unit, reportUser: (String, String) -> Unit) { + fun onKebabBtnClick( + feed: Feed, + fetchUserType: (Long) -> ProfileUserType, + removeFeed: (Long) -> Unit, + reportUser: (String, String) -> Unit, + banUser: (Feed, String) -> Unit, + ) { when (fetchUserType(feed.postAuthorId)) { ProfileUserType.AUTH -> navigateToBottomSheet(BottomSheetType.DELETE_FEED) ProfileUserType.MEMBER -> navigateToBottomSheet(BottomSheetType.REPORT) + ProfileUserType.ADMIN -> navigateToTwoLabelBottomSheet(BottomSheetType.REPORT, BottomSheetType.BAN) ProfileUserType.EMPTY -> return } handleDialogResult { dialogType -> @@ -41,11 +50,17 @@ class FeedActionHandler( trackEvent(CLICK_DELETE_POST) removeFeed(feed.feedId) } + DialogType.REPORT -> { navController.popBackStack() reportUser(feed.postAuthorNickname, "${feed.title}\n${feed.content}") } + DialogType.BAN -> { + navController.popBackStack() + banUser(feed, BanTriggerType.CONTENT.name.lowercase()) + } + else -> Unit } } @@ -85,11 +100,17 @@ class FeedActionHandler( handleBottomSheetResult() } + private fun navigateToTwoLabelBottomSheet(firstType: BottomSheetType, secondType: BottomSheetType) { + TwoLabelBottomSheet.show(context, navController, firstType, secondType) + handleBottomSheetResult() + } + private fun handleBottomSheetResult() { fragmentManager.setFragmentResultListener(BOTTOM_SHEET_RESULT, lifecycleOwner) { _, bundle -> when (bundle.getString(BOTTOM_SHEET_TYPE)) { BottomSheetType.DELETE_FEED.name -> navigateToDialog(DialogType.DELETE_FEED) BottomSheetType.REPORT.name -> navigateToDialog(DialogType.REPORT) + BottomSheetType.BAN.name -> navigateToDialog(DialogType.BAN) } } } From 81270dc17013fa7c2728b5c42ae958832a9b0163 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 16:59:45 +0900 Subject: [PATCH 11/28] #106 [FEAT] : set up to post ban api --- .../java/com/teamwable/home/HomeFragment.kt | 6 ++-- .../java/com/teamwable/home/HomeViewModel.kt | 28 +++++++++++++++++-- .../homedetail/HomeDetailFragment.kt | 3 +- .../homedetail/HomeDetailViewModel.kt | 22 ++++++++++++++- .../profiletabs/ProfileFeedListFragment.kt | 3 +- .../profiletabs/ProfileFeedListViewModel.kt | 15 ++++++++-- 6 files changed, 68 insertions(+), 9 deletions(-) diff --git a/feature/home/src/main/java/com/teamwable/home/HomeFragment.kt b/feature/home/src/main/java/com/teamwable/home/HomeFragment.kt index 2c1a61db..ac1569c2 100644 --- a/feature/home/src/main/java/com/teamwable/home/HomeFragment.kt +++ b/feature/home/src/main/java/com/teamwable/home/HomeFragment.kt @@ -130,6 +130,7 @@ class HomeFragment : BindingFragment(FragmentHomeBinding::i fetchUserType = { viewModel.fetchUserType(it) }, removeFeed = { viewModel.removeFeed(it) }, reportUser = { nickname, content -> viewModel.reportUser(nickname, content) }, + banUser = { trigger, banType -> viewModel.banUser(Triple(trigger.postAuthorId, banType, trigger.feedId)) }, ) } @@ -139,8 +140,8 @@ class HomeFragment : BindingFragment(FragmentHomeBinding::i private fun handleProfileNavigation(id: Long) { when (viewModel.fetchUserType(id)) { ProfileUserType.AUTH -> (activity as Navigation).navigateToProfileAuthFragment() - ProfileUserType.MEMBER -> findNavController().deepLinkNavigateTo(requireContext(), DeepLinkDestination.Profile, mapOf(PROFILE_USER_ID to id)) - ProfileUserType.EMPTY -> return + in setOf(ProfileUserType.MEMBER, ProfileUserType.ADMIN) -> findNavController().deepLinkNavigateTo(requireContext(), DeepLinkDestination.Profile, mapOf(PROFILE_USER_ID to id)) + else -> return } } @@ -219,6 +220,7 @@ class HomeFragment : BindingFragment(FragmentHomeBinding::i if (isRemoved) viewModel.updateFeedRemoveState(feed.feedId) viewModel.updateFeedGhostState(feed.postAuthorId, feed.isPostAuthorGhost) viewModel.updateFeedLikeState(feed.feedId, LikeState(feed.isLiked, feed.likedNumber)) + viewModel.updateFeedBanState(feed.feedId, feed.isBlind) } } diff --git a/feature/home/src/main/java/com/teamwable/home/HomeViewModel.kt b/feature/home/src/main/java/com/teamwable/home/HomeViewModel.kt index 8c64b26b..3227b233 100644 --- a/feature/home/src/main/java/com/teamwable/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/teamwable/home/HomeViewModel.kt @@ -44,13 +44,16 @@ class HomeViewModel @Inject constructor( private val removedFeedsFlow = MutableStateFlow(setOf()) private val ghostedFeedsFlow = MutableStateFlow(setOf()) private val likeFeedsFlow = MutableStateFlow(mapOf()) + private val banFeedsFlow = MutableStateFlow(setOf()) private val feedsFlow = feedRepository.getHomeFeeds().cachedIn(viewModelScope) private var authId = -1L + private var isAdmin = false init { fetchAuthId() fetchIsPushAlarmAllowed() + fetchIsAdmin() } private fun fetchAuthId() { @@ -63,6 +66,11 @@ class HomeViewModel @Inject constructor( } } + private fun fetchIsAdmin() = viewModelScope.launch { + userInfoRepository.getIsAdmin() + .collectLatest { isAdmin = it } + } + private fun fetchIsPushAlarmAllowed() { viewModelScope.launch { userInfoRepository.getIsPushAlarmAllowed().collectLatest { @@ -81,13 +89,14 @@ class HomeViewModel @Inject constructor( } fun updateFeeds(): Flow> { - return combine(feedsFlow, removedFeedsFlow, ghostedFeedsFlow, likeFeedsFlow) { feedsFlow, removedFeedIds, ghostedUserIds, likeStates -> + return combine(feedsFlow, removedFeedsFlow, ghostedFeedsFlow, likeFeedsFlow, banFeedsFlow) { feedsFlow, removedFeedIds, ghostedUserIds, likeStates, banState -> feedsFlow .filter { removedFeedIds.contains(it.feedId).not() } .map { data -> val likeState = likeStates[data.feedId] ?: LikeState(data.isLiked, data.likedNumber) val transformedGhost = if (ghostedUserIds.contains(data.postAuthorId)) data.copy(isPostAuthorGhost = true) else data - transformedGhost.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) + val transformedBan = if (banState.contains(data.feedId)) transformedGhost.copy(isBlind = true) else transformedGhost + transformedBan.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) } } } @@ -100,6 +109,8 @@ class HomeViewModel @Inject constructor( ProfileUserType.EMPTY } + isAdmin -> ProfileUserType.ADMIN + else -> ProfileUserType.MEMBER } } @@ -172,6 +183,19 @@ class HomeViewModel @Inject constructor( userInfoRepository.saveIsPushAlarmAllowed(isPushAlarmAllowed) } } + + fun banUser(banInfo: Triple) = viewModelScope.launch { + profileRepository.postBan(banInfo) + .onSuccess { + updateFeedBanState(feedId = banInfo.third, isBan = true) + _event.emit(HomeSideEffect.ShowSnackBar(SnackbarType.BAN)) + } + .onFailure { _uiState.value = HomeUiState.Error(it.message.toString()) } + } + + fun updateFeedBanState(feedId: Long, isBan: Boolean) { + banFeedsFlow.update { it.toMutableSet().apply { if (isBan) add(feedId) } } + } } sealed interface HomeUiState { diff --git a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt index 060b1b59..3de50803 100644 --- a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt +++ b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt @@ -212,6 +212,7 @@ class HomeDetailFragment : BindingFragment(FragmentHo fetchUserType = { viewModel.fetchUserType(it) }, removeFeed = { viewModel.removeFeed(it) }, reportUser = { nickname, content -> viewModel.reportUser(nickname, content) }, + banUser = { trigger, banType -> viewModel.banUser(Triple(trigger.postAuthorId, banType, trigger.feedId)) }, ) } @@ -257,7 +258,7 @@ class HomeDetailFragment : BindingFragment(FragmentHo when (viewModel.fetchUserType(id)) { ProfileUserType.AUTH -> (activity as Navigation).navigateToProfileAuthFragment() ProfileUserType.MEMBER -> findNavController().deepLinkNavigateTo(requireContext(), DeepLinkDestination.Profile, mapOf(PROFILE_USER_ID to id)) - ProfileUserType.EMPTY -> return + else -> return } } diff --git a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt index c4df4bd9..cf29ecf3 100644 --- a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt +++ b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt @@ -47,11 +47,14 @@ class HomeDetailViewModel @Inject constructor( private val removedCommentsFlow = MutableStateFlow(setOf()) private val ghostedFlow = MutableStateFlow(setOf()) private val likeCommentsFlow = MutableStateFlow(mapOf()) + private val banFeedsFlow = MutableStateFlow(setOf()) private var authId: Long = -1 + private var isAdmin = false init { fetchAuthId() + fetchIsAdmin() } private fun fetchAuthId() { @@ -62,13 +65,19 @@ class HomeDetailViewModel @Inject constructor( } } + private fun fetchIsAdmin() = viewModelScope.launch { + userInfoRepository.getIsAdmin() + .collectLatest { isAdmin = it } + } + fun updateHomeDetailToFlow(feed: Feed): Flow> { val feedFlow = flowOf(PagingData.from(listOf(feed))).cachedIn(viewModelScope) - return combine(feedFlow, ghostedFlow, likeFeedsFlow) { feedsFlow, ghostedUserIds, likeStates -> + return combine(feedFlow, ghostedFlow, likeFeedsFlow, banFeedsFlow) { feedsFlow, ghostedUserIds, likeStates, banState -> feedsFlow .map { data -> val likeState = likeStates[data.feedId] ?: LikeState(data.isLiked, data.likedNumber) val transformedGhost = if (ghostedUserIds.contains(data.postAuthorId)) data.copy(isPostAuthorGhost = true) else data + val transformedBan = if (banState.contains(data.feedId)) transformedGhost.copy(isBlind = true) else transformedGhost transformedGhost.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) } } @@ -95,6 +104,8 @@ class HomeDetailViewModel @Inject constructor( ProfileUserType.EMPTY } + isAdmin -> ProfileUserType.ADMIN + else -> ProfileUserType.MEMBER } } @@ -178,6 +189,15 @@ class HomeDetailViewModel @Inject constructor( .onFailure { _uiState.value = HomeDetailUiState.Error(it.message.toString()) } } } + + fun banUser(banInfo: Triple) = viewModelScope.launch { + profileRepository.postBan(banInfo) + .onSuccess { + banFeedsFlow.value = banFeedsFlow.value.toMutableSet().apply { add(banInfo.third) } + _event.emit(HomeDetailSideEffect.ShowSnackBar(SnackbarType.BAN)) + } + .onFailure { _uiState.value = HomeDetailUiState.Error(it.message.toString()) } + } } sealed interface HomeDetailUiState { diff --git a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListFragment.kt b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListFragment.kt index 429a555d..378b9ef4 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListFragment.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListFragment.kt @@ -116,6 +116,7 @@ class ProfileFeedListFragment : BindingFragment(Frag fetchUserType = { userType }, removeFeed = { viewModel.removeFeed(it) }, reportUser = { nickname, content -> viewModel.reportUser(nickname, content) }, + banUser = { trigger, banType -> viewModel.banUser(Triple(trigger.postAuthorId, banType, trigger.feedId)) }, ) } @@ -163,7 +164,7 @@ class ProfileFeedListFragment : BindingFragment(Frag tvProfileFeedMemberEmpty.visible(isEmpty) } - ProfileUserType.EMPTY -> return + else -> return } } diff --git a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListViewModel.kt b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListViewModel.kt index 16d39108..d0bacb18 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListViewModel.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileFeedListViewModel.kt @@ -38,16 +38,18 @@ class ProfileFeedListViewModel @Inject constructor( private val removedFeedsFlow = MutableStateFlow(setOf()) private val ghostedFeedsFlow = MutableStateFlow(setOf()) private val likeFeedsFlow = MutableStateFlow(mapOf()) + private val banFeedsFlow = MutableStateFlow(setOf()) fun updateFeeds(userId: Long): Flow> { val feedsFlow = feedRepository.getProfileFeeds(userId).cachedIn(viewModelScope) - return combine(feedsFlow, removedFeedsFlow, ghostedFeedsFlow, likeFeedsFlow) { feedsFlow, removedFeedIds, ghostedUserIds, likeStates -> + return combine(feedsFlow, removedFeedsFlow, ghostedFeedsFlow, likeFeedsFlow, banFeedsFlow) { feedsFlow, removedFeedIds, ghostedUserIds, likeStates, banState -> feedsFlow .filter { removedFeedIds.contains(it.feedId).not() } .map { data -> val likeState = likeStates[data.feedId] ?: LikeState(data.isLiked, data.likedNumber) val transformedGhost = if (ghostedUserIds.contains(data.postAuthorId)) data.copy(isPostAuthorGhost = true) else data - transformedGhost.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) + val transformedBan = if (banState.contains(data.feedId)) transformedGhost.copy(isBlind = true) else transformedGhost + transformedBan.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) } } } @@ -94,6 +96,15 @@ class ProfileFeedListViewModel @Inject constructor( .onFailure { _uiState.value = ProfileFeedUiState.Error(it.message.toString()) } } } + + fun banUser(banInfo: Triple) = viewModelScope.launch { + profileRepository.postBan(banInfo) + .onSuccess { + banFeedsFlow.value = banFeedsFlow.value.toMutableSet().apply { add(banInfo.third) } + _event.emit(ProfileFeedSideEffect.ShowSnackBar(SnackbarType.BAN)) + } + .onFailure { _uiState.value = ProfileFeedUiState.Error(it.message.toString()) } + } } sealed interface ProfileFeedUiState { From 2f3a66b7f0fbdf53375474106f769eee255ef63c Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:00:50 +0900 Subject: [PATCH 12/28] #106 [FEAT] : add admin branch at profile of member --- .../profile/profile/ProfileMemberFragment.kt | 3 +-- .../profile/profile/ProfileMemberViewModel.kt | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberFragment.kt b/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberFragment.kt index f111cc17..4b1325da 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberFragment.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberFragment.kt @@ -13,7 +13,6 @@ import com.teamwable.ui.extensions.stringOf import com.teamwable.ui.extensions.viewLifeCycle import com.teamwable.ui.extensions.viewLifeCycleScope import com.teamwable.ui.extensions.visible -import com.teamwable.ui.type.ProfileUserType import com.teamwable.ui.util.Arg import com.teamwable.ui.util.Navigation import dagger.hilt.android.AndroidEntryPoint @@ -69,7 +68,7 @@ class ProfileMemberFragment : BindingProfileFragment() { } private fun setProfilePagerAdapter(data: Profile) { - binding.vpProfile.adapter = ProfilePagerStateAdapter(this, data.id, data.nickName, ProfileUserType.MEMBER) + binding.vpProfile.adapter = ProfilePagerStateAdapter(this, data.id, data.nickName, viewModel.fetchUserType()) TabLayoutMediator( binding.tlProfile, binding.vpProfile, ) { tab, position -> diff --git a/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberViewModel.kt b/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberViewModel.kt index 5a21782b..76b412e7 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberViewModel.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profile/ProfileMemberViewModel.kt @@ -3,20 +3,30 @@ package com.teamwable.profile.profile import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.teamwable.data.repository.ProfileRepository +import com.teamwable.data.repository.UserInfoRepository import com.teamwable.model.Profile +import com.teamwable.ui.type.ProfileUserType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ProfileMemberViewModel @Inject constructor( private val profileRepository: ProfileRepository, + private val userInfoRepository: UserInfoRepository, ) : ViewModel() { private val _uiState = MutableStateFlow(ProfileMemberUiState.Loading) val uiState = _uiState.asStateFlow() + private var isAdmin = false + + init { + fetchIsAdmin() + } + fun fetchProfileInfo(userId: Long) { viewModelScope.launch { profileRepository.getProfileInfo(userId) @@ -26,6 +36,16 @@ class ProfileMemberViewModel @Inject constructor( .onFailure { _uiState.value = ProfileMemberUiState.Error(it.message.toString()) } } } + + private fun fetchIsAdmin() = viewModelScope.launch { + userInfoRepository.getIsAdmin() + .collectLatest { isAdmin = it } + } + + fun fetchUserType(): ProfileUserType = when (isAdmin) { + true -> ProfileUserType.ADMIN + false -> ProfileUserType.MEMBER + } } sealed interface ProfileMemberUiState { From 06160f407fb6c40e2f4bb79db2d40279a2a733a3 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:01:11 +0900 Subject: [PATCH 13/28] #106 [ADD] : add string of ban --- core/ui/src/main/res/values/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 9f10ab2e..4a454d95 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -26,11 +26,15 @@ 나가기 취소 + 밴하시겠어요? + 1회 누적 - 해당 글(게시글 or 댓글 or 답글)만 블라인드 처리\n2회 누적 - 해당 사용자 글 전체 블라인드 처리 + 밴하기 피드 이미지입니다. 투명도 %s%% · %s + 블라인드 처리된 게시글이에요. 답글쓰기 @@ -42,6 +46,7 @@ 답글을 남겼어요 덕분에 와블이 더 온화해지고 있어요! 신고 접수가 완료되었어요.\n24시간 내에 조치할 예정이에요. + 밴 ! 완 ! 료 ! commentGhost From 1f6c0ee79d07628a3f7dcd4ea3834d4e47456adf Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:46:15 +0900 Subject: [PATCH 14/28] #106 [UI] : set up comment blind ui --- core/ui/src/main/res/layout/item_comment.xml | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/ui/src/main/res/layout/item_comment.xml b/core/ui/src/main/res/layout/item_comment.xml index 38a894c7..a9154f86 100644 --- a/core/ui/src/main/res/layout/item_comment.xml +++ b/core/ui/src/main/res/layout/item_comment.xml @@ -88,7 +88,7 @@ android:textColor="@color/gray_800" app:layout_constraintEnd_toEndOf="@id/guideline_end" app:layout_constraintStart_toStartOf="@id/tv_comment_ghost_level" - app:layout_constraintTop_toBottomOf="@id/tv_comment_ghost_level" + app:layout_constraintTop_toBottomOf="@id/tv_comment_blind" tools:text="@string/dummy" /> + + From 03c1161deedb4147d4416517078e3cf203152a6b Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:47:18 +0900 Subject: [PATCH 15/28] #106 [FEAT] : add isBlind value at comment dto and update profile comment list api version --- .../teamwable/data/mapper/toModel/ResponseCommentDtoMapper.kt | 1 + core/model/src/main/java/com/teamwable/model/Comment.kt | 1 + .../java/com/teamwable/network/datasource/CommentService.kt | 2 +- .../com/teamwable/network/dto/response/ResponseCommentDto.kt | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseCommentDtoMapper.kt b/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseCommentDtoMapper.kt index 37f074a9..d80b5317 100644 --- a/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseCommentDtoMapper.kt +++ b/core/data/src/main/java/com/teamwable/data/mapper/toModel/ResponseCommentDtoMapper.kt @@ -17,4 +17,5 @@ internal fun ResponseCommentDto.toComment(): Comment = this.time, this.memberFanTeam, this.contentId, + this.isBlind ?: false, ) diff --git a/core/model/src/main/java/com/teamwable/model/Comment.kt b/core/model/src/main/java/com/teamwable/model/Comment.kt index 905604f2..65339e78 100644 --- a/core/model/src/main/java/com/teamwable/model/Comment.kt +++ b/core/model/src/main/java/com/teamwable/model/Comment.kt @@ -13,6 +13,7 @@ data class Comment( val uploadTime: String, val postAuthorTeamTag: String, val feedId: Long?, + val isBlind: Boolean, val ghostColor: String = GhostColor.DEFAULT_0, val isAuth: Boolean = false, ) diff --git a/core/network/src/main/java/com/teamwable/network/datasource/CommentService.kt b/core/network/src/main/java/com/teamwable/network/datasource/CommentService.kt index 19e352d2..33f544a1 100644 --- a/core/network/src/main/java/com/teamwable/network/datasource/CommentService.kt +++ b/core/network/src/main/java/com/teamwable/network/datasource/CommentService.kt @@ -20,7 +20,7 @@ interface CommentService { @Query(value = "cursor") cursor: Long = -1, ): BaseResponse> - @GET("api/v2/member/{memberId}/comments") + @GET("api/v3/member/{memberId}/comments") suspend fun getProfileComments( @Path(value = "memberId") contentId: Long, @Query(value = "cursor") cursor: Long = -1, diff --git a/core/network/src/main/java/com/teamwable/network/dto/response/ResponseCommentDto.kt b/core/network/src/main/java/com/teamwable/network/dto/response/ResponseCommentDto.kt index c0098b82..0af3a719 100644 --- a/core/network/src/main/java/com/teamwable/network/dto/response/ResponseCommentDto.kt +++ b/core/network/src/main/java/com/teamwable/network/dto/response/ResponseCommentDto.kt @@ -19,4 +19,5 @@ data class ResponseCommentDto( @SerialName("commentImageUrl") val commentImageUrl: String? = null, @SerialName("memberFanTeam") val memberFanTeam: String, @SerialName("contentId") val contentId: Long? = null, + @SerialName("isBlind") val isBlind: Boolean? = null, // TODO::임시로 nullable 받음 대댓글 구현후 non-null 변경 예정 ) From e0f4d047041be8ffe860893016a2c8cf653733d7 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:48:13 +0900 Subject: [PATCH 16/28] #106 [FEAT] : add ban action at comment action handler --- .../ui/shareAdapter/CommentViewHolder.kt | 6 +++++ .../teamwable/ui/util/CommentActionHandler.kt | 22 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/teamwable/ui/shareAdapter/CommentViewHolder.kt b/core/ui/src/main/java/com/teamwable/ui/shareAdapter/CommentViewHolder.kt index bbfeea30..d8a65209 100644 --- a/core/ui/src/main/java/com/teamwable/ui/shareAdapter/CommentViewHolder.kt +++ b/core/ui/src/main/java/com/teamwable/ui/shareAdapter/CommentViewHolder.kt @@ -49,9 +49,15 @@ class CommentViewHolder private constructor( viewCommentTransparentBg.setBackgroundColor(Color.parseColor(comment.ghostColor)) btnCommentGhost.visible(!comment.isAuth) spacerComment.visible(!comment.isAuth) + setBlindVisible(comment.isBlind) } } + private fun setBlindVisible(isBlind: Boolean) = with(binding) { + tvCommentContent.visible(!isBlind) + tvCommentBlind.visible(isBlind) + } + companion object { fun from(parent: ViewGroup, commentClickListener: CommentClickListener): CommentViewHolder = CommentViewHolder( diff --git a/core/ui/src/main/java/com/teamwable/ui/util/CommentActionHandler.kt b/core/ui/src/main/java/com/teamwable/ui/util/CommentActionHandler.kt index 021dac0c..5287693e 100644 --- a/core/ui/src/main/java/com/teamwable/ui/util/CommentActionHandler.kt +++ b/core/ui/src/main/java/com/teamwable/ui/util/CommentActionHandler.kt @@ -11,7 +11,9 @@ import com.teamwable.model.Comment import com.teamwable.model.LikeState import com.teamwable.ui.component.BottomSheet import com.teamwable.ui.component.TwoButtonDialog +import com.teamwable.ui.component.TwoLabelBottomSheet import com.teamwable.ui.shareAdapter.CommentViewHolder +import com.teamwable.ui.type.BanTriggerType import com.teamwable.ui.type.BottomSheetType import com.teamwable.ui.type.DialogType import com.teamwable.ui.type.ProfileUserType @@ -26,10 +28,17 @@ class CommentActionHandler( private val fragmentManager: FragmentManager, private val lifecycleOwner: LifecycleOwner, ) { - fun onKebabBtnClick(comment: Comment, fetchUserType: (Long) -> ProfileUserType, removeComment: (Long) -> Unit, reportUser: (String, String) -> Unit) { + fun onKebabBtnClick( + comment: Comment, + fetchUserType: (Long) -> ProfileUserType, + removeComment: (Long) -> Unit, + reportUser: (String, String) -> Unit, + banUser: (Comment, String) -> Unit, + ) { when (fetchUserType(comment.postAuthorId)) { ProfileUserType.AUTH -> navigateToBottomSheet(BottomSheetType.DELETE_FEED) ProfileUserType.MEMBER -> navigateToBottomSheet(BottomSheetType.REPORT) + ProfileUserType.ADMIN -> navigateToTwoLabelBottomSheet(BottomSheetType.REPORT, BottomSheetType.BAN) ProfileUserType.EMPTY -> return } handleDialogResult { dialogType -> @@ -40,6 +49,11 @@ class CommentActionHandler( reportUser(comment.postAuthorNickname, comment.content) } + DialogType.BAN -> { + navController.popBackStack() + banUser(comment, BanTriggerType.COMMENT.name.lowercase()) + } + else -> Unit } } @@ -74,11 +88,17 @@ class CommentActionHandler( handleBottomSheetResult() } + private fun navigateToTwoLabelBottomSheet(firstType: BottomSheetType, secondType: BottomSheetType) { + TwoLabelBottomSheet.show(context, navController, firstType, secondType) + handleBottomSheetResult() + } + private fun handleBottomSheetResult() { fragmentManager.setFragmentResultListener(BOTTOM_SHEET_RESULT, lifecycleOwner) { _, bundle -> when (bundle.getString(BOTTOM_SHEET_TYPE)) { BottomSheetType.DELETE_FEED.name -> navigateToDialog(DialogType.DELETE_FEED) BottomSheetType.REPORT.name -> navigateToDialog(DialogType.REPORT) + BottomSheetType.BAN.name -> navigateToDialog(DialogType.BAN) } } } From af0cc777e5a24f0fa7d6874123a411d35a7f29bb Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:49:12 +0900 Subject: [PATCH 17/28] #106 [FEAT] : set up to post ban api at profile comment list --- .../teamwable/homedetail/HomeDetailFragment.kt | 1 + .../profiletabs/ProfileCommentListFragment.kt | 3 ++- .../profiletabs/ProfileCommentListViewModel.kt | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt index 3de50803..3604ecbb 100644 --- a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt +++ b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt @@ -246,6 +246,7 @@ class HomeDetailFragment : BindingFragment(FragmentHo fetchUserType = { viewModel.fetchUserType(it) }, removeComment = { viewModel.removeComment(it) }, reportUser = { nickname, content -> viewModel.reportUser(nickname, content) }, + banUser = { trigger, banType -> }, // TODO::대댓글 구현 후 구현 ) } diff --git a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListFragment.kt b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListFragment.kt index 33d5169d..8fd34089 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListFragment.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListFragment.kt @@ -106,6 +106,7 @@ class ProfileCommentListFragment : BindingFragment viewModel.reportUser(nickname, content) }, + banUser = { trigger, banType -> viewModel.banUser(Triple(trigger.postAuthorId, banType, trigger.commentId)) }, ) } @@ -151,7 +152,7 @@ class ProfileCommentListFragment : BindingFragment return + else -> return } } diff --git a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListViewModel.kt b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListViewModel.kt index c4eaebc4..77324495 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListViewModel.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profiletabs/ProfileCommentListViewModel.kt @@ -38,16 +38,18 @@ class ProfileCommentListViewModel @Inject constructor( private val removedCommentsFlow = MutableStateFlow(setOf()) private val ghostedCommentsFlow = MutableStateFlow(setOf()) private val likeCommentsFlow = MutableStateFlow(mapOf()) + private val banFeedsFlow = MutableStateFlow(setOf()) fun updateComments(userId: Long): Flow> { val commentsFlow = commentRepository.getProfileComments(userId).cachedIn(viewModelScope) - return combine(commentsFlow, removedCommentsFlow, ghostedCommentsFlow, likeCommentsFlow) { commentsFlow, removedCommentIds, ghostedUserIds, likeStates -> + return combine(commentsFlow, removedCommentsFlow, ghostedCommentsFlow, likeCommentsFlow, banFeedsFlow) { commentsFlow, removedCommentIds, ghostedUserIds, likeStates, banState -> commentsFlow .filter { removedCommentIds.contains(it.commentId).not() } .map { data -> val likeState = likeStates[data.commentId] ?: LikeState(data.isLiked, data.likedNumber) val transformedGhost = if (ghostedUserIds.contains(data.postAuthorId)) data.copy(isPostAuthorGhost = true) else data - transformedGhost.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) + val transformedBan = if (banState.contains(data.commentId)) transformedGhost.copy(isBlind = true) else transformedGhost + transformedBan.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) } } } @@ -94,6 +96,15 @@ class ProfileCommentListViewModel @Inject constructor( .onFailure { _uiState.value = ProfileCommentUiState.Error(it.message.toString()) } } } + + fun banUser(banInfo: Triple) = viewModelScope.launch { + profileRepository.postBan(banInfo) + .onSuccess { + banFeedsFlow.value = banFeedsFlow.value.toMutableSet().apply { add(banInfo.third) } + _event.emit(ProfileCommentSideEffect.ShowSnackBar(SnackbarType.BAN)) + } + .onFailure { _uiState.value = ProfileCommentUiState.Error(it.message.toString()) } + } } sealed interface ProfileCommentUiState { From 0d40b1e063b482046153b605f21986d8705d5c27 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 17:49:30 +0900 Subject: [PATCH 18/28] #106 [ADD] : add string of comment ban --- core/ui/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 4a454d95..c957ee97 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ 답글쓰기 + 블라인드 처리된 내용이에요. 댓글 남기는 중 From 9a595de17db6df5474ab8a4fd5df1da0b8871faa Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 20:54:45 +0900 Subject: [PATCH 19/28] #106 [UI] : set up profile level ui --- .../main/res/drawable/ic_profile_level.xml | 44 +++++++++++++++++++ .../src/main/res/layout/fragment_profile.xml | 20 +++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 core/common/src/main/res/drawable/ic_profile_level.xml diff --git a/core/common/src/main/res/drawable/ic_profile_level.xml b/core/common/src/main/res/drawable/ic_profile_level.xml new file mode 100644 index 00000000..0f18bb7b --- /dev/null +++ b/core/common/src/main/res/drawable/ic_profile_level.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + diff --git a/feature/profile/src/main/res/layout/fragment_profile.xml b/feature/profile/src/main/res/layout/fragment_profile.xml index dc9fda88..bdd467d6 100644 --- a/feature/profile/src/main/res/layout/fragment_profile.xml +++ b/feature/profile/src/main/res/layout/fragment_profile.xml @@ -58,18 +58,30 @@ tools:src="@drawable/img_empty" /> + + Date: Thu, 21 Nov 2024 20:55:22 +0900 Subject: [PATCH 20/28] #106 [DEL] : remove unnecessary file --- .../res/drawable/ic_profile_tag_level_1.xml | 50 ------------------- 1 file changed, 50 deletions(-) delete mode 100644 core/common/src/main/res/drawable/ic_profile_tag_level_1.xml diff --git a/core/common/src/main/res/drawable/ic_profile_tag_level_1.xml b/core/common/src/main/res/drawable/ic_profile_tag_level_1.xml deleted file mode 100644 index 8da0ebb1..00000000 --- a/core/common/src/main/res/drawable/ic_profile_tag_level_1.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - From dd6932151eb48b5918c8a1107df4bbae4132713d Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 20:58:33 +0900 Subject: [PATCH 21/28] #106 [FEAT] : set up level text --- .../java/com/teamwable/profile/profile/BindingProfileFragment.kt | 1 + feature/profile/src/main/res/values/strings.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/feature/profile/src/main/java/com/teamwable/profile/profile/BindingProfileFragment.kt b/feature/profile/src/main/java/com/teamwable/profile/profile/BindingProfileFragment.kt index c9464406..60ba17c8 100644 --- a/feature/profile/src/main/java/com/teamwable/profile/profile/BindingProfileFragment.kt +++ b/feature/profile/src/main/java/com/teamwable/profile/profile/BindingProfileFragment.kt @@ -48,6 +48,7 @@ abstract class BindingProfileFragment : Fragment() { tvProfileNickname.text = data.nickName tvProfileInfo.text = getString(R.string.label_profile_info, data.teamTag.ifBlank { TeamTag.LCK.name }, data.lckYears) tvProfileGhostPercentage.text = getString(R.string.label_ghost_percentage, data.ghost) + tvProfileLevel.text = getString(R.string.label_profile_level, data.level) setSwipeLayout() setGhostProgress(data.ghost) } diff --git a/feature/profile/src/main/res/values/strings.xml b/feature/profile/src/main/res/values/strings.xml index 661f736f..b200cb5c 100644 --- a/feature/profile/src/main/res/values/strings.xml +++ b/feature/profile/src/main/res/values/strings.xml @@ -59,6 +59,7 @@ 댓글 아직 작성한 댓글이 없어요. 아직 %s님이\n댓글을 작성하지 않았어요. + LV. %d 프로필 편집 From c8f60f6fa935ff773ffa31f7b11123279dd819e5 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Thu, 21 Nov 2024 22:13:29 +0900 Subject: [PATCH 22/28] #106 [FEAT] : change home loading view --- .../src/main/java/com/teamwable/main/MainActivity.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/feature/main/src/main/java/com/teamwable/main/MainActivity.kt b/feature/main/src/main/java/com/teamwable/main/MainActivity.kt index 2bf687b3..6bfedccf 100644 --- a/feature/main/src/main/java/com/teamwable/main/MainActivity.kt +++ b/feature/main/src/main/java/com/teamwable/main/MainActivity.kt @@ -156,6 +156,7 @@ class MainActivity : AppCompatActivity(), Navigation { R.id.navigation_error, com.teamwable.ui.R.id.navigation_two_button_dialog, com.teamwable.profile.R.id.navigation_profile_edit, + com.teamwable.ui.R.id.navigation_two_label_bottomsheet, ), ) } @@ -184,13 +185,14 @@ class MainActivity : AppCompatActivity(), Navigation { } private fun initBottomNaviSelectedListener(navController: NavController) { + var previousTabId = R.id.graph_home binding.bnvMain.setOnItemSelectedListener { when (it.itemId) { R.id.graph_home -> { trackEvent(CLICK_HOME_BOTNAVI) lifecycleScope.launch { delay(100) - findHomeFragment()?.updateToLoadingState() + if (previousTabId == R.id.graph_profile) findHomeFragment()?.updateToLoadingState() } } @@ -198,7 +200,10 @@ class MainActivity : AppCompatActivity(), Navigation { R.id.graph_notification -> trackEvent(CLICK_NOTI_BOTNAVI) R.id.graph_profile -> trackEvent(CLICK_MYPROFILE_BOTNAVI) } - + lifecycleScope.launch { + delay(200) + previousTabId = it.itemId + } it.onNavDestinationSelected(navController) } } From 98e64dd326aabc45460bf4d5f279afd0b8e513dd Mon Sep 17 00:00:00 2001 From: Sohyun Date: Fri, 22 Nov 2024 15:40:09 +0900 Subject: [PATCH 23/28] #106 [FIX] : fix navigation tag --- core/ui/src/main/res/navigation/graph_ui.xml | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/core/ui/src/main/res/navigation/graph_ui.xml b/core/ui/src/main/res/navigation/graph_ui.xml index d15bec59..357302f4 100644 --- a/core/ui/src/main/res/navigation/graph_ui.xml +++ b/core/ui/src/main/res/navigation/graph_ui.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/graph_ui" - app:startDestination="@id/navigation_two_label_bottomsheet"> + app:startDestination="@id/navigation_two_button_dialog"> - - - + tools:layout="@layout/bottomsheet_two_label"> + + + + From 537368708e8210f5313cef98a418f02926285812 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Fri, 22 Nov 2024 15:55:32 +0900 Subject: [PATCH 24/28] #106 [FIX] : fix value call --- .../main/java/com/teamwable/homedetail/HomeDetailViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt index cf29ecf3..ae4f90f5 100644 --- a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt +++ b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailViewModel.kt @@ -78,7 +78,7 @@ class HomeDetailViewModel @Inject constructor( val likeState = likeStates[data.feedId] ?: LikeState(data.isLiked, data.likedNumber) val transformedGhost = if (ghostedUserIds.contains(data.postAuthorId)) data.copy(isPostAuthorGhost = true) else data val transformedBan = if (banState.contains(data.feedId)) transformedGhost.copy(isBlind = true) else transformedGhost - transformedGhost.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) + transformedBan.copy(likedNumber = likeState.count, isLiked = likeState.isLiked) } } } From ac3963acce18a0db54d6f9d89352a4db391b988e Mon Sep 17 00:00:00 2001 From: Sohyun Date: Fri, 22 Nov 2024 16:03:22 +0900 Subject: [PATCH 25/28] #106 [UI] : change comment like color --- core/ui/src/main/res/color/sel_share_comment_like_color.xml | 5 +++++ core/ui/src/main/res/layout/item_comment.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 core/ui/src/main/res/color/sel_share_comment_like_color.xml diff --git a/core/ui/src/main/res/color/sel_share_comment_like_color.xml b/core/ui/src/main/res/color/sel_share_comment_like_color.xml new file mode 100644 index 00000000..11c3f320 --- /dev/null +++ b/core/ui/src/main/res/color/sel_share_comment_like_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/core/ui/src/main/res/layout/item_comment.xml b/core/ui/src/main/res/layout/item_comment.xml index a9154f86..3c8c373c 100644 --- a/core/ui/src/main/res/layout/item_comment.xml +++ b/core/ui/src/main/res/layout/item_comment.xml @@ -150,7 +150,7 @@ android:layout_height="20dp" android:layout_marginTop="16dp" android:background="@drawable/sel_feed_heart" - android:backgroundTint="@color/gray_600" + android:backgroundTint="@color/sel_share_comment_like_color" android:button="@null" app:layout_constraintDimensionRatio="1" app:layout_constraintStart_toStartOf="@id/tv_comment_ghost_level" From 8060bace850acb5fb9de81d8302d68a7dce8b31d Mon Sep 17 00:00:00 2001 From: Sohyun Date: Fri, 22 Nov 2024 16:08:22 +0900 Subject: [PATCH 26/28] #106 [UI] : set up home empty text gone, extract string --- feature/home/src/main/res/layout/fragment_home.xml | 3 ++- feature/home/src/main/res/values/strings.xml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/feature/home/src/main/res/layout/fragment_home.xml b/feature/home/src/main/res/layout/fragment_home.xml index 6866954a..a652aa15 100644 --- a/feature/home/src/main/res/layout/fragment_home.xml +++ b/feature/home/src/main/res/layout/fragment_home.xml @@ -75,9 +75,10 @@ android:id="@+id/tv_empty" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="아직 작성된 글이 없어요." + android:text="@string/label_home_empty" android:textAppearance="@style/TextAppearance.Wable.Body2" android:textColor="@color/gray_500" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/feature/home/src/main/res/values/strings.xml b/feature/home/src/main/res/values/strings.xml index 0a89bb6d..4fc8c4f3 100644 --- a/feature/home/src/main/res/values/strings.xml +++ b/feature/home/src/main/res/values/strings.xml @@ -3,12 +3,12 @@ 포스팅으로 가는 버튼입니다. + 아직 작성된 글이 없어요. 게시글 %s에게 댓글 남기기... - 팬으로서 저희가 해야 할 건\n끊임없는 응원이에요 From 3ecaab34b3057dcd4daa7783c6f32ff96c1c19a4 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Sun, 24 Nov 2024 23:48:15 +0900 Subject: [PATCH 27/28] #106 [FIX] : change login api end point --- .../main/java/com/teamwable/network/datasource/AuthService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/network/src/main/java/com/teamwable/network/datasource/AuthService.kt b/core/network/src/main/java/com/teamwable/network/datasource/AuthService.kt index 168ce9ee..83c7c367 100644 --- a/core/network/src/main/java/com/teamwable/network/datasource/AuthService.kt +++ b/core/network/src/main/java/com/teamwable/network/datasource/AuthService.kt @@ -10,7 +10,7 @@ import retrofit2.http.Header import retrofit2.http.POST interface AuthService { - @POST("api/v1/auth") + @POST("api/v2/auth") suspend fun postLogin( @Body requestSocialLogin: RequestSocialLoginDto, @Header("Authorization") kakaoToken: String, From 67f947fc58a4b4a3dd668e2bf13dc3cac61c7e50 Mon Sep 17 00:00:00 2001 From: Sohyun Date: Sun, 24 Nov 2024 23:48:46 +0900 Subject: [PATCH 28/28] #106 [FIX] : fix navigate to profile at home detail --- .../main/java/com/teamwable/homedetail/HomeDetailFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt index 3604ecbb..c023c210 100644 --- a/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt +++ b/feature/home/src/main/java/com/teamwable/homedetail/HomeDetailFragment.kt @@ -258,7 +258,7 @@ class HomeDetailFragment : BindingFragment(FragmentHo private fun handleProfileNavigation(id: Long) { when (viewModel.fetchUserType(id)) { ProfileUserType.AUTH -> (activity as Navigation).navigateToProfileAuthFragment() - ProfileUserType.MEMBER -> findNavController().deepLinkNavigateTo(requireContext(), DeepLinkDestination.Profile, mapOf(PROFILE_USER_ID to id)) + in setOf(ProfileUserType.MEMBER, ProfileUserType.ADMIN) -> findNavController().deepLinkNavigateTo(requireContext(), DeepLinkDestination.Profile, mapOf(PROFILE_USER_ID to id)) else -> return } }