Skip to content

Commit

Permalink
Merge pull request #3904 from nextcloud/issue-3898-ban
Browse files Browse the repository at this point in the history
🚫 Allow Banning Users and Guests
  • Loading branch information
rapterjet2004 authored Aug 6, 2024
2 parents 75824c4 + 46d3fd1 commit 9866062
Show file tree
Hide file tree
Showing 18 changed files with 796 additions and 17 deletions.
24 changes: 21 additions & 3 deletions app/src/main/java/com/nextcloud/talk/api/NcApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -333,7 +335,7 @@ Observable<Unit> registerDeviceForNotificationsWithPushProxy(@Url String url,
*/
@DELETE
Observable<Void> unregisterDeviceForNotificationsWithProxy(@Url String url,
@QueryMap Map<String,String> fields);
@QueryMap Map<String, String> fields);

@FormUrlEncoded
@PUT
Expand Down Expand Up @@ -704,9 +706,25 @@ Observable<InvitationOverall> getInvitations(@Header("Authorization") String aut

@POST
Observable<GenericOverall> acceptInvitation(@Header("Authorization") String authorization,
@Url String url);
@Url String url);

@DELETE
Observable<GenericOverall> rejectInvitation(@Header("Authorization") String authorization,
@Url String url);
@Url String url);

@GET
Observable<TalkBanOverall> listBans(@Header("Authorization") String authorization,
@Url String url);

@FormUrlEncoded
@POST
Observable<TalkBan> banActor(@Header("Authorization") String authorization,
@Url String url,
@Field("actorType") String actorType,
@Field("actorId") String actorId,
@Field("internalNote") String internalNote);

@DELETE
Observable<GenericOverall> unbanActor(@Header("Authorization") String authorization,
@Url String url);
}
15 changes: 15 additions & 0 deletions app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,6 +30,7 @@ interface ChatRepository {
timeStamp: Int,
chatApiVersion: Int
): Observable<Reminder>

fun getReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable<Reminder>
fun deleteReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable<GenericOverall>
fun shareToNotes(
Expand All @@ -37,6 +39,7 @@ interface ChatRepository {
message: String,
displayName: String
): Observable<GenericOverall> // last two fields are false

fun checkForNoteToSelf(credentials: String, url: String, includeStatus: Boolean): Observable<RoomsOverall>
fun shareLocationToNotes(
credentials: String,
Expand All @@ -45,6 +48,7 @@ interface ChatRepository {
objectId: String,
metadata: String
): Observable<GenericOverall>

fun leaveRoom(credentials: String, url: String): Observable<GenericOverall>
fun sendChatMessage(
credentials: String,
Expand All @@ -54,9 +58,20 @@ interface ChatRepository {
replyTo: Int,
sendWithoutNotification: Boolean
): Observable<GenericOverall>

fun pullChatMessages(credentials: String, url: String, fieldMap: HashMap<String, Int>): Observable<Response<*>>
fun deleteChatMessage(credentials: String, url: String): Observable<ChatOverallSingleMessage>
fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall>
fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable<GenericOverall>
fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage>
fun listBans(credentials: String, url: String): Observable<List<TalkBan>>
fun banActor(
credentials: String,
url: String,
actorType: String,
actorId: String,
internalNote: String
): Observable<TalkBan>

fun unbanActor(credentials: String, url: String): Observable<GenericOverall>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -179,4 +180,22 @@ class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
override fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage> {
return ncApi.editChatMessage(credentials, url, text).map { it }
}

override fun listBans(credentials: String, url: String): Observable<List<TalkBan>> {
return ncApi.listBans(credentials, url).map { it.ocs?.data }
}

override fun banActor(
credentials: String,
url: String,
actorType: String,
actorId: String,
internalNote: String
): Observable<TalkBan> {
return ncApi.banActor(credentials, url, actorType, actorId, internalNote)
}

override fun unbanActor(credentials: String, url: String): Observable<GenericOverall> {
return ncApi.unbanActor(credentials, url)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -181,6 +185,7 @@ class ConversationInfoActivity :
binding.leaveConversationAction.setOnClickListener { leaveConversation() }
binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
binding.addParticipantsAction.setOnClickListener { addParticipants() }
binding.listBansButton.setOnClickListener { listBans() }

viewModel.getRoom(conversationUser, conversationToken)

Expand Down Expand Up @@ -233,6 +238,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() {
Expand Down Expand Up @@ -569,6 +588,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<String>()
Expand Down Expand Up @@ -734,6 +764,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 {
Expand Down Expand Up @@ -1068,6 +1107,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(
Expand Down Expand Up @@ -1264,6 +1307,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
) {
Expand Down Expand Up @@ -1296,25 +1348,61 @@ 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 -> {}
}
}
}
}
return true
}

private fun MaterialDialog.handleBan(participant: Participant) {
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1))
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()
)
removeAttendeeFromConversation(apiVersion, participant)
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
Expand Down Expand Up @@ -1353,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
}

/**
Expand Down
Loading

0 comments on commit 9866062

Please sign in to comment.