From 5f58647d56be30307fff3767d0ccb6f031c56b9a Mon Sep 17 00:00:00 2001 From: rapterjet2004 Date: Wed, 8 May 2024 11:27:21 -0500 Subject: [PATCH] Allows Banning - New option to ban participant if your a moderator and not in one-2-one - New fragment to see previous bans, unban if wanted Signed-off-by: rapterjet2004 --- .../java/com/nextcloud/talk/api/NcApi.java | 19 ++ .../talk/chat/data/ChatRepository.kt | 10 ++ .../data/network/NetworkChatRepositoryImpl.kt | 19 ++ .../ConversationInfoActivity.kt | 111 ++++++++++-- .../viewmodel/ConversationInfoViewModel.kt | 99 ++++++++++- .../talk/models/json/participants/TalkBan.kt | 35 ++++ .../models/json/participants/TalkBanOCS.kt | 26 +++ .../json/participants/TalkBanOverall.kt | 23 +++ .../talk/ui/dialog/DialogBanListFragment.kt | 162 ++++++++++++++++++ .../java/com/nextcloud/talk/utils/ApiUtils.kt | 4 + .../nextcloud/talk/utils/CapabilitiesUtil.kt | 7 +- .../main/res/drawable/baseline_block_24.xml | 19 ++ .../res/layout/activity_conversation_info.xml | 29 ++++ app/src/main/res/layout/ban_item_list.xml | 84 +++++++++ app/src/main/res/layout/dialog_ban_actor.xml | 107 ++++++++++++ .../res/layout/fragment_dialog_ban_list.xml | 56 ++++++ app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 10 +- 18 files changed, 806 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBan.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOCS.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOverall.kt create mode 100644 app/src/main/java/com/nextcloud/talk/ui/dialog/DialogBanListFragment.kt create mode 100644 app/src/main/res/drawable/baseline_block_24.xml create mode 100644 app/src/main/res/layout/ban_item_list.xml create mode 100644 app/src/main/res/layout/dialog_ban_actor.xml create mode 100644 app/src/main/res/layout/fragment_dialog_ban_list.xml diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index ea3f247181..cbd17d3461 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -25,6 +25,8 @@ import com.nextcloud.talk.models.json.opengraph.OpenGraphOverall; import com.nextcloud.talk.models.json.participants.AddParticipantOverall; import com.nextcloud.talk.models.json.participants.ParticipantsOverall; +import com.nextcloud.talk.models.json.participants.TalkBan; +import com.nextcloud.talk.models.json.participants.TalkBanOverall; import com.nextcloud.talk.models.json.push.PushRegistrationOverall; import com.nextcloud.talk.models.json.reactions.ReactionsOverall; import com.nextcloud.talk.models.json.reminder.ReminderOverall; @@ -709,4 +711,21 @@ Observable acceptInvitation(@Header("Authorization") String auth @DELETE Observable rejectInvitation(@Header("Authorization") String authorization, @Url String url); + + @GET + Observable listBans(@Header("Authorization") String authorization, + @Url String url); + + @FormUrlEncoded + @POST + Observable banActor(@Header("Authorization") String authorization, + @Url String url, + @Field("actorType") String actorType, + @Field("actorId") String actorId, + @Field("internalNote") String internalNote); + + @DELETE + Observable unbanActor(@Header("Authorization") String authorization, + @Url String url, + @Query("banId") int banId); } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt index 5858e38d15..59c7944a64 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt @@ -13,6 +13,7 @@ import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.TalkBan import com.nextcloud.talk.models.json.reminder.Reminder import io.reactivex.Observable import retrofit2.Response @@ -59,4 +60,13 @@ interface ChatRepository { fun createRoom(credentials: String, url: String, map: Map): Observable fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable fun editChatMessage(credentials: String, url: String, text: String): Observable + fun listBans(credentials: String, url: String): Observable> + fun banActor( + credentials: String, + url: String, + actorType: String, + actorId: String, + internalNote: String + ): Observable + fun unbanActor(credentials: String, url: String, banId: Int): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt index 57e3fd98f8..50c35b5f1d 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt @@ -15,6 +15,7 @@ import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.TalkBan import com.nextcloud.talk.models.json.reminder.Reminder import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observable @@ -179,4 +180,22 @@ class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository { override fun editChatMessage(credentials: String, url: String, text: String): Observable { return ncApi.editChatMessage(credentials, url, text).map { it } } + + override fun listBans(credentials: String, url: String): Observable> { + return ncApi.listBans(credentials, url).map { it.ocs?.data } + } + + override fun banActor( + credentials: String, + url: String, + actorType: String, + actorId: String, + internalNote: String + ): Observable { + return ncApi.banActor(credentials, url, actorType, actorId, internalNote) + } + + override fun unbanActor(credentials: String, url: String, banId: Int): Observable { + return ncApi.unbanActor(credentials, url, banId) + } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index 8a058d1dc0..b2259af99f 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -22,6 +22,7 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.ViewModelProvider import androidx.work.Data import androidx.work.OneTimeWorkRequest @@ -47,6 +48,7 @@ import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityConversationInfoBinding +import com.nextcloud.talk.databinding.DialogBanActorBinding import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.extensions.loadConversationAvatar import com.nextcloud.talk.extensions.loadNoteToSelfAvatar @@ -60,6 +62,7 @@ import com.nextcloud.talk.models.domain.LobbyState import com.nextcloud.talk.models.domain.NotificationLevel import com.nextcloud.talk.models.domain.converters.DomainEnumNotificationLevelConverter import com.nextcloud.talk.models.json.capabilities.SpreedCapability +import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant.ActorType.CIRCLES @@ -68,6 +71,7 @@ import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.repositories.conversations.ConversationsRepository import com.nextcloud.talk.shareditems.activities.SharedItemsActivity +import com.nextcloud.talk.ui.dialog.DialogBanListFragment import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.ConversationUtils @@ -186,6 +190,7 @@ class ConversationInfoActivity : binding.leaveConversationAction.setOnClickListener { leaveConversation() } binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() } binding.addParticipantsAction.setOnClickListener { addParticipants() } + binding.listBansButton.setOnClickListener { listBans() } viewModel.getRoom(conversationUser, conversationToken) @@ -235,6 +240,20 @@ class ConversationInfoActivity : else -> {} } } + + viewModel.getBanActorState.observe(this) { state -> + when (state) { + is ConversationInfoViewModel.BanActorSuccessState -> { + getListOfParticipants() // Refresh the list of participants + } + + ConversationInfoViewModel.BanActorErrorState -> { + Snackbar.make(binding.root, "Error banning actor", Snackbar.LENGTH_SHORT).show() + } + + else -> {} + } + } } private fun setupActionBar() { @@ -571,6 +590,17 @@ class ConversationInfoActivity : }) } + private fun listBans() { + val fragmentManager = supportFragmentManager + val newFragment = DialogBanListFragment(conversationToken) + val transaction = fragmentManager.beginTransaction() + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + transaction + .add(android.R.id.content, newFragment) + .addToBackStack(null) + .commit() + } + private fun addParticipants() { val bundle = Bundle() val existingParticipantsId = arrayListOf() @@ -736,6 +766,15 @@ class ConversationInfoActivity : binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE } + binding.listBansButton.visibility = + if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities) && + ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation!!.type + ) { + VISIBLE + } else { + GONE + } + if (conversation!!.notificationCalls === null) { binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE } else { @@ -1070,6 +1109,10 @@ class ConversationInfoActivity : } } + private fun banActor(actorType: String, actorId: String, internalNote: String) { + viewModel.banActor(conversationUser, conversationToken, actorType, actorId, internalNote) + } + private fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { if (apiVersion >= ApiUtils.API_V4) { ncApi.removeAttendeeFromConversation( @@ -1266,6 +1309,15 @@ class ConversationInfoActivity : ) ) + if (CapabilitiesUtil.isBanningAvailable(conversationUser.capabilities?.spreedCapability!!)) { + items.add( + BasicListItemWithImage( + R.drawable.baseline_block_24, + "Ban Participant" + ) + ) + } + if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.GUEST_MODERATOR ) { @@ -1298,18 +1350,24 @@ class ConversationInfoActivity : actionToTrigger++ } - if (actionToTrigger == 0) { - // Pin, nothing to do - } else if (actionToTrigger == 1) { - // Promote/demote - if (apiVersion >= ApiUtils.API_V4) { - toggleModeratorStatus(apiVersion, participant) - } else { - toggleModeratorStatusLegacy(apiVersion, participant) + when (actionToTrigger) { + DEMOTE_OR_PROMOTE -> { + if (apiVersion >= ApiUtils.API_V4) { + toggleModeratorStatus(apiVersion, participant) + } else { + toggleModeratorStatusLegacy(apiVersion, participant) + } } - } else if (actionToTrigger == 2) { - // Remove from conversation - removeAttendeeFromConversation(apiVersion, participant) + + REMOVE_FROM_CONVERSATION -> { + removeAttendeeFromConversation(apiVersion, participant) + } + + BAN_FROM_CONVERSATION -> { + handleBan(participant) + } + + else -> {} } } } @@ -1317,6 +1375,34 @@ class ConversationInfoActivity : return true } + private fun MaterialDialog.handleBan(participant: Participant) { + val binding = DialogBanActorBinding.inflate(layoutInflater) + val actorTypeConverter = EnumActorTypeConverter() + val dialog = MaterialAlertDialogBuilder(context) + .setView(binding.root) + .create() + binding.avatarImage.loadUserAvatar( + conversationUser, + participant.actorId!!, + true, + false + ) + binding.displayNameText.text = participant.actorId + binding.buttonBan.setOnClickListener { + banActor( + actorTypeConverter.convertToString(participant.actorType!!), + participant.actorId!!, + binding.banActorEdit.text.toString() + ) + dialog.dismiss() + } + binding.buttonClose.setOnClickListener { dialog.dismiss() } + viewThemeUtils.material.colorTextInputLayout(binding.banActorEditLayout) + viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.buttonBan) + viewThemeUtils.material.colorMaterialButtonText(binding.buttonClose) + dialog.show() + } + private fun setUpNotificationSettings(module: DatabaseStorageModule) { binding.notificationSettingsView.notificationSettingsImportantConversation.setOnClickListener { val isChecked = binding.notificationSettingsView.importantConversationSwitch.isChecked @@ -1355,6 +1441,9 @@ class ConversationInfoActivity : private const val LOW_EMPHASIS_OPACITY: Float = 0.38f private const val RECORDING_CONSENT_NOT_REQUIRED_FOR_CONVERSATION: Int = 0 private const val RECORDING_CONSENT_REQUIRED_FOR_CONVERSATION: Int = 1 + private const val DEMOTE_OR_PROMOTE = 1 + private const val REMOVE_FROM_CONVERSATION = 2 + private const val BAN_FROM_CONVERSATION = 3 } /** diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index a81e3ab187..a6c816fda1 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -16,6 +16,9 @@ import com.nextcloud.talk.chat.data.ChatRepository import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.json.capabilities.SpreedCapability +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.TalkBan +import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -31,8 +34,9 @@ class ConversationInfoViewModel @Inject constructor( PAUSED, RESUMED } + lateinit var currentLifeCycleFlag: LifeCycleFlag - public val disposableSet = mutableSetOf() + val disposableSet = mutableSetOf() override fun onResume(owner: LifecycleOwner) { super.onResume(owner) @@ -49,6 +53,27 @@ class ConversationInfoViewModel @Inject constructor( sealed interface ViewState + class ListBansSuccessState(val talkBans: List) : ViewState + object ListBansErrorState : ViewState + + private val _getTalkBanState: MutableLiveData = MutableLiveData() + val getTalkBanState: LiveData + get() = _getTalkBanState + + class BanActorSuccessState(val talkBan: TalkBan) : ViewState + object BanActorErrorState : ViewState + + private val _getBanActorState: MutableLiveData = MutableLiveData() + val getBanActorState: LiveData + get() = _getBanActorState + + object UnBanActorSuccessState : ViewState + object UnBanActorErrorState : ViewState + + private val _getUnBanActorState: MutableLiveData = MutableLiveData() + val getUnBanActorState: LiveData + get() = _getUnBanActorState + object GetRoomStartState : ViewState object GetRoomErrorState : ViewState open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState @@ -103,6 +128,78 @@ class ConversationInfoViewModel @Inject constructor( } } + fun listBans(user: User, token: String) { + val url = ApiUtils.getUrlForBans(user.baseUrl!!, token) + chatRepository.listBans(user.getCredentials(), url) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer> { + override fun onSubscribe(p0: Disposable) { + // unused atm + } + + override fun onError(e: Throwable) { + _getTalkBanState.value = ListBansErrorState + } + + override fun onComplete() { + // unused atm + } + + override fun onNext(talkBans: List) { + _getTalkBanState.value = ListBansSuccessState(talkBans) + } + }) + } + + fun banActor(user: User, token: String, actorType: String, actorId: String, internalNote: String) { + val url = ApiUtils.getUrlForBans(user.baseUrl!!, token) + chatRepository.banActor(user.getCredentials(), url, actorType, actorId, internalNote) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(p0: Disposable) { + // unused atm + } + + override fun onError(e: Throwable) { + _getBanActorState.value = BanActorErrorState + } + + override fun onComplete() { + // unused atm + } + + override fun onNext(talkBan: TalkBan) { + _getBanActorState.value = BanActorSuccessState(talkBan) + } + }) + } + + fun unbanActor(user: User, token: String, banId: Int) { + val url = ApiUtils.getUrlForBans(user.baseUrl!!, token) + chatRepository.unbanActor(user.getCredentials(), url, banId) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(p0: Disposable) { + // unused atm + } + + override fun onError(p0: Throwable) { + _getUnBanActorState.value = UnBanActorErrorState + } + + override fun onComplete() { + // unused atm + } + + override fun onNext(p0: GenericOverall) { + _getUnBanActorState.value = UnBanActorSuccessState + } + }) + } + inner class GetRoomObserver : Observer { override fun onSubscribe(d: Disposable) { // unused atm diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBan.kt b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBan.kt new file mode 100644 index 0000000000..21570b4c6a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBan.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Julius Linus + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.participants + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TalkBan( + @JsonField(name = ["id"]) + var id: String?, + @JsonField(name = ["actorType"]) + var actorType: String?, + @JsonField(name = ["actorId"]) + var actorId: String?, + @JsonField(name = ["bannedType"]) + var bannedType: String?, + @JsonField(name = ["bannedId"]) + var bannedId: String?, + @JsonField(name = ["bannedTime"]) + var bannedTime: Int?, + @JsonField(name = ["internalNote"]) + var internalNote: String? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null, null, null, null, null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOCS.kt new file mode 100644 index 0000000000..9dd62a2d9a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOCS.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Julius Linus + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.participants + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericMeta +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TalkBanOCS( + @JsonField(name = ["meta"]) + var meta: GenericMeta?, + @JsonField(name = ["data"]) + var data: List? = null +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOverall.kt new file mode 100644 index 0000000000..08174ec776 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/TalkBanOverall.kt @@ -0,0 +1,23 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Julius Linus + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.participants + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TalkBanOverall( + @JsonField(name = ["ocs"]) + var ocs: TalkBanOCS? = null +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/DialogBanListFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/DialogBanListFragment.kt new file mode 100644 index 0000000000..c94001e2cf --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/DialogBanListFragment.kt @@ -0,0 +1,162 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2024 Julius Linus + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.nextcloud.talk.ui.dialog + +import android.annotation.SuppressLint +import android.graphics.drawable.Icon +import android.os.Bundle +import android.text.format.DateUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.ViewModelProvider +import autodagger.AutoInjector +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.BanItemListBinding +import com.nextcloud.talk.databinding.FragmentDialogBanListBinding +import com.nextcloud.talk.models.json.participants.TalkBan +import com.nextcloud.talk.ui.theme.ViewThemeUtils +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class DialogBanListFragment(val roomToken: String) : DialogFragment() { + + lateinit var binding: FragmentDialogBanListBinding + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + + @Inject + lateinit var currentUserProvider: CurrentUserProviderNew + + lateinit var viewModel: ConversationInfoViewModel + private lateinit var conversationUser: User + + private val adapter = object : BaseAdapter() { + private var bans: List = mutableListOf() + private var banState: Array = emptyArray() + + fun setItems(items: List) { + bans = items + banState = Array(items.size) { false } + } + + override fun getCount(): Int { + return bans.size + } + + override fun getItem(position: Int): Any { + return bans[position] + } + + override fun getItemId(position: Int): Long { + return bans[position].bannedTime!!.toLong() + } + + @SuppressLint("ViewHolder") + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val binding = BanItemListBinding.inflate(LayoutInflater.from(context)) + binding.banActorName.text = bans[position].bannedId + val time = bans[position].bannedTime!!.toLong() + binding.banTime.text = DateUtils.formatDateTime( + requireContext(), + time, + DateUtils.FORMAT_SHOW_DATE + ) + binding.banReason.text = bans[position].internalNote + binding.banItem.setOnClickListener { + banState[position] = !banState[position] + val icon = if (banState[position]) { + Icon.createWithResource(context, R.drawable.ic_keyboard_arrow_down) + } else { + Icon.createWithResource(context, R.drawable.ic_chevron_right) + } + icon.setTint(resources.getColor(R.color.nc_grey, null)) + binding.banListItemDrop.setImageIcon(icon) + + binding.banReason.visibility = if (banState[position]) View.VISIBLE else View.GONE + } + binding.unbanBtn.setOnClickListener { unBanActor(bans[position].id!!.toInt()) } + return binding.root + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + binding = FragmentDialogBanListBinding.inflate(LayoutInflater.from(context)) + viewModel = + ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java] + conversationUser = currentUserProvider.currentUser.blockingGet() + + themeView() + initObservers() + initListeners() + getBanList() + return binding.root + } + + private fun initObservers() { + viewModel.getTalkBanState.observe(viewLifecycleOwner) { state -> + when (state) { + is ConversationInfoViewModel.ListBansSuccessState -> { + adapter.setItems(state.talkBans) + binding.banListView.adapter = adapter + } + + is ConversationInfoViewModel.ListBansErrorState -> { + } + + else -> {} + } + } + + viewModel.getUnBanActorState.observe(viewLifecycleOwner) { state -> + when (state) { + ConversationInfoViewModel.UnBanActorSuccessState -> { + getBanList() // Refresh the ban list + } + + ConversationInfoViewModel.UnBanActorErrorState -> { + Snackbar.make(binding.root, "Error unbanning actor", Snackbar.LENGTH_SHORT).show() + } + + else -> {} + } + } + } + + private fun themeView() { + viewThemeUtils.platform.colorViewBackground(binding.root) + } + + private fun initListeners() { + binding.closeBtn.setOnClickListener { dismiss() } + } + + private fun getBanList() { + viewModel.listBans(conversationUser, roomToken) + } + + private fun unBanActor(banId: Int) { + viewModel.unbanActor(conversationUser, roomToken, banId) + } + + companion object { + @JvmStatic + fun newInstance(roomToken: String) = DialogBanListFragment(roomToken) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt index d010344fe2..77af40f6ca 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -572,4 +572,8 @@ object ApiUtils { fun getUrlForRoomCapabilities(version: Int, baseUrl: String?, token: String?): String { return getUrlForRooms(version, baseUrl) + "/" + token + "/capabilities" } + + fun getUrlForBans(baseUrl: String, token: String): String { + return "$baseUrl/ocs/v1.php$SPREED_API_VERSION/ban/$token" + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt index a895f160d2..22cf130137 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt @@ -53,7 +53,8 @@ enum class SpreedFeatures(val value: String) { CHAT_PERMISSION("chat-permission"), CONVERSATION_PERMISSION("conversation-permissions"), FEDERATION_V1("federation-v1"), - DELETE_MESSAGES_UNLIMITED("delete-messages-unlimited") + DELETE_MESSAGES_UNLIMITED("delete-messages-unlimited"), + BAN_V1("ban-v1") } @Suppress("TooManyFunctions") @@ -209,6 +210,10 @@ object CapabilitiesUtil { return RECORDING_CONSENT_NOT_REQUIRED } + fun isBanningAvailable(spreedCapabilities: SpreedCapability): Boolean { + return hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.BAN_V1) + } + // endregion //region SpreedCapabilities that can't be used with federation as the settings for them are global diff --git a/app/src/main/res/drawable/baseline_block_24.xml b/app/src/main/res/drawable/baseline_block_24.xml new file mode 100644 index 0000000000..920379cb37 --- /dev/null +++ b/app/src/main/res/drawable/baseline_block_24.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/layout/activity_conversation_info.xml b/app/src/main/res/layout/activity_conversation_info.xml index 8efae01e0d..63738ce3d8 100644 --- a/app/src/main/res/layout/activity_conversation_info.xml +++ b/app/src/main/res/layout/activity_conversation_info.xml @@ -354,6 +354,35 @@ tools:listitem="@layout/rv_item_conversation_info_participant" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_ban_actor.xml b/app/src/main/res/layout/dialog_ban_actor.xml new file mode 100644 index 0000000000..0bcf22574a --- /dev/null +++ b/app/src/main/res/layout/dialog_ban_actor.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_dialog_ban_list.xml b/app/src/main/res/layout/fragment_dialog_ban_list.xml new file mode 100644 index 0000000000..b01ed83981 --- /dev/null +++ b/app/src/main/res/layout/fragment_dialog_ban_list.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index bfc6aa91e2..c28b32f755 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -82,6 +82,7 @@ 24dp 24dp 21dp + 12sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b9b33e95da..eb59bb8651 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,7 +15,7 @@ How to translate with transifex: --> -