diff --git a/CHANGES.md b/CHANGES.md index ad2d8942f8..5eef68fc5d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,8 @@ Features ✨: Improvements πŸ™Œ: - Sending events is now retried only 3 times, so we avoid blocking the sending queue too long. - Display warning when fail to send events in room list + - Improve UI of edit role action in member profile + - Moderation |Β New screen to display list of banned users in room settings, with unban action Bugfix πŸ›: - Fix theme issue on Room directory screen (#1613) @@ -17,6 +19,9 @@ Bugfix πŸ›: - Fix Infinite loop at startup when migrating account from Riot (#1699) - Fix Element crashes in loop after initial sync (#1709) - Remove inner mx-reply tags before replying + - Fix timeline items not loading when there are only filtered events + - Fix "Voice & Video" grayed out in Settings (#1733) + - Fix Allow VOIP call in all rooms with 2 participants (even if not DM) Translations πŸ—£: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index b6b0555433..19eac8dc32 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -67,7 +67,7 @@ data class RoomSummary constructor( get() = tags.any { it.name == RoomTag.ROOM_TAG_FAVOURITE } val canStartCall: Boolean - get() = isDirect && joinedMembersCount == 2 + get() = joinedMembersCount == 2 companion object { const val NOT_IN_BREADCRUMBS = -1 diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 3bb9eca766..063cd8ee4d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -169,7 +169,7 @@ internal class DefaultTimeline( filteredEvents = nonFilteredEvents.where() .filterEventsWithSettings() .findAll() - filteredEvents.addChangeListener(eventsChangeListener) + nonFilteredEvents.addChangeListener(eventsChangeListener) handleInitialLoad() if (settings.shouldHandleHiddenReadReceipts()) { hiddenReadReceipts.start(realm, filteredEvents, nonFilteredEvents, this) diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 8e4f95ed54..bc5a985d9f 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -77,6 +77,7 @@ import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment import im.vector.riotx.features.roommemberprofile.devices.DeviceListFragment import im.vector.riotx.features.roommemberprofile.devices.DeviceTrustInfoActionFragment import im.vector.riotx.features.roomprofile.RoomProfileFragment +import im.vector.riotx.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment @@ -534,4 +535,9 @@ interface FragmentModule { @IntoMap @FragmentKey(ContactsBookFragment::class) fun bindPhoneBookFragment(fragment: ContactsBookFragment): Fragment + + @Binds + @IntoMap + @FragmentKey(RoomBannedMemberListFragment::class) + fun bindRoomBannedMemberListFragment(fragment: RoomBannedMemberListFragment): Fragment } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/LoadingItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/LoadingItem.kt index 8f9f478a40..e48b680126 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/LoadingItem.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/LoadingItem.kt @@ -16,7 +16,9 @@ package im.vector.riotx.core.epoxy +import android.widget.ProgressBar import android.widget.TextView +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotx.R @@ -26,14 +28,16 @@ import im.vector.riotx.core.extensions.setTextOrHide abstract class LoadingItem : VectorEpoxyModel() { @EpoxyAttribute var loadingText: String? = null + @EpoxyAttribute var showLoader: Boolean = true override fun bind(holder: Holder) { super.bind(holder) - + holder.progressBar.isVisible = showLoader holder.textView.setTextOrHide(loadingText) } class Holder : VectorEpoxyHolder() { val textView by bind(R.id.loadingText) + val progressBar by bind(R.id.loadingProgress) } } diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/BaseProfileMatrixItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/BaseProfileMatrixItem.kt new file mode 100644 index 0000000000..58005ac788 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/BaseProfileMatrixItem.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.core.epoxy.profiles + +import android.view.View +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel +import im.vector.matrix.android.api.util.MatrixItem +import im.vector.riotx.core.epoxy.VectorEpoxyModel +import im.vector.riotx.core.extensions.setTextOrHide +import im.vector.riotx.features.crypto.util.toImageRes +import im.vector.riotx.features.home.AvatarRenderer + +abstract class BaseProfileMatrixItem : VectorEpoxyModel() { + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute lateinit var matrixItem: MatrixItem + @EpoxyAttribute var editable: Boolean = true + @EpoxyAttribute + var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null + @EpoxyAttribute var clickListener: View.OnClickListener? = null + + override fun bind(holder: T) { + super.bind(holder) + val bestName = matrixItem.getBestName() + val matrixId = matrixItem.id + .takeIf { it != bestName } + // Special case for ThreePid fake matrix item + .takeIf { it != "@" } + holder.view.setOnClickListener(clickListener?.takeIf { editable }) + holder.titleView.text = bestName + holder.subtitleView.setTextOrHide(matrixId) + holder.editableView.isVisible = editable + avatarRenderer.render(matrixItem, holder.avatarImageView) + holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes()) + } +} diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt index b89da07984..f704c7dd7d 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItem.kt @@ -20,43 +20,14 @@ package im.vector.riotx.core.epoxy.profiles import android.view.View import android.widget.ImageView import android.widget.TextView -import androidx.core.view.isVisible -import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass -import im.vector.matrix.android.api.crypto.RoomEncryptionTrustLevel -import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R import im.vector.riotx.core.epoxy.VectorEpoxyHolder -import im.vector.riotx.core.epoxy.VectorEpoxyModel -import im.vector.riotx.core.extensions.setTextOrHide -import im.vector.riotx.features.crypto.util.toImageRes -import im.vector.riotx.features.home.AvatarRenderer @EpoxyModelClass(layout = R.layout.item_profile_matrix_item) -abstract class ProfileMatrixItem : VectorEpoxyModel() { +abstract class ProfileMatrixItem : BaseProfileMatrixItem() { - @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer - @EpoxyAttribute lateinit var matrixItem: MatrixItem - @EpoxyAttribute var editable: Boolean = true - @EpoxyAttribute var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null - @EpoxyAttribute var clickListener: View.OnClickListener? = null - - override fun bind(holder: Holder) { - super.bind(holder) - val bestName = matrixItem.getBestName() - val matrixId = matrixItem.id - .takeIf { it != bestName } - // Special case for ThreePid fake matrix item - .takeIf { it != "@" } - holder.view.setOnClickListener(clickListener?.takeIf { editable }) - holder.titleView.text = bestName - holder.subtitleView.setTextOrHide(matrixId) - holder.editableView.isVisible = editable - avatarRenderer.render(matrixItem, holder.avatarImageView) - holder.avatarDecorationImageView.setImageResource(userEncryptionTrustLevel.toImageRes()) - } - - class Holder : VectorEpoxyHolder() { + open class Holder : VectorEpoxyHolder() { val titleView by bind(R.id.matrixItemTitle) val subtitleView by bind(R.id.matrixItemSubtitle) val avatarImageView by bind(R.id.matrixItemAvatar) diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItemWithProgress.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItemWithProgress.kt new file mode 100644 index 0000000000..1ad538ec6d --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/profiles/ProfileMatrixItemWithProgress.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package im.vector.riotx.core.epoxy.profiles + +import android.widget.ProgressBar +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.riotx.R + +@EpoxyModelClass(layout = R.layout.item_profile_matrix_item_progress) +abstract class ProfileMatrixItemWithProgress : BaseProfileMatrixItem() { + + @EpoxyAttribute var inProgress: Boolean = true + + override fun bind(holder: Holder) { + super.bind(holder) + holder.progress.isVisible = inProgress + } + + class Holder : ProfileMatrixItem.Holder() { + val progress by bind(R.id.matrixItemProgress) + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt index 9815bac275..d79972c59a 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt @@ -74,7 +74,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View) fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) -// fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent) + + // fun onFileMessageClicked(eventId: String, messageFileContent: MessageFileContent) // fun onAudioMessageClicked(messageAudioContent: MessageAudioContent) fun onEditedDecorationClicked(informationData: MessageInformationData) @@ -107,7 +108,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec fun onUrlLongClicked(url: String): Boolean } - private var showingForwardLoader = false // Map eventId to adapter position private val adapterPositionMapping = HashMap() private val modelCache = arrayListOf() @@ -233,7 +233,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun buildModels() { val timestamp = System.currentTimeMillis() - showingForwardLoader = LoadingItem_() + + val showingForwardLoader = LoadingItem_() .id("forward_loading_item_$timestamp") .setVisibilityStateChangedListener(Timeline.Direction.FORWARDS) .addWhenLoading(Timeline.Direction.FORWARDS) @@ -242,12 +243,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec add(timelineModels) // Avoid displaying two loaders if there is no elements between them - if (!showingForwardLoader || timelineModels.isNotEmpty()) { - LoadingItem_() - .id("backward_loading_item_$timestamp") - .setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS) - .addWhenLoading(Timeline.Direction.BACKWARDS) - } + val showBackwardsLoader = !showingForwardLoader || timelineModels.isNotEmpty() + // We can hide the loader but still add the item to controller so it can trigger backwards pagination + LoadingItem_() + .id("backward_loading_item_$timestamp") + .setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS) + .showLoader(showBackwardsLoader) + .addWhenLoading(Timeline.Direction.BACKWARDS) } // Timeline.LISTENER *************************************************************************** diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt index 845e628bc6..7bb7e99c5f 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt @@ -19,12 +19,10 @@ package im.vector.riotx.features.login import android.content.Context import android.content.Intent import android.view.View -import android.view.ViewGroup import androidx.annotation.CallSuper import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar import androidx.core.view.ViewCompat -import androidx.core.view.children import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -73,14 +71,6 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { get() = supportFragmentManager.findFragmentById(R.id.loginFragmentContainer) private val commonOption: (FragmentTransaction) -> Unit = { ft -> - // Find the loginLogo on the current Fragment, this should not return null - (topFragment?.view as? ViewGroup) - // Find findViewById does not work, I do not know why - // findViewById(R.id.loginLogo) - ?.children - ?.firstOrNull { it.id == R.id.loginLogo } - ?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // TODO ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) } @@ -145,7 +135,6 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable { addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java, option = { ft -> - findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } // TODO Disabled because it provokes a flickering diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index 1ad72ad424..a12d496e45 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -236,10 +236,12 @@ class RoomMemberProfileController @Inject constructor( if (canEditPowerLevel) { buildProfileAction( id = "edit_power_level", - editable = false, - title = powerLevelsStr, + editable = true, + title = stringProvider.getString(R.string.power_level_title), + subtitle = powerLevelsStr, divider = canKick || canBan, dividerColor = dividerColor, + editableRes = R.drawable.ic_edit, action = { callback?.onEditPowerLevel(userPowerLevel) } ) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt index b61075be80..c628ba6359 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileActivity.kt @@ -32,6 +32,7 @@ import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.features.room.RequireActiveMembershipViewEvents import im.vector.riotx.features.room.RequireActiveMembershipViewModel import im.vector.riotx.features.room.RequireActiveMembershipViewState +import im.vector.riotx.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment import im.vector.riotx.features.roomprofile.settings.RoomSettingsFragment import im.vector.riotx.features.roomprofile.uploads.RoomUploadsFragment @@ -81,9 +82,10 @@ class RoomProfileActivity : .observe() .subscribe { sharedAction -> when (sharedAction) { - is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() - is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() - is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() + is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() } } .disposeOnDestroy() @@ -114,6 +116,10 @@ class RoomProfileActivity : addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) } + private fun openBannedRoomMembers() { + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomBannedMemberListFragment::class.java, roomProfileArgs) + } + override fun configure(toolbar: Toolbar) { configureToolbar(toolbar) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt index 5f8c000497..249247cd63 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileController.kt @@ -41,6 +41,7 @@ class RoomProfileController @Inject constructor( interface Callback { fun onLearnMoreClicked() fun onMemberListClicked() + fun onBannedMemberListClicked() fun onNotificationsClicked() fun onUploadsClicked() fun onSettingsClicked() @@ -92,6 +93,16 @@ class RoomProfileController @Inject constructor( accessory = R.drawable.ic_shield_warning.takeIf { hasWarning } ?: 0, action = { callback?.onMemberListClicked() } ) + + if (data.bannedMembership.invoke()?.isNotEmpty() == true) { + buildProfileAction( + id = "banned_list", + title = stringProvider.getString(R.string.room_settings_banned_users_title), + dividerColor = dividerColor, + icon = R.drawable.ic_settings_root_labs, + action = { callback?.onBannedMemberListClicked() } + ) + } buildProfileAction( id = "uploads", title = stringProvider.getString(R.string.room_profile_section_more_uploads), diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 1ff5094517..d65ec82921 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -206,6 +206,10 @@ class RoomProfileFragment @Inject constructor( roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomMembers) } + override fun onBannedMemberListClicked() { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenBannedRoomMembers) + } + override fun onSettingsClicked() { roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomSettings) } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt index 71142c0aae..76d87b3a93 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileSharedAction.kt @@ -25,4 +25,5 @@ sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomSettings : RoomProfileSharedAction() object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() + object OpenBannedRoomMembers : RoomProfileSharedAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt index bab0331ccb..5ad74f8663 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewModel.kt @@ -26,6 +26,8 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.permalinks.PermalinkFactory import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams +import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap @@ -61,7 +63,8 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini } private fun observeRoomSummary() { - room.rx().liveRoomSummary() + val rxRoom = room.rx() + rxRoom.liveRoomSummary() .unwrap() .execute { copy(roomSummary = it) @@ -73,10 +76,17 @@ class RoomProfileViewModel @AssistedInject constructor(@Assisted private val ini .subscribe { val powerLevelsHelper = PowerLevelsHelper(it) setState { - copy(canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR)) + copy(canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR)) } } .disposeOnClear() + + rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + .execute { + copy( + bannedMembership = it + ) + } } override fun handle(action: RoomProfileAction) = when (action) { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt index 76cc3f1f07..3cab6f38f4 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileViewState.kt @@ -20,11 +20,13 @@ package im.vector.riotx.features.roomprofile import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary import im.vector.matrix.android.api.session.room.model.RoomSummary data class RoomProfileViewState( val roomId: String, val roomSummary: Async = Uninitialized, + val bannedMembership: Async> = Uninitialized, val canChangeAvatar: Boolean = false ) : MvRxState { diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberAction.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberAction.kt new file mode 100644 index 0000000000..081af556d7 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberAction.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.riotx.core.platform.VectorViewModelAction + +sealed class RoomBannedListMemberAction : VectorViewModelAction { + data class QueryInfo(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() + data class UnBanUser(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberViewModel.kt new file mode 100644 index 0000000000..35f5589e9f --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedListMemberViewModel.kt @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.query.QueryStringValue +import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.members.roomMemberQueryParams +import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.api.session.room.model.RoomMemberContent +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.session.room.powerlevels.PowerLevelsHelper +import im.vector.matrix.android.internal.util.awaitCallback +import im.vector.matrix.rx.rx +import im.vector.matrix.rx.unwrap +import im.vector.riotx.R +import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.features.powerlevel.PowerLevelsObservableFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, + private val stringProvider: StringProvider, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomBannedMemberListViewState): RoomBannedListMemberViewModel + } + + private val room = session.getRoom(initialState.roomId)!! + + init { + val rxRoom = room.rx() + + room.rx().liveRoomSummary() + .unwrap() + .execute { async -> + copy(roomSummary = async) + } + + rxRoom.liveRoomMembers(roomMemberQueryParams { memberships = listOf(Membership.BAN) }) + .execute { + copy( + bannedMemberSummaries = it + ) + } + + val powerLevelsContentLive = PowerLevelsObservableFactory(room).createObservable() + + powerLevelsContentLive.subscribe { + val powerLevelsHelper = PowerLevelsHelper(it) + setState { copy(canUserBan = powerLevelsHelper.isUserAbleToBan(session.myUserId)) } + }.disposeOnClear() + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedListMemberViewModel? { + val fragment: RoomBannedMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + override fun handle(action: RoomBannedListMemberAction) { + when (action) { + is RoomBannedListMemberAction.QueryInfo -> onQueryBanInfo(action.roomMemberSummary) + is RoomBannedListMemberAction.UnBanUser -> unBanUser(action.roomMemberSummary) + } + } + + private fun onQueryBanInfo(roomMemberSummary: RoomMemberSummary) { + val bannedEvent = room.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(roomMemberSummary.userId)) + val content = bannedEvent?.getClearContent().toModel() + if (content?.membership != Membership.BAN) { + // may be report error? + return + } + + val reason = content.reason + val bannedBy = bannedEvent?.senderId ?: return + + _viewEvents.post(RoomBannedViewEvents.ShowBannedInfo(bannedBy, reason ?: "", roomMemberSummary)) + } + + private fun unBanUser(roomMemberSummary: RoomMemberSummary) { + setState { + copy(onGoingModerationAction = this.onGoingModerationAction + roomMemberSummary.userId) + } + viewModelScope.launch(Dispatchers.IO) { + try { + awaitCallback { + room.unban(roomMemberSummary.userId, null, it) + } + } catch (failure: Throwable) { + _viewEvents.post(RoomBannedViewEvents.ToastError(stringProvider.getString(R.string.failed_to_unban))) + } finally { + setState { + copy( + onGoingModerationAction = onGoingModerationAction - roomMemberSummary.userId + ) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListController.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListController.kt new file mode 100644 index 0000000000..51801af187 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListController.kt @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.riotx.R +import im.vector.riotx.core.epoxy.dividerItem +import im.vector.riotx.core.epoxy.profiles.buildProfileSection +import im.vector.riotx.core.epoxy.profiles.profileMatrixItemWithProgress +import im.vector.riotx.core.extensions.join +import im.vector.riotx.core.resources.ColorProvider +import im.vector.riotx.core.resources.StringProvider +import im.vector.riotx.core.ui.list.genericFooterItem +import im.vector.riotx.features.home.AvatarRenderer +import javax.inject.Inject + +class RoomBannedMemberListController @Inject constructor( + private val avatarRenderer: AvatarRenderer, + private val stringProvider: StringProvider, + colorProvider: ColorProvider +) : TypedEpoxyController() { + + interface Callback { + fun onUnbanClicked(roomMember: RoomMemberSummary) + } + + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) + + var callback: Callback? = null + + init { + setData(null) + } + + override fun buildModels(data: RoomBannedMemberListViewState?) { + val bannedList = data?.bannedMemberSummaries?.invoke() ?: return + + buildProfileSection( + stringProvider.getString(R.string.room_settings_banned_users_title) + ) + + bannedList.join( + each = { _, roomMember -> + val actionInProgress = data.onGoingModerationAction.contains(roomMember.userId) + profileMatrixItemWithProgress { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + apply { + if (actionInProgress) { + inProgress(true) + editable(false) + } else { + inProgress(false) + editable(true) + clickListener { _ -> + callback?.onUnbanClicked(roomMember) + } + } + } + } + }, + between = { _, roomMemberBefore -> + dividerItem { + id("divider_${roomMemberBefore.userId}") + color(dividerColor) + } + } + ) + + genericFooterItem { + id("footer") + text(stringProvider.getQuantityString(R.plurals.room_settings_banned_users_count, bannedList.size, bannedList.size)) + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListFragment.kt new file mode 100644 index 0000000000..a6ebcf1acf --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListFragment.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AlertDialog +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.util.toMatrixItem +import im.vector.riotx.R +import im.vector.riotx.core.extensions.cleanup +import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.core.utils.toast +import im.vector.riotx.features.home.AvatarRenderer +import im.vector.riotx.features.roomprofile.RoomProfileArgs +import kotlinx.android.synthetic.main.fragment_room_setting_generic.* +import javax.inject.Inject + +class RoomBannedMemberListFragment @Inject constructor( + val viewModelFactory: RoomBannedListMemberViewModel.Factory, + private val roomMemberListController: RoomBannedMemberListController, + private val avatarRenderer: AvatarRenderer +) : VectorBaseFragment(), RoomBannedMemberListController.Callback { + + private val viewModel: RoomBannedListMemberViewModel by fragmentViewModel() + private val roomProfileArgs: RoomProfileArgs by args() + + override fun getLayoutResId() = R.layout.fragment_room_setting_generic + + override fun onUnbanClicked(roomMember: RoomMemberSummary) { + viewModel.handle(RoomBannedListMemberAction.QueryInfo(roomMember)) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + roomMemberListController.callback = this + setupToolbar(roomSettingsToolbar) + recyclerView.configureWith(roomMemberListController, hasFixedSize = true) + + viewModel.observeViewEvents { + when (it) { + is RoomBannedViewEvents.ShowBannedInfo -> { + val canBan = withState(viewModel) { state -> state.canUserBan } + AlertDialog.Builder(requireActivity()) + .setTitle(getString(R.string.member_banned_by, it.bannedByUserId)) + .setMessage(getString(R.string.reason_colon, it.banReason)) + .setPositiveButton(R.string.ok, null) + .apply { + if (canBan) { + setNegativeButton(R.string.room_participants_action_unban) { _, _ -> + viewModel.handle(RoomBannedListMemberAction.UnBanUser(it.roomMemberSummary)) + } + } + } + .show() + } + is RoomBannedViewEvents.ToastError -> { + requireActivity().toast(it.info) + } + } + } + } + + override fun onDestroyView() { + recyclerView.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { viewState -> + roomMemberListController.setData(viewState) + renderRoomSummary(viewState) + } + + private fun renderRoomSummary(state: RoomBannedMemberListViewState) { + state.roomSummary()?.let { + roomSettingsToolbarTitleView.text = it.displayName + avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView) + } + } +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListViewState.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListViewState.kt new file mode 100644 index 0000000000..5b3d4e0826 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedMemberListViewState.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.matrix.android.api.session.room.model.RoomSummary +import im.vector.riotx.features.roomprofile.RoomProfileArgs + +data class RoomBannedMemberListViewState( + val roomId: String, + val roomSummary: Async = Uninitialized, + val bannedMemberSummaries: Async> = Uninitialized, + val onGoingModerationAction: List = emptyList(), + val canUserBan: Boolean = false +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) +} diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedViewEvents.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedViewEvents.kt new file mode 100644 index 0000000000..69e422b022 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/banned/RoomBannedViewEvents.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.roomprofile.banned + +import im.vector.matrix.android.api.session.room.model.RoomMemberSummary +import im.vector.riotx.core.platform.VectorViewEvents + +sealed class RoomBannedViewEvents : VectorViewEvents { + data class ShowBannedInfo(val bannedByUserId: String, val banReason: String, val roomMemberSummary: RoomMemberSummary) : RoomBannedViewEvents() + data class ToastError(val info: String) : RoomBannedViewEvents() +} diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutController.kt index 4711726a7b..fc428cf0ac 100644 --- a/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/riotx/features/signout/soft/SoftLogoutController.kt @@ -28,7 +28,6 @@ import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.features.login.LoginMode import im.vector.riotx.features.signout.soft.epoxy.loginCenterButtonItem import im.vector.riotx.features.signout.soft.epoxy.loginErrorWithRetryItem -import im.vector.riotx.features.signout.soft.epoxy.loginHeaderItem import im.vector.riotx.features.signout.soft.epoxy.loginPasswordFormItem import im.vector.riotx.features.signout.soft.epoxy.loginRedButtonItem import im.vector.riotx.features.signout.soft.epoxy.loginTextItem @@ -65,9 +64,6 @@ class SoftLogoutController @Inject constructor( } private fun buildHeader(state: SoftLogoutViewState) { - loginHeaderItem { - id("header") - } loginTitleItem { id("title") text(stringProvider.getString(R.string.soft_logout_title)) diff --git a/vector/src/main/java/im/vector/riotx/features/signout/soft/epoxy/LoginHeaderItem.kt b/vector/src/main/java/im/vector/riotx/features/signout/soft/epoxy/LoginHeaderItem.kt deleted file mode 100644 index b4433b01f2..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/signout/soft/epoxy/LoginHeaderItem.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.riotx.features.signout.soft.epoxy - -import com.airbnb.epoxy.EpoxyModelClass -import im.vector.riotx.R -import im.vector.riotx.core.epoxy.VectorEpoxyHolder -import im.vector.riotx.core.epoxy.VectorEpoxyModel - -@EpoxyModelClass(layout = R.layout.item_login_header) -abstract class LoginHeaderItem : VectorEpoxyModel() { - class Holder : VectorEpoxyHolder() -} diff --git a/vector/src/main/res/drawable/element_logotype.xml b/vector/src/main/res/drawable/element_logotype.xml deleted file mode 100644 index 0419447b32..0000000000 --- a/vector/src/main/res/drawable/element_logotype.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - diff --git a/vector/src/main/res/drawable/element_logotype_combined.xml b/vector/src/main/res/drawable/element_logotype_combined.xml new file mode 100644 index 0000000000..7516bd63e1 --- /dev/null +++ b/vector/src/main/res/drawable/element_logotype_combined.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/vector/src/main/res/drawable/ic_edit.xml b/vector/src/main/res/drawable/ic_edit.xml index 1ad914fc99..33214d4246 100644 --- a/vector/src/main/res/drawable/ic_edit.xml +++ b/vector/src/main/res/drawable/ic_edit.xml @@ -1,22 +1,20 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> diff --git a/vector/src/main/res/layout/activity_signed_out.xml b/vector/src/main/res/layout/activity_signed_out.xml index cfe9316677..20b03a6a2c 100644 --- a/vector/src/main/res/layout/activity_signed_out.xml +++ b/vector/src/main/res/layout/activity_signed_out.xml @@ -6,11 +6,6 @@ android:layout_height="match_parent" android:background="?riotx_background"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" /> - - - - - - diff --git a/vector/src/main/res/layout/item_login_header.xml b/vector/src/main/res/layout/item_login_header.xml deleted file mode 100644 index d033c4f9f9..0000000000 --- a/vector/src/main/res/layout/item_login_header.xml +++ /dev/null @@ -1,9 +0,0 @@ - - diff --git a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml new file mode 100644 index 0000000000..ccc6f66518 --- /dev/null +++ b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/values-land/styles_login.xml b/vector/src/main/res/values-land/styles_login.xml index 29ddebedd2..d781ec5f1e 100644 --- a/vector/src/main/res/values-land/styles_login.xml +++ b/vector/src/main/res/values-land/styles_login.xml @@ -1,19 +1,4 @@ - - diff --git a/vector/src/main/res/values-v21/styles_login.xml b/vector/src/main/res/values-v21/styles_login.xml index 22eeec5528..78554f9ebb 100644 --- a/vector/src/main/res/values-v21/styles_login.xml +++ b/vector/src/main/res/values-v21/styles_login.xml @@ -1,8 +1,5 @@ - \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 876206d519..70a39ee926 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -992,6 +992,10 @@ Banned users + + 1 banned user + %d banned users + Advanced @@ -2499,6 +2503,7 @@ Not all features in Riot are implemented in Element yet. Main missing (and comin Enter the URL of an identity server Submit Set role + Role Open chat Mute the microphone Unmute the microphone @@ -2560,4 +2565,7 @@ Not all features in Riot are implemented in Element yet. Main missing (and comin Revoke invite Revoke invite to %1$s? + + Banned by %1$s + Failed to UnBan user diff --git a/vector/src/main/res/values/styles_login.xml b/vector/src/main/res/values/styles_login.xml index f1f067f623..c5180384a8 100644 --- a/vector/src/main/res/values/styles_login.xml +++ b/vector/src/main/res/values/styles_login.xml @@ -8,27 +8,9 @@ 36dp - - - - -