mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-22 04:55:29 +03:00
Merge pull request #3904 from nextcloud/issue-3898-ban
🚫 Allow Banning Users and Guests
This commit is contained in:
commit
9866062704
18 changed files with 796 additions and 17 deletions
|
@ -25,6 +25,8 @@ import com.nextcloud.talk.models.json.notifications.NotificationOverall;
|
|||
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;
|
||||
|
@ -333,7 +335,7 @@ public interface NcApi {
|
|||
*/
|
||||
@DELETE
|
||||
Observable<Void> unregisterDeviceForNotificationsWithProxy(@Url String url,
|
||||
@QueryMap Map<String,String> fields);
|
||||
@QueryMap Map<String, String> fields);
|
||||
|
||||
@FormUrlEncoded
|
||||
@PUT
|
||||
|
@ -704,9 +706,25 @@ public interface NcApi {
|
|||
|
||||
@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);
|
||||
}
|
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -45,6 +48,7 @@ interface ChatRepository {
|
|||
objectId: String,
|
||||
metadata: String
|
||||
): Observable<GenericOverall>
|
||||
|
||||
fun leaveRoom(credentials: String, url: String): Observable<GenericOverall>
|
||||
fun sendChatMessage(
|
||||
credentials: String,
|
||||
|
@ -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>
|
||||
}
|
||||
|
|
|
@ -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<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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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>()
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
) {
|
||||
|
@ -1296,18 +1348,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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1315,6 +1373,36 @@ class ConversationInfoActivity :
|
|||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<Disposable>()
|
||||
val disposableSet = mutableSetOf<Disposable>()
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
super.onResume(owner)
|
||||
|
@ -49,6 +53,27 @@ class ConversationInfoViewModel @Inject constructor(
|
|||
|
||||
sealed interface ViewState
|
||||
|
||||
class ListBansSuccessState(val talkBans: List<TalkBan>) : ViewState
|
||||
object ListBansErrorState : ViewState
|
||||
|
||||
private val _getTalkBanState: MutableLiveData<ViewState> = MutableLiveData()
|
||||
val getTalkBanState: LiveData<ViewState>
|
||||
get() = _getTalkBanState
|
||||
|
||||
class BanActorSuccessState(val talkBan: TalkBan) : ViewState
|
||||
object BanActorErrorState : ViewState
|
||||
|
||||
private val _getBanActorState: MutableLiveData<ViewState> = MutableLiveData()
|
||||
val getBanActorState: LiveData<ViewState>
|
||||
get() = _getBanActorState
|
||||
|
||||
object UnBanActorSuccessState : ViewState
|
||||
object UnBanActorErrorState : ViewState
|
||||
|
||||
private val _getUnBanActorState: MutableLiveData<ViewState> = MutableLiveData()
|
||||
val getUnBanActorState: LiveData<ViewState>
|
||||
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<List<TalkBan>> {
|
||||
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<TalkBan>) {
|
||||
_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<TalkBan> {
|
||||
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.getUrlForUnban(user.baseUrl!!, token, banId)
|
||||
chatRepository.unbanActor(user.getCredentials(), url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<GenericOverall> {
|
||||
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<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
* 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 = ["moderatorActorType"])
|
||||
var moderatorActorType: String?,
|
||||
@JsonField(name = ["moderatorActorId"])
|
||||
var moderatorActorId: String?,
|
||||
@JsonField(name = ["moderatorDisplayName"])
|
||||
var moderatorDisplayName: String?,
|
||||
@JsonField(name = ["bannedActorType"])
|
||||
var bannedActorType: String?,
|
||||
@JsonField(name = ["bannedActorId"])
|
||||
var bannedActorId: String?,
|
||||
@JsonField(name = ["bannedDisplayName"])
|
||||
var bannedDisplayName: 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, null, null)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
* 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<TalkBan>? = null
|
||||
) : Parcelable {
|
||||
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
|
||||
constructor() : this(null, null)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
* 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)
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
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<TalkBan> = mutableListOf()
|
||||
|
||||
fun setItems(items: List<TalkBan>) {
|
||||
bans = items
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
|
||||
val binding = BanItemListBinding.inflate(LayoutInflater.from(context))
|
||||
binding.banActorName.text = bans[position].bannedDisplayName
|
||||
val time = bans[position].bannedTime!!.toLong() * ONE_SEC
|
||||
binding.banTime.text = DateUtils.formatDateTime(
|
||||
requireContext(),
|
||||
time,
|
||||
(DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME)
|
||||
)
|
||||
binding.banReason.text = bans[position].internalNote
|
||||
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) {
|
||||
is ConversationInfoViewModel.UnBanActorSuccessState -> {
|
||||
getBanList()
|
||||
}
|
||||
|
||||
is ConversationInfoViewModel.UnBanActorErrorState -> {
|
||||
Snackbar.make(binding.root, getString(R.string.error_unbanning), 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)
|
||||
const val ONE_SEC = 1000L
|
||||
}
|
||||
}
|
|
@ -572,4 +572,12 @@ 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"
|
||||
}
|
||||
|
||||
fun getUrlForUnban(baseUrl: String, token: String, banId: Int): String {
|
||||
return "${getUrlForBans(baseUrl, token)}/$banId"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
@ -213,6 +214,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
|
||||
|
|
19
app/src/main/res/drawable/baseline_block_24.xml
Normal file
19
app/src/main/res/drawable/baseline_block_24.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2024 Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:tint="@color/design_default_color_error"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z" />
|
||||
|
||||
</vector>
|
|
@ -354,6 +354,35 @@
|
|||
tools:listitem="@layout/rv_item_conversation_info_participant" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/list_bans_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_margin"
|
||||
android:paddingTop="@dimen/standard_half_margin"
|
||||
android:paddingEnd="@dimen/standard_margin"
|
||||
android:paddingBottom="@dimen/standard_half_margin"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/baseline_block_24"
|
||||
app:tint="@color/grey_600" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/show_banned_participants"
|
||||
android:textSize="@dimen/headline_text_size" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/danger_zone_options"
|
||||
android:layout_width="match_parent"
|
||||
|
|
72
app/src/main/res/layout/ban_item_list.xml
Normal file
72
app/src/main/res/layout/ban_item_list.xml
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/ban_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/ban_actor_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/ListItem"
|
||||
android:textSize="@dimen/md_title_textsize"
|
||||
tools:text="User 2" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/ban_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:textSize="@dimen/sm_text_size"
|
||||
tools:text="11th August, 2023" />
|
||||
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/ban_reason"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:text="Was being mean"
|
||||
android:padding="@dimen/standard_half_padding" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/unban_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_half_margin"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/unban"
|
||||
android:textColor="@color/hwSecurityRed"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
107
app/src/main/res/layout/dialog_ban_actor.xml
Normal file
107
app/src/main/res/layout/dialog_ban_actor.xml
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2024 Julius Linus <julius.linus@nextcloud.com>
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:background="@color/white"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/standard_margin"
|
||||
android:text="@string/ban_actor"
|
||||
android:textSize="@dimen/md_title_textsize" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/ban_actor_profile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:animateLayoutChanges="true"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/standard_margin"
|
||||
android:contentDescription="@string/avatar"
|
||||
tools:src="@drawable/account_circle_48dp" />
|
||||
|
||||
<androidx.emoji2.widget.EmojiTextView
|
||||
android:id="@+id/display_name_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/avatar_image"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/margin_between_elements"
|
||||
android:textSize="@dimen/headline_text_size"
|
||||
tools:text="Jane Doe" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/ban_actor_edit_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/standard_margin"
|
||||
android:hint="@string/internal_note">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/ban_actor_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textShortMessage"
|
||||
android:lines="1"
|
||||
android:padding="@dimen/standard_half_padding" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:gravity="end"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/dialog_padding"
|
||||
android:paddingEnd="@dimen/dialog_padding"
|
||||
android:paddingBottom="@dimen/dialog_padding_top_bottom">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_close"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/min_size_clickable_area"
|
||||
android:text="@string/close" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_ban"
|
||||
style="@style/Button.Primary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/min_size_clickable_area"
|
||||
android:text="@string/ban" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
56
app/src/main/res/layout/fragment_dialog_ban_list.xml
Normal file
56
app/src/main/res/layout/fragment_dialog_ban_list.xml
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2024 Julius Linus <juliuslinus1@gmail.com>
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.dialog.DialogBanListFragment"
|
||||
tools:background="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginHorizontal="@dimen/standard_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/close_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/close"
|
||||
android:src="@drawable/ic_close_search"/>
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/ban_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="@dimen/standard_margin"
|
||||
android:text="@string/bans_list"
|
||||
android:textSize="@dimen/md_title_textsize" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/ban_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
</ListView>
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
|
@ -83,6 +83,7 @@
|
|||
<dimen name="notification_icon_width">24dp</dimen>
|
||||
<dimen name="notification_icon_height">24dp</dimen>
|
||||
<dimen name="notification_icon_layout_right_end_margin">21dp</dimen>
|
||||
<dimen name="sm_text_size">12sp</dimen>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -336,7 +336,6 @@ How to translate with transifex:
|
|||
<string name="nc_add_emojis">Add emojis</string>
|
||||
|
||||
|
||||
|
||||
<string name="nc_push_to_talk">Push-to-talk</string>
|
||||
<string name="nc_push_to_talk_desc">With microphone disabled, click&hold to use Push-to-talk</string>
|
||||
<string name="nc_configure_cert_auth">Select authentication certificate</string>
|
||||
|
@ -793,4 +792,12 @@ How to translate with transifex:
|
|||
<string name="message_last_edited_by">Edited by %1$s</string>
|
||||
<string name="share_link_to_conversation">Join conversation %1$s at %2$s</string>
|
||||
<string name="nc_conversation_settings">Conversation settings</string>
|
||||
<string name="show_banned_participants">Show Banned Participants</string>
|
||||
<string name="bans_list">Bans List</string>
|
||||
<string name="unban">Unban</string>
|
||||
<string name="internal_note">Internal Note</string>
|
||||
<string name="ban_actor">Ban Actor</string>
|
||||
<string name="ban">Ban</string>
|
||||
<string name="show_ban_reason">Show ban reason</string>
|
||||
<string name="error_unbanning">Error occured when unbanning actor</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue