From 92ceb0e8fb49a7ebfad946e3278c6d9db97566a9 Mon Sep 17 00:00:00 2001 From: Dominic Fischer Date: Fri, 13 Nov 2020 18:59:40 +0000 Subject: [PATCH 01/73] Convert IntegrationManagerService to suspend functions Signed-off-by: Dominic Fischer --- .../IntegrationManagerService.kt | 11 ++----- .../DefaultIntegrationManagerService.kt | 14 ++++----- .../integrationmanager/IntegrationManager.kt | 31 +++++-------------- .../settings/VectorSettingsGeneralFragment.kt | 5 +-- .../RoomWidgetPermissionViewModel.kt | 27 ++++++---------- .../permissions/WidgetPermissionsHelper.kt | 5 +-- 6 files changed, 30 insertions(+), 63 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt index e27d81edb7..60af93888e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/integrationmanager/IntegrationManagerService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.integrationmanager -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This is the entry point to manage integration. You can grab an instance of this service through an active session. */ @@ -80,19 +77,17 @@ interface IntegrationManagerService { /** * Offers to enable or disable the integration. * @param enable the param to change - * @param callback the matrix callback to listen for result. * @return Cancelable */ - fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable + suspend fun setIntegrationEnabled(enable: Boolean) /** * Offers to allow or disallow a widget. * @param stateEventId the eventId of the state event defining the widget. * @param allowed the param to change - * @param callback the matrix callback to listen for result. * @return Cancelable */ - fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable + suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) /** * Returns true if the widget is allowed, false otherwise. @@ -105,7 +100,7 @@ interface IntegrationManagerService { * @param widgetType the widget type to check for * @param domain the domain to check for */ - fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable + suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) /** * Returns true if the widget domain is allowed, false otherwise. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt index 753e865b4a..482ecbd8d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt @@ -16,10 +16,8 @@ package org.matrix.android.sdk.internal.session.integrationmanager -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService -import org.matrix.android.sdk.api.util.Cancelable import javax.inject.Inject internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService { @@ -44,20 +42,20 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val return integrationManager.isIntegrationEnabled() } - override fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable { - return integrationManager.setIntegrationEnabled(enable, callback) + override suspend fun setIntegrationEnabled(enable: Boolean) { + return integrationManager.setIntegrationEnabled(enable) } - override fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable { - return integrationManager.setWidgetAllowed(stateEventId, allowed, callback) + override suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) { + return integrationManager.setWidgetAllowed(stateEventId, allowed) } override fun isWidgetAllowed(stateEventId: String): Boolean { return integrationManager.isWidgetAllowed(stateEventId) } - override fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable { - return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback) + override suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) { + return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed) } override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt index df4e407415..ebd57ce657 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt @@ -20,15 +20,12 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.util.NoOpCancellable import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.extensions.observeNotNull @@ -41,7 +38,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccoun import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import timber.log.Timber import javax.inject.Inject @@ -137,22 +133,17 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri return integrationProvisioningContent?.enabled ?: false } - fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback): Cancelable { + suspend fun setIntegrationEnabled(enable: Boolean) { val isIntegrationEnabled = isIntegrationEnabled() if (enable == isIntegrationEnabled) { - callback.onSuccess(Unit) - return NoOpCancellable + return } val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable) val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent) - return updateUserAccountDataTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + return updateUserAccountDataTask.execute(params) } - fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) { val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS) val currentContent = currentAllowedWidgets?.content?.toModel() val newContent = if (currentContent == null) { @@ -165,11 +156,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri currentContent.copy(widgets = allowedWidgets) } val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) - return updateUserAccountDataTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + return updateUserAccountDataTask.execute(params) } fun isWidgetAllowed(stateEventId: String): Boolean { @@ -178,7 +165,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri return currentContent?.widgets?.get(stateEventId) ?: false } - fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback): Cancelable { + suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) { val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS) val currentContent = currentAllowedWidgets?.content?.toModel() val newContent = if (currentContent == null) { @@ -195,11 +182,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri currentContent.copy(native = nativeAllowedWidgets) } val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent) - return updateUserAccountDataTask - .configureWith(params) { - this.callback = callback - } - .executeBy(taskExecutor) + return updateUserAccountDataTask.execute(params) } fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt index b1ccabfb76..5a7ceb4084 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsGeneralFragment.kt @@ -58,7 +58,6 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.failure.isInvalidPassword import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService @@ -214,7 +213,9 @@ class VectorSettingsGeneralFragment @Inject constructor( it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> // Disable it while updating the state, will be re-enabled by the account data listener. it.isEnabled = false - session.integrationManagerService().setIntegrationEnabled(newValue as Boolean, NoOpMatrixCallback()) + lifecycleScope.launch { + session.integrationManagerService().setIntegrationEnabled(newValue as Boolean) + } true } } diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index cb40e5672b..eb588ec9ae 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.widgets.model.WidgetType -import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import timber.log.Timber import java.net.URL @@ -106,14 +105,11 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in if (state.permissionData()?.isWebviewWidget.orFalse()) { WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, false) } else { - awaitCallback { - session.integrationManagerService().setNativeWidgetDomainAllowed( - state.permissionData.invoke()?.widget?.type?.preferred ?: "", - state.permissionData.invoke()?.widgetDomain ?: "", - false, - it - ) - } + session.integrationManagerService().setNativeWidgetDomainAllowed( + state.permissionData.invoke()?.widget?.type?.preferred ?: "", + state.permissionData.invoke()?.widgetDomain ?: "", + false + ) } } catch (failure: Throwable) { Timber.v("Failure revoking widget: ${state.widgetId}") @@ -131,14 +127,11 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in if (state.permissionData()?.isWebviewWidget.orFalse()) { WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, true) } else { - awaitCallback { - session.integrationManagerService().setNativeWidgetDomainAllowed( - state.permissionData.invoke()?.widget?.type?.preferred ?: "", - state.permissionData.invoke()?.widgetDomain ?: "", - true, - it - ) - } + session.integrationManagerService().setNativeWidgetDomainAllowed( + state.permissionData.invoke()?.widget?.type?.preferred ?: "", + state.permissionData.invoke()?.widgetDomain ?: "", + true + ) } } catch (failure: Throwable) { Timber.v("Failure allowing widget: ${state.widgetId}") diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/WidgetPermissionsHelper.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/WidgetPermissionsHelper.kt index 871e73592d..5664609a99 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/WidgetPermissionsHelper.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/WidgetPermissionsHelper.kt @@ -19,7 +19,6 @@ package im.vector.app.features.widgets.permissions import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.widgets.WidgetService -import org.matrix.android.sdk.internal.util.awaitCallback class WidgetPermissionsHelper(private val integrationManagerService: IntegrationManagerService, private val widgetService: WidgetService) { @@ -30,8 +29,6 @@ class WidgetPermissionsHelper(private val integrationManagerService: Integration widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE) ).firstOrNull() val eventId = widget?.event?.eventId ?: return - awaitCallback { - integrationManagerService.setWidgetAllowed(eventId, allow, it) - } + integrationManagerService.setWidgetAllowed(eventId, allow) } } From a3a2c0a9a8f1c388e58f03409fb23665f33d2267 Mon Sep 17 00:00:00 2001 From: Dominic Fischer Date: Fri, 13 Nov 2020 19:10:08 +0000 Subject: [PATCH 02/73] Convert UploadsService to suspend functions Signed-off-by: Dominic Fischer --- .../sdk/api/session/room/uploads/UploadsService.kt | 7 +------ .../session/room/uploads/DefaultUploadsService.kt | 13 ++----------- .../roomprofile/uploads/RoomUploadsViewModel.kt | 5 +---- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/uploads/UploadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/uploads/UploadsService.kt index c3cc1eb9ee..e2462d007d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/uploads/UploadsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/uploads/UploadsService.kt @@ -16,9 +16,6 @@ package org.matrix.android.sdk.api.session.room.uploads -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - /** * This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level. */ @@ -29,7 +26,5 @@ interface UploadsService { * @param numberOfEvents the expected number of events to retrieve. The result can contain less events. * @param since token to get next page, or null to get the first page */ - fun getUploads(numberOfEvents: Int, - since: String?, - callback: MatrixCallback): Cancelable + suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt index 824bd23c01..895f1cf50d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt @@ -18,17 +18,12 @@ package org.matrix.android.sdk.internal.session.room.uploads import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult import org.matrix.android.sdk.api.session.room.uploads.UploadsService -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith internal class DefaultUploadsService @AssistedInject constructor( @Assisted private val roomId: String, - private val taskExecutor: TaskExecutor, private val getUploadsTask: GetUploadsTask, private val cryptoService: CryptoService ) : UploadsService { @@ -38,11 +33,7 @@ internal class DefaultUploadsService @AssistedInject constructor( fun create(roomId: String): UploadsService } - override fun getUploads(numberOfEvents: Int, since: String?, callback: MatrixCallback): Cancelable { - return getUploadsTask - .configureWith(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since)) { - this.callback = callback - } - .executeBy(taskExecutor) + override suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult { + return getUploadsTask.execute(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since)) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 76b1a9e0c3..763eed5474 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -33,7 +33,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.getFileUrl -import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx @@ -90,9 +89,7 @@ class RoomUploadsViewModel @AssistedInject constructor( viewModelScope.launch { try { - val result = awaitCallback { - room.getUploads(20, token, it) - } + val result = room.getUploads(20, token) token = result.nextToken From 27050b911ba2ef3a99ed960735319e76e3f4958a Mon Sep 17 00:00:00 2001 From: Dominic Fischer Date: Fri, 13 Nov 2020 19:37:21 +0000 Subject: [PATCH 03/73] Convert TermsService to suspend functions Signed-off-by: Dominic Fischer --- .../sdk/api/session/terms/TermsService.kt | 16 ++++-------- .../session/terms/DefaultTermsService.kt | 26 +++++++------------ .../change/SetIdentityServerViewModel.kt | 5 +--- .../features/terms/ReviewTermsViewModel.kt | 21 +++++---------- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt index 2d88125662..10ce0829d0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/terms/TermsService.kt @@ -16,22 +16,16 @@ package org.matrix.android.sdk.api.session.terms -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable - interface TermsService { enum class ServiceType { IntegrationManager, IdentityService } - fun getTerms(serviceType: ServiceType, - baseUrl: String, - callback: MatrixCallback): Cancelable + suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse - fun agreeToTerms(serviceType: ServiceType, - baseUrl: String, - agreedUrls: List, - token: String?, - callback: MatrixCallback): Cancelable + suspend fun agreeToTerms(serviceType: ServiceType, + baseUrl: String, + agreedUrls: List, + token: String?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt index 5eb97cee3a..41914cc799 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt @@ -17,11 +17,10 @@ package org.matrix.android.sdk.internal.session.terms import dagger.Lazy -import org.matrix.android.sdk.api.MatrixCallback +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.terms.GetTermsResponse import org.matrix.android.sdk.api.session.terms.TermsService -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.RetrofitFactory @@ -33,8 +32,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTe import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.launchToCallback import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.ensureTrailingSlash import okhttp3.OkHttpClient @@ -49,13 +46,11 @@ internal class DefaultTermsService @Inject constructor( private val getOpenIdTokenTask: GetOpenIdTokenTask, private val identityRegisterTask: IdentityRegisterTask, private val updateUserAccountDataTask: UpdateUserAccountDataTask, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val taskExecutor: TaskExecutor + private val coroutineDispatchers: MatrixCoroutineDispatchers ) : TermsService { - override fun getTerms(serviceType: TermsService.ServiceType, - baseUrl: String, - callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { + override suspend fun getTerms(serviceType: TermsService.ServiceType, + baseUrl: String): GetTermsResponse { + return withContext(coroutineDispatchers.main) { val url = buildUrl(baseUrl, serviceType) val termsResponse = executeRequest(null) { apiCall = termsAPI.getTerms("${url}terms") @@ -64,12 +59,11 @@ internal class DefaultTermsService @Inject constructor( } } - override fun agreeToTerms(serviceType: TermsService.ServiceType, - baseUrl: String, - agreedUrls: List, - token: String?, - callback: MatrixCallback): Cancelable { - return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { + override suspend fun agreeToTerms(serviceType: TermsService.ServiceType, + baseUrl: String, + agreedUrls: List, + token: String?) { + withContext(coroutineDispatchers.main) { val url = buildUrl(baseUrl, serviceType) val tokenToUse = token?.takeIf { it.isNotEmpty() } ?: getToken(baseUrl) diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index 9331f67812..0f07a0353f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -31,7 +31,6 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError -import org.matrix.android.sdk.api.session.terms.GetTermsResponse import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.internal.util.awaitCallback import java.net.UnknownHostException @@ -117,9 +116,7 @@ class SetIdentityServerViewModel @AssistedInject constructor( private suspend fun checkTerms(baseUrl: String) { try { - val data = awaitCallback { - mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl, it) - } + val data = mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl) // has all been accepted? val resp = data.serverResponse diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt index df822807ee..89d6e970cc 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt @@ -28,8 +28,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.terms.GetTermsResponse -import org.matrix.android.sdk.internal.util.awaitCallback import timber.log.Timber class ReviewTermsViewModel @AssistedInject constructor( @@ -94,15 +92,12 @@ class ReviewTermsViewModel @AssistedInject constructor( viewModelScope.launch { try { - awaitCallback { - session.agreeToTerms( - termsArgs.type, - termsArgs.baseURL, - agreedUrls, - termsArgs.token, - it - ) - } + session.agreeToTerms( + termsArgs.type, + termsArgs.baseURL, + agreedUrls, + termsArgs.token + ) _viewEvents.post(ReviewTermsViewEvents.Success) } catch (failure: Throwable) { Timber.e(failure, "Failed to agree to terms") @@ -122,9 +117,7 @@ class ReviewTermsViewModel @AssistedInject constructor( viewModelScope.launch { try { - val data = awaitCallback { - session.getTerms(termsArgs.type, termsArgs.baseURL, it) - } + val data = session.getTerms(termsArgs.type, termsArgs.baseURL) val terms = data.serverResponse.getLocalizedTerms(action.preferredLanguageCode).map { Term(it.localizedUrl ?: "", it.localizedName ?: "", From 221eddd9956c513cda3e9c3d063cbda38a1356ba Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 27 Nov 2020 15:02:08 +0300 Subject: [PATCH 04/73] Add changelog. --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 761d8a4634..9b57903455 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Improvements 🙌: - Room creation form: add advanced section to disable federation (#1314) - Move "Enable Encryption" from room setting screen to room profile screen (#2394) - Improve Invite user screen (seamless search for matrix ID) + - Add Setting Item to Change PIN (#2462) Bugfix 🐛: - Fix crash on AttachmentViewer (#2365) From 89e7e28bfae672a4ea92d0ceb0677ddf7007381c Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 27 Nov 2020 15:22:31 +0300 Subject: [PATCH 05/73] Add settings item to change pin. --- vector/src/main/res/values/strings.xml | 2 ++ vector/src/main/res/xml/vector_settings_pin.xml | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index f82e7f6fe7..1641617fb1 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2686,6 +2686,8 @@ Require PIN after 2 minutes PIN code is required after 2 minutes of not using Element. PIN code is required every time you open Element. + Change PIN + Change the current PIN, you will have to validate your current PIN first. Confirm PIN to disable PIN Can\'t open a room where you are banned from. Can\'t find this room. Make sure it exists. diff --git a/vector/src/main/res/xml/vector_settings_pin.xml b/vector/src/main/res/xml/vector_settings_pin.xml index 27eb275b09..20e240ee35 100644 --- a/vector/src/main/res/xml/vector_settings_pin.xml +++ b/vector/src/main/res/xml/vector_settings_pin.xml @@ -7,6 +7,12 @@ android:summary="@string/settings_security_pin_code_summary" android:title="@string/settings_security_pin_code_title" /> + + Date: Fri, 27 Nov 2020 13:18:07 +0000 Subject: [PATCH 06/73] Remove redundant returns Signed-off-by: Dominic Fischer --- .../integrationmanager/DefaultIntegrationManagerService.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt index 482ecbd8d6..8bf6437009 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/DefaultIntegrationManagerService.kt @@ -43,11 +43,11 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val } override suspend fun setIntegrationEnabled(enable: Boolean) { - return integrationManager.setIntegrationEnabled(enable) + integrationManager.setIntegrationEnabled(enable) } override suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) { - return integrationManager.setWidgetAllowed(stateEventId, allowed) + integrationManager.setWidgetAllowed(stateEventId, allowed) } override fun isWidgetAllowed(stateEventId: String): Boolean { @@ -55,7 +55,7 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val } override suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) { - return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed) + integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed) } override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean { From 245aa6e9e714cd55e54e9a613f57da3d8982f482 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 27 Nov 2020 17:17:24 +0300 Subject: [PATCH 07/73] Create a new pin when tap on change pin item. --- .../app/features/settings/VectorPreferences.kt | 1 + .../settings/VectorSettingsPinFragment.kt | 16 ++++++++++++++++ vector/src/main/res/values/strings.xml | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 5872c1fa1c..9d6ed0246c 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -165,6 +165,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { // Security const val SETTINGS_SECURITY_USE_FLAG_SECURE = "SETTINGS_SECURITY_USE_FLAG_SECURE" const val SETTINGS_SECURITY_USE_PIN_CODE_FLAG = "SETTINGS_SECURITY_USE_PIN_CODE_FLAG" + const val SETTINGS_SECURITY_CHANGE_PIN_CODE_FLAG = "SETTINGS_SECURITY_CHANGE_PIN_CODE_FLAG" private const val SETTINGS_SECURITY_USE_BIOMETRICS_FLAG = "SETTINGS_SECURITY_USE_BIOMETRICS_FLAG" private const val SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG = "SETTINGS_SECURITY_USE_GRACE_PERIOD_FLAG" const val SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG = "SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt index 37465258f6..94328dc44a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt @@ -21,6 +21,7 @@ import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R import im.vector.app.core.extensions.registerStartForActivityResult +import im.vector.app.core.preference.VectorPreference import im.vector.app.features.navigation.Navigator import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.pin.PinCodeStore @@ -41,6 +42,10 @@ class VectorSettingsPinFragment @Inject constructor( findPreference(VectorPreferences.SETTINGS_SECURITY_USE_PIN_CODE_FLAG)!! } + private val changePinCodePref by lazy { + findPreference(VectorPreferences.SETTINGS_SECURITY_CHANGE_PIN_CODE_FLAG)!! + } + private val useCompleteNotificationPref by lazy { findPreference(VectorPreferences.SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG)!! } @@ -74,6 +79,17 @@ class VectorSettingsPinFragment @Inject constructor( } true } + + changePinCodePref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + if (hasPinCode) { + navigator.openPinCode( + requireContext(), + pinActivityResultLauncher, + PinMode.CREATE + ) + } + true + } } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 1641617fb1..bbb09e8df8 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2687,7 +2687,7 @@ PIN code is required after 2 minutes of not using Element. PIN code is required every time you open Element. Change PIN - Change the current PIN, you will have to validate your current PIN first. + Change your current PIN Confirm PIN to disable PIN Can\'t open a room where you are banned from. Can\'t find this room. Make sure it exists. From cf70916764b3b5a3347e38333877c4256e7f8f80 Mon Sep 17 00:00:00 2001 From: Valere Date: Sat, 28 Nov 2020 00:41:29 +0100 Subject: [PATCH 08/73] Fix / double bottomsheet effect --- CHANGES.md | 2 +- .../im/vector/app/core/di/FragmentModule.kt | 6 ++ .../verification/QuadSLoadingFragment.kt | 25 +++++ .../crypto/verification/VerificationAction.kt | 1 + .../verification/VerificationBottomSheet.kt | 20 ++-- .../VerificationBottomSheetViewModel.kt | 94 ++++++++++++------- .../src/main/res/layout/fragment_progress.xml | 14 +++ 7 files changed, 118 insertions(+), 44 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt create mode 100644 vector/src/main/res/layout/fragment_progress.xml diff --git a/CHANGES.md b/CHANGES.md index e48281081b..5b250bcf1a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Improvements 🙌: - Bugfix 🐛: - - + - Double bottomsheet effect after verify with passphrase Translations 🗣: - diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 32c98922fb..2c6a8225d8 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -36,6 +36,7 @@ import im.vector.app.features.crypto.recover.BootstrapMigrateBackupFragment import im.vector.app.features.crypto.recover.BootstrapSaveRecoveryKeyFragment import im.vector.app.features.crypto.recover.BootstrapSetupRecoveryKeyFragment import im.vector.app.features.crypto.recover.BootstrapWaitingFragment +import im.vector.app.features.crypto.verification.QuadSLoadingFragment import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment @@ -418,6 +419,11 @@ interface FragmentModule { @FragmentKey(VerificationCancelFragment::class) fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment + @Binds + @IntoMap + @FragmentKey(QuadSLoadingFragment::class) + fun bindQuadSLoadingFragment(fragment: QuadSLoadingFragment): Fragment + @Binds @IntoMap @FragmentKey(VerificationNotMeFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.kt new file mode 100644 index 0000000000..a0ab1c86a7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/QuadSLoadingFragment.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.app.features.crypto.verification + +import im.vector.app.R +import im.vector.app.core.platform.VectorBaseFragment +import javax.inject.Inject + +class QuadSLoadingFragment @Inject constructor() : VectorBaseFragment() { + override fun getLayoutResId() = R.layout.fragment_progress +} diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt index a32a9de97f..a5142ad8bf 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationAction.kt @@ -31,5 +31,6 @@ sealed class VerificationAction : VectorViewModelAction { object SkipVerification : VerificationAction() object VerifyFromPassphrase : VerificationAction() data class GotResultFromSsss(val cypherData: String, val alias: String) : VerificationAction() + object CancelledFromSsss : VerificationAction() object SecuredStorageHasBeenReset : VerificationAction() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index 35ea96de6f..f310a6e3a3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -106,7 +106,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { viewModel.observeViewEvents { when (it) { - is VerificationBottomSheetViewEvents.Dismiss -> dismiss() + is VerificationBottomSheetViewEvents.Dismiss -> dismiss() is VerificationBottomSheetViewEvents.AccessSecretStore -> { secretStartForActivityResult.launch(SharedSecureStorageActivity.newIntent( requireContext(), @@ -115,7 +115,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS )) } - is VerificationBottomSheetViewEvents.ModalError -> { + is VerificationBottomSheetViewEvents.ModalError -> { AlertDialog.Builder(requireContext()) .setTitle(getString(R.string.dialog_title_error)) .setMessage(it.errorMessage) @@ -124,7 +124,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { .show() Unit } - VerificationBottomSheetViewEvents.GoToSettings -> { + VerificationBottomSheetViewEvents.GoToSettings -> { dismiss() (activity as? VectorBaseActivity)?.navigator?.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY) } @@ -155,6 +155,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { // all have been reset, so we are verified? viewModel.handle(VerificationAction.SecuredStorageHasBeenReset) } + } else { + viewModel.handle(VerificationAction.CancelledFromSsss) } } @@ -209,6 +211,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { return@withState } + if (state.selfVerificationMode && state.verifyingFrom4S) { + showFragment(QuadSLoadingFragment::class, Bundle()) + return@withState + } if (state.selfVerificationMode && state.verifiedFromPrivateKeys) { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) @@ -242,7 +248,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) }) } - is VerificationTxState.Verified -> { + is VerificationTxState.Verified -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) @@ -258,7 +264,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } when (state.qrTransactionState) { - is VerificationTxState.QrScannedByOther -> { + is VerificationTxState.QrScannedByOther -> { showFragment(VerificationQrScannedByOtherFragment::class, Bundle()) return@withState } @@ -272,13 +278,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { }) return@withState } - is VerificationTxState.Verified -> { + is VerificationTxState.Verified -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) return@withState } - is VerificationTxState.Cancelled -> { + is VerificationTxState.Cancelled -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)) }) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index aa20a9a992..611853d48b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -32,6 +32,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.Session @@ -70,6 +71,7 @@ data class VerificationBottomSheetViewState( // true when we display the loading and we wait for the other (incoming request) val selfVerificationMode: Boolean = false, val verifiedFromPrivateKeys: Boolean = false, + val verifyingFrom4S: Boolean = false, val isMe: Boolean = false, val currentDeviceCanCrossSign: Boolean = false, val userWantsToCancel: Boolean = false, @@ -170,7 +172,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } } else { // if the verification is already done you can't cancel anymore - if (state.pendingRequest.invoke()?.cancelConclusion != null || state.sasTransactionState is VerificationTxState.TerminalTxState) { + if (state.pendingRequest.invoke()?.cancelConclusion != null + || state.sasTransactionState is VerificationTxState.TerminalTxState + || state.verifyingFrom4S) { // you cannot cancel anymore } else { setState { @@ -346,6 +350,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( _viewEvents.post(VerificationBottomSheetViewEvents.Dismiss) } is VerificationAction.VerifyFromPassphrase -> { + setState { copy(verifyingFrom4S = true) } _viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore) } is VerificationAction.GotResultFromSsss -> { @@ -354,56 +359,73 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( VerificationAction.SecuredStorageHasBeenReset -> { if (session.cryptoService().crossSigningService().allPrivateKeysKnown()) { setState { - copy(quadSHasBeenReset = true) + copy(quadSHasBeenReset = true, verifyingFrom4S = false) } } Unit } + VerificationAction.CancelledFromSsss -> { + setState { + copy(verifyingFrom4S = false) + } + } }.exhaustive } private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) { - try { - action.cypherData.fromBase64().inputStream().use { ins -> - val res = session.loadSecureSecret>(ins, action.alias) - val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys( - res?.get(MASTER_KEY_SSSS_NAME), - res?.get(USER_SIGNING_KEY_SSSS_NAME), - res?.get(SELF_SIGNING_KEY_SSSS_NAME) - ) - if (trustResult.isVerified()) { - // Sign this device and upload the signature - session.sessionParams.deviceId?.let { deviceId -> - session.cryptoService() - .crossSigningService().trustDevice(deviceId, object : MatrixCallback { - override fun onFailure(failure: Throwable) { - Timber.w(failure, "Failed to sign my device after recovery") - } - }) - } + viewModelScope.launch(Dispatchers.IO) { + try { + action.cypherData.fromBase64().inputStream().use { ins -> + val res = session.loadSecureSecret>(ins, action.alias) + val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys( + res?.get(MASTER_KEY_SSSS_NAME), + res?.get(USER_SIGNING_KEY_SSSS_NAME), + res?.get(SELF_SIGNING_KEY_SSSS_NAME) + ) + if (trustResult.isVerified()) { + // Sign this device and upload the signature + session.sessionParams.deviceId?.let { deviceId -> + session.cryptoService() + .crossSigningService().trustDevice(deviceId, object : MatrixCallback { + override fun onFailure(failure: Throwable) { + Timber.w(failure, "Failed to sign my device after recovery") + } + }) + } - setState { - copy(verifiedFromPrivateKeys = true) - } + setState { + copy( + verifyingFrom4S = false, + verifiedFromPrivateKeys = true + ) + } - // try to get keybackup key - } else { - // POP UP something - _viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys))) + // try the keybackup + tentativeRestoreBackup(res) + } else { + setState { + copy( + verifyingFrom4S = false + ) + } + // POP UP something + _viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys))) + } } - - // try the keybackup - tentativeRestoreBackup(res) - Unit + } catch (failure: Throwable) { + setState { + copy( + verifyingFrom4S = false + ) + } + _viewEvents.post( + VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error))) } - } catch (failure: Throwable) { - _viewEvents.post( - VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error))) } } private fun tentativeRestoreBackup(res: Map?) { - viewModelScope.launch(Dispatchers.IO) { + GlobalScope.launch(Dispatchers.IO) { try { val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also { Timber.v("## Keybackup secret not restored from SSSS") @@ -460,7 +482,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } when (tx) { - is SasVerificationTransaction -> { + is SasVerificationTransaction -> { if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) { // A SAS tx has been started following this request setState { diff --git a/vector/src/main/res/layout/fragment_progress.xml b/vector/src/main/res/layout/fragment_progress.xml new file mode 100644 index 0000000000..a7a2076209 --- /dev/null +++ b/vector/src/main/res/layout/fragment_progress.xml @@ -0,0 +1,14 @@ + + + + + + From c8a8e0f2da50b5f6808b12adf6d9f9c6f14957a6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 10:54:22 +0100 Subject: [PATCH 09/73] Format source --- .../crypto/verification/VerificationBottomSheet.kt | 14 +++++++------- .../VerificationBottomSheetViewModel.kt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index f310a6e3a3..a9b76366df 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -106,7 +106,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { viewModel.observeViewEvents { when (it) { - is VerificationBottomSheetViewEvents.Dismiss -> dismiss() + is VerificationBottomSheetViewEvents.Dismiss -> dismiss() is VerificationBottomSheetViewEvents.AccessSecretStore -> { secretStartForActivityResult.launch(SharedSecureStorageActivity.newIntent( requireContext(), @@ -115,7 +115,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS )) } - is VerificationBottomSheetViewEvents.ModalError -> { + is VerificationBottomSheetViewEvents.ModalError -> { AlertDialog.Builder(requireContext()) .setTitle(getString(R.string.dialog_title_error)) .setMessage(it.errorMessage) @@ -124,7 +124,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { .show() Unit } - VerificationBottomSheetViewEvents.GoToSettings -> { + VerificationBottomSheetViewEvents.GoToSettings -> { dismiss() (activity as? VectorBaseActivity)?.navigator?.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY) } @@ -248,7 +248,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) }) } - is VerificationTxState.Verified -> { + is VerificationTxState.Verified -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) @@ -264,7 +264,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { } when (state.qrTransactionState) { - is VerificationTxState.QrScannedByOther -> { + is VerificationTxState.QrScannedByOther -> { showFragment(VerificationQrScannedByOtherFragment::class, Bundle()) return@withState } @@ -278,13 +278,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { }) return@withState } - is VerificationTxState.Verified -> { + is VerificationTxState.Verified -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe)) }) return@withState } - is VerificationTxState.Cancelled -> { + is VerificationTxState.Cancelled -> { showFragment(VerificationConclusionFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)) }) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 611853d48b..23ed9b6483 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -482,7 +482,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } when (tx) { - is SasVerificationTransaction -> { + is SasVerificationTransaction -> { if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) { // A SAS tx has been started following this request setState { From 4d9b9cb959e433ad6b1f2e10c9030408e1f6c797 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 20 Nov 2020 14:23:25 +0100 Subject: [PATCH 10/73] Deprecated event m.room.aliases --- .../android/sdk/api/session/events/model/EventType.kt | 6 ++++++ .../sdk/api/session/room/model/RoomAliasesContent.kt | 3 +++ 2 files changed, 9 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 0a7f3ff09f..68874a1fb1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -49,6 +49,12 @@ object EventType { const val STATE_ROOM_JOIN_RULES = "m.room.join_rules" const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access" const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels" + + /** + * Note that this Event has been deprecated, see + * - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events + * - https://github.com/matrix-org/matrix-doc/pull/2432 + */ const val STATE_ROOM_ALIASES = "m.room.aliases" const val STATE_ROOM_TOMBSTONE = "m.room.tombstone" const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomAliasesContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomAliasesContent.kt index f70e013786..59989f3045 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomAliasesContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomAliasesContent.kt @@ -21,6 +21,9 @@ import com.squareup.moshi.JsonClass /** * Class representing the EventType.STATE_ROOM_ALIASES state event content + * Note that this Event has been deprecated, see + * - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events + * - https://github.com/matrix-org/matrix-doc/pull/2432 */ @JsonClass(generateAdapter = true) data class RoomAliasesContent( From 03715e0939bbfee23f82e9274a2a4a5683dcba28 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 20 Nov 2020 14:29:48 +0100 Subject: [PATCH 11/73] Do not use m.room.aliases event to compute a room name (#2428) --- .../session/room/membership/RoomDisplayNameResolver.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index a7dfcfc96f..f744af94c6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -71,12 +71,6 @@ internal class RoomDisplayNameResolver @Inject constructor( return name } - val aliases = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_ALIASES, stateKey = "")?.root - name = ContentMapper.map(aliases?.content).toModel()?.aliases?.firstOrNull() - if (!name.isNullOrEmpty()) { - return name - } - val roomMembers = RoomMemberHelper(realm, roomId) val activeMembers = roomMembers.queryActiveRoomMembersEvent().findAll() From e2a89c22dab42d213525f3d0b8e4555348e37530 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 20 Nov 2020 14:37:19 +0100 Subject: [PATCH 12/73] Do not show m.room.aliases event in the timeline (#2428) --- .../home/room/detail/timeline/factory/TimelineItemFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 575f28b610..243cbbd0e6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -53,7 +53,6 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.STATE_ROOM_AVATAR, EventType.STATE_ROOM_MEMBER, EventType.STATE_ROOM_THIRD_PARTY_INVITE, - EventType.STATE_ROOM_ALIASES, EventType.STATE_ROOM_CANONICAL_ALIAS, EventType.STATE_ROOM_JOIN_RULES, EventType.STATE_ROOM_HISTORY_VISIBILITY, @@ -79,6 +78,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me encryptedItemFactory.create(event, nextEvent, highlight, callback) } } + EventType.STATE_ROOM_ALIASES, EventType.KEY_VERIFICATION_ACCEPT, EventType.KEY_VERIFICATION_START, EventType.KEY_VERIFICATION_KEY, From 7c2fea862374348d997be28af06d6916d1f70d64 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 20 Nov 2020 14:55:16 +0100 Subject: [PATCH 13/73] Typo --- matrix-sdk-android/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index f77cd3203d..c4b579df63 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -246,7 +246,7 @@ %1$s removed %2$s as an address for this room. - %1$s removed %3$s as addresses for this room. + %1$s removed %2$s as addresses for this room. From 0d93105bcd140ed9940ec6beb5db0b31ac504084 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 20 Nov 2020 15:29:35 +0100 Subject: [PATCH 14/73] Rended m.room.canonical_alias event in the timeline, considering alt_aliases (#2428) --- .../room/model/RoomCanonicalAliasContent.kt | 11 ++- .../src/main/res/values/strings.xml | 27 +++++++ .../timeline/format/NoticeEventFormatter.kt | 75 ++++++++++++++++--- 3 files changed, 102 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomCanonicalAliasContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomCanonicalAliasContent.kt index 5487b2ff82..4e8bd2e71b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomCanonicalAliasContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomCanonicalAliasContent.kt @@ -24,5 +24,14 @@ import com.squareup.moshi.JsonClass */ @JsonClass(generateAdapter = true) data class RoomCanonicalAliasContent( - @Json(name = "alias") val canonicalAlias: String? = null + /** + * The canonical alias for the room. If not present, null, or empty the room should be considered to have no canonical alias. + */ + @Json(name = "alias") val canonicalAlias: String? = null, + + /** + * Alternative aliases the room advertises. + * This list can have aliases despite the alias field being null, empty, or otherwise not present. + */ + @Json(name = "alt_aliases") val alternativeAliases: List? = null ) diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml index c4b579df63..7a0fe1d735 100644 --- a/matrix-sdk-android/src/main/res/values/strings.xml +++ b/matrix-sdk-android/src/main/res/values/strings.xml @@ -262,6 +262,33 @@ "%1$s removed the main address for this room." "You removed the main address for this room." + + %1$s added the alternative address %2$s for this room. + %1$s added the alternative addresses %2$s for this room. + + + + You added the alternative address %1$s for this room. + You added the alternative addresses %1$s for this room. + + + + %1$s removed the alternative address %2$s for this room. + %1$s removed the alternative addresses %2$s for this room. + + + + You removed the alternative address %1$s for this room. + You removed the alternative addresses %1$s for this room. + + + %1$s changed the alternative addresses for this room. + You changed the alternative addresses for this room. + %1$s changed the main and alternative addresses for this room. + You changed the main and alternative addresses for this room. + %1$s changed the addresses for this room. + You changed the addresses for this room. + "%1$s has allowed guests to join the room." "You have allowed guests to join the room." "%1$s has allowed guests to join here." diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index c4cc2e87b0..0db6a374e9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -465,21 +465,76 @@ class NoticeEventFormatter @Inject constructor( private fun formatRoomCanonicalAliasEvent(event: Event, senderName: String?): String? { val eventContent: RoomCanonicalAliasContent? = event.getClearContent().toModel() - val canonicalAlias = eventContent?.canonicalAlias - return canonicalAlias - ?.takeIf { it.isNotBlank() } - ?.let { - if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_set_by_you, it) - } else { - sp.getString(R.string.notice_room_canonical_alias_set, senderName, it) - } + val prevContent: RoomCanonicalAliasContent? = event.resolvedPrevContent().toModel() + val canonicalAlias = eventContent?.canonicalAlias?.takeIf { it.isNotEmpty() } + val prevCanonicalAlias = prevContent?.canonicalAlias?.takeIf { it.isNotEmpty() } + val altAliases = eventContent?.alternativeAliases.orEmpty() + val prevAltAliases = prevContent?.alternativeAliases.orEmpty() + val added = altAliases - prevAltAliases + val removed = prevAltAliases - altAliases + + return if (added.isEmpty() && removed.isEmpty() && canonicalAlias == prevCanonicalAlias) { + // in case there is no difference between the two events say something as we can't simply hide the event from here + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_no_change_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_no_change, senderName) + } + } else if (added.isEmpty() && removed.isEmpty()) { + // Canonical has changed + if (canonicalAlias != null) { + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_set_by_you, canonicalAlias) + } else { + sp.getString(R.string.notice_room_canonical_alias_set, senderName, canonicalAlias) } - ?: if (event.isSentByCurrentUser()) { + } else { + if (event.isSentByCurrentUser()) { sp.getString(R.string.notice_room_canonical_alias_unset_by_you) } else { sp.getString(R.string.notice_room_canonical_alias_unset, senderName) } + } + } else if (added.isEmpty()) { + if (canonicalAlias == prevCanonicalAlias) { + // Some alternative has been removed + if (event.isSentByCurrentUser()) { + sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed_by_you, removed.size, removed.joinToString()) + } else { + sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed, removed.size, senderName, removed.joinToString()) + } + } else { + // Main and removed + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed, senderName) + } + } + } else if (removed.isEmpty()) { + if (canonicalAlias == prevCanonicalAlias) { + // Some alternative has been added + if (event.isSentByCurrentUser()) { + sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added_by_you, added.size, added.joinToString()) + } else { + sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added, added.size, senderName, added.joinToString()) + } + } else { + // Main and added + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed, senderName) + } + } + } else { + // Alternative added and removed + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_alternative_changed_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_alternative_changed, senderName) + } + } } private fun formatRoomGuestAccessEvent(event: Event, senderName: String?, rs: RoomSummary?): String? { From a6f56ace2441d0079f463ddb8cba81d10dc8142a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 13:05:23 +0100 Subject: [PATCH 15/73] Create a dedicated screen to manage room alias (#2428) - WIP --- .../java/org/matrix/android/sdk/rx/RxRoom.kt | 8 - .../android/sdk/api/session/room/Room.kt | 2 + .../api/session/room/alias/AliasService.kt | 24 +++ .../sdk/internal/session/room/DefaultRoom.kt | 3 + .../sdk/internal/session/room/RoomAPI.kt | 8 + .../sdk/internal/session/room/RoomFactory.kt | 3 + .../sdk/internal/session/room/RoomModule.kt | 5 + .../session/room/alias/DefaultAliasService.kt | 36 ++++ .../session/room/alias/GetAliasesResponse.kt | 28 +++ .../room/alias/GetRoomLocalAliasesTask.kt | 44 +++++ .../im/vector/app/core/di/FragmentModule.kt | 6 + .../roomprofile/RoomProfileActivity.kt | 6 + .../roomprofile/RoomProfileSharedAction.kt | 1 + .../roomprofile/alias/RoomAliasAction.kt | 31 +++ .../roomprofile/alias/RoomAliasController.kt | 120 ++++++++++++ .../roomprofile/alias/RoomAliasFragment.kt | 112 +++++++++++ .../roomprofile/alias/RoomAliasViewEvents.kt | 28 +++ .../roomprofile/alias/RoomAliasViewModel.kt | 182 ++++++++++++++++++ .../roomprofile/alias/RoomAliasViewState.kt | 41 ++++ .../settings/RoomSettingsAction.kt | 1 - .../settings/RoomSettingsController.kt | 25 ++- .../settings/RoomSettingsFragment.kt | 8 +- .../settings/RoomSettingsViewModel.kt | 14 +- .../settings/RoomSettingsViewState.kt | 2 - vector/src/main/res/values/strings.xml | 19 ++ 25 files changed, 718 insertions(+), 39 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt index 86f2d26808..826ada358b 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt @@ -127,14 +127,6 @@ class RxRoom(private val room: Room) { room.updateName(name, it) } - fun addRoomAlias(alias: String): Completable = completableBuilder { - room.addRoomAlias(alias, it) - } - - fun updateCanonicalAlias(alias: String): Completable = completableBuilder { - room.updateCanonicalAlias(alias, it) - } - fun updateHistoryReadability(readability: RoomHistoryVisibility): Completable = completableBuilder { room.updateHistoryReadability(readability, it) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index 837bda031b..cb6690b5c5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.room import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.room.alias.AliasService import org.matrix.android.sdk.api.session.room.call.RoomCallService import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService import org.matrix.android.sdk.api.session.room.members.MembershipService @@ -46,6 +47,7 @@ interface Room : DraftService, ReadService, TypingService, + AliasService, TagsService, MembershipService, StateService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt new file mode 100644 index 0000000000..c7d5657157 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.api.session.room.alias + +interface AliasService { + /** + * Get list of local alias of the room + */ + suspend fun getRoomAliases(): List +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index c7bb640f7c..7a819250cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.alias.AliasService import org.matrix.android.sdk.api.session.room.call.RoomCallService import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.model.RoomSummary @@ -58,6 +59,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, private val roomCallService: RoomCallService, private val readService: ReadService, private val typingService: TypingService, + private val aliasService: AliasService, private val tagsService: TagsService, private val cryptoService: CryptoService, private val relationService: RelationService, @@ -76,6 +78,7 @@ internal class DefaultRoom @Inject constructor(override val roomId: String, RoomCallService by roomCallService, ReadService by readService, TypingService by typingService, + AliasService by aliasService, TagsService by tagsService, RelationService by relationService, MembershipService by roomMembersService, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index fc80842f73..f0a8dc5177 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtoc import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody +import org.matrix.android.sdk.internal.session.room.alias.GetAliasesResponse import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.internal.session.room.create.CreateRoomBody import org.matrix.android.sdk.internal.session.room.create.CreateRoomResponse @@ -332,10 +333,17 @@ internal interface RoomAPI { * Add alias to the room. * @param roomAlias the room alias. */ + // TODO Remove (https://github.com/matrix-org/matrix-doc/blob/rav/proposal/alt_canonical_aliases/proposals/2432-revised-alias-publishing.md) @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") fun addRoomAlias(@Path("roomAlias") roomAlias: String, @Body body: AddRoomAliasBody): Call + /** + * Get local aliases of this room + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2432/rooms/{roomId}/aliases") + fun getAliases(@Path("roomId") roomId: String): Call + /** * Inform that the user is starting to type or has stopped typing */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt index d4fa040d06..63370a1ad8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomFactory.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.room import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.session.room.alias.DefaultAliasService import org.matrix.android.sdk.internal.session.room.call.DefaultRoomCallService import org.matrix.android.sdk.internal.session.room.draft.DefaultDraftService import org.matrix.android.sdk.internal.session.room.membership.DefaultMembershipService @@ -54,6 +55,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService: private val roomCallServiceFactory: DefaultRoomCallService.Factory, private val readServiceFactory: DefaultReadService.Factory, private val typingServiceFactory: DefaultTypingService.Factory, + private val aliasServiceFactory: DefaultAliasService.Factory, private val tagsServiceFactory: DefaultTagsService.Factory, private val relationServiceFactory: DefaultRelationService.Factory, private val membershipServiceFactory: DefaultMembershipService.Factory, @@ -76,6 +78,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService: roomCallService = roomCallServiceFactory.create(roomId), readService = readServiceFactory.create(roomId), typingService = typingServiceFactory.create(roomId), + aliasService = aliasServiceFactory.create(roomId), tagsService = tagsServiceFactory.create(roomId), cryptoService = cryptoService, relationService = relationServiceFactory.create(roomId), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 6381796ee0..70721264bd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -29,7 +29,9 @@ import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.DefaultAddRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomIdByAliasTask +import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomLocalAliasesTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask +import org.matrix.android.sdk.internal.session.room.alias.GetRoomLocalAliasesTask import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask import org.matrix.android.sdk.internal.session.room.directory.DefaultGetPublicRoomTask @@ -181,6 +183,9 @@ internal abstract class RoomModule { @Binds abstract fun bindGetRoomIdByAliasTask(task: DefaultGetRoomIdByAliasTask): GetRoomIdByAliasTask + @Binds + abstract fun bindGetRoomLocalAliasesTask(task: DefaultGetRoomLocalAliasesTask): GetRoomLocalAliasesTask + @Binds abstract fun bindAddRoomAliasTask(task: DefaultAddRoomAliasTask): AddRoomAliasTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt new file mode 100644 index 0000000000..129eb82333 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.alias + +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import org.matrix.android.sdk.api.session.room.alias.AliasService + +internal class DefaultAliasService @AssistedInject constructor( + @Assisted private val roomId: String, + private val getRoomLocalAliasesTask: GetRoomLocalAliasesTask +) : AliasService { + + @AssistedInject.Factory + interface Factory { + fun create(roomId: String): AliasService + } + + override suspend fun getRoomAliases(): List { + return getRoomLocalAliasesTask.execute(GetRoomLocalAliasesTask.Params(roomId)) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt new file mode 100644 index 0000000000..499abf33aa --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.alias + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class GetAliasesResponse( + /** + * The list of aliases currently defined on the local server for the given room + */ + @Json(name = "aliases") val aliases: List = emptyList() +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt new file mode 100644 index 0000000000..7cfce4ecdc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomLocalAliasesTask.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.alias + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetRoomLocalAliasesTask : Task> { + data class Params( + val roomId: String + ) +} + +internal class DefaultGetRoomLocalAliasesTask @Inject constructor( + private val roomAPI: RoomAPI, + private val eventBus: EventBus +) : GetRoomLocalAliasesTask { + + override suspend fun execute(params: GetRoomLocalAliasesTask.Params): List { + // We do not check for "org.matrix.msc2432", so the API may be missing + val response = executeRequest(eventBus) { + apiCall = roomAPI.getAliases(roomId = params.roomId) + } + + return response.aliases + } +} diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 32c98922fb..60c14d1579 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -83,6 +83,7 @@ import im.vector.app.features.roomprofile.RoomProfileFragment import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment import im.vector.app.features.roomprofile.settings.RoomSettingsFragment +import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import im.vector.app.features.roomprofile.uploads.files.RoomUploadsFilesFragment import im.vector.app.features.roomprofile.uploads.media.RoomUploadsMediaFragment @@ -363,6 +364,11 @@ interface FragmentModule { @FragmentKey(RoomSettingsFragment::class) fun bindRoomSettingsFragment(fragment: RoomSettingsFragment): Fragment + @Binds + @IntoMap + @FragmentKey(RoomAliasFragment::class) + fun bindRoomAliasFragment(fragment: RoomAliasFragment): Fragment + @Binds @IntoMap @FragmentKey(RoomMemberProfileFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index 609042ffa4..2204150a35 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -36,6 +36,7 @@ import im.vector.app.features.room.RequireActiveMembershipViewState import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment import im.vector.app.features.roomprofile.settings.RoomSettingsFragment +import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import javax.inject.Inject @@ -100,6 +101,7 @@ class RoomProfileActivity : when (sharedAction) { is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + is RoomProfileSharedAction.OpenRoomAlias -> openRoomAlias() is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() } @@ -135,6 +137,10 @@ class RoomProfileActivity : addFragmentToBackstack(R.id.simpleFragmentContainer, RoomSettingsFragment::class.java, roomProfileArgs) } + private fun openRoomAlias() { + addFragmentToBackstack(R.id.simpleFragmentContainer, RoomAliasFragment::class.java, roomProfileArgs) + } + private fun openRoomMembers() { addFragmentToBackstack(R.id.simpleFragmentContainer, RoomMemberListFragment::class.java, roomProfileArgs) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt index 0052ddee99..0449f0db15 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt @@ -23,6 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction */ sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomSettings : RoomProfileSharedAction() + object OpenRoomAlias : RoomProfileSharedAction() object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() object OpenBannedRoomMembers : RoomProfileSharedAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt new file mode 100644 index 0000000000..cb5916747a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -0,0 +1,31 @@ +/* + * 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.app.features.roomprofile.alias + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class RoomAliasAction : VectorViewModelAction { + // Canonical + data class AddAlias(val alias: String) : RoomAliasAction() + data class RemoveAlias(val alias: String) : RoomAliasAction() + data class SetCanonicalAlias(val canonicalAlias: String) : RoomAliasAction() + object UnSetCanonicalAlias : RoomAliasAction() + + // Local + data class AddLocalAlias(val aliasLocalPart: String) : RoomAliasAction() + data class RemoveLocalAlias(val alias: String) : RoomAliasAction() +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt new file mode 100644 index 0000000000..b8e1a12688 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -0,0 +1,120 @@ +/* + * 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.app.features.roomprofile.alias + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import im.vector.app.R +import im.vector.app.core.epoxy.errorWithRetryItem +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.epoxy.profiles.buildProfileSection +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.settings.threepids.threePidItem +import javax.inject.Inject + +class RoomAliasController @Inject constructor( + private val stringProvider: StringProvider, + private val errorFormatter: ErrorFormatter, + colorProvider: ColorProvider +) : TypedEpoxyController() { + + interface Callback { + fun removeAlias(altAlias: String) + fun setCanonicalAlias(alias: String) + fun unsetCanonicalAlias() + fun addLocalAlias(alias: String) + fun removeLocalAlias(alias: String) + } + + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) + + var callback: Callback? = null + + init { + setData(null) + } + + override fun buildModels(data: RoomAliasViewState?) { + data ?: return + + buildProfileSection( + stringProvider.getString(R.string.room_alias_published_alias_title) + ) + settingsInfoItem { + id("publishedInfo") + helperTextResId(R.string.room_alias_published_alias_subtitle) + } + + // TODO Canonical + if (data.alternativeAliases.isNotEmpty()) { + settingsInfoItem { + id("otherPublished") + helperTextResId(R.string.room_alias_published_other) + } + data.alternativeAliases.forEachIndexed { idx, altAlias -> + // TODO Rename this item to a more generic name + threePidItem { + id("alt_$idx") + title(altAlias) + deleteClickListener { callback?.removeAlias(altAlias) } + } + } + } + + // Local + buildProfileSection( + stringProvider.getString(R.string.room_alias_local_address_title) + ) + settingsInfoItem { + id("localInfo") + helperText(stringProvider.getString(R.string.room_alias_local_address_subtitle, data.homeServerName)) + } + + buildLocalInfo(data) + } + + private fun buildLocalInfo(data: RoomAliasViewState) { + when (val localAliases = data.localAliases) { + is Uninitialized -> { + loadingItem { + id("loadingAliases") + } + } + is Success -> { + localAliases().forEachIndexed { idx, localAlias -> + // TODO Rename this item to a more generic name + threePidItem { + id("loc_$idx") + title(localAlias) + deleteClickListener { callback?.removeLocalAlias(localAlias) } + } + } + } + is Fail -> { + errorWithRetryItem { + id("alt_error") + text(errorFormatter.toHumanReadable(localAliases.error)) + } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt new file mode 100644 index 0000000000..3947937643 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -0,0 +1,112 @@ +/* + * 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.app.features.roomprofile.alias + +import android.os.Bundle +import android.view.View +import androidx.core.view.isVisible +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.toast +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomprofile.RoomProfileArgs +import kotlinx.android.synthetic.main.fragment_room_setting_generic.* +import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class RoomAliasFragment @Inject constructor( + val viewModelFactory: RoomAliasViewModel.Factory, + private val controller: RoomAliasController, + private val avatarRenderer: AvatarRenderer +) : + VectorBaseFragment(), + RoomAliasController.Callback { + + private val viewModel: RoomAliasViewModel by fragmentViewModel() + private val roomProfileArgs: RoomProfileArgs by args() + + override fun getLayoutResId() = R.layout.fragment_room_setting_generic + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + controller.callback = this + setupToolbar(roomSettingsToolbar) + roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) + waiting_view_status_text.setText(R.string.please_wait) + waiting_view_status_text.isVisible = true + + viewModel.observeViewEvents { + when (it) { + is RoomAliasViewEvents.Failure -> showFailure(it.throwable) + RoomAliasViewEvents.Success -> showSuccess() + }.exhaustive + } + } + + private fun showSuccess() { + activity?.toast(R.string.room_settings_save_success) + } + + override fun onDestroyView() { + controller.callback = null + roomSettingsRecyclerView.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { viewState -> + controller.setData(viewState) + renderRoomSummary(viewState) + } + + private fun renderRoomSummary(state: RoomAliasViewState) { + waiting_view.isVisible = state.isLoading + + state.roomSummary()?.let { + roomSettingsToolbarTitleView.text = it.displayName + avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView) + } + + invalidateOptionsMenu() + } + + override fun removeAlias(altAlias: String) { + viewModel.handle(RoomAliasAction.RemoveAlias(altAlias)) + } + + override fun setCanonicalAlias(alias: String) { + viewModel.handle(RoomAliasAction.SetCanonicalAlias(alias)) + } + + override fun unsetCanonicalAlias() { + viewModel.handle(RoomAliasAction.UnSetCanonicalAlias) + } + + override fun addLocalAlias(alias: String) { + viewModel.handle(RoomAliasAction.AddLocalAlias(alias)) + } + + override fun removeLocalAlias(alias: String) { + viewModel.handle(RoomAliasAction.RemoveLocalAlias(alias)) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewEvents.kt new file mode 100644 index 0000000000..bbd44741b5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewEvents.kt @@ -0,0 +1,28 @@ +/* + * 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.app.features.roomprofile.alias + +import im.vector.app.core.platform.VectorViewEvents + +/** + * Transient events for room settings screen + */ +sealed class RoomAliasViewEvents : VectorViewEvents { + data class Failure(val throwable: Throwable) : RoomAliasViewEvents() + object Success : RoomAliasViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt new file mode 100644 index 0000000000..104b003387 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -0,0 +1,182 @@ +/* + * 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.app.features.roomprofile.alias + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.rx.mapOptional +import org.matrix.android.sdk.rx.rx +import org.matrix.android.sdk.rx.unwrap + +class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, + private val session: Session) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomAliasViewState): RoomAliasViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomAliasViewState): RoomAliasViewModel? { + val fragment: RoomAliasFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.viewModelFactory.create(state) + } + } + + private val room = session.getRoom(initialState.roomId)!! + + init { + initHomeServerName() + observeRoomSummary() + observeMowerLevel() + observeRoomCanonicalAlias() + getRoomAlias() + } + + private fun initHomeServerName() { + setState { + copy( + homeServerName = session.myUserId.substringAfter(":") + ) + } + } + + private fun getRoomAlias() { + setState { + copy( + localAliases = Loading() + ) + } + + viewModelScope.launch { + runCatching { room.getRoomAliases() } + .fold( + { + setState { copy(localAliases = Success(it)) } + }, + { + setState { copy(localAliases = Fail(it)) } + } + ) + } + } + + private fun observeRoomSummary() { + room.rx().liveRoomSummary() + .unwrap() + .execute { async -> + copy( + roomSummary = async + ) + } + } + + private fun observeMowerLevel() { + PowerLevelsObservableFactory(room) + .createObservable() + .subscribe { + val powerLevelsHelper = PowerLevelsHelper(it) + val permissions = RoomAliasViewState.ActionPermissions( + canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, + EventType.STATE_ROOM_CANONICAL_ALIAS), + ) + setState { copy(actionPermissions = permissions) } + } + .disposeOnClear() + } + + /** + * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. + */ + private fun observeRoomCanonicalAlias() { + room.rx() + .liveStateEvent(EventType.STATE_ROOM_CANONICAL_ALIAS, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + .subscribe { + setState { + copy( + canonicalAlias = it.canonicalAlias, + alternativeAliases = it.alternativeAliases.orEmpty() + ) + } + } + .disposeOnClear() + } + + override fun handle(action: RoomAliasAction) { + when (action) { + is RoomAliasAction.AddAlias -> handleAddAlias(action) + is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() + is RoomAliasAction.AddLocalAlias -> handleAddLocalAlias(action) + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + }.exhaustive + } + + private fun handleAddAlias(action: RoomAliasAction.AddAlias) { + TODO("Not yet implemented") + } + + private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) { + TODO("Not yet implemented") + } + + private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) { + //room.updateCanonicalAlias() + TODO("Not yet implemented") + } + + private fun handleUnsetCanonicalAlias() { + TODO("Not yet implemented") + } + + private fun handleAddLocalAlias(action: RoomAliasAction.AddLocalAlias) { + TODO("Not yet implemented") + } + + private fun handleRemoveLocalAlias(action: RoomAliasAction.RemoveLocalAlias) { + TODO("Not yet implemented") + } + + private fun postLoading(isLoading: Boolean) { + setState { + copy(isLoading = isLoading) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt new file mode 100644 index 0000000000..be9bde27a6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -0,0 +1,41 @@ +/* + * 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.app.features.roomprofile.alias + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +data class RoomAliasViewState( + val roomId: String, + val homeServerName: String = "", + val roomSummary: Async = Uninitialized, + val isLoading: Boolean = false, + val canonicalAlias: String? = null, + val alternativeAliases: List = emptyList(), + val localAliases: Async> = Uninitialized, + val actionPermissions: ActionPermissions = ActionPermissions() +) : MvRxState { + + constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + + data class ActionPermissions( + val canChangeCanonicalAlias: Boolean = false + ) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt index f0a7b38478..f88a7cbfd5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt @@ -24,7 +24,6 @@ sealed class RoomSettingsAction : VectorViewModelAction { data class SetRoomName(val newName: String) : RoomSettingsAction() data class SetRoomTopic(val newTopic: String) : RoomSettingsAction() data class SetRoomHistoryVisibility(val visibility: RoomHistoryVisibility) : RoomSettingsAction() - data class SetRoomCanonicalAlias(val newCanonicalAlias: String) : RoomSettingsAction() object Save : RoomSettingsAction() object Cancel : RoomSettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index 3c73e6ed46..6c944ef2f8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -46,7 +46,7 @@ class RoomSettingsController @Inject constructor( fun onNameChanged(name: String) fun onTopicChanged(topic: String) fun onHistoryVisibilityClicked() - fun onAliasChanged(alias: String) + fun onOpenAlias() } private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) @@ -67,13 +67,13 @@ class RoomSettingsController @Inject constructor( id("avatar") enabled(data.actionPermissions.canChangeAvatar) when (val avatarAction = data.avatarAction) { - RoomSettingsViewState.AvatarAction.None -> { + RoomSettingsViewState.AvatarAction.None -> { // Use the current value avatarRenderer(avatarRenderer) // We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. matrixItem(roomSummary.toMatrixItem().copy(avatarUrl = data.currentRoomAvatarUrl)) } - RoomSettingsViewState.AvatarAction.DeleteAvatar -> + RoomSettingsViewState.AvatarAction.DeleteAvatar -> imageUri(null) is RoomSettingsViewState.AvatarAction.UpdateAvatar -> imageUri(avatarAction.newAvatarUri) @@ -108,16 +108,15 @@ class RoomSettingsController @Inject constructor( } } - formEditTextItem { - id("alias") - enabled(data.actionPermissions.canChangeCanonicalAlias) - value(data.newCanonicalAlias ?: roomSummary.canonicalAlias) - hint(stringProvider.getString(R.string.room_settings_addresses_add_new_address)) - - onTextChange { text -> - callback?.onAliasChanged(text) - } - } + buildProfileAction( + id = "alias", + title = stringProvider.getString(R.string.room_settings_alias_title), + subtitle = stringProvider.getString(R.string.room_settings_alias_subtitle), + dividerColor = dividerColor, + divider = true, + editable = true, + action = { callback?.onOpenAlias() } + ) buildProfileAction( id = "historyReadability", diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 6637b7d943..01ee1cf9b8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -39,6 +39,8 @@ import im.vector.app.core.utils.toast import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.features.roomprofile.RoomProfileSharedAction +import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.matrix.android.sdk.api.session.events.model.toModel @@ -61,6 +63,7 @@ class RoomSettingsFragment @Inject constructor( GalleryOrCameraDialogHelper.Listener { private val viewModel: RoomSettingsViewModel by fragmentViewModel() + private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private val roomProfileArgs: RoomProfileArgs by args() private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) @@ -70,6 +73,7 @@ class RoomSettingsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) controller.callback = this setupToolbar(roomSettingsToolbar) roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) @@ -164,8 +168,8 @@ class RoomSettingsFragment @Inject constructor( return@withState } - override fun onAliasChanged(alias: String) { - viewModel.handle(RoomSettingsAction.SetRoomCanonicalAlias(alias)) + override fun onOpenAlias() { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAlias) } override fun onImageReady(uri: Uri?) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 05a75a585b..7e76f85d44 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -68,12 +68,10 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: selectSubscribe( RoomSettingsViewState::avatarAction, RoomSettingsViewState::newName, - RoomSettingsViewState::newCanonicalAlias, RoomSettingsViewState::newTopic, RoomSettingsViewState::newHistoryVisibility, RoomSettingsViewState::roomSummary) { avatarAction, newName, - newCanonicalAlias, newTopic, newHistoryVisibility, asyncSummary -> @@ -83,7 +81,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: showSaveAction = avatarAction !is RoomSettingsViewState.AvatarAction.None || summary?.name != newName || summary?.topic != newTopic - || summary?.canonicalAlias != newCanonicalAlias?.takeIf { it.isNotEmpty() } || newHistoryVisibility != null ) } @@ -99,8 +96,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: historyVisibilityEvent = room.getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY), roomSummary = async, newName = roomSummary?.name, - newTopic = roomSummary?.topic, - newCanonicalAlias = roomSummary?.canonicalAlias + newTopic = roomSummary?.topic ) } @@ -113,8 +109,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), - canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, - EventType.STATE_ROOM_CANONICAL_ALIAS), canChangeHistoryReadability = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_HISTORY_VISIBILITY) ) @@ -143,7 +137,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: is RoomSettingsAction.SetRoomName -> setState { copy(newName = action.newName) } is RoomSettingsAction.SetRoomTopic -> setState { copy(newTopic = action.newTopic) } is RoomSettingsAction.SetRoomHistoryVisibility -> setState { copy(newHistoryVisibility = action.visibility) } - is RoomSettingsAction.SetRoomCanonicalAlias -> setState { copy(newCanonicalAlias = action.newCanonicalAlias) } is RoomSettingsAction.Save -> saveSettings() is RoomSettingsAction.Cancel -> cancel() }.exhaustive @@ -191,11 +184,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: operationList.add(room.rx().updateTopic(state.newTopic ?: "")) } - if (state.newCanonicalAlias != null && summary?.canonicalAlias != state.newCanonicalAlias.takeIf { it.isNotEmpty() }) { - operationList.add(room.rx().addRoomAlias(state.newCanonicalAlias)) - operationList.add(room.rx().updateCanonicalAlias(state.newCanonicalAlias)) - } - if (state.newHistoryVisibility != null) { operationList.add(room.rx().updateHistoryReadability(state.newHistoryVisibility)) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index 2cadc8f798..bdcd9e6bc7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -35,7 +35,6 @@ data class RoomSettingsViewState( val newName: String? = null, val newTopic: String? = null, val newHistoryVisibility: RoomHistoryVisibility? = null, - val newCanonicalAlias: String? = null, val showSaveAction: Boolean = false, val actionPermissions: ActionPermissions = ActionPermissions() ) : MvRxState { @@ -46,7 +45,6 @@ data class RoomSettingsViewState( val canChangeAvatar: Boolean = false, val canChangeName: Boolean = false, val canChangeTopic: Boolean = false, - val canChangeCanonicalAlias: Boolean = false, val canChangeHistoryReadability: Boolean = false ) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 2cb6eb9af9..59bd17ee70 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1022,6 +1022,25 @@ Who can read history? Who can access this room? + + Room addresses + See and managed addresses of this room + + Room Addresses + Published Addresses + Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first. + Main address + Other published addresses: + + Publish this room to the public in %1$s\'s room directory? + No other published addresses yet, add one below + New published address (e.g. #alias:server) + + Local Addresses + + Set addresses for this room so users can find this room through your homeserver (%1$s) + This room has no local addresses + Anyone Members only (since the point in time of selecting this option) From 5b618ba1f3283e988621ad6892fb6eb30ffe8065 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 14:30:48 +0100 Subject: [PATCH 16/73] Create RoomDirectoryAPI, and handle deletion of alias --- .../sdk/api/session/room/RoomService.kt | 5 ++ .../session/directory/DirectoryAPI.kt | 53 +++++++++++++++++++ .../session/room/DefaultRoomService.kt | 6 +++ .../sdk/internal/session/room/RoomAPI.kt | 19 ------- .../sdk/internal/session/room/RoomModule.kt | 13 +++++ .../session/room/alias/AddRoomAliasTask.kt | 10 ++-- .../session/room/alias/DeleteRoomAliasTask.kt | 43 +++++++++++++++ .../room/alias/GetRoomIdByAliasTask.kt | 6 +-- .../session/room/create/CreateRoomTask.kt | 4 +- .../roomprofile/alias/RoomAliasFragment.kt | 25 +++++++-- .../roomprofile/alias/RoomAliasViewModel.kt | 21 +++++--- vector/src/main/res/values/strings.xml | 1 + 12 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index f30037e5c2..477bef66cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -122,6 +122,11 @@ interface RoomService { searchOnServer: Boolean, callback: MatrixCallback>): Cancelable + /** + * Delete a room alias + */ + suspend fun deleteRoomAlias(roomAlias: String) + /** * Return a live data of all local changes membership that happened since the session has been opened. * It allows you to track this in your client to known what is currently being processed by the SDK. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt new file mode 100644 index 0000000000..122d5fef92 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.directory + +import org.matrix.android.sdk.internal.network.NetworkConstants +import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Path + +internal interface DirectoryAPI { + /** + * Get the room ID associated to the room alias. + * + * @param roomAlias the room alias. + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") + fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call + + /** + * Add alias to the room. + * @param roomAlias the room alias. + */ + // TODO Remove (https://github.com/matrix-org/matrix-doc/blob/rav/proposal/alt_canonical_aliases/proposals/2432-revised-alias-publishing.md) + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") + fun addRoomAlias(@Path("roomAlias") roomAlias: String, + @Body body: AddRoomAliasBody): Call + + /** + * Delete a room aliases + * @param roomAlias the room alias. + */ + @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") + fun deleteRoomAlias(@Path("roomAlias") roomAlias: String): Call +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 28656463c1..9ec985e0b6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource @@ -53,6 +54,7 @@ internal class DefaultRoomService @Inject constructor( private val markAllRoomsReadTask: MarkAllRoomsReadTask, private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, private val roomIdByAliasTask: GetRoomIdByAliasTask, + private val deleteRoomAliasTask: DeleteRoomAliasTask, private val roomGetter: RoomGetter, private val roomSummaryDataSource: RoomSummaryDataSource, private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, @@ -125,6 +127,10 @@ internal class DefaultRoomService @Inject constructor( .executeBy(taskExecutor) } + override suspend fun deleteRoomAlias(roomAlias: String) { + deleteRoomAliasTask.execute(DeleteRoomAliasTask.Params(roomAlias)) + } + override fun getChangeMembershipsLive(): LiveData> { return roomChangeMembershipStateDataSource.getLiveStates() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index f0a8dc5177..44f52f5f3a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -23,9 +23,7 @@ import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsRe import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.network.NetworkConstants -import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasBody import org.matrix.android.sdk.internal.session.room.alias.GetAliasesResponse -import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.internal.session.room.create.CreateRoomBody import org.matrix.android.sdk.internal.session.room.create.CreateRoomResponse import org.matrix.android.sdk.internal.session.room.create.JoinRoomResponse @@ -321,23 +319,6 @@ internal interface RoomAPI { @Path("eventId") eventId: String, @Body body: ReportContentBody): Call - /** - * Get the room ID associated to the room alias. - * - * @param roomAlias the room alias. - */ - @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") - fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call - - /** - * Add alias to the room. - * @param roomAlias the room alias. - */ - // TODO Remove (https://github.com/matrix-org/matrix-doc/blob/rav/proposal/alt_canonical_aliases/proposals/2432-revised-alias-publishing.md) - @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") - fun addRoomAlias(@Path("roomAlias") roomAlias: String, - @Body body: AddRoomAliasBody): Call - /** * Get local aliases of this room */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 70721264bd..8ad82e0e15 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -26,10 +26,13 @@ import org.matrix.android.sdk.api.session.room.RoomDirectoryService import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.internal.session.DefaultFileService import org.matrix.android.sdk.internal.session.SessionScope +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI import org.matrix.android.sdk.internal.session.room.alias.AddRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.DefaultAddRoomAliasTask +import org.matrix.android.sdk.internal.session.room.alias.DefaultDeleteRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.alias.DefaultGetRoomLocalAliasesTask +import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask import org.matrix.android.sdk.internal.session.room.alias.GetRoomLocalAliasesTask import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask @@ -92,6 +95,13 @@ internal abstract class RoomModule { return retrofit.create(RoomAPI::class.java) } + @Provides + @JvmStatic + @SessionScope + fun providesDirectoryAPI(retrofit: Retrofit): DirectoryAPI { + return retrofit.create(DirectoryAPI::class.java) + } + @Provides @JvmStatic fun providesParser(): Parser { @@ -189,6 +199,9 @@ internal abstract class RoomModule { @Binds abstract fun bindAddRoomAliasTask(task: DefaultAddRoomAliasTask): AddRoomAliasTask + @Binds + abstract fun bindDeleteRoomAliasTask(task: DefaultDeleteRoomAliasTask): DeleteRoomAliasTask + @Binds abstract fun bindSendTypingTask(task: DefaultSendTypingTask): SendTypingTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt index 695be3f633..d3ef16bc3d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.session.room.alias -import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.room.RoomAPI -import org.matrix.android.sdk.internal.task.Task import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject internal interface AddRoomAliasTask : Task { @@ -30,13 +30,13 @@ internal interface AddRoomAliasTask : Task { } internal class DefaultAddRoomAliasTask @Inject constructor( - private val roomAPI: RoomAPI, + private val directoryAPI: DirectoryAPI, private val eventBus: EventBus ) : AddRoomAliasTask { override suspend fun execute(params: AddRoomAliasTask.Params) { executeRequest(eventBus) { - apiCall = roomAPI.addRoomAlias( + apiCall = directoryAPI.addRoomAlias( roomAlias = params.roomAlias, body = AddRoomAliasBody( roomId = params.roomId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt new file mode 100644 index 0000000000..3400fd994c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DeleteRoomAliasTask.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.alias + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface DeleteRoomAliasTask : Task { + data class Params( + val roomAlias: String + ) +} + +internal class DefaultDeleteRoomAliasTask @Inject constructor( + private val directoryAPI: DirectoryAPI, + private val eventBus: EventBus +) : DeleteRoomAliasTask { + + override suspend fun execute(params: DeleteRoomAliasTask.Params) { + executeRequest(eventBus) { + apiCall = directoryAPI.deleteRoomAlias( + roomAlias = params.roomAlias + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt index 58a119cc77..3c47ee6ef0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetRoomIdByAliasTask.kt @@ -25,7 +25,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.findByAlias import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.room.RoomAPI +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject @@ -38,7 +38,7 @@ internal interface GetRoomIdByAliasTask : Task(eventBus) { - apiCall = roomAPI.getRoomIdByAlias(params.roomAlias) + apiCall = directoryAPI.getRoomIdByAlias(params.roomAlias) } }?.roomId Optional.from(roomId) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 0fe9b0ba68..309b8dbfaa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI import org.matrix.android.sdk.internal.session.room.RoomAPI import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask @@ -47,6 +48,7 @@ internal interface CreateRoomTask : Task internal class DefaultCreateRoomTask @Inject constructor( private val roomAPI: RoomAPI, + private val directoryAPI: DirectoryAPI, @UserId private val userId: String, @SessionDatabase private val monarchy: Monarchy, private val directChatsHelper: DirectChatsHelper, @@ -72,7 +74,7 @@ internal class DefaultCreateRoomTask @Inject constructor( val fullAlias = "#" + params.roomAliasName + ":" + userId.substringAfter(":") try { executeRequest(eventBus) { - apiCall = roomAPI.getRoomIdByAlias(fullAlias) + apiCall = directoryAPI.getRoomIdByAlias(fullAlias) } } catch (throwable: Throwable) { if (throwable is Failure.ServerError && throwable.httpCode == 404) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 3947937643..fe3ca048a8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -16,13 +16,16 @@ package im.vector.app.features.roomprofile.alias +import android.content.DialogInterface import android.os.Bundle import android.view.View +import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.R +import im.vector.app.core.dialogs.withColoredButton import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive @@ -59,7 +62,7 @@ class RoomAliasFragment @Inject constructor( viewModel.observeViewEvents { when (it) { is RoomAliasViewEvents.Failure -> showFailure(it.throwable) - RoomAliasViewEvents.Success -> showSuccess() + RoomAliasViewEvents.Success -> showSuccess() }.exhaustive } } @@ -91,7 +94,15 @@ class RoomAliasFragment @Inject constructor( } override fun removeAlias(altAlias: String) { - viewModel.handle(RoomAliasAction.RemoveAlias(altAlias)) + AlertDialog.Builder(requireContext()) + .setTitle(R.string.dialog_title_confirmation) + .setMessage(getString(R.string.room_alias_delete_confirmation, altAlias)) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.delete) { _, _ -> + viewModel.handle(RoomAliasAction.RemoveAlias(altAlias)) + } + .show() + .withColoredButton(DialogInterface.BUTTON_POSITIVE) } override fun setCanonicalAlias(alias: String) { @@ -107,6 +118,14 @@ class RoomAliasFragment @Inject constructor( } override fun removeLocalAlias(alias: String) { - viewModel.handle(RoomAliasAction.RemoveLocalAlias(alias)) + AlertDialog.Builder(requireContext()) + .setTitle(R.string.dialog_title_confirmation) + .setMessage(getString(R.string.room_alias_delete_confirmation, alias)) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.delete) { _, _ -> + viewModel.handle(RoomAliasAction.RemoveLocalAlias(alias)) + } + .show() + .withColoredButton(DialogInterface.BUTTON_POSITIVE) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 104b003387..ff3565b4bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -140,12 +140,12 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { - is RoomAliasAction.AddAlias -> handleAddAlias(action) - is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.AddAlias -> handleAddAlias(action) + is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) - RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() - is RoomAliasAction.AddLocalAlias -> handleAddLocalAlias(action) - is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() + is RoomAliasAction.AddLocalAlias -> handleAddLocalAlias(action) + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } @@ -154,7 +154,16 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) { - TODO("Not yet implemented") + setState { + copy(isLoading = true) + } + viewModelScope.launch { + runCatching { session.deleteRoomAlias(action.alias) } + .onFailure { _viewEvents.post(RoomAliasViewEvents.Failure(it)) } + setState { + copy(isLoading = false) + } + } } private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 59bd17ee70..a52d98a4b4 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1031,6 +1031,7 @@ Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first. Main address Other published addresses: + Delete the address \"%1$s\"? Publish this room to the public in %1$s\'s room directory? No other published addresses yet, add one below From 27fc5f265f7458046749c1d3e8c006a6fd68219a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 16:50:00 +0100 Subject: [PATCH 17/73] Add/Remove local alias (#2428) --- .../api/session/room/alias/AliasService.kt | 5 ++ .../api/session/room/alias/RoomAliasError.kt | 23 ++++++ .../session/room/failure/CreateRoomFailure.kt | 7 +- .../api/session/room/state/StateService.kt | 5 -- .../session/directory/DirectoryAPI.kt | 1 - .../session/room/alias/AddRoomAliasTask.kt | 10 ++- .../session/room/alias/DefaultAliasService.kt | 7 +- .../alias/RoomAliasAvailabilityChecker.kt | 61 ++++++++++++++ .../session/room/create/CreateRoomTask.kt | 33 ++------ .../session/room/state/DefaultStateService.kt | 8 -- .../createroom/CreateRoomController.kt | 17 ++-- .../createroom/CreateRoomFragment.kt | 2 +- .../createroom/RoomAliasErrorFormatter.kt | 36 ++++++++ .../roomprofile/alias/RoomAliasAction.kt | 3 +- .../roomprofile/alias/RoomAliasController.kt | 38 +++++++-- .../roomprofile/alias/RoomAliasFragment.kt | 15 +++- .../roomprofile/alias/RoomAliasViewModel.kt | 82 ++++++++++++++----- .../roomprofile/alias/RoomAliasViewState.kt | 2 + vector/src/main/res/values/strings.xml | 1 + 19 files changed, 262 insertions(+), 94 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasErrorFormatter.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt index c7d5657157..3060824f0f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt @@ -21,4 +21,9 @@ interface AliasService { * Get list of local alias of the room */ suspend fun getRoomAliases(): List + + /** + * Add local alias to the room + */ + suspend fun addAlias(aliasLocalPart: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt new file mode 100644 index 0000000000..1dbdd9cced --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt @@ -0,0 +1,23 @@ +/* + * 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 org.matrix.android.sdk.api.session.room.alias + +sealed class RoomAliasError : Throwable() { + object AliasEmpty : RoomAliasError() + object AliasNotAvailable : RoomAliasError() + object AliasInvalid : RoomAliasError() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt index b4e2dc645c..208cdd4556 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt @@ -18,13 +18,10 @@ package org.matrix.android.sdk.api.session.room.failure import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError sealed class CreateRoomFailure : Failure.FeatureFailure() { object CreatedWithTimeout : CreateRoomFailure() data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure() - sealed class RoomAliasError : CreateRoomFailure() { - object AliasEmpty : RoomAliasError() - object AliasNotAvailable : RoomAliasError() - object AliasInvalid : RoomAliasError() - } + data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index e4baa58c30..0b25138f57 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -38,11 +38,6 @@ interface StateService { */ fun updateName(name: String, callback: MatrixCallback): Cancelable - /** - * Add new alias to the room. - */ - fun addRoomAlias(roomAlias: String, callback: MatrixCallback): Cancelable - /** * Update the canonical alias of the room */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt index 122d5fef92..3eff4b05aa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt @@ -39,7 +39,6 @@ internal interface DirectoryAPI { * Add alias to the room. * @param roomAlias the room alias. */ - // TODO Remove (https://github.com/matrix-org/matrix-doc/blob/rav/proposal/alt_canonical_aliases/proposals/2432-revised-alias-publishing.md) @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") fun addRoomAlias(@Path("roomAlias") roomAlias: String, @Body body: AddRoomAliasBody): Call diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt index d3ef16bc3d..4dad476f03 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt @@ -17,27 +17,33 @@ package org.matrix.android.sdk.internal.session.room.alias import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker.Companion.toFullAlias import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject internal interface AddRoomAliasTask : Task { data class Params( val roomId: String, - val roomAlias: String + val aliasLocalPart: String ) } internal class DefaultAddRoomAliasTask @Inject constructor( + @UserId private val userId: String, private val directoryAPI: DirectoryAPI, + private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker, private val eventBus: EventBus ) : AddRoomAliasTask { override suspend fun execute(params: AddRoomAliasTask.Params) { + aliasAvailabilityChecker.check(params.aliasLocalPart) + executeRequest(eventBus) { apiCall = directoryAPI.addRoomAlias( - roomAlias = params.roomAlias, + roomAlias = params.aliasLocalPart.toFullAlias(userId), body = AddRoomAliasBody( roomId = params.roomId ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt index 129eb82333..b6c69224e6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt @@ -22,7 +22,8 @@ import org.matrix.android.sdk.api.session.room.alias.AliasService internal class DefaultAliasService @AssistedInject constructor( @Assisted private val roomId: String, - private val getRoomLocalAliasesTask: GetRoomLocalAliasesTask + private val getRoomLocalAliasesTask: GetRoomLocalAliasesTask, + private val addRoomAliasTask: AddRoomAliasTask ) : AliasService { @AssistedInject.Factory @@ -33,4 +34,8 @@ internal class DefaultAliasService @AssistedInject constructor( override suspend fun getRoomAliases(): List { return getRoomLocalAliasesTask.execute(GetRoomLocalAliasesTask.Params(roomId)) } + + override suspend fun addAlias(aliasLocalPart: String) { + addRoomAliasTask.execute(AddRoomAliasTask.Params(roomId, aliasLocalPart)) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt new file mode 100644 index 0000000000..0abb158521 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt @@ -0,0 +1,61 @@ +/* + * 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 org.matrix.android.sdk.internal.session.room.alias + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError +import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import javax.inject.Inject + +internal class RoomAliasAvailabilityChecker @Inject constructor( + @UserId private val userId: String, + private val directoryAPI: DirectoryAPI, + private val eventBus: EventBus +) { + @Throws(RoomAliasError::class) + suspend fun check(aliasLocalPart: String?) { + if (aliasLocalPart.isNullOrEmpty()) { + throw RoomAliasError.AliasEmpty + } + // Check alias availability + val fullAlias = aliasLocalPart.toFullAlias(userId) + try { + executeRequest(eventBus) { + apiCall = directoryAPI.getRoomIdByAlias(fullAlias) + } + } catch (throwable: Throwable) { + if (throwable is Failure.ServerError && throwable.httpCode == 404) { + // This is a 404, so the alias is available: nominal case + null + } else { + // Other error, propagate it + throw throwable + } + } + ?.let { + // Alias already exists: error case + throw RoomAliasError.AliasNotAvailable + } + } + + companion object { + internal fun String.toFullAlias(userId: String) = "#" + this + ":" + userId.substringAfter(":") + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 309b8dbfaa..ef792ab98e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.TimeoutCancellationException import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset @@ -31,11 +32,9 @@ import org.matrix.android.sdk.internal.database.model.RoomEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.directory.DirectoryAPI import org.matrix.android.sdk.internal.session.room.RoomAPI -import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker import org.matrix.android.sdk.internal.session.room.read.SetReadMarkersTask import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask @@ -48,9 +47,8 @@ internal interface CreateRoomTask : Task internal class DefaultCreateRoomTask @Inject constructor( private val roomAPI: RoomAPI, - private val directoryAPI: DirectoryAPI, - @UserId private val userId: String, @SessionDatabase private val monarchy: Monarchy, + private val aliasAvailabilityChecker: RoomAliasAvailabilityChecker, private val directChatsHelper: DirectChatsHelper, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val readMarkersTask: SetReadMarkersTask, @@ -67,28 +65,11 @@ internal class DefaultCreateRoomTask @Inject constructor( } else null if (params.preset == CreateRoomPreset.PRESET_PUBLIC_CHAT) { - if (params.roomAliasName.isNullOrEmpty()) { - throw CreateRoomFailure.RoomAliasError.AliasEmpty - } - // Check alias availability - val fullAlias = "#" + params.roomAliasName + ":" + userId.substringAfter(":") try { - executeRequest(eventBus) { - apiCall = directoryAPI.getRoomIdByAlias(fullAlias) - } - } catch (throwable: Throwable) { - if (throwable is Failure.ServerError && throwable.httpCode == 404) { - // This is a 404, so the alias is available: nominal case - null - } else { - // Other error, propagate it - throw throwable - } + aliasAvailabilityChecker.check(params.roomAliasName) + } catch (aliasError: RoomAliasError) { + throw CreateRoomFailure.AliasError(aliasError) } - ?.let { - // Alias already exists: error case - throw CreateRoomFailure.RoomAliasError.AliasNotAvailable - } } val createRoomBody = createRoomBodyBuilder.build(params) @@ -106,7 +87,7 @@ internal class DefaultCreateRoomTask @Inject constructor( } else if (throwable.httpCode == 400 && throwable.error.code == MatrixError.M_UNKNOWN && throwable.error.message == "Invalid characters in room alias") { - throw CreateRoomFailure.RoomAliasError.AliasInvalid + throw CreateRoomFailure.AliasError(RoomAliasError.AliasInvalid) } } throw throwable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 3463b26c8a..1f5b174299 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -104,14 +104,6 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private ) } - override fun addRoomAlias(roomAlias: String, callback: MatrixCallback): Cancelable { - return addRoomAliasTask - .configureWith(AddRoomAliasTask.Params(roomId, roomAlias)) { - this.callback = callback - } - .executeBy(taskExecutor) - } - override fun updateCanonicalAlias(alias: String, callback: MatrixCallback): Cancelable { return sendStateEvent( eventType = EventType.STATE_ROOM_CANONICAL_ALIAS, diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index aaf7b6ead5..de9ecb2825 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -20,7 +20,6 @@ import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import im.vector.app.R -import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.settingsSectionTitleItem import im.vector.app.features.form.formAdvancedToggleItem @@ -28,11 +27,13 @@ import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableAvatarItem import im.vector.app.features.form.formSubmitButtonItem import im.vector.app.features.form.formSwitchItem +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import javax.inject.Inject -class CreateRoomController @Inject constructor(private val stringProvider: StringProvider, - private val errorFormatter: ErrorFormatter +class CreateRoomController @Inject constructor( + private val stringProvider: StringProvider, + private val roomAliasErrorFormatter: RoomAliasErrorFormatter ) : TypedEpoxyController() { var listener: Listener? = null @@ -103,15 +104,7 @@ class CreateRoomController @Inject constructor(private val stringProvider: Strin enabled(enableFormElement) value(viewState.roomType.aliasLocalPart) homeServer(":" + viewState.homeServerName) - errorMessage( - when ((viewState.asyncCreateRoomRequest as? Fail)?.error) { - is CreateRoomFailure.RoomAliasError.AliasEmpty -> R.string.create_room_alias_empty - is CreateRoomFailure.RoomAliasError.AliasNotAvailable -> R.string.create_room_alias_already_in_use - is CreateRoomFailure.RoomAliasError.AliasInvalid -> R.string.create_room_alias_invalid - else -> null - } - ?.let { stringProvider.getString(it) } - ) + errorMessage(roomAliasErrorFormatter.format((((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)) onTextChange { value -> listener?.setAliasLocalPart(value) } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index fb90752764..204a99929b 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -84,7 +84,7 @@ class CreateRoomFragment @Inject constructor( override fun showFailure(throwable: Throwable) { // Note: RoomAliasError are displayed directly in the form - if (throwable !is CreateRoomFailure.RoomAliasError) { + if (throwable !is CreateRoomFailure.AliasError) { super.showFailure(throwable) } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasErrorFormatter.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasErrorFormatter.kt new file mode 100644 index 0000000000..7a23a79ab3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/RoomAliasErrorFormatter.kt @@ -0,0 +1,36 @@ +/* + * 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.app.features.roomdirectory.createroom + +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError +import javax.inject.Inject + +class RoomAliasErrorFormatter @Inject constructor( + private val stringProvider: StringProvider +) { + fun format(roomAliasError: RoomAliasError?): String? { + return when (roomAliasError) { + is RoomAliasError.AliasEmpty -> R.string.create_room_alias_empty + is RoomAliasError.AliasNotAvailable -> R.string.create_room_alias_already_in_use + is RoomAliasError.AliasInvalid -> R.string.create_room_alias_invalid + else -> null + } + ?.let { stringProvider.getString(it) } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt index cb5916747a..a65065f167 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -26,6 +26,7 @@ sealed class RoomAliasAction : VectorViewModelAction { object UnSetCanonicalAlias : RoomAliasAction() // Local - data class AddLocalAlias(val aliasLocalPart: String) : RoomAliasAction() data class RemoveLocalAlias(val alias: String) : RoomAliasAction() + data class SetNewLocalAliasLocalPart(val aliasLocalPart: String) : RoomAliasAction() + object AddLocalAlias : RoomAliasAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index b8e1a12688..cf133da7b7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -25,28 +25,30 @@ import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.error.ErrorFormatter -import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.form.formSubmitButtonItem +import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter +import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem import im.vector.app.features.settings.threepids.threePidItem +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import javax.inject.Inject class RoomAliasController @Inject constructor( private val stringProvider: StringProvider, private val errorFormatter: ErrorFormatter, - colorProvider: ColorProvider + private val roomAliasErrorFormatter: RoomAliasErrorFormatter ) : TypedEpoxyController() { interface Callback { fun removeAlias(altAlias: String) fun setCanonicalAlias(alias: String) fun unsetCanonicalAlias() - fun addLocalAlias(alias: String) fun removeLocalAlias(alias: String) + fun setNewLocalAliasLocalPart(value: String) + fun addLocalAlias() } - private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) - var callback: Callback? = null init { @@ -81,6 +83,10 @@ class RoomAliasController @Inject constructor( } // Local + buildLocalInfo(data) + } + + private fun buildLocalInfo(data: RoomAliasViewState) { buildProfileSection( stringProvider.getString(R.string.room_alias_local_address_title) ) @@ -89,10 +95,6 @@ class RoomAliasController @Inject constructor( helperText(stringProvider.getString(R.string.room_alias_local_address_subtitle, data.homeServerName)) } - buildLocalInfo(data) - } - - private fun buildLocalInfo(data: RoomAliasViewState) { when (val localAliases = data.localAliases) { is Uninitialized -> { loadingItem { @@ -116,5 +118,23 @@ class RoomAliasController @Inject constructor( } } } + + // Add local + roomAliasEditItem { + id("newLocalAlias") + value(data.newLocalAlias) + homeServer(":" + data.homeServerName) + showBottomSeparator(false) + errorMessage(roomAliasErrorFormatter.format((data.asyncNewLocalAliasRequest as? Fail)?.error as? RoomAliasError)) + onTextChange { value -> + callback?.setNewLocalAliasLocalPart(value) + } + } + + formSubmitButtonItem { + id("submit") + buttonTitleId(R.string.action_add) + buttonClickListener { callback?.addLocalAlias() } + } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index fe3ca048a8..2f7526ebff 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -35,6 +35,7 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* +import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -67,6 +68,12 @@ class RoomAliasFragment @Inject constructor( } } + override fun showFailure(throwable: Throwable) { + if (throwable !is RoomAliasError) { + super.showFailure(throwable) + } + } + private fun showSuccess() { activity?.toast(R.string.room_settings_save_success) } @@ -113,8 +120,12 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.UnSetCanonicalAlias) } - override fun addLocalAlias(alias: String) { - viewModel.handle(RoomAliasAction.AddLocalAlias(alias)) + override fun setNewLocalAliasLocalPart(value: String) { + viewModel.handle(RoomAliasAction.SetNewLocalAliasLocalPart(value)) + } + + override fun addLocalAlias() { + viewModel.handle(RoomAliasAction.AddLocalAlias) } override fun removeLocalAlias(alias: String) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index ff3565b4bb..319947c18e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -22,6 +22,7 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject @@ -62,9 +63,9 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo init { initHomeServerName() observeRoomSummary() - observeMowerLevel() + observePowerLevel() observeRoomCanonicalAlias() - getRoomAlias() + fetchRoomAlias() } private fun initHomeServerName() { @@ -75,7 +76,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } } - private fun getRoomAlias() { + private fun fetchRoomAlias() { setState { copy( localAliases = Loading() @@ -105,7 +106,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } } - private fun observeMowerLevel() { + private fun observePowerLevel() { PowerLevelsObservableFactory(room) .createObservable() .subscribe { @@ -140,29 +141,35 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { - is RoomAliasAction.AddAlias -> handleAddAlias(action) - is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) - is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) - RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() - is RoomAliasAction.AddLocalAlias -> handleAddLocalAlias(action) - is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + is RoomAliasAction.AddAlias -> handleAddAlias() + is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() + is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) + RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } - private fun handleAddAlias(action: RoomAliasAction.AddAlias) { - TODO("Not yet implemented") + private fun handleSetNewLocalAliasLocalPart(action: RoomAliasAction.SetNewLocalAliasLocalPart) { + setState { + copy( + newLocalAlias = action.aliasLocalPart, + asyncNewLocalAliasRequest = Uninitialized + ) + } + } + + private fun handleAddAlias() { + TODO() } private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) { - setState { - copy(isLoading = true) - } + postLoading(true) viewModelScope.launch { runCatching { session.deleteRoomAlias(action.alias) } .onFailure { _viewEvents.post(RoomAliasViewEvents.Failure(it)) } - setState { - copy(isLoading = false) - } + postLoading(false) } } @@ -172,15 +179,48 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun handleUnsetCanonicalAlias() { + // room.updateCanonicalAlias() TODO("Not yet implemented") } - private fun handleAddLocalAlias(action: RoomAliasAction.AddLocalAlias) { - TODO("Not yet implemented") + private fun handleAddLocalAlias() = withState { state -> + setState { + copy( + isLoading = true, + asyncNewLocalAliasRequest = Loading() + ) + } + viewModelScope.launch { + runCatching { room.addAlias(state.newLocalAlias) } + .onFailure { + setState { + copy( + isLoading = false, + asyncNewLocalAliasRequest = Fail(it) + ) + } + _viewEvents.post(RoomAliasViewEvents.Failure(it)) + } + .onSuccess { + setState { + copy( + isLoading = false, + asyncNewLocalAliasRequest = Uninitialized + ) + } + fetchRoomAlias() + } + } } private fun handleRemoveLocalAlias(action: RoomAliasAction.RemoveLocalAlias) { - TODO("Not yet implemented") + postLoading(true) + viewModelScope.launch { + runCatching { session.deleteRoomAlias(action.alias) } + .onFailure { _viewEvents.post(RoomAliasViewEvents.Failure(it)) } + .onSuccess { fetchRoomAlias() } + postLoading(false) + } } private fun postLoading(isLoading: Boolean) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index be9bde27a6..e2021245d8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -30,6 +30,8 @@ data class RoomAliasViewState( val canonicalAlias: String? = null, val alternativeAliases: List = emptyList(), val localAliases: Async> = Uninitialized, + val newLocalAlias: String = "", + val asyncNewLocalAliasRequest: Async = Uninitialized, val actionPermissions: ActionPermissions = ActionPermissions() ) : MvRxState { diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index a52d98a4b4..c97785e74c 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -137,6 +137,7 @@ Open Close Copy + Add Copied to clipboard Disable From e1abd5a05112b41e4933694aeca10f6aaff5f17c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 16:59:32 +0100 Subject: [PATCH 18/73] Fix layout issue --- vector/src/main/res/layout/item_settings_three_pid.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vector/src/main/res/layout/item_settings_three_pid.xml b/vector/src/main/res/layout/item_settings_three_pid.xml index a175788d86..0040840ce9 100644 --- a/vector/src/main/res/layout/item_settings_three_pid.xml +++ b/vector/src/main/res/layout/item_settings_three_pid.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?riotx_background" android:minHeight="64dp" android:paddingStart="@dimen/layout_horizontal_margin" android:paddingEnd="@dimen/layout_horizontal_margin"> @@ -12,19 +13,20 @@ android:id="@+id/item_settings_three_pid_icon" android:layout_width="16dp" android:layout_height="16dp" + android:layout_marginEnd="8dp" android:scaleType="center" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/item_settings_three_pid_title" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:src="@drawable/ic_phone" app:tint="?riotx_text_secondary" - tools:ignore="MissingPrefix" /> + tools:ignore="MissingPrefix" + tools:src="@drawable/ic_phone" /> Date: Mon, 23 Nov 2020 17:17:37 +0100 Subject: [PATCH 19/73] Prepare to update canonical alias state --- .../api/session/room/state/StateService.kt | 4 +- .../session/room/state/DefaultStateService.kt | 9 ++- .../roomprofile/alias/RoomAliasViewModel.kt | 62 ++++++++++++------- 3 files changed, 50 insertions(+), 25 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 0b25138f57..1d048f2459 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -40,8 +40,10 @@ interface StateService { /** * Update the canonical alias of the room + * @param alias the canonical alias, or null to reset the canonical alias of this room + * @param altAliases the alternative aliases for this room. It should include the canonical alias if any. */ - fun updateCanonicalAlias(alias: String, callback: MatrixCallback): Cancelable + fun updateCanonicalAlias(alias: String?, altAliases: List, callback: MatrixCallback): Cancelable /** * Update the history readability of the room diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 1f5b174299..8fa0857837 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -24,6 +24,8 @@ import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.util.Cancelable @@ -104,10 +106,13 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private ) } - override fun updateCanonicalAlias(alias: String, callback: MatrixCallback): Cancelable { + override fun updateCanonicalAlias(alias: String?, altAliases: List, callback: MatrixCallback): Cancelable { return sendStateEvent( eventType = EventType.STATE_ROOM_CANONICAL_ALIAS, - body = mapOf("alias" to alias), + body = RoomCanonicalAliasContent( + canonicalAlias = alias, + alternativeAliases = altAliases + ).toContent(), callback = callback, stateKey = null ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 319947c18e..cf05eb134e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -30,6 +30,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType @@ -141,13 +142,13 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { - is RoomAliasAction.AddAlias -> handleAddAlias() - is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) - is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) - RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() + is RoomAliasAction.AddAlias -> handleAddAlias() + is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) - RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() - is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } @@ -160,27 +161,44 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } } - private fun handleAddAlias() { + private fun handleAddAlias() = withState { state -> TODO() } - private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) { + private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) = withState { state -> + updateCanonicalAlias( + state.canonicalAlias, + state.alternativeAliases - action.alias + ) + } + + private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state -> + updateCanonicalAlias( + action.canonicalAlias, + state.alternativeAliases + ) + } + + private fun handleUnsetCanonicalAlias() = withState { state -> + updateCanonicalAlias( + null, + state.alternativeAliases + ) + } + + private fun updateCanonicalAlias(canonicalAlias: String?, alternativeAliases: List) { postLoading(true) - viewModelScope.launch { - runCatching { session.deleteRoomAlias(action.alias) } - .onFailure { _viewEvents.post(RoomAliasViewEvents.Failure(it)) } - postLoading(false) + room.updateCanonicalAlias(canonicalAlias, alternativeAliases, object : MatrixCallback { + override fun onSuccess(data: Unit) { + postLoading(false) + } + + override fun onFailure(failure: Throwable) { + postLoading(false) + _viewEvents.post(RoomAliasViewEvents.Failure(failure)) + } } - } - - private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) { - //room.updateCanonicalAlias() - TODO("Not yet implemented") - } - - private fun handleUnsetCanonicalAlias() { - // room.updateCanonicalAlias() - TODO("Not yet implemented") + ) } private fun handleAddLocalAlias() = withState { state -> From 3c069f8b79fda5a75eef10978d6dae34faebc906 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 17:38:38 +0100 Subject: [PATCH 20/73] Add published aliases --- .../roomprofile/alias/RoomAliasAction.kt | 6 +-- .../roomprofile/alias/RoomAliasController.kt | 52 +++++++++++++++---- .../roomprofile/alias/RoomAliasFragment.kt | 10 ++-- .../roomprofile/alias/RoomAliasViewModel.kt | 23 ++++---- .../roomprofile/alias/RoomAliasViewState.kt | 6 ++- vector/src/main/res/values/strings.xml | 3 +- 6 files changed, 71 insertions(+), 29 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt index a65065f167..ffedaf2539 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -20,10 +20,10 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class RoomAliasAction : VectorViewModelAction { // Canonical - data class AddAlias(val alias: String) : RoomAliasAction() + data class SetNewAlias(val aliasLocalPart: String) : RoomAliasAction() + object AddAlias : RoomAliasAction() data class RemoveAlias(val alias: String) : RoomAliasAction() - data class SetCanonicalAlias(val canonicalAlias: String) : RoomAliasAction() - object UnSetCanonicalAlias : RoomAliasAction() + data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction() // Local data class RemoveLocalAlias(val alias: String) : RoomAliasAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index cf133da7b7..2737c92fed 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.profiles.buildProfileSection import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.settingsInfoItem +import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formSubmitButtonItem import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem @@ -41,9 +42,10 @@ class RoomAliasController @Inject constructor( ) : TypedEpoxyController() { interface Callback { + fun setNewAlias(value: String) + fun addAlias() fun removeAlias(altAlias: String) - fun setCanonicalAlias(alias: String) - fun unsetCanonicalAlias() + fun setCanonicalAlias(alias: String?) fun removeLocalAlias(alias: String) fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() @@ -67,16 +69,44 @@ class RoomAliasController @Inject constructor( } // TODO Canonical - if (data.alternativeAliases.isNotEmpty()) { + settingsInfoItem { + id("otherPublished") + helperTextResId(R.string.room_alias_published_other) + } + if (data.alternativeAliases.isEmpty()) { settingsInfoItem { - id("otherPublished") - helperTextResId(R.string.room_alias_published_other) + id("otherPublishedEmpty") + if (data.actionPermissions.canChangeCanonicalAlias) { + helperTextResId(R.string.room_alias_address_empty_can_add) + } else { + helperTextResId(R.string.room_alias_address_empty) + } } - data.alternativeAliases.forEachIndexed { idx, altAlias -> - // TODO Rename this item to a more generic name - threePidItem { - id("alt_$idx") - title(altAlias) + } + + if (data.actionPermissions.canChangeCanonicalAlias) { + formEditTextItem { + id("addAlias") + value(data.newAlias) + showBottomSeparator(false) + hint(stringProvider.getString(R.string.room_alias_address_hint)) + onTextChange { text -> + callback?.setNewAlias(text) + } + } + formSubmitButtonItem { + id("submit") + buttonTitleId(R.string.action_add) + buttonClickListener { callback?.addAlias() } + } + } + + data.alternativeAliases.forEachIndexed { idx, altAlias -> + // TODO Rename this item to a more generic name + threePidItem { + id("alt_$idx") + title(altAlias) + if (data.actionPermissions.canChangeCanonicalAlias) { deleteClickListener { callback?.removeAlias(altAlias) } } } @@ -132,7 +162,7 @@ class RoomAliasController @Inject constructor( } formSubmitButtonItem { - id("submit") + id("submitLocal") buttonTitleId(R.string.action_add) buttonClickListener { callback?.addLocalAlias() } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 2f7526ebff..7b878b6590 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -112,12 +112,16 @@ class RoomAliasFragment @Inject constructor( .withColoredButton(DialogInterface.BUTTON_POSITIVE) } - override fun setCanonicalAlias(alias: String) { + override fun setCanonicalAlias(alias: String?) { viewModel.handle(RoomAliasAction.SetCanonicalAlias(alias)) } - override fun unsetCanonicalAlias() { - viewModel.handle(RoomAliasAction.UnSetCanonicalAlias) + override fun setNewAlias(value: String) { + viewModel.handle(RoomAliasAction.SetNewAlias(value)) + } + + override fun addAlias() { + viewModel.handle(RoomAliasAction.AddAlias) } override fun setNewLocalAliasLocalPart(value: String) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index cf05eb134e..9d848e98e8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -142,16 +142,25 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { + is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) is RoomAliasAction.AddAlias -> handleAddAlias() is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) - RoomAliasAction.UnSetCanonicalAlias -> handleUnsetCanonicalAlias() is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } + private fun handleSetNewAlias(action: RoomAliasAction.SetNewAlias) { + setState { + copy( + newAlias = action.aliasLocalPart, + asyncNewAliasRequest = Uninitialized + ) + } + } + private fun handleSetNewLocalAliasLocalPart(action: RoomAliasAction.SetNewLocalAliasLocalPart) { setState { copy( @@ -162,7 +171,10 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun handleAddAlias() = withState { state -> - TODO() + updateCanonicalAlias( + state.canonicalAlias, + state.alternativeAliases + state.newAlias + ) } private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) = withState { state -> @@ -179,13 +191,6 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo ) } - private fun handleUnsetCanonicalAlias() = withState { state -> - updateCanonicalAlias( - null, - state.alternativeAliases - ) - } - private fun updateCanonicalAlias(canonicalAlias: String?, alternativeAliases: List) { postLoading(true) room.updateCanonicalAlias(canonicalAlias, alternativeAliases, object : MatrixCallback { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index e2021245d8..3e376b75c9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -26,13 +26,15 @@ data class RoomAliasViewState( val roomId: String, val homeServerName: String = "", val roomSummary: Async = Uninitialized, + val actionPermissions: ActionPermissions = ActionPermissions(), val isLoading: Boolean = false, val canonicalAlias: String? = null, val alternativeAliases: List = emptyList(), + val newAlias: String = "", + val asyncNewAliasRequest: Async = Uninitialized, val localAliases: Async> = Uninitialized, val newLocalAlias: String = "", - val asyncNewLocalAliasRequest: Async = Uninitialized, - val actionPermissions: ActionPermissions = ActionPermissions() + val asyncNewLocalAliasRequest: Async = Uninitialized ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c97785e74c..33b4575435 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1035,7 +1035,8 @@ Delete the address \"%1$s\"? Publish this room to the public in %1$s\'s room directory? - No other published addresses yet, add one below + No other published addresses yet, add one below. + No other published addresses yet. New published address (e.g. #alias:server) Local Addresses From 893ebd969059c5198d4ade3a30d12267d761263e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 17:54:14 +0100 Subject: [PATCH 21/73] Fix UI error --- .../roomprofile/alias/RoomAliasController.kt | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 2737c92fed..f51cd610c2 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -60,6 +60,13 @@ class RoomAliasController @Inject constructor( override fun buildModels(data: RoomAliasViewState?) { data ?: return + // Published + buildPublishInfo(data) + // Local + buildLocalInfo(data) + } + + private fun buildPublishInfo(data: RoomAliasViewState) { buildProfileSection( stringProvider.getString(R.string.room_alias_published_alias_title) ) @@ -68,11 +75,8 @@ class RoomAliasController @Inject constructor( helperTextResId(R.string.room_alias_published_alias_subtitle) } - // TODO Canonical - settingsInfoItem { - id("otherPublished") - helperTextResId(R.string.room_alias_published_other) - } + // TODO Set/Unset Canonical + if (data.alternativeAliases.isEmpty()) { settingsInfoItem { id("otherPublishedEmpty") @@ -82,6 +86,21 @@ class RoomAliasController @Inject constructor( helperTextResId(R.string.room_alias_address_empty) } } + } else { + settingsInfoItem { + id("otherPublished") + helperTextResId(R.string.room_alias_published_other) + } + data.alternativeAliases.forEachIndexed { idx, altAlias -> + // TODO Rename this item to a more generic name + threePidItem { + id("alt_$idx") + title(altAlias) + if (data.actionPermissions.canChangeCanonicalAlias) { + deleteClickListener { callback?.removeAlias(altAlias) } + } + } + } } if (data.actionPermissions.canChangeCanonicalAlias) { @@ -100,20 +119,6 @@ class RoomAliasController @Inject constructor( buttonClickListener { callback?.addAlias() } } } - - data.alternativeAliases.forEachIndexed { idx, altAlias -> - // TODO Rename this item to a more generic name - threePidItem { - id("alt_$idx") - title(altAlias) - if (data.actionPermissions.canChangeCanonicalAlias) { - deleteClickListener { callback?.removeAlias(altAlias) } - } - } - } - - // Local - buildLocalInfo(data) } private fun buildLocalInfo(data: RoomAliasViewState) { From ed4676bb6c859b7382e2268acf25b5cd8e9c62f3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 23 Nov 2020 17:59:25 +0100 Subject: [PATCH 22/73] Cleanup and avoid duplicate --- .../session/room/state/DefaultStateService.kt | 2 +- .../roomprofile/alias/RoomAliasViewModel.kt | 23 +++++++++++-------- .../roomprofile/alias/RoomAliasViewState.kt | 1 - 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 8fa0857837..96fb71503a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -111,7 +111,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private eventType = EventType.STATE_ROOM_CANONICAL_ALIAS, body = RoomCanonicalAliasContent( canonicalAlias = alias, - alternativeAliases = altAliases + alternativeAliases = altAliases.distinct() ).toContent(), callback = callback, stateKey = null diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 9d848e98e8..9c8a7f9df8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -142,21 +142,20 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { - is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) - is RoomAliasAction.AddAlias -> handleAddAlias() - is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) - is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) + is RoomAliasAction.AddAlias -> handleAddAlias() + is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) - RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() - is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } private fun handleSetNewAlias(action: RoomAliasAction.SetNewAlias) { setState { copy( - newAlias = action.aliasLocalPart, - asyncNewAliasRequest = Uninitialized + newAlias = action.aliasLocalPart ) } } @@ -195,7 +194,12 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo postLoading(true) room.updateCanonicalAlias(canonicalAlias, alternativeAliases, object : MatrixCallback { override fun onSuccess(data: Unit) { - postLoading(false) + setState { + copy( + isLoading = false, + newAlias = "" + ) + } } override fun onFailure(failure: Throwable) { @@ -228,6 +232,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo setState { copy( isLoading = false, + newLocalAlias = "", asyncNewLocalAliasRequest = Uninitialized ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index 3e376b75c9..03e3843aab 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -31,7 +31,6 @@ data class RoomAliasViewState( val canonicalAlias: String? = null, val alternativeAliases: List = emptyList(), val newAlias: String = "", - val asyncNewAliasRequest: Async = Uninitialized, val localAliases: Async> = Uninitialized, val newLocalAlias: String = "", val asyncNewLocalAliasRequest: Async = Uninitialized From 3a06ef395904fd9ea9c7d761013a0be0c3e979f1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:04:44 +0100 Subject: [PATCH 23/73] Improve form UX + local echo in case of success --- .../discovery/SettingsContinueCancelItem.kt | 5 + .../roomprofile/alias/RoomAliasAction.kt | 2 + .../roomprofile/alias/RoomAliasController.kt | 92 +++++++++++++------ .../roomprofile/alias/RoomAliasFragment.kt | 8 ++ .../roomprofile/alias/RoomAliasViewModel.kt | 72 ++++++++++++--- .../roomprofile/alias/RoomAliasViewState.kt | 11 ++- vector/src/main/res/values/strings.xml | 3 + 7 files changed, 149 insertions(+), 44 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt b/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt index c9ad23f1a9..b59b24fe55 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/SettingsContinueCancelItem.kt @@ -27,6 +27,9 @@ import im.vector.app.core.epoxy.onClick @EpoxyModelClass(layout = R.layout.item_settings_continue_cancel) abstract class SettingsContinueCancelItem : EpoxyModelWithHolder() { + @EpoxyAttribute + var continueText: String? = null + @EpoxyAttribute var continueOnClick: ClickListener? = null @@ -37,6 +40,8 @@ abstract class SettingsContinueCancelItem : EpoxyModelWithHolder() { interface Callback { + fun toggleManualPublishForm() fun setNewAlias(value: String) fun addAlias() fun removeAlias(altAlias: String) fun setCanonicalAlias(alias: String?) fun removeLocalAlias(alias: String) + fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() } @@ -104,19 +109,37 @@ class RoomAliasController @Inject constructor( } if (data.actionPermissions.canChangeCanonicalAlias) { - formEditTextItem { - id("addAlias") - value(data.newAlias) - showBottomSeparator(false) - hint(stringProvider.getString(R.string.room_alias_address_hint)) - onTextChange { text -> - callback?.setNewAlias(text) + buildPublishManuallyForm(data) + } + } + + private fun buildPublishManuallyForm(data: RoomAliasViewState) { + when (data.publishManuallyState) { + RoomAliasViewState.AddAliasState.Hidden -> Unit + RoomAliasViewState.AddAliasState.Closed -> { + settingsButtonItem { + id("publishManually") + colorProvider(colorProvider) + buttonTitleId(R.string.room_alias_published_alias_add_manually) + buttonClickListener { callback?.toggleManualPublishForm() } } } - formSubmitButtonItem { - id("submit") - buttonTitleId(R.string.action_add) - buttonClickListener { callback?.addAlias() } + is RoomAliasViewState.AddAliasState.Editing -> { + formEditTextItem { + id("publishManuallyEdit") + value(data.publishManuallyState.value) + showBottomSeparator(false) + hint(stringProvider.getString(R.string.room_alias_address_hint)) + onTextChange { text -> + callback?.setNewAlias(text) + } + } + settingsContinueCancelItem { + id("publishManuallySubmit") + continueText(stringProvider.getString(R.string.room_alias_published_alias_add_manually_submit)) + continueOnClick { callback?.addAlias() } + cancelOnClick { callback?.toggleManualPublishForm() } + } } } } @@ -155,21 +178,38 @@ class RoomAliasController @Inject constructor( } // Add local - roomAliasEditItem { - id("newLocalAlias") - value(data.newLocalAlias) - homeServer(":" + data.homeServerName) - showBottomSeparator(false) - errorMessage(roomAliasErrorFormatter.format((data.asyncNewLocalAliasRequest as? Fail)?.error as? RoomAliasError)) - onTextChange { value -> - callback?.setNewLocalAliasLocalPart(value) - } - } + buildAddLocalAlias(data) + } - formSubmitButtonItem { - id("submitLocal") - buttonTitleId(R.string.action_add) - buttonClickListener { callback?.addLocalAlias() } + private fun buildAddLocalAlias(data: RoomAliasViewState) { + when (data.newLocalAliasState) { + RoomAliasViewState.AddAliasState.Hidden -> Unit + RoomAliasViewState.AddAliasState.Closed -> { + settingsButtonItem { + id("newLocalAliasButton") + colorProvider(colorProvider) + buttonTitleId(R.string.room_alias_local_address_add) + buttonClickListener { callback?.toggleLocalAliasForm() } + } + } + is RoomAliasViewState.AddAliasState.Editing -> { + roomAliasEditItem { + id("newLocalAlias") + value(data.newLocalAliasState.value) + homeServer(":" + data.homeServerName) + showBottomSeparator(false) + errorMessage(roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError)) + onTextChange { value -> + callback?.setNewLocalAliasLocalPart(value) + } + } + settingsContinueCancelItem { + id("newLocalAliasSubmit") + continueText(stringProvider.getString(R.string.action_add)) + continueOnClick { callback?.addLocalAlias() } + cancelOnClick { callback?.toggleLocalAliasForm() } + } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 7b878b6590..20ea7a8901 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -116,6 +116,10 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.SetCanonicalAlias(alias)) } + override fun toggleManualPublishForm() { + viewModel.handle(RoomAliasAction.ToggleManualPublishForm) + } + override fun setNewAlias(value: String) { viewModel.handle(RoomAliasAction.SetNewAlias(value)) } @@ -124,6 +128,10 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.AddAlias) } + override fun toggleLocalAliasForm() { + viewModel.handle(RoomAliasAction.ToggleAddLocalAliasForm) + } + override fun setNewLocalAliasLocalPart(value: String) { viewModel.handle(RoomAliasAction.SetNewLocalAliasLocalPart(value)) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 9c8a7f9df8..a4f0d97818 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -142,20 +142,46 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { + RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm() is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) is RoomAliasAction.AddAlias -> handleAddAlias() is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm() is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) }.exhaustive } + private fun handleToggleAddLocalAliasForm() { + setState { + copy( + newLocalAliasState = when (newLocalAliasState) { + RoomAliasViewState.AddAliasState.Hidden -> RoomAliasViewState.AddAliasState.Hidden + RoomAliasViewState.AddAliasState.Closed -> RoomAliasViewState.AddAliasState.Editing("", Uninitialized) + is RoomAliasViewState.AddAliasState.Editing -> RoomAliasViewState.AddAliasState.Closed + } + ) + } + } + + private fun handleToggleManualPublishForm() { + setState { + copy( + publishManuallyState = when (publishManuallyState) { + RoomAliasViewState.AddAliasState.Hidden -> RoomAliasViewState.AddAliasState.Hidden + RoomAliasViewState.AddAliasState.Closed -> RoomAliasViewState.AddAliasState.Editing("", Uninitialized) + is RoomAliasViewState.AddAliasState.Editing -> RoomAliasViewState.AddAliasState.Closed + } + ) + } + } + private fun handleSetNewAlias(action: RoomAliasAction.SetNewAlias) { setState { copy( - newAlias = action.aliasLocalPart + publishManuallyState = RoomAliasViewState.AddAliasState.Editing(action.aliasLocalPart, Uninitialized) ) } } @@ -163,16 +189,16 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo private fun handleSetNewLocalAliasLocalPart(action: RoomAliasAction.SetNewLocalAliasLocalPart) { setState { copy( - newLocalAlias = action.aliasLocalPart, - asyncNewLocalAliasRequest = Uninitialized + newLocalAliasState = RoomAliasViewState.AddAliasState.Editing(action.aliasLocalPart, Uninitialized) ) } } private fun handleAddAlias() = withState { state -> + val newAlias = (state.newLocalAliasState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState updateCanonicalAlias( state.canonicalAlias, - state.alternativeAliases + state.newAlias + state.alternativeAliases + newAlias ) } @@ -197,7 +223,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo setState { copy( isLoading = false, - newAlias = "" + publishManuallyState = RoomAliasViewState.AddAliasState.Closed ) } } @@ -206,24 +232,25 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo postLoading(false) _viewEvents.post(RoomAliasViewEvents.Failure(failure)) } - } - ) + }) } private fun handleAddLocalAlias() = withState { state -> + val previousState = (state.newLocalAliasState as? RoomAliasViewState.AddAliasState.Editing) ?: return@withState + setState { copy( isLoading = true, - asyncNewLocalAliasRequest = Loading() + newLocalAliasState = previousState.copy(asyncRequest = Loading()) ) } viewModelScope.launch { - runCatching { room.addAlias(state.newLocalAlias) } + runCatching { room.addAlias(previousState.value) } .onFailure { setState { copy( isLoading = false, - asyncNewLocalAliasRequest = Fail(it) + newLocalAliasState = previousState.copy(asyncRequest = Fail(it)) ) } _viewEvents.post(RoomAliasViewEvents.Failure(it)) @@ -232,8 +259,9 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo setState { copy( isLoading = false, - newLocalAlias = "", - asyncNewLocalAliasRequest = Uninitialized + newLocalAliasState = RoomAliasViewState.AddAliasState.Closed, + // Local echo + localAliases = Success(localAliases().orEmpty() + previousState.value) ) } fetchRoomAlias() @@ -245,9 +273,23 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo postLoading(true) viewModelScope.launch { runCatching { session.deleteRoomAlias(action.alias) } - .onFailure { _viewEvents.post(RoomAliasViewEvents.Failure(it)) } - .onSuccess { fetchRoomAlias() } - postLoading(false) + .onFailure { + setState { + copy(isLoading = false) + } + _viewEvents.post(RoomAliasViewEvents.Failure(it)) + } + .onSuccess { + // Local echo + setState { + copy( + isLoading = false, + // Local echo + localAliases = Success(localAliases().orEmpty() - action.alias) + ) + } + fetchRoomAlias() + } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index 03e3843aab..114b69c1ad 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -30,10 +30,9 @@ data class RoomAliasViewState( val isLoading: Boolean = false, val canonicalAlias: String? = null, val alternativeAliases: List = emptyList(), - val newAlias: String = "", + val publishManuallyState: AddAliasState = AddAliasState.Hidden, val localAliases: Async> = Uninitialized, - val newLocalAlias: String = "", - val asyncNewLocalAliasRequest: Async = Uninitialized + val newLocalAliasState: AddAliasState = AddAliasState.Hidden ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) @@ -41,4 +40,10 @@ data class RoomAliasViewState( data class ActionPermissions( val canChangeCanonicalAlias: Boolean = false ) + + sealed class AddAliasState { + object Hidden : AddAliasState() + object Closed : AddAliasState() + data class Editing(val value: String, val asyncRequest: Async = Uninitialized) : AddAliasState() + } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 33b4575435..36f2b38dab 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1032,6 +1032,8 @@ Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first. Main address Other published addresses: + Published a new address manually + Publish Delete the address \"%1$s\"? Publish this room to the public in %1$s\'s room directory? @@ -1043,6 +1045,7 @@ Set addresses for this room so users can find this room through your homeserver (%1$s) This room has no local addresses + Add a local address Anyone From 0a9b23427274b133df01db96a21f8d9262de407d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:05:25 +0100 Subject: [PATCH 24/73] Renaming --- .../im/vector/app/features/roomprofile/alias/RoomAliasAction.kt | 2 +- .../vector/app/features/roomprofile/alias/RoomAliasViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt index ffea09f5fd..b695ab0a1e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class RoomAliasAction : VectorViewModelAction { // Canonical object ToggleManualPublishForm : RoomAliasAction() - data class SetNewAlias(val aliasLocalPart: String) : RoomAliasAction() + data class SetNewAlias(val alias: String) : RoomAliasAction() object AddAlias : RoomAliasAction() data class RemoveAlias(val alias: String) : RoomAliasAction() data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index a4f0d97818..89ede6e825 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -181,7 +181,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo private fun handleSetNewAlias(action: RoomAliasAction.SetNewAlias) { setState { copy( - publishManuallyState = RoomAliasViewState.AddAliasState.Editing(action.aliasLocalPart, Uninitialized) + publishManuallyState = RoomAliasViewState.AddAliasState.Editing(action.alias, Uninitialized) ) } } From 82b23d9a1390f7e9553df4e8a001a48f37072920 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:08:39 +0100 Subject: [PATCH 25/73] Ensure we push only clean m.room.canonical_alias event --- .../internal/session/room/state/DefaultStateService.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 96fb71503a..3d6e869607 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -111,7 +111,13 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private eventType = EventType.STATE_ROOM_CANONICAL_ALIAS, body = RoomCanonicalAliasContent( canonicalAlias = alias, - alternativeAliases = altAliases.distinct() + alternativeAliases = altAliases + // Ensure there is no duplicate + .distinct() + // Ensure the canonical alias is not also included in the alt alias + .minus(listOfNotNull(alias)) + // Sort for the cleanup + .sorted() ).toContent(), callback = callback, stateKey = null From 74ffbd46793ee66c8ec1c37221cd23460f1ff88e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:14:58 +0100 Subject: [PATCH 26/73] Ensure the forms are displayable --- .../roomprofile/alias/RoomAliasViewModel.kt | 15 ++++++++++++++- .../roomprofile/alias/RoomAliasViewState.kt | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 89ede6e825..e4d0c0c069 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -116,7 +116,20 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_CANONICAL_ALIAS), ) - setState { copy(actionPermissions = permissions) } + setState { + val newPublishManuallyState = if (permissions.canChangeCanonicalAlias) { + when (publishManuallyState) { + RoomAliasViewState.AddAliasState.Hidden -> RoomAliasViewState.AddAliasState.Closed + else -> publishManuallyState + } + } else { + RoomAliasViewState.AddAliasState.Hidden + } + copy( + actionPermissions = permissions, + publishManuallyState = newPublishManuallyState + ) + } } .disposeOnClear() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index 114b69c1ad..e1736296c6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -32,7 +32,7 @@ data class RoomAliasViewState( val alternativeAliases: List = emptyList(), val publishManuallyState: AddAliasState = AddAliasState.Hidden, val localAliases: Async> = Uninitialized, - val newLocalAliasState: AddAliasState = AddAliasState.Hidden + val newLocalAliasState: AddAliasState = AddAliasState.Closed ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) From 8dbb984ead061f12f2cf7911cf73c960d7701d5b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:18:17 +0100 Subject: [PATCH 27/73] Sort the aliases --- .../app/features/roomprofile/alias/RoomAliasViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index e4d0c0c069..92f635482f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -88,7 +88,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo runCatching { room.getRoomAliases() } .fold( { - setState { copy(localAliases = Success(it)) } + setState { copy(localAliases = Success(it.sorted())) } }, { setState { copy(localAliases = Fail(it)) } @@ -146,7 +146,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo setState { copy( canonicalAlias = it.canonicalAlias, - alternativeAliases = it.alternativeAliases.orEmpty() + alternativeAliases = it.alternativeAliases.orEmpty().sorted() ) } } @@ -274,7 +274,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo isLoading = false, newLocalAliasState = RoomAliasViewState.AddAliasState.Closed, // Local echo - localAliases = Success(localAliases().orEmpty() + previousState.value) + localAliases = Success((localAliases().orEmpty() + previousState.value).sorted()) ) } fetchRoomAlias() From 36564f0c7520d64e769f85bd827ede9abbdb1d9e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:20:12 +0100 Subject: [PATCH 28/73] copy/paste error --- .../vector/app/features/roomprofile/alias/RoomAliasViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 92f635482f..fac8d52159 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -208,7 +208,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } private fun handleAddAlias() = withState { state -> - val newAlias = (state.newLocalAliasState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState + val newAlias = (state.publishManuallyState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState updateCanonicalAlias( state.canonicalAlias, state.alternativeAliases + newAlias From 2cf2233643e455c7c2552be17dc30f30b9a43a83 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:23:02 +0100 Subject: [PATCH 29/73] Naming convention --- ...ction.kt => RoomBannedMemberListAction.kt} | 8 +++---- .../banned/RoomBannedMemberListFragment.kt | 14 +++++------ ...s.kt => RoomBannedMemberListViewEvents.kt} | 6 ++--- ...el.kt => RoomBannedMemberListViewModel.kt} | 24 +++++++++---------- 4 files changed, 26 insertions(+), 26 deletions(-) rename vector/src/main/java/im/vector/app/features/roomprofile/banned/{RoomBannedListMemberAction.kt => RoomBannedMemberListAction.kt} (82%) rename vector/src/main/java/im/vector/app/features/roomprofile/banned/{RoomBannedViewEvents.kt => RoomBannedMemberListViewEvents.kt} (83%) rename vector/src/main/java/im/vector/app/features/roomprofile/banned/{RoomBannedListMemberViewModel.kt => RoomBannedMemberListViewModel.kt} (84%) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListAction.kt similarity index 82% rename from vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt rename to vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListAction.kt index ca7d567d90..8f6f5afba1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListAction.kt @@ -19,8 +19,8 @@ package im.vector.app.features.roomprofile.banned import im.vector.app.core.platform.VectorViewModelAction import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -sealed class RoomBannedListMemberAction : VectorViewModelAction { - data class QueryInfo(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() - data class UnBanUser(val roomMemberSummary: RoomMemberSummary) : RoomBannedListMemberAction() - data class Filter(val filter: String) : RoomBannedListMemberAction() +sealed class RoomBannedMemberListAction : VectorViewModelAction { + data class QueryInfo(val roomMemberSummary: RoomMemberSummary) : RoomBannedMemberListAction() + data class UnBanUser(val roomMemberSummary: RoomMemberSummary) : RoomBannedMemberListAction() + data class Filter(val filter: String) : RoomBannedMemberListAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt index 797e6c8aa3..349321c87a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt @@ -37,18 +37,18 @@ import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject class RoomBannedMemberListFragment @Inject constructor( - val viewModelFactory: RoomBannedListMemberViewModel.Factory, + val viewModelFactory: RoomBannedMemberListViewModel.Factory, private val roomMemberListController: RoomBannedMemberListController, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), RoomBannedMemberListController.Callback { - private val viewModel: RoomBannedListMemberViewModel by fragmentViewModel() + private val viewModel: RoomBannedMemberListViewModel 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)) + viewModel.handle(RoomBannedMemberListAction.QueryInfo(roomMember)) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -60,7 +60,7 @@ class RoomBannedMemberListFragment @Inject constructor( viewModel.observeViewEvents { when (it) { - is RoomBannedViewEvents.ShowBannedInfo -> { + is RoomBannedMemberListViewEvents.ShowBannedInfo -> { val canBan = withState(viewModel) { state -> state.canUserBan } AlertDialog.Builder(requireActivity()) .setTitle(getString(R.string.member_banned_by, it.bannedByUserId)) @@ -69,13 +69,13 @@ class RoomBannedMemberListFragment @Inject constructor( .apply { if (canBan) { setNegativeButton(R.string.room_participants_action_unban) { _, _ -> - viewModel.handle(RoomBannedListMemberAction.UnBanUser(it.roomMemberSummary)) + viewModel.handle(RoomBannedMemberListAction.UnBanUser(it.roomMemberSummary)) } } } .show() } - is RoomBannedViewEvents.ToastError -> { + is RoomBannedMemberListViewEvents.ToastError -> { requireActivity().toast(it.info) } } @@ -96,7 +96,7 @@ class RoomBannedMemberListFragment @Inject constructor( } override fun onQueryTextChange(newText: String): Boolean { - viewModel.handle(RoomBannedListMemberAction.Filter(newText)) + viewModel.handle(RoomBannedMemberListAction.Filter(newText)) return true } }) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewEvents.kt similarity index 83% rename from vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt rename to vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewEvents.kt index 6b59debe96..4b1dc018ee 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewEvents.kt @@ -19,7 +19,7 @@ package im.vector.app.features.roomprofile.banned import im.vector.app.core.platform.VectorViewEvents import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary -sealed class RoomBannedViewEvents : VectorViewEvents { - data class ShowBannedInfo(val bannedByUserId: String, val banReason: String, val roomMemberSummary: RoomMemberSummary) : RoomBannedViewEvents() - data class ToastError(val info: String) : RoomBannedViewEvents() +sealed class RoomBannedMemberListViewEvents : VectorViewEvents { + data class ShowBannedInfo(val bannedByUserId: String, val banReason: String, val roomMemberSummary: RoomMemberSummary) : RoomBannedMemberListViewEvents() + data class ToastError(val info: String) : RoomBannedMemberListViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt similarity index 84% rename from vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt rename to vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 1cce2f96cb..0cecd22fa0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedListMemberViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -42,14 +42,14 @@ import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap -class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, +class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, private val session: Session) - : VectorViewModel(initialState) { + : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { - fun create(initialState: RoomBannedMemberListViewState): RoomBannedListMemberViewModel + fun create(initialState: RoomBannedMemberListViewState): RoomBannedMemberListViewModel } private val room = session.getRoom(initialState.roomId)!! @@ -78,24 +78,24 @@ class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initia }.disposeOnClear() } - companion object : MvRxViewModelFactory { + companion object : MvRxViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedListMemberViewModel? { + override fun create(viewModelContext: ViewModelContext, state: RoomBannedMemberListViewState): RoomBannedMemberListViewModel? { val fragment: RoomBannedMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.viewModelFactory.create(state) } } - override fun handle(action: RoomBannedListMemberAction) { + override fun handle(action: RoomBannedMemberListAction) { when (action) { - is RoomBannedListMemberAction.QueryInfo -> onQueryBanInfo(action.roomMemberSummary) - is RoomBannedListMemberAction.UnBanUser -> unBanUser(action.roomMemberSummary) - is RoomBannedListMemberAction.Filter -> handleFilter(action) + is RoomBannedMemberListAction.QueryInfo -> onQueryBanInfo(action.roomMemberSummary) + is RoomBannedMemberListAction.UnBanUser -> unBanUser(action.roomMemberSummary) + is RoomBannedMemberListAction.Filter -> handleFilter(action) }.exhaustive } - private fun handleFilter(action: RoomBannedListMemberAction.Filter) { + private fun handleFilter(action: RoomBannedMemberListAction.Filter) { setState { copy( filter = action.filter @@ -114,7 +114,7 @@ class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initia val reason = content.reason val bannedBy = bannedEvent?.senderId ?: return - _viewEvents.post(RoomBannedViewEvents.ShowBannedInfo(bannedBy, reason ?: "", roomMemberSummary)) + _viewEvents.post(RoomBannedMemberListViewEvents.ShowBannedInfo(bannedBy, reason ?: "", roomMemberSummary)) } private fun unBanUser(roomMemberSummary: RoomMemberSummary) { @@ -127,7 +127,7 @@ class RoomBannedListMemberViewModel @AssistedInject constructor(@Assisted initia room.unban(roomMemberSummary.userId, null, it) } } catch (failure: Throwable) { - _viewEvents.post(RoomBannedViewEvents.ToastError(stringProvider.getString(R.string.failed_to_unban))) + _viewEvents.post(RoomBannedMemberListViewEvents.ToastError(stringProvider.getString(R.string.failed_to_unban))) } finally { setState { copy( From 8f80f375f01afc4bb912af18e5ecee12c3d33616 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:37:55 +0100 Subject: [PATCH 30/73] Prepare alias bottom sheet --- .../app/core/epoxy/profiles/ProfileActionItem.kt | 6 ++++-- .../core/epoxy/profiles/ProfileItemExtensions.kt | 4 +--- .../roomprofile/alias/RoomAliasController.kt | 16 +++++++--------- .../roomprofile/alias/RoomAliasFragment.kt | 4 ++++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt index 3bef38d4cb..f52051f989 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt @@ -26,8 +26,10 @@ import androidx.core.widget.ImageViewCompat import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.themes.ThemeUtils @@ -67,11 +69,11 @@ abstract class ProfileActionItem : VectorEpoxyModel() var destructive: Boolean = false @EpoxyAttribute - var listener: View.OnClickListener? = null + var listener: ClickListener? = null override fun bind(holder: Holder) { super.bind(holder) - holder.view.setOnClickListener(listener) + holder.view.onClick(listener) if (listener == null) { holder.view.isClickable = false } diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileItemExtensions.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileItemExtensions.kt index fdbe9f7f94..99acd6cb36 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileItemExtensions.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileItemExtensions.kt @@ -59,9 +59,7 @@ fun EpoxyController.buildProfileAction( accessoryRes(accessory) accessoryMatrixItem(accessoryMatrixItem) avatarRenderer(avatarRenderer) - listener { _ -> - action?.invoke() - } + listener(action) } if (divider) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index fe19f65629..a8c1222a5f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -24,6 +24,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.profiles.buildProfileSection +import im.vector.app.core.epoxy.profiles.profileActionItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider @@ -33,7 +34,6 @@ import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.form.formEditTextItem import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem -import im.vector.app.features.settings.threepids.threePidItem import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import javax.inject.Inject @@ -48,12 +48,14 @@ class RoomAliasController @Inject constructor( fun toggleManualPublishForm() fun setNewAlias(value: String) fun addAlias() + // TODO Delete some methods below fun removeAlias(altAlias: String) fun setCanonicalAlias(alias: String?) fun removeLocalAlias(alias: String) fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() + fun openAlias(alias: String, isPublished: Boolean) } var callback: Callback? = null @@ -97,13 +99,10 @@ class RoomAliasController @Inject constructor( helperTextResId(R.string.room_alias_published_other) } data.alternativeAliases.forEachIndexed { idx, altAlias -> - // TODO Rename this item to a more generic name - threePidItem { + profileActionItem { id("alt_$idx") title(altAlias) - if (data.actionPermissions.canChangeCanonicalAlias) { - deleteClickListener { callback?.removeAlias(altAlias) } - } + listener { callback?.openAlias(altAlias, true) } } } } @@ -161,11 +160,10 @@ class RoomAliasController @Inject constructor( } is Success -> { localAliases().forEachIndexed { idx, localAlias -> - // TODO Rename this item to a more generic name - threePidItem { + profileActionItem { id("loc_$idx") title(localAlias) - deleteClickListener { callback?.removeLocalAlias(localAlias) } + listener { callback?.openAlias(localAlias, false) } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 20ea7a8901..b14783d9cd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -140,6 +140,10 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.AddLocalAlias) } + override fun openAlias(alias: String, isPublished: Boolean) { + TODO() + } + override fun removeLocalAlias(alias: String) { AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_title_confirmation) From d9c209aa87a2c7ab377adf9b61ccc28413095ad8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 06:49:42 +0100 Subject: [PATCH 31/73] Cleanup --- .../home/room/list/actions/RoomListQuickActionsBottomSheet.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt index e3a5db4b97..f41104cae1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsBottomSheet.kt @@ -77,6 +77,7 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R override fun onDestroyView() { recyclerView.cleanup() + roomListActionsEpoxyController.listener = null super.onDestroyView() } From c5e6e004dd416bd96a5806909e18dc89ba4ab546 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 07:59:57 +0100 Subject: [PATCH 32/73] Room alias detail --- .idea/dictionaries/bmarty.xml | 1 + .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../im/vector/app/core/di/ViewModelModule.kt | 6 + .../roomprofile/alias/RoomAliasAction.kt | 5 +- .../roomprofile/alias/RoomAliasController.kt | 4 - .../roomprofile/alias/RoomAliasFragment.kt | 54 +++++++-- .../roomprofile/alias/RoomAliasViewModel.kt | 26 +++-- .../alias/detail/RoomAliasBottomSheet.kt | 104 ++++++++++++++++++ .../detail/RoomAliasBottomSheetController.kt | 85 ++++++++++++++ .../RoomAliasBottomSheetSharedAction.kt | 56 ++++++++++ ...omAliasBottomSheetSharedActionViewModel.kt | 25 +++++ .../alias/detail/RoomAliasBottomSheetState.kt | 35 ++++++ .../detail/RoomAliasBottomSheetViewModel.kt | 58 ++++++++++ vector/src/main/res/drawable/ic_trash_24.xml | 76 +++++++------ vector/src/main/res/values/strings.xml | 3 + 15 files changed, 479 insertions(+), 61 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml index d13e40248f..5ad39614b7 100644 --- a/.idea/dictionaries/bmarty.xml +++ b/.idea/dictionaries/bmarty.xml @@ -31,6 +31,7 @@ ssss sygnal threepid + unpublish unwedging diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index 818a32fca3..2518e32ce5 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -67,6 +67,7 @@ import im.vector.app.features.roomdirectory.createroom.CreateRoomActivity import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roomprofile.RoomProfileActivity +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet import im.vector.app.features.share.IncomingShareActivity @@ -153,6 +154,7 @@ interface ScreenComponent { fun inject(bottomSheet: ViewEditHistoryBottomSheet) fun inject(bottomSheet: DisplayReadReceiptsBottomSheet) fun inject(bottomSheet: RoomListQuickActionsBottomSheet) + fun inject(bottomSheet: RoomAliasBottomSheet) fun inject(bottomSheet: VerificationBottomSheet) fun inject(bottomSheet: DeviceVerificationInfoBottomSheet) fun inject(bottomSheet: DeviceListBottomSheet) diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt index 7ae8bc9c2e..3399f98d43 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt @@ -35,6 +35,7 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA import im.vector.app.features.reactions.EmojiChooserViewModel import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel @Module @@ -105,6 +106,11 @@ interface ViewModelModule { @ViewModelKey(RoomListQuickActionsSharedActionViewModel::class) fun bindRoomListQuickActionsSharedActionViewModel(viewModel: RoomListQuickActionsSharedActionViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(RoomAliasBottomSheetSharedActionViewModel::class) + fun bindRoomAliasBottomSheetSharedActionViewModel(viewModel: RoomAliasBottomSheetSharedActionViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RoomDirectorySharedActionViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt index b695ab0a1e..4054d6f63a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -22,8 +22,9 @@ sealed class RoomAliasAction : VectorViewModelAction { // Canonical object ToggleManualPublishForm : RoomAliasAction() data class SetNewAlias(val alias: String) : RoomAliasAction() - object AddAlias : RoomAliasAction() - data class RemoveAlias(val alias: String) : RoomAliasAction() + object ManualPublishAlias : RoomAliasAction() + data class PublishAlias(val alias: String) : RoomAliasAction() + data class UnpublishAlias(val alias: String) : RoomAliasAction() data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction() // Local diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index a8c1222a5f..6790bee2a8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -48,10 +48,6 @@ class RoomAliasController @Inject constructor( fun toggleManualPublishForm() fun setNewAlias(value: String) fun addAlias() - // TODO Delete some methods below - fun removeAlias(altAlias: String) - fun setCanonicalAlias(alias: String?) - fun removeLocalAlias(alias: String) fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index b14783d9cd..f54cff5c6c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -30,9 +30,13 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.shareText import im.vector.app.core.utils.toast import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedAction +import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.matrix.android.sdk.api.session.room.alias.RoomAliasError @@ -48,12 +52,16 @@ class RoomAliasFragment @Inject constructor( RoomAliasController.Callback { private val viewModel: RoomAliasViewModel by fragmentViewModel() + private lateinit var sharedActionViewModel: RoomAliasBottomSheetSharedActionViewModel + private val roomProfileArgs: RoomProfileArgs by args() override fun getLayoutResId() = R.layout.fragment_room_setting_generic override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + sharedActionViewModel = activityViewModelProvider.get(RoomAliasBottomSheetSharedActionViewModel::class.java) + controller.callback = this setupToolbar(roomSettingsToolbar) roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) @@ -63,9 +71,30 @@ class RoomAliasFragment @Inject constructor( viewModel.observeViewEvents { when (it) { is RoomAliasViewEvents.Failure -> showFailure(it.throwable) - RoomAliasViewEvents.Success -> showSuccess() + RoomAliasViewEvents.Success -> showSuccess() }.exhaustive } + + sharedActionViewModel + .observe() + .subscribe { handleAliasAction(it) } + .disposeOnDestroyView() + } + + private fun handleAliasAction(action: RoomAliasBottomSheetSharedAction?) { + when (action) { + is RoomAliasBottomSheetSharedAction.ShareAlias -> shareAlias(action.matrixTo) + is RoomAliasBottomSheetSharedAction.PublishAlias -> viewModel.handle(RoomAliasAction.PublishAlias(action.alias)) + is RoomAliasBottomSheetSharedAction.UnPublishAlias -> unpublishAlias(action.alias) + is RoomAliasBottomSheetSharedAction.DeleteAlias -> removeLocalAlias(action.alias) + is RoomAliasBottomSheetSharedAction.SetMainAlias -> viewModel.handle(RoomAliasAction.SetCanonicalAlias(action.alias)) + RoomAliasBottomSheetSharedAction.UnsetMainAlias -> viewModel.handle(RoomAliasAction.SetCanonicalAlias(canonicalAlias = null)) + null -> Unit + } + } + + private fun shareAlias(matrixTo: String) { + shareText(requireContext(), matrixTo) } override fun showFailure(throwable: Throwable) { @@ -100,22 +129,18 @@ class RoomAliasFragment @Inject constructor( invalidateOptionsMenu() } - override fun removeAlias(altAlias: String) { + private fun unpublishAlias(altAlias: String) { AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_title_confirmation) .setMessage(getString(R.string.room_alias_delete_confirmation, altAlias)) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.delete) { _, _ -> - viewModel.handle(RoomAliasAction.RemoveAlias(altAlias)) + viewModel.handle(RoomAliasAction.UnpublishAlias(altAlias)) } .show() .withColoredButton(DialogInterface.BUTTON_POSITIVE) } - override fun setCanonicalAlias(alias: String?) { - viewModel.handle(RoomAliasAction.SetCanonicalAlias(alias)) - } - override fun toggleManualPublishForm() { viewModel.handle(RoomAliasAction.ToggleManualPublishForm) } @@ -125,7 +150,7 @@ class RoomAliasFragment @Inject constructor( } override fun addAlias() { - viewModel.handle(RoomAliasAction.AddAlias) + viewModel.handle(RoomAliasAction.ManualPublishAlias) } override fun toggleLocalAliasForm() { @@ -140,11 +165,18 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.AddLocalAlias) } - override fun openAlias(alias: String, isPublished: Boolean) { - TODO() + override fun openAlias(alias: String, isPublished: Boolean) = withState(viewModel) { state -> + RoomAliasBottomSheet + .newInstance( + alias = alias, + isPublished = isPublished, + isMainAlias = alias == state.canonicalAlias, + canEditCanonicalAlias = state.actionPermissions.canChangeCanonicalAlias + ) + .show(childFragmentManager, "ROOM_ALIAS_ACTIONS") } - override fun removeLocalAlias(alias: String) { + private fun removeLocalAlias(alias: String) { AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_title_confirmation) .setMessage(getString(R.string.room_alias_delete_confirmation, alias)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index fac8d52159..ff9c023a21 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -157,13 +157,14 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo when (action) { RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm() is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) - is RoomAliasAction.AddAlias -> handleAddAlias() - is RoomAliasAction.RemoveAlias -> handleRemoveAlias(action) + is RoomAliasAction.ManualPublishAlias -> handleAddAlias() + is RoomAliasAction.UnpublishAlias -> handleRemoveAlias(action) is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm() is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + is RoomAliasAction.PublishAlias -> handleAddAliasManually(action) }.exhaustive } @@ -210,22 +211,29 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo private fun handleAddAlias() = withState { state -> val newAlias = (state.publishManuallyState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState updateCanonicalAlias( - state.canonicalAlias, - state.alternativeAliases + newAlias + canonicalAlias = state.canonicalAlias, + alternativeAliases = state.alternativeAliases + newAlias ) } - private fun handleRemoveAlias(action: RoomAliasAction.RemoveAlias) = withState { state -> + private fun handleAddAliasManually(action: RoomAliasAction.PublishAlias) = withState { state -> updateCanonicalAlias( - state.canonicalAlias, - state.alternativeAliases - action.alias + canonicalAlias = state.canonicalAlias, + alternativeAliases = state.alternativeAliases + action.alias + ) + } + + private fun handleRemoveAlias(action: RoomAliasAction.UnpublishAlias) = withState { state -> + updateCanonicalAlias( + canonicalAlias = state.canonicalAlias, + alternativeAliases = state.alternativeAliases - action.alias ) } private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state -> updateCanonicalAlias( - action.canonicalAlias, - state.alternativeAliases + canonicalAlias = action.canonicalAlias, + alternativeAliases = state.alternativeAliases ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt new file mode 100644 index 0000000000..f9968d86da --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt @@ -0,0 +1,104 @@ +/* + * 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.app.features.roomprofile.alias.detail + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import kotlinx.android.parcel.Parcelize +import javax.inject.Inject + +@Parcelize +data class RoomAliasBottomSheetArgs( + val alias: String, + val isPublished: Boolean, + val isMainAlias: Boolean, + val canEditCanonicalAlias: Boolean +) : Parcelable + +/** + * Bottom sheet fragment that shows room alias information with list of contextual actions + */ +class RoomAliasBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomAliasBottomSheetController.Listener { + + private lateinit var sharedActionViewModel: RoomAliasBottomSheetSharedActionViewModel + @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool + @Inject lateinit var roomAliasBottomSheetViewModelFactory: RoomAliasBottomSheetViewModel.Factory + @Inject lateinit var controller: RoomAliasBottomSheetController + + private val viewModel: RoomAliasBottomSheetViewModel by fragmentViewModel(RoomAliasBottomSheetViewModel::class) + + @BindView(R.id.bottomSheetRecyclerView) + lateinit var recyclerView: RecyclerView + + override val showExpanded = true + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override fun getLayoutResId() = R.layout.bottom_sheet_generic_list + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sharedActionViewModel = activityViewModelProvider.get(RoomAliasBottomSheetSharedActionViewModel::class.java) + recyclerView.configureWith(controller, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) + controller.listener = this + } + + override fun onDestroyView() { + recyclerView.cleanup() + controller.listener = null + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { + controller.setData(it) + super.invalidate() + } + + override fun didSelectMenuAction(quickAction: RoomAliasBottomSheetSharedAction) { + sharedActionViewModel.post(quickAction) + + dismiss() + } + + companion object { + fun newInstance(alias: String, + isPublished: Boolean, + isMainAlias: Boolean, + canEditCanonicalAlias: Boolean): RoomAliasBottomSheet { + return RoomAliasBottomSheet().apply { + setArguments(RoomAliasBottomSheetArgs( + alias = alias, + isPublished = isPublished, + isMainAlias = isMainAlias, + canEditCanonicalAlias = canEditCanonicalAlias + )) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt new file mode 100644 index 0000000000..2f4cb357b4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt @@ -0,0 +1,85 @@ +/* + * 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.app.features.roomprofile.alias.detail + +import android.view.View +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.core.epoxy.bottomsheet.bottomSheetActionItem +import im.vector.app.core.epoxy.dividerItem +import im.vector.app.core.epoxy.profiles.profileActionItem +import javax.inject.Inject + +/** + * Epoxy controller for room alias actions + */ +class RoomAliasBottomSheetController @Inject constructor() : TypedEpoxyController() { + + var listener: Listener? = null + + override fun buildModels(state: RoomAliasBottomSheetState) { + profileActionItem { + id("alias") + title(state.alias) + subtitle(state.matrixToLink) + } + + // Notifications + dividerItem { + id("aliasSeparator") + } + + var idx = 0 + // Share + state.matrixToLink?.let { + RoomAliasBottomSheetSharedAction.ShareAlias(it).toBottomSheetItem(++idx) + } + + // Action on published alias + if (state.isPublished) { + // Published address + if (state.canEditCanonicalAlias) { + if (state.isMainAlias) { + RoomAliasBottomSheetSharedAction.UnsetMainAlias.toBottomSheetItem(++idx) + } else { + RoomAliasBottomSheetSharedAction.SetMainAlias(state.alias).toBottomSheetItem(++idx) + } + RoomAliasBottomSheetSharedAction.UnPublishAlias(state.alias).toBottomSheetItem(++idx) + } + } else { + // Local address + if (state.canEditCanonicalAlias) { + // Publish + RoomAliasBottomSheetSharedAction.PublishAlias(state.alias).toBottomSheetItem(++idx) + } + // Delete + RoomAliasBottomSheetSharedAction.DeleteAlias(state.alias).toBottomSheetItem(++idx) + } + } + + private fun RoomAliasBottomSheetSharedAction.toBottomSheetItem(index: Int) { + return bottomSheetActionItem { + id("action_$index") + iconRes(iconResId) + textRes(titleRes) + destructive(this@toBottomSheetItem.destructive) + listener(View.OnClickListener { listener?.didSelectMenuAction(this@toBottomSheetItem) }) + } + } + + interface Listener { + fun didSelectMenuAction(quickAction: RoomAliasBottomSheetSharedAction) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt new file mode 100644 index 0000000000..13909c401f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt @@ -0,0 +1,56 @@ +/* + * 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.app.features.roomprofile.alias.detail + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import im.vector.app.R +import im.vector.app.core.platform.VectorSharedAction + +sealed class RoomAliasBottomSheetSharedAction( + @StringRes val titleRes: Int, + @DrawableRes val iconResId: Int = 0, + val destructive: Boolean = false) + : VectorSharedAction { + + data class ShareAlias(val matrixTo: String) : RoomAliasBottomSheetSharedAction( + R.string.share, + R.drawable.ic_material_share + ) + + data class PublishAlias(val alias: String) : RoomAliasBottomSheetSharedAction( + R.string.room_alias_action_publish + ) + + data class UnPublishAlias(val alias: String) : RoomAliasBottomSheetSharedAction( + R.string.room_alias_action_unpublish + ) + + data class DeleteAlias(val alias: String) : RoomAliasBottomSheetSharedAction( + R.string.delete, + R.drawable.ic_trash_24, + true + ) + + data class SetMainAlias(val alias: String) : RoomAliasBottomSheetSharedAction( + R.string.room_settings_set_main_address + ) + + object UnsetMainAlias : RoomAliasBottomSheetSharedAction( + R.string.room_settings_unset_main_address + ) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedActionViewModel.kt new file mode 100644 index 0000000000..5f71783515 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedActionViewModel.kt @@ -0,0 +1,25 @@ +/* + * 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.app.features.roomprofile.alias.detail + +import im.vector.app.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +/** + * Activity shared view model to handle room alias quick actions + */ +class RoomAliasBottomSheetSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt new file mode 100644 index 0000000000..97ffcdf30c --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt @@ -0,0 +1,35 @@ +/* + * 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.app.features.roomprofile.alias.detail + +import com.airbnb.mvrx.MvRxState + +data class RoomAliasBottomSheetState( + val alias: String, + val matrixToLink: String? = null, + val isPublished: Boolean, + val isMainAlias: Boolean, + val canEditCanonicalAlias: Boolean +) : MvRxState { + + constructor(args: RoomAliasBottomSheetArgs) : this( + alias = args.alias, + isPublished = args.isPublished, + isMainAlias = args.isMainAlias, + canEditCanonicalAlias = args.canEditCanonicalAlias + ) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt new file mode 100644 index 0000000000..7f723cae53 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt @@ -0,0 +1,58 @@ +/* + * 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.app.features.roomprofile.alias.detail + +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.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import org.matrix.android.sdk.api.session.Session + +class RoomAliasBottomSheetViewModel @AssistedInject constructor( + @Assisted initialState: RoomAliasBottomSheetState, + session: Session +) : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomAliasBottomSheetState): RoomAliasBottomSheetViewModel? { + val fragment: RoomAliasBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.roomAliasBottomSheetViewModelFactory.create(state) + } + } + + init { + setState { + copy( + matrixToLink = session.permalinkService().createPermalink(alias) + ) + } + } + + override fun handle(action: EmptyAction) { + // No op + } +} diff --git a/vector/src/main/res/drawable/ic_trash_24.xml b/vector/src/main/res/drawable/ic_trash_24.xml index 266855d50c..27ad2e29d7 100644 --- a/vector/src/main/res/drawable/ic_trash_24.xml +++ b/vector/src/main/res/drawable/ic_trash_24.xml @@ -1,41 +1,47 @@ - - - - - + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 36f2b38dab..327161233d 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1047,6 +1047,9 @@ This room has no local addresses Add a local address + Publish this address + Unpublish this address + Anyone Members only (since the point in time of selecting this option) From f5ae95d7f16bcdce7ad8b995c7c48fe30f11c5b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 08:34:01 +0100 Subject: [PATCH 33/73] Close form only if necessary --- .../roomprofile/alias/RoomAliasViewModel.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index ff9c023a21..3110c22e21 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -164,7 +164,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) - is RoomAliasAction.PublishAlias -> handleAddAliasManually(action) + is RoomAliasAction.PublishAlias -> handlePublishAlias(action) }.exhaustive } @@ -212,39 +212,45 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo val newAlias = (state.publishManuallyState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState updateCanonicalAlias( canonicalAlias = state.canonicalAlias, - alternativeAliases = state.alternativeAliases + newAlias + alternativeAliases = state.alternativeAliases + newAlias, + closeForm = true ) } - private fun handleAddAliasManually(action: RoomAliasAction.PublishAlias) = withState { state -> + private fun handlePublishAlias(action: RoomAliasAction.PublishAlias) = withState { state -> updateCanonicalAlias( canonicalAlias = state.canonicalAlias, - alternativeAliases = state.alternativeAliases + action.alias + alternativeAliases = state.alternativeAliases + action.alias, + closeForm = false ) } private fun handleRemoveAlias(action: RoomAliasAction.UnpublishAlias) = withState { state -> updateCanonicalAlias( canonicalAlias = state.canonicalAlias, - alternativeAliases = state.alternativeAliases - action.alias - ) + alternativeAliases = state.alternativeAliases - action.alias, + closeForm = false ) } private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state -> updateCanonicalAlias( canonicalAlias = action.canonicalAlias, - alternativeAliases = state.alternativeAliases + alternativeAliases = state.alternativeAliases, + closeForm = false ) } - private fun updateCanonicalAlias(canonicalAlias: String?, alternativeAliases: List) { + private fun updateCanonicalAlias(canonicalAlias: String?, alternativeAliases: List, closeForm: Boolean) { postLoading(true) room.updateCanonicalAlias(canonicalAlias, alternativeAliases, object : MatrixCallback { override fun onSuccess(data: Unit) { setState { copy( isLoading = false, - publishManuallyState = RoomAliasViewState.AddAliasState.Closed + publishManuallyState = if (closeForm) RoomAliasViewState.AddAliasState.Closed else publishManuallyState, + // Local echo + canonicalAlias = canonicalAlias, + alternativeAliases = alternativeAliases ) } } From 2d4cbde72c906d77ed8d272094b01fb4a286a36a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 08:56:56 +0100 Subject: [PATCH 34/73] Render canonical alias --- .../roomprofile/alias/RoomAliasController.kt | 30 ++++++++++++------- .../roomprofile/alias/RoomAliasFragment.kt | 21 ++++++------- .../roomprofile/alias/RoomAliasViewModel.kt | 16 +++++----- .../detail/RoomAliasBottomSheetController.kt | 1 + vector/src/main/res/values/strings.xml | 3 ++ 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 6790bee2a8..d3d8a7bed7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -51,7 +51,7 @@ class RoomAliasController @Inject constructor( fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() - fun openAlias(alias: String, isPublished: Boolean) + fun openAliasDetail(alias: String, isPublished: Boolean) } var callback: Callback? = null @@ -78,7 +78,17 @@ class RoomAliasController @Inject constructor( helperTextResId(R.string.room_alias_published_alias_subtitle) } - // TODO Set/Unset Canonical + data.canonicalAlias + ?.takeIf { it.isNotEmpty() } + ?.let { canonicalAlias -> + + profileActionItem { + id("canonical") + title(data.canonicalAlias) + subtitle(stringProvider.getString(R.string.room_alias_published_alias_main)) + listener { callback?.openAliasDetail(canonicalAlias, true) } + } + } if (data.alternativeAliases.isEmpty()) { settingsInfoItem { @@ -98,7 +108,7 @@ class RoomAliasController @Inject constructor( profileActionItem { id("alt_$idx") title(altAlias) - listener { callback?.openAlias(altAlias, true) } + listener { callback?.openAliasDetail(altAlias, true) } } } } @@ -110,8 +120,8 @@ class RoomAliasController @Inject constructor( private fun buildPublishManuallyForm(data: RoomAliasViewState) { when (data.publishManuallyState) { - RoomAliasViewState.AddAliasState.Hidden -> Unit - RoomAliasViewState.AddAliasState.Closed -> { + RoomAliasViewState.AddAliasState.Hidden -> Unit + RoomAliasViewState.AddAliasState.Closed -> { settingsButtonItem { id("publishManually") colorProvider(colorProvider) @@ -154,16 +164,16 @@ class RoomAliasController @Inject constructor( id("loadingAliases") } } - is Success -> { + is Success -> { localAliases().forEachIndexed { idx, localAlias -> profileActionItem { id("loc_$idx") title(localAlias) - listener { callback?.openAlias(localAlias, false) } + listener { callback?.openAliasDetail(localAlias, false) } } } } - is Fail -> { + is Fail -> { errorWithRetryItem { id("alt_error") text(errorFormatter.toHumanReadable(localAliases.error)) @@ -177,8 +187,8 @@ class RoomAliasController @Inject constructor( private fun buildAddLocalAlias(data: RoomAliasViewState) { when (data.newLocalAliasState) { - RoomAliasViewState.AddAliasState.Hidden -> Unit - RoomAliasViewState.AddAliasState.Closed -> { + RoomAliasViewState.AddAliasState.Hidden -> Unit + RoomAliasViewState.AddAliasState.Closed -> { settingsButtonItem { id("newLocalAliasButton") colorProvider(colorProvider) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index f54cff5c6c..9d3b7feda6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -113,29 +113,26 @@ class RoomAliasFragment @Inject constructor( super.onDestroyView() } - override fun invalidate() = withState(viewModel) { viewState -> - controller.setData(viewState) - renderRoomSummary(viewState) + override fun invalidate() = withState(viewModel) { state -> + waiting_view.isVisible = state.isLoading + controller.setData(state) + renderRoomSummary(state) } private fun renderRoomSummary(state: RoomAliasViewState) { - waiting_view.isVisible = state.isLoading - state.roomSummary()?.let { roomSettingsToolbarTitleView.text = it.displayName avatarRenderer.render(it.toMatrixItem(), roomSettingsToolbarAvatarImageView) } - - invalidateOptionsMenu() } - private fun unpublishAlias(altAlias: String) { + private fun unpublishAlias(alias: String) { AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_title_confirmation) - .setMessage(getString(R.string.room_alias_delete_confirmation, altAlias)) + .setMessage(getString(R.string.room_alias_unpublish_confirmation, alias)) .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.delete) { _, _ -> - viewModel.handle(RoomAliasAction.UnpublishAlias(altAlias)) + .setPositiveButton(R.string.action_unpublish) { _, _ -> + viewModel.handle(RoomAliasAction.UnpublishAlias(alias)) } .show() .withColoredButton(DialogInterface.BUTTON_POSITIVE) @@ -165,7 +162,7 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.AddLocalAlias) } - override fun openAlias(alias: String, isPublished: Boolean) = withState(viewModel) { state -> + override fun openAliasDetail(alias: String, isPublished: Boolean) = withState(viewModel) { state -> RoomAliasBottomSheet .newInstance( alias = alias, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 3110c22e21..8cfd589bb3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -157,8 +157,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo when (action) { RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm() is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) - is RoomAliasAction.ManualPublishAlias -> handleAddAlias() - is RoomAliasAction.UnpublishAlias -> handleRemoveAlias(action) + is RoomAliasAction.ManualPublishAlias -> handleManualPublishAlias() + is RoomAliasAction.UnpublishAlias -> handleUnpublishAlias(action) is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm() is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) @@ -208,7 +208,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo } } - private fun handleAddAlias() = withState { state -> + private fun handleManualPublishAlias() = withState { state -> val newAlias = (state.publishManuallyState as? RoomAliasViewState.AddAliasState.Editing)?.value ?: return@withState updateCanonicalAlias( canonicalAlias = state.canonicalAlias, @@ -225,7 +225,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo ) } - private fun handleRemoveAlias(action: RoomAliasAction.UnpublishAlias) = withState { state -> + private fun handleUnpublishAlias(action: RoomAliasAction.UnpublishAlias) = withState { state -> updateCanonicalAlias( canonicalAlias = state.canonicalAlias, alternativeAliases = state.alternativeAliases - action.alias, @@ -235,7 +235,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state -> updateCanonicalAlias( canonicalAlias = action.canonicalAlias, - alternativeAliases = state.alternativeAliases, + // Ensure the previous canonical alias is moved to the alt aliases + alternativeAliases = (state.alternativeAliases + listOfNotNull(state.canonicalAlias)).distinct(), closeForm = false ) } @@ -247,10 +248,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo setState { copy( isLoading = false, - publishManuallyState = if (closeForm) RoomAliasViewState.AddAliasState.Closed else publishManuallyState, - // Local echo - canonicalAlias = canonicalAlias, - alternativeAliases = alternativeAliases + publishManuallyState = if (closeForm) RoomAliasViewState.AddAliasState.Closed else publishManuallyState ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt index 2f4cb357b4..56a93d1527 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt @@ -34,6 +34,7 @@ class RoomAliasBottomSheetController @Inject constructor() : TypedEpoxyControlle id("alias") title(state.alias) subtitle(state.matrixToLink) + editable(false) } // Notifications diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 327161233d..6e55567428 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -138,6 +138,7 @@ Close Copy Add + Unpublish Copied to clipboard Disable @@ -1030,10 +1031,12 @@ Room Addresses Published Addresses Published addresses can be used by anyone on any server to join your room. To publish an address, it needs to be set as a local address first. + This is the main address Main address Other published addresses: Published a new address manually Publish + Unpublish the address \"%1$s\"? Delete the address \"%1$s\"? Publish this room to the public in %1$s\'s room directory? From a570528f6c6e2f693c24fa050e6198c2cb1b979c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 09:26:02 +0100 Subject: [PATCH 35/73] More fixes --- .../app/features/roomprofile/alias/RoomAliasController.kt | 8 ++++---- .../app/features/roomprofile/alias/RoomAliasFragment.kt | 5 +++-- .../app/features/roomprofile/alias/RoomAliasViewModel.kt | 2 +- .../app/features/roomprofile/alias/RoomAliasViewState.kt | 3 +++ .../roomprofile/alias/detail/RoomAliasBottomSheet.kt | 3 +++ .../alias/detail/RoomAliasBottomSheetController.kt | 6 ++++-- .../roomprofile/alias/detail/RoomAliasBottomSheetState.kt | 2 ++ vector/src/main/res/values/strings.xml | 2 +- 8 files changed, 21 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index d3d8a7bed7..56f3cb77c5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -51,7 +51,7 @@ class RoomAliasController @Inject constructor( fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() - fun openAliasDetail(alias: String, isPublished: Boolean) + fun openAliasDetail(alias: String) } var callback: Callback? = null @@ -86,7 +86,7 @@ class RoomAliasController @Inject constructor( id("canonical") title(data.canonicalAlias) subtitle(stringProvider.getString(R.string.room_alias_published_alias_main)) - listener { callback?.openAliasDetail(canonicalAlias, true) } + listener { callback?.openAliasDetail(canonicalAlias) } } } @@ -108,7 +108,7 @@ class RoomAliasController @Inject constructor( profileActionItem { id("alt_$idx") title(altAlias) - listener { callback?.openAliasDetail(altAlias, true) } + listener { callback?.openAliasDetail(altAlias) } } } } @@ -169,7 +169,7 @@ class RoomAliasController @Inject constructor( profileActionItem { id("loc_$idx") title(localAlias) - listener { callback?.openAliasDetail(localAlias, false) } + listener { callback?.openAliasDetail(localAlias) } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 9d3b7feda6..dd02691259 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -162,12 +162,13 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.AddLocalAlias) } - override fun openAliasDetail(alias: String, isPublished: Boolean) = withState(viewModel) { state -> + override fun openAliasDetail(alias: String) = withState(viewModel) { state -> RoomAliasBottomSheet .newInstance( alias = alias, - isPublished = isPublished, + isPublished = alias in state.allPublishedAliases, isMainAlias = alias == state.canonicalAlias, + isLocal = alias in state.localAliases().orEmpty(), canEditCanonicalAlias = state.actionPermissions.canChangeCanonicalAlias ) .show(childFragmentManager, "ROOM_ALIAS_ACTIONS") diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 8cfd589bb3..4aed4a55bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -236,7 +236,7 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo updateCanonicalAlias( canonicalAlias = action.canonicalAlias, // Ensure the previous canonical alias is moved to the alt aliases - alternativeAliases = (state.alternativeAliases + listOfNotNull(state.canonicalAlias)).distinct(), + alternativeAliases = state.allPublishedAliases, closeForm = false ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index e1736296c6..cffff2ec4f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -37,6 +37,9 @@ data class RoomAliasViewState( constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + val allPublishedAliases: List + get() = (alternativeAliases + listOfNotNull(canonicalAlias)).distinct() + data class ActionPermissions( val canChangeCanonicalAlias: Boolean = false ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt index f9968d86da..86702d1507 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheet.kt @@ -36,6 +36,7 @@ data class RoomAliasBottomSheetArgs( val alias: String, val isPublished: Boolean, val isMainAlias: Boolean, + val isLocal: Boolean, val canEditCanonicalAlias: Boolean ) : Parcelable @@ -90,12 +91,14 @@ class RoomAliasBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomAliasBot fun newInstance(alias: String, isPublished: Boolean, isMainAlias: Boolean, + isLocal: Boolean, canEditCanonicalAlias: Boolean): RoomAliasBottomSheet { return RoomAliasBottomSheet().apply { setArguments(RoomAliasBottomSheetArgs( alias = alias, isPublished = isPublished, isMainAlias = isMainAlias, + isLocal = isLocal, canEditCanonicalAlias = canEditCanonicalAlias )) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt index 56a93d1527..157037c13d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetController.kt @@ -59,9 +59,11 @@ class RoomAliasBottomSheetController @Inject constructor() : TypedEpoxyControlle } RoomAliasBottomSheetSharedAction.UnPublishAlias(state.alias).toBottomSheetItem(++idx) } - } else { + } + + if (state.isLocal) { // Local address - if (state.canEditCanonicalAlias) { + if (state.canEditCanonicalAlias && state.isPublished.not()) { // Publish RoomAliasBottomSheetSharedAction.PublishAlias(state.alias).toBottomSheetItem(++idx) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt index 97ffcdf30c..a61075cef6 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetState.kt @@ -23,6 +23,7 @@ data class RoomAliasBottomSheetState( val matrixToLink: String? = null, val isPublished: Boolean, val isMainAlias: Boolean, + val isLocal: Boolean, val canEditCanonicalAlias: Boolean ) : MvRxState { @@ -30,6 +31,7 @@ data class RoomAliasBottomSheetState( alias = args.alias, isPublished = args.isPublished, isMainAlias = args.isMainAlias, + isLocal = args.isLocal, canEditCanonicalAlias = args.canEditCanonicalAlias ) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 6e55567428..d9df53d99f 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1034,7 +1034,7 @@ This is the main address Main address Other published addresses: - Published a new address manually + Publish a new address manually Publish Unpublish the address \"%1$s\"? Delete the address \"%1$s\"? From 90e0006caef776fcf657fe2121635d24cc3aa55c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 11:01:07 +0100 Subject: [PATCH 36/73] Room directory visibility management --- CHANGES.md | 1 + .../api/session/room/RoomDirectoryService.kt | 11 +++ .../session/directory/DirectoryAPI.kt | 18 +++++ .../directory/RoomDirectoryVisibilityJson.kt | 29 +++++++ .../room/DefaultRoomDirectoryService.kt | 20 ++++- .../sdk/internal/session/room/RoomModule.kt | 10 +++ .../GetRoomDirectoryVisibilityTask.kt | 44 +++++++++++ .../SetRoomDirectoryVisibilityTask.kt | 47 ++++++++++++ .../app/features/form/FormSwitchItem.kt | 2 +- .../roomprofile/alias/RoomAliasAction.kt | 4 + .../roomprofile/alias/RoomAliasController.kt | 38 +++++++++- .../roomprofile/alias/RoomAliasFragment.kt | 5 ++ .../roomprofile/alias/RoomAliasViewModel.kt | 76 ++++++++++++++++--- .../roomprofile/alias/RoomAliasViewState.kt | 2 + vector/src/main/res/values/strings.xml | 7 +- 15 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/RoomDirectoryVisibilityJson.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt diff --git a/CHANGES.md b/CHANGES.md index e48281081b..30713deb18 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ Features ✨: - Create DMs with users by scanning their QR code (#2025) - Add Invite friends quick invite actions (#2348) - Add friend by scanning QR code, show your code to friends (#2025) + - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428) Improvements 🙌: - New room creation tile with quick action (#2346) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index dc5b3d55f5..61970ce848 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.room import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol @@ -39,4 +40,14 @@ interface RoomDirectoryService { * Includes both the available protocols and all fields required for queries against each protocol. */ fun getThirdPartyProtocol(callback: MatrixCallback>): Cancelable + + /** + * Get the visibility of a room in the directory + */ + suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility + + /** + * Set the visibility of a room in the directory + */ + suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt index 3eff4b05aa..d0de5f2397 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt @@ -35,6 +35,24 @@ internal interface DirectoryAPI { @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call + /** + * Get the room directory visibility. + * + * @param roomId the room id. + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}") + fun getRoomDirectoryVisibility(@Path("roomId") roomId: String): Call + + /** + * Set the room directory visibility. + * + * @param roomId the room id. + * @param body the body containing the new directory visibility + */ + @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}") + fun setRoomDirectoryVisibility(@Path("roomId") roomId: String, + @Body body: RoomDirectoryVisibilityJson): Call + /** * Add alias to the room. * @param roomAlias the room alias. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/RoomDirectoryVisibilityJson.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/RoomDirectoryVisibilityJson.kt new file mode 100644 index 0000000000..ddf927a3dc --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/RoomDirectoryVisibilityJson.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.directory + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility + +@JsonClass(generateAdapter = true) +internal data class RoomDirectoryVisibilityJson( + /** + * The visibility of the room in the directory. One of: ["private", "public"] + */ + @Json(name = "visibility") val visibility: RoomDirectoryVisibility +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt index a091b5f85e..0d41c6f35e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomDirectoryService.kt @@ -18,19 +18,25 @@ package org.matrix.android.sdk.internal.session.room import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.RoomDirectoryService +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask +import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask +import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import javax.inject.Inject -internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask, - private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, - private val taskExecutor: TaskExecutor) : RoomDirectoryService { +internal class DefaultRoomDirectoryService @Inject constructor( + private val getPublicRoomTask: GetPublicRoomTask, + private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask, + private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask, + private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask, + private val taskExecutor: TaskExecutor) : RoomDirectoryService { override fun getPublicRooms(server: String?, publicRoomsParams: PublicRoomsParams, @@ -49,4 +55,12 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu } .executeBy(taskExecutor) } + + override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility { + return getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId)) + } + + override suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) { + setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility)) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 8ad82e0e15..3a94396a61 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -38,9 +38,13 @@ import org.matrix.android.sdk.internal.session.room.alias.GetRoomLocalAliasesTas import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask import org.matrix.android.sdk.internal.session.room.directory.DefaultGetPublicRoomTask +import org.matrix.android.sdk.internal.session.room.directory.DefaultGetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask +import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask +import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask +import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask @@ -139,6 +143,12 @@ internal abstract class RoomModule { @Binds abstract fun bindGetPublicRoomTask(task: DefaultGetPublicRoomTask): GetPublicRoomTask + @Binds + abstract fun bindGetRoomDirectoryVisibilityTask(task: DefaultGetRoomDirectoryVisibilityTask): GetRoomDirectoryVisibilityTask + + @Binds + abstract fun bindSetRoomDirectoryVisibilityTask(task: DefaultSetRoomDirectoryVisibilityTask): SetRoomDirectoryVisibilityTask + @Binds abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt new file mode 100644 index 0000000000..fbdd6a03eb --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/GetRoomDirectoryVisibilityTask.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.directory + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface GetRoomDirectoryVisibilityTask : Task { + data class Params( + val roomId: String + ) +} + +internal class DefaultGetRoomDirectoryVisibilityTask @Inject constructor( + private val directoryAPI: DirectoryAPI, + private val eventBus: EventBus +) : GetRoomDirectoryVisibilityTask { + + override suspend fun execute(params: GetRoomDirectoryVisibilityTask.Params): RoomDirectoryVisibility { + return executeRequest(eventBus) { + apiCall = directoryAPI.getRoomDirectoryVisibility(params.roomId) + } + .visibility + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt new file mode 100644 index 0000000000..33b12aa1ca --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/directory/SetRoomDirectoryVisibilityTask.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.internal.session.room.directory + +import org.greenrobot.eventbus.EventBus +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility +import org.matrix.android.sdk.internal.network.executeRequest +import org.matrix.android.sdk.internal.session.directory.DirectoryAPI +import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson +import org.matrix.android.sdk.internal.task.Task +import javax.inject.Inject + +internal interface SetRoomDirectoryVisibilityTask : Task { + data class Params( + val roomId: String, + val roomDirectoryVisibility: RoomDirectoryVisibility + ) +} + +internal class DefaultSetRoomDirectoryVisibilityTask @Inject constructor( + private val directoryAPI: DirectoryAPI, + private val eventBus: EventBus +) : SetRoomDirectoryVisibilityTask { + + override suspend fun execute(params: SetRoomDirectoryVisibilityTask.Params) { + executeRequest(eventBus) { + apiCall = directoryAPI.setRoomDirectoryVisibility( + params.roomId, + RoomDirectoryVisibilityJson(visibility = params.roomDirectoryVisibility) + ) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/form/FormSwitchItem.kt b/vector/src/main/java/im/vector/app/features/form/FormSwitchItem.kt index 0b274cccd8..ff4f8711e0 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormSwitchItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormSwitchItem.kt @@ -61,8 +61,8 @@ abstract class FormSwitchItem : VectorEpoxyModel() { holder.switchView.isEnabled = enabled + holder.switchView.setOnCheckedChangeListener(null) holder.switchView.isChecked = switchChecked - holder.switchView.setOnCheckedChangeListener { _, isChecked -> listener?.invoke(isChecked) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt index 4054d6f63a..80e1603453 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasAction.kt @@ -17,6 +17,7 @@ package im.vector.app.features.roomprofile.alias import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility sealed class RoomAliasAction : VectorViewModelAction { // Canonical @@ -27,6 +28,9 @@ sealed class RoomAliasAction : VectorViewModelAction { data class UnpublishAlias(val alias: String) : RoomAliasAction() data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction() + // Room directory + data class SetRoomDirectoryVisibility(val roomDirectoryVisibility: RoomDirectoryVisibility) : RoomAliasAction() + // Local data class RemoveLocalAlias(val alias: String) : RoomAliasAction() object ToggleAddLocalAliasForm : RoomAliasAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 56f3cb77c5..64caa1e525 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -18,6 +18,7 @@ package im.vector.app.features.roomprofile.alias import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.R @@ -32,9 +33,11 @@ import im.vector.app.features.discovery.settingsButtonItem import im.vector.app.features.discovery.settingsContinueCancelItem import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.form.formEditTextItem +import im.vector.app.features.form.formSwitchItem import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem import org.matrix.android.sdk.api.session.room.alias.RoomAliasError +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import javax.inject.Inject class RoomAliasController @Inject constructor( @@ -48,6 +51,7 @@ class RoomAliasController @Inject constructor( fun toggleManualPublishForm() fun setNewAlias(value: String) fun addAlias() + fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility) fun toggleLocalAliasForm() fun setNewLocalAliasLocalPart(value: String) fun addLocalAlias() @@ -63,12 +67,42 @@ class RoomAliasController @Inject constructor( override fun buildModels(data: RoomAliasViewState?) { data ?: return - // Published + // Published alias buildPublishInfo(data) - // Local + // Room directory visibility + buildRoomDirectoryVisibility(data) + // Local alias buildLocalInfo(data) } + private fun buildRoomDirectoryVisibility(data: RoomAliasViewState) { + when (data.roomDirectoryVisibility) { + Uninitialized -> Unit + is Loading -> Unit + is Success -> { + formSwitchItem { + id("roomVisibility") + title(stringProvider.getString(R.string.room_alias_publish_to_directory, data.homeServerName)) + showDivider(false) + switchChecked(data.roomDirectoryVisibility() == RoomDirectoryVisibility.PUBLIC) + listener { + if (it) { + callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PUBLIC) + } else { + callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PRIVATE) + } + } + } + } + is Fail -> { + errorWithRetryItem { + text(stringProvider.getString(R.string.room_alias_publish_to_directory_error, + errorFormatter.toHumanReadable(data.roomDirectoryVisibility.error))) + } + } + } + } + private fun buildPublishInfo(data: RoomAliasViewState) { buildProfileSection( stringProvider.getString(R.string.room_alias_published_alias_title) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index dd02691259..916b40970e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -40,6 +40,7 @@ import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetShare import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.matrix.android.sdk.api.session.room.alias.RoomAliasError +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -150,6 +151,10 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.ManualPublishAlias) } + override fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility) { + viewModel.handle(RoomAliasAction.SetRoomDirectoryVisibility(roomDirectoryVisibility)) + } + override fun toggleLocalAliasForm() { viewModel.handle(RoomAliasAction.ToggleAddLocalAliasForm) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 4aed4a55bb..58aee5f084 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -67,6 +67,35 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo observePowerLevel() observeRoomCanonicalAlias() fetchRoomAlias() + fetchRoomDirectoryVisibility() + } + + private fun fetchRoomDirectoryVisibility() { + setState { + copy( + roomDirectoryVisibility = Loading() + ) + } + viewModelScope.launch { + runCatching { + session.getRoomDirectoryVisibility(room.roomId) + }.fold( + { + setState { + copy( + roomDirectoryVisibility = Success(it) + ) + } + }, + { + setState { + copy( + roomDirectoryVisibility = Fail(it) + ) + } + } + ) + } } private fun initHomeServerName() { @@ -155,19 +184,43 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo override fun handle(action: RoomAliasAction) { when (action) { - RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm() - is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) - is RoomAliasAction.ManualPublishAlias -> handleManualPublishAlias() - is RoomAliasAction.UnpublishAlias -> handleUnpublishAlias(action) - is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) - RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm() - is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) - RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() - is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) - is RoomAliasAction.PublishAlias -> handlePublishAlias(action) + RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm() + is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action) + is RoomAliasAction.ManualPublishAlias -> handleManualPublishAlias() + is RoomAliasAction.UnpublishAlias -> handleUnpublishAlias(action) + is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action) + is RoomAliasAction.SetRoomDirectoryVisibility -> handleSetRoomDirectoryVisibility(action) + RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm() + is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action) + RoomAliasAction.AddLocalAlias -> handleAddLocalAlias() + is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action) + is RoomAliasAction.PublishAlias -> handlePublishAlias(action) }.exhaustive } + private fun handleSetRoomDirectoryVisibility(action: RoomAliasAction.SetRoomDirectoryVisibility) { + postLoading(true) + viewModelScope.launch { + runCatching { + session.setRoomDirectoryVisibility(room.roomId, action.roomDirectoryVisibility) + }.fold( + { + setState { + copy( + isLoading = false, + // Local echo, no need to fetch the data from the server again + roomDirectoryVisibility = Success(action.roomDirectoryVisibility) + ) + } + }, + { + postLoading(false) + _viewEvents.post(RoomAliasViewEvents.Failure(it)) + } + ) + } + } + private fun handleToggleAddLocalAliasForm() { setState { copy( @@ -229,7 +282,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo updateCanonicalAlias( canonicalAlias = state.canonicalAlias, alternativeAliases = state.alternativeAliases - action.alias, - closeForm = false ) + closeForm = false + ) } private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt index cffff2ec4f..f6341f4f64 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs +import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomSummary data class RoomAliasViewState( @@ -27,6 +28,7 @@ data class RoomAliasViewState( val homeServerName: String = "", val roomSummary: Async = Uninitialized, val actionPermissions: ActionPermissions = ActionPermissions(), + val roomDirectoryVisibility: Async = Uninitialized, val isLoading: Boolean = false, val canonicalAlias: String? = null, val alternativeAliases: List = emptyList(), diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index d9df53d99f..5e15b3f429 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1026,7 +1026,7 @@ Room addresses - See and managed addresses of this room + See and managed addresses of this room, and its visibility in the room directory. Room Addresses Published Addresses @@ -1053,6 +1053,11 @@ Publish this address Unpublish this address + + Publish this room to the public in %1$s\'s room directory? + + Unable to retrieve the current room directory visibility (%1$s). + Anyone Members only (since the point in time of selecting this option) From 0da0857970d2dd50bc7f33c23922ebe83d20b761 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 11:14:48 +0100 Subject: [PATCH 37/73] Cleanup --- .../android/sdk/api/session/room/alias/RoomAliasError.kt | 2 +- .../session/room/alias/RoomAliasAvailabilityChecker.kt | 2 +- .../session/room/membership/RoomDisplayNameResolver.kt | 1 - .../im/vector/app/core/epoxy/profiles/ProfileActionItem.kt | 1 - .../roomdirectory/createroom/CreateRoomController.kt | 6 ++++-- .../app/features/roomprofile/alias/RoomAliasViewModel.kt | 7 +++++-- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt index 1dbdd9cced..d2cb7c58a9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/RoomAliasError.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt index 0abb158521..d47450aaee 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 New Vector Ltd + * Copyright 2020 The Matrix.org Foundation C.I.C. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index f744af94c6..784b610af7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -21,7 +21,6 @@ import org.matrix.android.sdk.R import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.RoomAliasesContent import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent import org.matrix.android.sdk.api.session.room.model.RoomNameContent import org.matrix.android.sdk.internal.database.mapper.ContentMapper diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt index f52051f989..2769121afa 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileActionItem.kt @@ -17,7 +17,6 @@ package im.vector.app.core.epoxy.profiles import android.content.res.ColorStateList -import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index de9ecb2825..94b419797d 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -27,7 +27,6 @@ import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableAvatarItem import im.vector.app.features.form.formSubmitButtonItem import im.vector.app.features.form.formSwitchItem -import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import javax.inject.Inject @@ -104,7 +103,10 @@ class CreateRoomController @Inject constructor( enabled(enableFormElement) value(viewState.roomType.aliasLocalPart) homeServer(":" + viewState.homeServerName) - errorMessage(roomAliasErrorFormatter.format((((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)) + errorMessage( + roomAliasErrorFormatter.format( + (((viewState.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError) + ) onTextChange { value -> listener?.setAliasLocalPart(value) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 58aee5f084..66daf6e5db 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -142,8 +142,11 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo .subscribe { val powerLevelsHelper = PowerLevelsHelper(it) val permissions = RoomAliasViewState.ActionPermissions( - canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, - EventType.STATE_ROOM_CANONICAL_ALIAS), + canChangeCanonicalAlias = powerLevelsHelper.isUserAllowedToSend( + userId = session.myUserId, + isState = true, + eventType = EventType.STATE_ROOM_CANONICAL_ALIAS + ) ) setState { val newPublishManuallyState = if (permissions.canChangeCanonicalAlias) { From 412fc78c9af90c69c3088f5cf9e18602d36b6029 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 11:27:36 +0100 Subject: [PATCH 38/73] Cleanup --- .../android/sdk/internal/session/directory/DirectoryAPI.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt index d0de5f2397..6a50f3ee37 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/directory/DirectoryAPI.kt @@ -62,7 +62,7 @@ internal interface DirectoryAPI { @Body body: AddRoomAliasBody): Call /** - * Delete a room aliases + * Delete a room alias * @param roomAlias the room alias. */ @DELETE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}") From 6c7eb6ea8c7407d6ca6d4aac17e06590fc168b4a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 15:45:45 +0100 Subject: [PATCH 39/73] Small wording change --- vector/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 5e15b3f429..d642f84d65 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1104,8 +1104,8 @@ You will have no main address specified for this room." Main address warnings - Set as Main Address - Unset as Main Address + Set as main address + Unset as main address Copy Room ID Copy Room Address From 50ddd3cf3118e8d91ec9d8adf2569b2d1a7b11ff Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 24 Nov 2020 16:14:38 +0100 Subject: [PATCH 40/73] Small cleanup, handle case of no local alias, handle unpublish of canonical alias --- .../sdk/internal/session/room/RoomAPI.kt | 3 ++- .../session/room/alias/GetAliasesResponse.kt | 2 +- .../roomprofile/alias/RoomAliasController.kt | 17 ++++++++++++----- .../roomprofile/alias/RoomAliasViewModel.kt | 3 ++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 44f52f5f3a..955a251b52 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -320,7 +320,8 @@ internal interface RoomAPI { @Body body: ReportContentBody): Call /** - * Get local aliases of this room + * Get a list of aliases maintained by the local server for the given room. + * Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-aliases */ @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2432/rooms/{roomId}/aliases") fun getAliases(@Path("roomId") roomId: String): Call diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt index 499abf33aa..5965924085 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/GetAliasesResponse.kt @@ -22,7 +22,7 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) internal data class GetAliasesResponse( /** - * The list of aliases currently defined on the local server for the given room + * Required. The server's local aliases on the room. Can be empty. */ @Json(name = "aliases") val aliases: List = emptyList() ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 64caa1e525..e1c8a3803e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -199,11 +199,18 @@ class RoomAliasController @Inject constructor( } } is Success -> { - localAliases().forEachIndexed { idx, localAlias -> - profileActionItem { - id("loc_$idx") - title(localAlias) - listener { callback?.openAliasDetail(localAlias) } + if (localAliases().isEmpty()) { + settingsInfoItem { + id("locEmpty") + helperTextResId(R.string.room_alias_local_address_empty) + } + } else { + localAliases().forEachIndexed { idx, localAlias -> + profileActionItem { + id("loc_$idx") + title(localAlias) + listener { callback?.openAliasDetail(localAlias) } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index 66daf6e5db..5873d9ce8a 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -283,7 +283,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo private fun handleUnpublishAlias(action: RoomAliasAction.UnpublishAlias) = withState { state -> updateCanonicalAlias( - canonicalAlias = state.canonicalAlias, + // We can also unpublish the canonical alias + canonicalAlias = state.canonicalAlias.takeIf { it != action.alias }, alternativeAliases = state.alternativeAliases - action.alias, closeForm = false ) From 9c53f0f88141163e128e5a6c88f6c9d20c04fe29 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 25 Nov 2020 17:41:39 +0100 Subject: [PATCH 41/73] Clarify aliasLocalPart --- .../android/sdk/api/session/room/alias/AliasService.kt | 3 +++ .../sdk/internal/session/room/alias/AddRoomAliasTask.kt | 8 ++++++-- .../session/room/alias/RoomAliasAvailabilityChecker.kt | 8 ++++++-- .../app/features/roomprofile/alias/RoomAliasController.kt | 4 ++-- .../app/features/roomprofile/alias/RoomAliasFragment.kt | 8 ++++---- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt index 3060824f0f..5fe7e99425 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/alias/AliasService.kt @@ -19,11 +19,14 @@ package org.matrix.android.sdk.api.session.room.alias interface AliasService { /** * Get list of local alias of the room + * @return the list of the aliases (full aliases, not only the local part) */ suspend fun getRoomAliases(): List /** * Add local alias to the room + * @param aliasLocalPart the local part of the alias. + * Ex: for the alias "#my_alias:example.org", the local part is "my_alias" */ suspend fun addAlias(aliasLocalPart: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt index 4dad476f03..9793750fa0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/AddRoomAliasTask.kt @@ -20,13 +20,17 @@ import org.greenrobot.eventbus.EventBus import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.directory.DirectoryAPI -import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker.Companion.toFullAlias +import org.matrix.android.sdk.internal.session.room.alias.RoomAliasAvailabilityChecker.Companion.toFullLocalAlias import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject internal interface AddRoomAliasTask : Task { data class Params( val roomId: String, + /** + * the local part of the alias. + * Ex: for the alias "#my_alias:example.org", the local part is "my_alias" + */ val aliasLocalPart: String ) } @@ -43,7 +47,7 @@ internal class DefaultAddRoomAliasTask @Inject constructor( executeRequest(eventBus) { apiCall = directoryAPI.addRoomAlias( - roomAlias = params.aliasLocalPart.toFullAlias(userId), + roomAlias = params.aliasLocalPart.toFullLocalAlias(userId), body = AddRoomAliasBody( roomId = params.roomId ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt index d47450aaee..25ba493891 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/RoomAliasAvailabilityChecker.kt @@ -29,13 +29,17 @@ internal class RoomAliasAvailabilityChecker @Inject constructor( private val directoryAPI: DirectoryAPI, private val eventBus: EventBus ) { + /** + * @param aliasLocalPart the local part of the alias. + * Ex: for the alias "#my_alias:example.org", the local part is "my_alias" + */ @Throws(RoomAliasError::class) suspend fun check(aliasLocalPart: String?) { if (aliasLocalPart.isNullOrEmpty()) { throw RoomAliasError.AliasEmpty } // Check alias availability - val fullAlias = aliasLocalPart.toFullAlias(userId) + val fullAlias = aliasLocalPart.toFullLocalAlias(userId) try { executeRequest(eventBus) { apiCall = directoryAPI.getRoomIdByAlias(fullAlias) @@ -56,6 +60,6 @@ internal class RoomAliasAvailabilityChecker @Inject constructor( } companion object { - internal fun String.toFullAlias(userId: String) = "#" + this + ":" + userId.substringAfter(":") + internal fun String.toFullLocalAlias(userId: String) = "#" + this + ":" + userId.substringAfter(":") } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index e1c8a3803e..49bccd79db 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -49,11 +49,11 @@ class RoomAliasController @Inject constructor( interface Callback { fun toggleManualPublishForm() - fun setNewAlias(value: String) + fun setNewAlias(alias: String) fun addAlias() fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility) fun toggleLocalAliasForm() - fun setNewLocalAliasLocalPart(value: String) + fun setNewLocalAliasLocalPart(aliasLocalPart: String) fun addLocalAlias() fun openAliasDetail(alias: String) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 916b40970e..56c3e76828 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -143,8 +143,8 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.ToggleManualPublishForm) } - override fun setNewAlias(value: String) { - viewModel.handle(RoomAliasAction.SetNewAlias(value)) + override fun setNewAlias(alias: String) { + viewModel.handle(RoomAliasAction.SetNewAlias(alias)) } override fun addAlias() { @@ -159,8 +159,8 @@ class RoomAliasFragment @Inject constructor( viewModel.handle(RoomAliasAction.ToggleAddLocalAliasForm) } - override fun setNewLocalAliasLocalPart(value: String) { - viewModel.handle(RoomAliasAction.SetNewLocalAliasLocalPart(value)) + override fun setNewLocalAliasLocalPart(aliasLocalPart: String) { + viewModel.handle(RoomAliasAction.SetNewLocalAliasLocalPart(aliasLocalPart)) } override fun addLocalAlias() { From 0cfea40b2477ec4ad92f5f12a097d743dfd6e3e1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 25 Nov 2020 17:57:42 +0100 Subject: [PATCH 42/73] Use when statement and modify the case to simplify the code --- .../timeline/format/NoticeEventFormatter.kt | 72 +++++++++---------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index 0db6a374e9..b67a82b105 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -473,67 +473,63 @@ class NoticeEventFormatter @Inject constructor( val added = altAliases - prevAltAliases val removed = prevAltAliases - altAliases - return if (added.isEmpty() && removed.isEmpty() && canonicalAlias == prevCanonicalAlias) { - // in case there is no difference between the two events say something as we can't simply hide the event from here - if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_no_change_by_you) - } else { - sp.getString(R.string.notice_room_canonical_alias_no_change, senderName) - } - } else if (added.isEmpty() && removed.isEmpty()) { - // Canonical has changed - if (canonicalAlias != null) { + return when { + added.isEmpty() && removed.isEmpty() && canonicalAlias == prevCanonicalAlias -> { + // No difference between the two events say something as we can't simply hide the event from here if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_set_by_you, canonicalAlias) + sp.getString(R.string.notice_room_canonical_alias_no_change_by_you) } else { - sp.getString(R.string.notice_room_canonical_alias_set, senderName, canonicalAlias) - } - } else { - if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_unset_by_you) - } else { - sp.getString(R.string.notice_room_canonical_alias_unset, senderName) + sp.getString(R.string.notice_room_canonical_alias_no_change, senderName) } } - } else if (added.isEmpty()) { - if (canonicalAlias == prevCanonicalAlias) { + added.isEmpty() && removed.isEmpty() -> { + // Canonical has changed + if (canonicalAlias != null) { + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_set_by_you, canonicalAlias) + } else { + sp.getString(R.string.notice_room_canonical_alias_set, senderName, canonicalAlias) + } + } else { + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_unset_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_unset, senderName) + } + } + } + added.isEmpty() && canonicalAlias == prevCanonicalAlias -> { // Some alternative has been removed if (event.isSentByCurrentUser()) { sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed_by_you, removed.size, removed.joinToString()) } else { sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_removed, removed.size, senderName, removed.joinToString()) } - } else { - // Main and removed - if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed_by_you) - } else { - sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed, senderName) - } } - } else if (removed.isEmpty()) { - if (canonicalAlias == prevCanonicalAlias) { + removed.isEmpty() && canonicalAlias == prevCanonicalAlias -> { // Some alternative has been added if (event.isSentByCurrentUser()) { sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added_by_you, added.size, added.joinToString()) } else { sp.getQuantityString(R.plurals.notice_room_canonical_alias_alternative_added, added.size, senderName, added.joinToString()) } - } else { - // Main and added + } + canonicalAlias == prevCanonicalAlias -> { + // Alternative added and removed + if (event.isSentByCurrentUser()) { + sp.getString(R.string.notice_room_canonical_alias_alternative_changed_by_you) + } else { + sp.getString(R.string.notice_room_canonical_alias_alternative_changed, senderName) + } + } + else -> { + // Main and removed, or main and added, or main and added and removed if (event.isSentByCurrentUser()) { sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed_by_you) } else { sp.getString(R.string.notice_room_canonical_alias_main_and_alternative_changed, senderName) } } - } else { - // Alternative added and removed - if (event.isSentByCurrentUser()) { - sp.getString(R.string.notice_room_canonical_alias_alternative_changed_by_you) - } else { - sp.getString(R.string.notice_room_canonical_alias_alternative_changed, senderName) - } } } From c0295645902dde3dc3567aacacadc1972505396a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 25 Nov 2020 18:03:38 +0100 Subject: [PATCH 43/73] Rename method and class for more clarity --- .../app/features/roomprofile/RoomProfileActivity.kt | 10 +++++----- .../features/roomprofile/RoomProfileSharedAction.kt | 2 +- .../roomprofile/settings/RoomSettingsController.kt | 4 ++-- .../roomprofile/settings/RoomSettingsFragment.kt | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index 2204150a35..696725d001 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -99,11 +99,11 @@ class RoomProfileActivity : .observe() .subscribe { sharedAction -> when (sharedAction) { - is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() - is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() - is RoomProfileSharedAction.OpenRoomAlias -> openRoomAlias() - is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() - is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() + is RoomProfileSharedAction.OpenRoomMembers -> openRoomMembers() + is RoomProfileSharedAction.OpenRoomSettings -> openRoomSettings() + is RoomProfileSharedAction.OpenRoomAliasesSettings -> openRoomAlias() + is RoomProfileSharedAction.OpenRoomUploads -> openRoomUploads() + is RoomProfileSharedAction.OpenBannedRoomMembers -> openBannedRoomMembers() } } .disposeOnDestroy() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt index 0449f0db15..83a610cf1b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileSharedAction.kt @@ -23,7 +23,7 @@ import im.vector.app.core.platform.VectorSharedAction */ sealed class RoomProfileSharedAction : VectorSharedAction { object OpenRoomSettings : RoomProfileSharedAction() - object OpenRoomAlias : RoomProfileSharedAction() + object OpenRoomAliasesSettings : RoomProfileSharedAction() object OpenRoomUploads : RoomProfileSharedAction() object OpenRoomMembers : RoomProfileSharedAction() object OpenBannedRoomMembers : RoomProfileSharedAction() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index 6c944ef2f8..ffae4a9dc5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -46,7 +46,7 @@ class RoomSettingsController @Inject constructor( fun onNameChanged(name: String) fun onTopicChanged(topic: String) fun onHistoryVisibilityClicked() - fun onOpenAlias() + fun onRoomAliasesClicked() } private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) @@ -115,7 +115,7 @@ class RoomSettingsController @Inject constructor( dividerColor = dividerColor, divider = true, editable = true, - action = { callback?.onOpenAlias() } + action = { callback?.onRoomAliasesClicked() } ) buildProfileAction( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 01ee1cf9b8..7bfbb73909 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -168,8 +168,8 @@ class RoomSettingsFragment @Inject constructor( return@withState } - override fun onOpenAlias() { - roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAlias) + override fun onRoomAliasesClicked() { + roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings) } override fun onImageReady(uri: Uri?) { From b2556cb29360312ae63fd9431470974fffe8ebe1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 11:00:58 +0100 Subject: [PATCH 44/73] Update change after release --- CHANGES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 30713deb18..0e3513965f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ Changes in Element 1.0.12 (2020-XX-XX) =================================================== Features ✨: - - + - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428) Improvements 🙌: - @@ -32,7 +32,6 @@ Features ✨: - Create DMs with users by scanning their QR code (#2025) - Add Invite friends quick invite actions (#2348) - Add friend by scanning QR code, show your code to friends (#2025) - - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428) Improvements 🙌: - New room creation tile with quick action (#2346) From 694397efc18eaf38118328db25cc7329d70209d5 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 30 Nov 2020 13:51:40 +0300 Subject: [PATCH 45/73] Create a new pin mode for changing pin code. --- vector/src/main/java/im/vector/app/features/pin/PinFragment.kt | 1 + vector/src/main/java/im/vector/app/features/pin/PinMode.kt | 3 ++- .../vector/app/features/settings/VectorSettingsPinFragment.kt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt index b6e238c2dc..378c7b853d 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt @@ -56,6 +56,7 @@ class PinFragment @Inject constructor( when (fragmentArgs.pinMode) { PinMode.CREATE -> showCreateFragment() PinMode.AUTH -> showAuthFragment() + PinMode.MODIFY -> showCreateFragment() // No need to create another function for now because texts are generic } } diff --git a/vector/src/main/java/im/vector/app/features/pin/PinMode.kt b/vector/src/main/java/im/vector/app/features/pin/PinMode.kt index c24ac5adf2..9801912bd6 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinMode.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinMode.kt @@ -18,5 +18,6 @@ package im.vector.app.features.pin enum class PinMode { CREATE, - AUTH + AUTH, + MODIFY } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt index 94328dc44a..1a04dab950 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPinFragment.kt @@ -85,7 +85,7 @@ class VectorSettingsPinFragment @Inject constructor( navigator.openPinCode( requireContext(), pinActivityResultLauncher, - PinMode.CREATE + PinMode.MODIFY ) } true From 096abd7ebf76b171d2be5fbdf8b30474894b1ae3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 13:33:29 +0100 Subject: [PATCH 46/73] Fix IllegalStateException: focus search returned a view that wasn't able to take focus! And no multiline input for room alias --- .../app/features/roomprofile/alias/RoomAliasController.kt | 2 ++ vector/src/main/res/layout/item_form_text_input.xml | 2 ++ vector/src/main/res/layout/item_room_alias_text_input.xml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index 49bccd79db..0b695031c5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -16,6 +16,7 @@ package im.vector.app.features.roomprofile.alias +import android.text.InputType import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading @@ -169,6 +170,7 @@ class RoomAliasController @Inject constructor( value(data.publishManuallyState.value) showBottomSeparator(false) hint(stringProvider.getString(R.string.room_alias_address_hint)) + inputType(InputType.TYPE_CLASS_TEXT) onTextChange { text -> callback?.setNewAlias(text) } diff --git a/vector/src/main/res/layout/item_form_text_input.xml b/vector/src/main/res/layout/item_form_text_input.xml index 594bfc1788..f7ce8e1c9f 100644 --- a/vector/src/main/res/layout/item_form_text_input.xml +++ b/vector/src/main/res/layout/item_form_text_input.xml @@ -20,10 +20,12 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> + diff --git a/vector/src/main/res/layout/item_room_alias_text_input.xml b/vector/src/main/res/layout/item_room_alias_text_input.xml index 9216fc6b7e..fd7a99f0f0 100644 --- a/vector/src/main/res/layout/item_room_alias_text_input.xml +++ b/vector/src/main/res/layout/item_room_alias_text_input.xml @@ -30,11 +30,13 @@ app:layout_constraintStart_toEndOf="@+id/itemRoomAliasHash" app:layout_constraintTop_toTopOf="parent"> + From 2736247d09db42681e25129a6fda0570d3a25cac Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 30 Nov 2020 18:12:26 +0300 Subject: [PATCH 47/73] Move cursor to the end to fix the jumping cursor bug. Fixes #2469 --- CHANGES.md | 1 + .../main/java/im/vector/app/features/form/FormEditTextItem.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 83d53b75a7..fe93df3a62 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ Improvements 🙌: Bugfix 🐛: - Double bottomsheet effect after verify with passphrase + - EditText cursor jumps to the start while typing fast (#2469) Translations 🗣: - diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 12538d314a..eb8945d29c 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -67,6 +67,8 @@ abstract class FormEditTextItem : VectorEpoxyModel() { // Update only if text is different and value is not null if (value != null && holder.textInputEditText.text.toString() != value) { holder.textInputEditText.setText(value) + // To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426 + holder.textInputEditText.setSelection(value?.length ?: 0) } holder.textInputEditText.isEnabled = enabled inputType?.let { holder.textInputEditText.inputType = it } From d07a95204b2cb0230588cfbc45258411cd1c2301 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 17:13:56 +0100 Subject: [PATCH 48/73] Better name --- .../features/roomprofile/settings/RoomSettingsController.kt | 4 ++-- .../features/roomprofile/settings/RoomSettingsViewModel.kt | 2 +- .../features/roomprofile/settings/RoomSettingsViewState.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index ffae4a9dc5..d5fe3ff720 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -124,8 +124,8 @@ class RoomSettingsController @Inject constructor( subtitle = newHistoryVisibility ?: historyVisibility, dividerColor = dividerColor, divider = false, - editable = data.actionPermissions.canChangeHistoryReadability, - action = { if (data.actionPermissions.canChangeHistoryReadability) callback?.onHistoryVisibilityClicked() } + editable = data.actionPermissions.canChangeHistoryVisibility, + action = { if (data.actionPermissions.canChangeHistoryVisibility) callback?.onHistoryVisibilityClicked() } ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 7e76f85d44..521cc7a662 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -109,7 +109,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR), canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), - canChangeHistoryReadability = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, + canChangeHistoryVisibility = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_HISTORY_VISIBILITY) ) setState { copy(actionPermissions = permissions) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index bdcd9e6bc7..7a98201680 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -45,7 +45,7 @@ data class RoomSettingsViewState( val canChangeAvatar: Boolean = false, val canChangeName: Boolean = false, val canChangeTopic: Boolean = false, - val canChangeHistoryReadability: Boolean = false + val canChangeHistoryVisibility: Boolean = false ) sealed class AvatarAction { From bb5d5ffc92bd982629151d2f68f5503dba3bb84d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 17:35:58 +0100 Subject: [PATCH 49/73] Improve room history visibility setting UX (#1579) And observe correctly the state event --- CHANGES.md | 1 + .../settings/RoomSettingsController.kt | 14 +++----------- .../settings/RoomSettingsFragment.kt | 3 +-- .../settings/RoomSettingsViewModel.kt | 18 ++++++++++++++++-- .../settings/RoomSettingsViewState.kt | 4 ++-- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 83d53b75a7..5e4cd70194 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -43,6 +43,7 @@ Improvements 🙌: - Move "Enable Encryption" from room setting screen to room profile screen (#2394) - Home empty screens quick design update (#2347) - Improve Invite user screen (seamless search for matrix ID) + - Improve room history visibility setting UX (#1579) Bugfix 🐛: - Fix crash on AttachmentViewer (#2365) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index d5fe3ff720..c3c74f0d7c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -26,9 +26,6 @@ import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableAvatarItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -60,9 +57,6 @@ class RoomSettingsController @Inject constructor( override fun buildModels(data: RoomSettingsViewState?) { val roomSummary = data?.roomSummary?.invoke() ?: return - val historyVisibility = data.historyVisibilityEvent?.let { formatRoomHistoryVisibilityEvent(it) } ?: "" - val newHistoryVisibility = data.newHistoryVisibility?.let { roomHistoryVisibilityFormatter.format(it) } - formEditableAvatarItem { id("avatar") enabled(data.actionPermissions.canChangeAvatar) @@ -118,6 +112,9 @@ class RoomSettingsController @Inject constructor( action = { callback?.onRoomAliasesClicked() } ) + val historyVisibility = roomHistoryVisibilityFormatter.format(data.currentHistoryVisibility) + val newHistoryVisibility = data.newHistoryVisibility?.let { roomHistoryVisibilityFormatter.format(it) } + buildProfileAction( id = "historyReadability", title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title), @@ -128,9 +125,4 @@ class RoomSettingsController @Inject constructor( action = { if (data.actionPermissions.canChangeHistoryVisibility) callback?.onHistoryVisibilityClicked() } ) } - - private fun formatRoomHistoryVisibilityEvent(event: Event): String? { - val historyVisibility = event.getClearContent().toModel()?.historyVisibility ?: return null - return roomHistoryVisibilityFormatter.format(historyVisibility) - } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 7bfbb73909..785368b2ff 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -147,8 +147,7 @@ class RoomSettingsFragment @Inject constructor( RoomHistoryVisibility.JOINED, RoomHistoryVisibility.WORLD_READABLE ) - val currentHistoryVisibility = - state.newHistoryVisibility ?: state.historyVisibilityEvent?.getClearContent().toModel()?.historyVisibility + val currentHistoryVisibility = state.newHistoryVisibility ?: state.currentHistoryVisibility val currentHistoryVisibilityIndex = historyVisibilities.indexOf(currentHistoryVisibility) AlertDialog.Builder(requireContext()).apply { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 521cc7a662..699e8ef6e2 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.rx.mapOptional import org.matrix.android.sdk.rx.rx @@ -60,6 +61,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: init { observeRoomSummary() + observeRoomHistoryVisibility() observeRoomAvatar() observeState() } @@ -81,7 +83,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: showSaveAction = avatarAction !is RoomSettingsViewState.AvatarAction.None || summary?.name != newName || summary?.topic != newTopic - || newHistoryVisibility != null + || (newHistoryVisibility != null && newHistoryVisibility != currentHistoryVisibility) ) } } @@ -93,7 +95,6 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: .execute { async -> val roomSummary = async.invoke() copy( - historyVisibilityEvent = room.getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY), roomSummary = async, newName = roomSummary?.name, newTopic = roomSummary?.topic @@ -117,6 +118,19 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: .disposeOnClear() } + private fun observeRoomHistoryVisibility() { + room.rx() + .liveStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + .subscribe { + it.historyVisibility?.let { + setState { copy(currentHistoryVisibility = it) } + } + } + .disposeOnClear() + } + /** * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index 7a98201680..fc9393d141 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -21,13 +21,13 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs -import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomSummary data class RoomSettingsViewState( val roomId: String, - val historyVisibilityEvent: Event? = null, + // Default value: https://matrix.org/docs/spec/client_server/r0.6.1#id88 + val currentHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.SHARED, val roomSummary: Async = Uninitialized, val isLoading: Boolean = false, val currentRoomAvatarUrl: String? = null, From 4171311095431da036ea2f18f9916889fa9ad361 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 19:03:49 +0100 Subject: [PATCH 50/73] Room history visibility: from ugly dialog to nice bottom sheet. Including a reusable bottom sheet pattern, for future join rules setting bottom sheet --- .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../im/vector/app/core/di/ViewModelModule.kt | 6 ++ .../core/ui/bottomsheet/BottomSheetGeneric.kt | 61 ++++++++++++++++ .../bottomsheet/BottomSheetGenericAction.kt | 43 +++++++++++ .../BottomSheetGenericController.kt | 43 +++++++++++ ...BottomSheetGenericSharedActionViewModel.kt | 25 +++++++ .../ui/bottomsheet/BottomSheetGenericState.kt | 21 ++++++ .../format/RoomHistoryVisibilityFormatter.kt | 14 ++-- .../settings/RoomSettingsFragment.kt | 48 +++++-------- .../BottomSheetRoomHistoryVisibilityAction.kt | 35 +++++++++ .../RoomHistoryVisibilityBottomSheet.kt | 71 +++++++++++++++++++ .../RoomHistoryVisibilityController.kt | 45 ++++++++++++ .../RoomHistoryVisibilityState.kt | 27 +++++++ .../RoomHistoryVisibilityViewModel.kt | 47 ++++++++++++ ...mHistoryVisibilitySharedActionViewModel.kt | 23 ++++++ 15 files changed, 477 insertions(+), 34 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericAction.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityState.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index 2518e32ce5..92a7c77aa9 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -68,6 +68,7 @@ import im.vector.app.features.roommemberprofile.RoomMemberProfileActivity import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roomprofile.RoomProfileActivity import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet +import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet import im.vector.app.features.share.IncomingShareActivity @@ -155,6 +156,7 @@ interface ScreenComponent { fun inject(bottomSheet: DisplayReadReceiptsBottomSheet) fun inject(bottomSheet: RoomListQuickActionsBottomSheet) fun inject(bottomSheet: RoomAliasBottomSheet) + fun inject(bottomSheet: RoomHistoryVisibilityBottomSheet) fun inject(bottomSheet: VerificationBottomSheet) fun inject(bottomSheet: DeviceVerificationInfoBottomSheet) fun inject(bottomSheet: DeviceListBottomSheet) diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt index 3399f98d43..5ef4ccbf3a 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt @@ -36,6 +36,7 @@ import im.vector.app.features.reactions.EmojiChooserViewModel import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel +import im.vector.app.features.roomprofile.settings.historyvisibility.SetRoomHistoryVisibilitySharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel @Module @@ -111,6 +112,11 @@ interface ViewModelModule { @ViewModelKey(RoomAliasBottomSheetSharedActionViewModel::class) fun bindRoomAliasBottomSheetSharedActionViewModel(viewModel: RoomAliasBottomSheetSharedActionViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(SetRoomHistoryVisibilitySharedActionViewModel::class) + fun bindSetRoomHistoryVisibilitySharedActionViewModel(viewModel: SetRoomHistoryVisibilitySharedActionViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RoomDirectorySharedActionViewModel::class) diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt new file mode 100644 index 0000000000..d2e1495f1b --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt @@ -0,0 +1,61 @@ +/* + * 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.app.core.ui.bottomsheet + +import android.os.Bundle +import android.view.View +import androidx.annotation.CallSuper +import androidx.recyclerview.widget.RecyclerView +import butterknife.BindView +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import javax.inject.Inject + +/** + * Generic Bottom sheet with actions + */ +abstract class BottomSheetGeneric : + VectorBaseBottomSheetDialogFragment(), + BottomSheetGenericController.Listener { + + @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool + + @BindView(R.id.bottomSheetRecyclerView) + lateinit var recyclerView: RecyclerView + + final override val showExpanded = true + + final override fun getLayoutResId() = R.layout.bottom_sheet_generic_list + + abstract fun getController(): BottomSheetGenericController + + @CallSuper + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + recyclerView.configureWith(getController(), viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) + getController().listener = this + } + + @CallSuper + override fun onDestroyView() { + recyclerView.cleanup() + getController().listener = null + super.onDestroyView() + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericAction.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericAction.kt new file mode 100644 index 0000000000..50a04a8fe1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericAction.kt @@ -0,0 +1,43 @@ +/* + * 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.app.core.ui.bottomsheet + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import im.vector.app.core.epoxy.bottomsheet.BottomSheetActionItem_ +import im.vector.app.core.platform.VectorSharedAction + +/** + * Parent class for a bottom sheet action + */ +open class BottomSheetGenericAction( + @StringRes open val titleRes: Int, + @DrawableRes open val iconResId: Int, + open val isSelected: Boolean, + open val destructive: Boolean +) : VectorSharedAction { + + fun toBottomSheetItem(): BottomSheetActionItem_ { + return BottomSheetActionItem_().apply { + id("action_$titleRes") + iconRes(iconResId) + textRes(titleRes) + selected(isSelected) + destructive(destructive) + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt new file mode 100644 index 0000000000..2dfa4fc93a --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt @@ -0,0 +1,43 @@ +/* + * 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.app.core.ui.bottomsheet + +import android.view.View +import com.airbnb.epoxy.TypedEpoxyController + +/** + * Epoxy controller for generic bottom sheet actions + */ +abstract class BottomSheetGenericController + : TypedEpoxyController() { + + var listener: Listener? = null + + abstract fun getActions(state: State): List + + override fun buildModels(state: State?) { + state ?: return + getActions(state).forEach { action -> + action.toBottomSheetItem() + .listener(View.OnClickListener { listener?.didSelectAction(action) }) + .addTo(this) + } + } + + interface Listener { + fun didSelectAction(action: Action) + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt new file mode 100644 index 0000000000..ba8bd4abba --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt @@ -0,0 +1,25 @@ +/* + * 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.app.core.ui.bottomsheet + +import im.vector.app.core.platform.VectorSharedAction +import im.vector.app.core.platform.VectorSharedActionViewModel + +/** + * Activity shared view model to handle bottom sheet quick actions + */ +abstract class BottomSheetGenericSharedActionViewModel : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt new file mode 100644 index 0000000000..b01216afad --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt @@ -0,0 +1,21 @@ +/* + * 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.app.core.ui.bottomsheet + +import com.airbnb.mvrx.MvRxState + +abstract class BottomSheetGenericState : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt index 4563e6a6ed..e2dc3932b2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.format +import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility @@ -26,11 +27,16 @@ class RoomHistoryVisibilityFormatter @Inject constructor( ) { fun format(roomHistoryVisibility: RoomHistoryVisibility): String { + return stringProvider.getString(getStringResId(roomHistoryVisibility)) + } + + @StringRes + fun getStringResId(roomHistoryVisibility: RoomHistoryVisibility): Int { return when (roomHistoryVisibility) { - RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared) - RoomHistoryVisibility.INVITED -> stringProvider.getString(R.string.notice_room_visibility_invited) - RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined) - RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable) + RoomHistoryVisibility.SHARED -> R.string.notice_room_visibility_shared + RoomHistoryVisibility.INVITED -> R.string.notice_room_visibility_invited + RoomHistoryVisibility.JOINED -> R.string.notice_room_visibility_joined + RoomHistoryVisibility.WORLD_READABLE -> R.string.notice_room_visibility_world_readable } } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 785368b2ff..4c51186623 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -37,15 +37,13 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.toast import im.vector.app.features.home.AvatarRenderer -import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.RoomProfileSharedAction import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel +import im.vector.app.features.roomprofile.settings.historyvisibility.SetRoomHistoryVisibilitySharedActionViewModel +import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.util.toMatrixItem import java.util.UUID import javax.inject.Inject @@ -53,7 +51,6 @@ import javax.inject.Inject class RoomSettingsFragment @Inject constructor( val viewModelFactory: RoomSettingsViewModel.Factory, private val controller: RoomSettingsController, - private val roomHistoryVisibilityFormatter: RoomHistoryVisibilityFormatter, colorProvider: ColorProvider, private val avatarRenderer: AvatarRenderer ) : @@ -64,6 +61,7 @@ class RoomSettingsFragment @Inject constructor( private val viewModel: RoomSettingsViewModel by fragmentViewModel() private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel + private lateinit var sharedActionViewModel: SetRoomHistoryVisibilitySharedActionViewModel private val roomProfileArgs: RoomProfileArgs by args() private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) @@ -74,6 +72,7 @@ class RoomSettingsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) + setupRoomHistoryVisibilitySharedViewModel() controller.callback = this setupToolbar(roomSettingsToolbar) roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) @@ -92,6 +91,16 @@ class RoomSettingsFragment @Inject constructor( } } + private fun setupRoomHistoryVisibilitySharedViewModel() { + sharedActionViewModel = activityViewModelProvider.get(SetRoomHistoryVisibilitySharedActionViewModel::class.java) + sharedActionViewModel + .observe() + .subscribe { action -> + viewModel.handle(RoomSettingsAction.SetRoomHistoryVisibility(action.roomHistoryVisibility)) + } + .disposeOnDestroyView() + } + private fun showSuccess() { activity?.toast(R.string.room_settings_save_success) } @@ -141,30 +150,9 @@ class RoomSettingsFragment @Inject constructor( } override fun onHistoryVisibilityClicked() = withState(viewModel) { state -> - val historyVisibilities = arrayOf( - RoomHistoryVisibility.SHARED, - RoomHistoryVisibility.INVITED, - RoomHistoryVisibility.JOINED, - RoomHistoryVisibility.WORLD_READABLE - ) val currentHistoryVisibility = state.newHistoryVisibility ?: state.currentHistoryVisibility - val currentHistoryVisibilityIndex = historyVisibilities.indexOf(currentHistoryVisibility) - - AlertDialog.Builder(requireContext()).apply { - setTitle(R.string.room_settings_room_read_history_rules_pref_title) - setSingleChoiceItems( - historyVisibilities - .map { roomHistoryVisibilityFormatter.format(it) } - .toTypedArray(), - currentHistoryVisibilityIndex) { dialog, which -> - if (which != currentHistoryVisibilityIndex) { - viewModel.handle(RoomSettingsAction.SetRoomHistoryVisibility(historyVisibilities[which])) - } - dialog.cancel() - } - show() - } - return@withState + RoomHistoryVisibilityBottomSheet.newInstance(currentHistoryVisibility) + .show(childFragmentManager, "RoomHistoryVisibilityBottomSheet") } override fun onRoomAliasesClicked() { @@ -185,10 +173,10 @@ class RoomSettingsFragment @Inject constructor( override fun onAvatarDelete() { withState(viewModel) { when (it.avatarAction) { - RoomSettingsViewState.AvatarAction.None -> { + RoomSettingsViewState.AvatarAction.None -> { viewModel.handle(RoomSettingsAction.SetAvatarAction(RoomSettingsViewState.AvatarAction.DeleteAvatar)) } - RoomSettingsViewState.AvatarAction.DeleteAvatar -> { + RoomSettingsViewState.AvatarAction.DeleteAvatar -> { /* Should not happen */ Unit } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt new file mode 100644 index 0000000000..45e7188952 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.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.app.features.roomprofile.settings.historyvisibility + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericAction +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility + +class BottomSheetRoomHistoryVisibilityAction( + val roomHistoryVisibility: RoomHistoryVisibility, + @StringRes titleRes: Int, + @DrawableRes iconResId: Int, + isSelected: Boolean, + destructive: Boolean +) : BottomSheetGenericAction( + titleRes = titleRes, + iconResId = iconResId, + isSelected = isSelected, + destructive = destructive +) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt new file mode 100644 index 0000000000..bf837516ad --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt @@ -0,0 +1,71 @@ +/* + * 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.app.features.roomprofile.settings.historyvisibility + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.ui.bottomsheet.BottomSheetGeneric +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController +import kotlinx.android.parcel.Parcelize +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import javax.inject.Inject + +@Parcelize +data class RoomHistoryVisibilityBottomSheetArgs( + val currentRoomHistoryVisibility: RoomHistoryVisibility +) : Parcelable + +class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric() { + + private lateinit var roomHistoryVisibilitySharedActionViewModel: SetRoomHistoryVisibilitySharedActionViewModel + @Inject lateinit var controller: RoomHistoryVisibilityController + @Inject lateinit var roomHistoryVisibilityViewModelFactory: RoomHistoryVisibilityViewModel.Factory + private val viewModel: RoomHistoryVisibilityViewModel by fragmentViewModel(RoomHistoryVisibilityViewModel::class) + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override fun getController(): BottomSheetGenericController = controller + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + roomHistoryVisibilitySharedActionViewModel = activityViewModelProvider.get(SetRoomHistoryVisibilitySharedActionViewModel::class.java) + } + + override fun didSelectAction(action: BottomSheetRoomHistoryVisibilityAction) { + roomHistoryVisibilitySharedActionViewModel.post(action) + dismiss() + } + + override fun invalidate() = withState(viewModel) { + controller.setData(it) + super.invalidate() + } + + companion object { + fun newInstance(currentRoomHistoryVisibility: RoomHistoryVisibility): RoomHistoryVisibilityBottomSheet { + return RoomHistoryVisibilityBottomSheet().apply { + setArguments(RoomHistoryVisibilityBottomSheetArgs(currentRoomHistoryVisibility)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt new file mode 100644 index 0000000000..bdb5f80198 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -0,0 +1,45 @@ +/* + * 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.app.features.roomprofile.settings.historyvisibility + +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController +import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import javax.inject.Inject + +class RoomHistoryVisibilityController @Inject constructor( + private val historyVisibilityFormatter: RoomHistoryVisibilityFormatter +) : BottomSheetGenericController() { + + override fun getActions(state: RoomHistoryVisibilityState): List { + return listOf( + RoomHistoryVisibility.SHARED, + RoomHistoryVisibility.INVITED, + RoomHistoryVisibility.JOINED, + RoomHistoryVisibility.WORLD_READABLE + ) + .map { roomHistoryVisibility -> + BottomSheetRoomHistoryVisibilityAction( + roomHistoryVisibility = roomHistoryVisibility, + titleRes = historyVisibilityFormatter.getStringResId(roomHistoryVisibility), + iconResId = 0, + isSelected = roomHistoryVisibility == state.currentRoomHistoryVisibility, + destructive = false + ) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityState.kt new file mode 100644 index 0000000000..0b651d5664 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityState.kt @@ -0,0 +1,27 @@ +/* + * 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.app.features.roomprofile.settings.historyvisibility + +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericState +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility + +data class RoomHistoryVisibilityState( + val currentRoomHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.SHARED +) : BottomSheetGenericState() { + + constructor(args: RoomHistoryVisibilityBottomSheetArgs) : this(currentRoomHistoryVisibility = args.currentRoomHistoryVisibility) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt new file mode 100644 index 0000000000..ce11f7b235 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt @@ -0,0 +1,47 @@ +/* + * 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.app.features.roomprofile.settings.historyvisibility + +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.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel + +class RoomHistoryVisibilityViewModel @AssistedInject constructor(@Assisted initialState: RoomHistoryVisibilityState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomHistoryVisibilityState): RoomHistoryVisibilityViewModel + } + + companion object : MvRxViewModelFactory { + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomHistoryVisibilityState): RoomHistoryVisibilityViewModel? { + val fragment: RoomHistoryVisibilityBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.roomHistoryVisibilityViewModelFactory.create(state) + } + } + + override fun handle(action: EmptyAction) { + // No op + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt new file mode 100644 index 0000000000..7f447f2384 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt @@ -0,0 +1,23 @@ +/* + * 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.app.features.roomprofile.settings.historyvisibility + +import im.vector.app.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +class SetRoomHistoryVisibilitySharedActionViewModel @Inject constructor() + : VectorSharedActionViewModel() From 637c54073ac52f0ae5286fc2d322699af1c18b19 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 20:49:23 +0100 Subject: [PATCH 51/73] Room history visibility: Use correct wording for the setting. --- .../bottomsheet/BottomSheetActionItem.kt | 11 ++++++++- .../bottomsheet/BottomSheetGenericAction.kt | 7 +++--- .../timeline/format/NoticeEventFormatter.kt | 6 ++--- .../format/RoomHistoryVisibilityFormatter.kt | 24 ++++++++++--------- .../settings/RoomSettingsController.kt | 5 +--- .../BottomSheetRoomHistoryVisibilityAction.kt | 5 ++-- .../RoomHistoryVisibilityController.kt | 2 +- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt index e28bec6874..3666cabce3 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt @@ -21,6 +21,7 @@ import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.isInvisible @@ -43,6 +44,10 @@ abstract class BottomSheetActionItem : VectorEpoxyModel()?.historyVisibility ?: return null - val formattedVisibility = roomHistoryVisibilityFormatter.format(historyVisibility) + val historyVisibilitySuffix = roomHistoryVisibilityFormatter.getNoticeSuffix(historyVisibility) return if (event.isSentByCurrentUser()) { sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility_by_you else R.string.notice_made_future_room_visibility_by_you, - formattedVisibility) + historyVisibilitySuffix) } else { sp.getString(if (rs.isDm()) R.string.notice_made_future_direct_room_visibility else R.string.notice_made_future_room_visibility, - senderName, formattedVisibility) + senderName, historyVisibilitySuffix) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt index e2dc3932b2..14769bc95b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/RoomHistoryVisibilityFormatter.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail.timeline.format -import androidx.annotation.StringRes import im.vector.app.R import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility @@ -25,18 +24,21 @@ import javax.inject.Inject class RoomHistoryVisibilityFormatter @Inject constructor( private val stringProvider: StringProvider ) { - - fun format(roomHistoryVisibility: RoomHistoryVisibility): String { - return stringProvider.getString(getStringResId(roomHistoryVisibility)) - } - - @StringRes - fun getStringResId(roomHistoryVisibility: RoomHistoryVisibility): Int { - return when (roomHistoryVisibility) { + fun getNoticeSuffix(roomHistoryVisibility: RoomHistoryVisibility): String { + return stringProvider.getString(when (roomHistoryVisibility) { + RoomHistoryVisibility.WORLD_READABLE -> R.string.notice_room_visibility_world_readable RoomHistoryVisibility.SHARED -> R.string.notice_room_visibility_shared RoomHistoryVisibility.INVITED -> R.string.notice_room_visibility_invited RoomHistoryVisibility.JOINED -> R.string.notice_room_visibility_joined - RoomHistoryVisibility.WORLD_READABLE -> R.string.notice_room_visibility_world_readable - } + }) + } + + fun getSetting(roomHistoryVisibility: RoomHistoryVisibility): String { + return stringProvider.getString(when (roomHistoryVisibility) { + RoomHistoryVisibility.WORLD_READABLE -> R.string.room_settings_read_history_entry_anyone + RoomHistoryVisibility.SHARED -> R.string.room_settings_read_history_entry_members_only_option_time_shared + RoomHistoryVisibility.INVITED -> R.string.room_settings_read_history_entry_members_only_invited + RoomHistoryVisibility.JOINED -> R.string.room_settings_read_history_entry_members_only_joined + }) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index c3c74f0d7c..96ae07e0e9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -112,13 +112,10 @@ class RoomSettingsController @Inject constructor( action = { callback?.onRoomAliasesClicked() } ) - val historyVisibility = roomHistoryVisibilityFormatter.format(data.currentHistoryVisibility) - val newHistoryVisibility = data.newHistoryVisibility?.let { roomHistoryVisibilityFormatter.format(it) } - buildProfileAction( id = "historyReadability", title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title), - subtitle = newHistoryVisibility ?: historyVisibility, + subtitle = roomHistoryVisibilityFormatter.getSetting(data.newHistoryVisibility ?: data.currentHistoryVisibility), dividerColor = dividerColor, divider = false, editable = data.actionPermissions.canChangeHistoryVisibility, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt index 45e7188952..9fc27070b3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt @@ -17,18 +17,17 @@ package im.vector.app.features.roomprofile.settings.historyvisibility import androidx.annotation.DrawableRes -import androidx.annotation.StringRes import im.vector.app.core.ui.bottomsheet.BottomSheetGenericAction import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility class BottomSheetRoomHistoryVisibilityAction( val roomHistoryVisibility: RoomHistoryVisibility, - @StringRes titleRes: Int, + title: String, @DrawableRes iconResId: Int, isSelected: Boolean, destructive: Boolean ) : BottomSheetGenericAction( - titleRes = titleRes, + title = title, iconResId = iconResId, isSelected = isSelected, destructive = destructive diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt index bdb5f80198..34b9f8571c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -35,7 +35,7 @@ class RoomHistoryVisibilityController @Inject constructor( .map { roomHistoryVisibility -> BottomSheetRoomHistoryVisibilityAction( roomHistoryVisibility = roomHistoryVisibility, - titleRes = historyVisibilityFormatter.getStringResId(roomHistoryVisibility), + title = historyVisibilityFormatter.getSetting(roomHistoryVisibility), iconResId = 0, isSelected = roomHistoryVisibility == state.currentRoomHistoryVisibility, destructive = false From 63b068f426a56c7c02b8d0274878c4e2d5d24e31 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 20:52:46 +0100 Subject: [PATCH 52/73] Reorder iso Element Web --- .../historyvisibility/RoomHistoryVisibilityController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt index 34b9f8571c..edfe6b34ac 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -27,10 +27,10 @@ class RoomHistoryVisibilityController @Inject constructor( override fun getActions(state: RoomHistoryVisibilityState): List { return listOf( + RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibility.SHARED, RoomHistoryVisibility.INVITED, - RoomHistoryVisibility.JOINED, - RoomHistoryVisibility.WORLD_READABLE + RoomHistoryVisibility.JOINED ) .map { roomHistoryVisibility -> BottomSheetRoomHistoryVisibilityAction( From 41dd67f1c1bb373e09381b1aa2c0d50585180bae Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 20:58:41 +0100 Subject: [PATCH 53/73] Renaming --- .../java/im/vector/app/core/di/ViewModelModule.kt | 6 +++--- .../roomprofile/settings/RoomSettingsFragment.kt | 12 ++++++------ ...ilityAction.kt => RoomHistoryVisibilityAction.kt} | 2 +- .../RoomHistoryVisibilityBottomSheet.kt | 10 +++++----- .../RoomHistoryVisibilityController.kt | 6 +++--- ...=> RoomHistoryVisibilitySharedActionViewModel.kt} | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) rename vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/{BottomSheetRoomHistoryVisibilityAction.kt => RoomHistoryVisibilityAction.kt} (96%) rename vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/{SetRoomHistoryVisibilitySharedActionViewModel.kt => RoomHistoryVisibilitySharedActionViewModel.kt} (83%) diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt index 5ef4ccbf3a..6751cf5c97 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt @@ -36,7 +36,7 @@ import im.vector.app.features.reactions.EmojiChooserViewModel import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel -import im.vector.app.features.roomprofile.settings.historyvisibility.SetRoomHistoryVisibilitySharedActionViewModel +import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel @Module @@ -114,8 +114,8 @@ interface ViewModelModule { @Binds @IntoMap - @ViewModelKey(SetRoomHistoryVisibilitySharedActionViewModel::class) - fun bindSetRoomHistoryVisibilitySharedActionViewModel(viewModel: SetRoomHistoryVisibilitySharedActionViewModel): ViewModel + @ViewModelKey(RoomHistoryVisibilitySharedActionViewModel::class) + fun bindRoomHistoryVisibilitySharedActionViewModel(viewModel: RoomHistoryVisibilitySharedActionViewModel): ViewModel @Binds @IntoMap diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 4c51186623..5dbf8470df 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -40,7 +40,7 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.RoomProfileSharedAction import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel -import im.vector.app.features.roomprofile.settings.historyvisibility.SetRoomHistoryVisibilitySharedActionViewModel +import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* @@ -61,7 +61,7 @@ class RoomSettingsFragment @Inject constructor( private val viewModel: RoomSettingsViewModel by fragmentViewModel() private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel - private lateinit var sharedActionViewModel: SetRoomHistoryVisibilitySharedActionViewModel + private lateinit var roomHistoryVisibilitySharedActionViewModel: RoomHistoryVisibilitySharedActionViewModel private val roomProfileArgs: RoomProfileArgs by args() private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) @@ -72,7 +72,7 @@ class RoomSettingsFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) - setupRoomHistoryVisibilitySharedViewModel() + setupRoomHistoryVisibilitySharedActionViewModel() controller.callback = this setupToolbar(roomSettingsToolbar) roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) @@ -91,9 +91,9 @@ class RoomSettingsFragment @Inject constructor( } } - private fun setupRoomHistoryVisibilitySharedViewModel() { - sharedActionViewModel = activityViewModelProvider.get(SetRoomHistoryVisibilitySharedActionViewModel::class.java) - sharedActionViewModel + private fun setupRoomHistoryVisibilitySharedActionViewModel() { + roomHistoryVisibilitySharedActionViewModel = activityViewModelProvider.get(RoomHistoryVisibilitySharedActionViewModel::class.java) + roomHistoryVisibilitySharedActionViewModel .observe() .subscribe { action -> viewModel.handle(RoomSettingsAction.SetRoomHistoryVisibility(action.roomHistoryVisibility)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt similarity index 96% rename from vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt rename to vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt index 9fc27070b3..f37964ae23 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/BottomSheetRoomHistoryVisibilityAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt @@ -20,7 +20,7 @@ import androidx.annotation.DrawableRes import im.vector.app.core.ui.bottomsheet.BottomSheetGenericAction import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility -class BottomSheetRoomHistoryVisibilityAction( +class RoomHistoryVisibilityAction( val roomHistoryVisibility: RoomHistoryVisibility, title: String, @DrawableRes iconResId: Int, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt index bf837516ad..813a58838c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt @@ -33,9 +33,9 @@ data class RoomHistoryVisibilityBottomSheetArgs( val currentRoomHistoryVisibility: RoomHistoryVisibility ) : Parcelable -class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric() { +class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric() { - private lateinit var roomHistoryVisibilitySharedActionViewModel: SetRoomHistoryVisibilitySharedActionViewModel + private lateinit var roomHistoryVisibilitySharedActionViewModel: RoomHistoryVisibilitySharedActionViewModel @Inject lateinit var controller: RoomHistoryVisibilityController @Inject lateinit var roomHistoryVisibilityViewModelFactory: RoomHistoryVisibilityViewModel.Factory private val viewModel: RoomHistoryVisibilityViewModel by fragmentViewModel(RoomHistoryVisibilityViewModel::class) @@ -44,14 +44,14 @@ class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric = controller + override fun getController(): BottomSheetGenericController = controller override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - roomHistoryVisibilitySharedActionViewModel = activityViewModelProvider.get(SetRoomHistoryVisibilitySharedActionViewModel::class.java) + roomHistoryVisibilitySharedActionViewModel = activityViewModelProvider.get(RoomHistoryVisibilitySharedActionViewModel::class.java) } - override fun didSelectAction(action: BottomSheetRoomHistoryVisibilityAction) { + override fun didSelectAction(action: RoomHistoryVisibilityAction) { roomHistoryVisibilitySharedActionViewModel.post(action) dismiss() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt index edfe6b34ac..9c9abc03a1 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -23,9 +23,9 @@ import javax.inject.Inject class RoomHistoryVisibilityController @Inject constructor( private val historyVisibilityFormatter: RoomHistoryVisibilityFormatter -) : BottomSheetGenericController() { +) : BottomSheetGenericController() { - override fun getActions(state: RoomHistoryVisibilityState): List { + override fun getActions(state: RoomHistoryVisibilityState): List { return listOf( RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibility.SHARED, @@ -33,7 +33,7 @@ class RoomHistoryVisibilityController @Inject constructor( RoomHistoryVisibility.JOINED ) .map { roomHistoryVisibility -> - BottomSheetRoomHistoryVisibilityAction( + RoomHistoryVisibilityAction( roomHistoryVisibility = roomHistoryVisibility, title = historyVisibilityFormatter.getSetting(roomHistoryVisibility), iconResId = 0, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt similarity index 83% rename from vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt rename to vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt index 7f447f2384..31c1c2631c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/SetRoomHistoryVisibilitySharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt @@ -19,5 +19,5 @@ package im.vector.app.features.roomprofile.settings.historyvisibility import im.vector.app.core.platform.VectorSharedActionViewModel import javax.inject.Inject -class SetRoomHistoryVisibilitySharedActionViewModel @Inject constructor() - : VectorSharedActionViewModel() +class RoomHistoryVisibilitySharedActionViewModel @Inject constructor() + : VectorSharedActionViewModel() From a813610c04da27090bbe89b973cc691582836e9f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 22:23:25 +0100 Subject: [PATCH 54/73] Room setting: update join rules and guest access (#2442) --- CHANGES.md | 1 + .../java/org/matrix/android/sdk/rx/RxRoom.kt | 6 ++ .../api/session/room/state/StateService.kt | 7 ++ .../session/room/state/DefaultStateService.kt | 29 ++++++++ .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../im/vector/app/core/di/ViewModelModule.kt | 6 ++ .../settings/RoomSettingsAction.kt | 4 + .../settings/RoomSettingsController.kt | 29 +++++++- .../settings/RoomSettingsFragment.kt | 22 ++++++ .../settings/RoomSettingsViewModel.kt | 56 +++++++++++++- .../settings/RoomSettingsViewState.kt | 15 +++- .../settings/joinrule/RoomJoinRuleAction.kt | 36 +++++++++ .../joinrule/RoomJoinRuleBottomSheet.kt | 73 +++++++++++++++++++ .../joinrule/RoomJoinRuleController.kt | 58 +++++++++++++++ .../RoomJoinRuleSharedActionViewModel.kt | 23 ++++++ .../settings/joinrule/RoomJoinRuleState.kt | 32 ++++++++ .../joinrule/RoomJoinRuleViewModel.kt | 47 ++++++++++++ vector/src/main/res/values/strings.xml | 1 + 18 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt create mode 100644 vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt diff --git a/CHANGES.md b/CHANGES.md index 5e4cd70194..86258ecc57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ Features ✨: - Create DMs with users by scanning their QR code (#2025) - Add Invite friends quick invite actions (#2348) - Add friend by scanning QR code, show your code to friends (#2025) + - Room setting: update join rules and guest access (#2442) Improvements 🙌: - New room creation tile with quick action (#2346) diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt index 826ada358b..bf4bcacc31 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxRoom.kt @@ -35,6 +35,8 @@ import org.matrix.android.sdk.api.util.toOptional import io.reactivex.Completable import io.reactivex.Observable import io.reactivex.Single +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules class RxRoom(private val room: Room) { @@ -131,6 +133,10 @@ class RxRoom(private val room: Room) { room.updateHistoryReadability(readability, it) } + fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?): Completable = completableBuilder { + room.updateJoinRule(joinRules, guestAccess, it) + } + fun updateAvatar(avatarUri: Uri, fileName: String): Completable = completableBuilder { room.updateAvatar(avatarUri, fileName, it) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt index 1d048f2459..74e3faf38a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt @@ -21,7 +21,9 @@ import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.Optional @@ -50,6 +52,11 @@ interface StateService { */ fun updateHistoryReadability(readability: RoomHistoryVisibility, callback: MatrixCallback): Cancelable + /** + * Update the join rule and/or the guest access + */ + fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback): Cancelable + /** * Update the avatar of the room */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 3d6e869607..6015d945c4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -25,8 +25,12 @@ import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomCanonicalAliasContent +import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules +import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.state.StateService import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.JsonDict @@ -133,6 +137,31 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private ) } + override fun updateJoinRule(joinRules: RoomJoinRules?, guestAccess: GuestAccess?, callback: MatrixCallback): Cancelable { + return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { + if (joinRules != null) { + awaitCallback { + sendStateEvent( + eventType = EventType.STATE_ROOM_JOIN_RULES, + body = RoomJoinRulesContent(joinRules).toContent(), + callback = it, + stateKey = null + ) + } + } + if (guestAccess != null) { + awaitCallback { + sendStateEvent( + eventType = EventType.STATE_ROOM_GUEST_ACCESS, + body = RoomGuestAccessContent(guestAccess).toContent(), + callback = it, + stateKey = null + ) + } + } + } + } + override fun updateAvatar(avatarUri: Uri, fileName: String, callback: MatrixCallback): Cancelable { return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) { val response = fileUploader.uploadFromUri(avatarUri, fileName, "image/jpeg") diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index 92a7c77aa9..f56a6a3d70 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -69,6 +69,7 @@ import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roomprofile.RoomProfileActivity import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet +import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet import im.vector.app.features.share.IncomingShareActivity @@ -157,6 +158,7 @@ interface ScreenComponent { fun inject(bottomSheet: RoomListQuickActionsBottomSheet) fun inject(bottomSheet: RoomAliasBottomSheet) fun inject(bottomSheet: RoomHistoryVisibilityBottomSheet) + fun inject(bottomSheet: RoomJoinRuleBottomSheet) fun inject(bottomSheet: VerificationBottomSheet) fun inject(bottomSheet: DeviceVerificationInfoBottomSheet) fun inject(bottomSheet: DeviceListBottomSheet) diff --git a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt index 6751cf5c97..bed2e0b850 100644 --- a/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/ViewModelModule.kt @@ -37,6 +37,7 @@ import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel +import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel @Module @@ -117,6 +118,11 @@ interface ViewModelModule { @ViewModelKey(RoomHistoryVisibilitySharedActionViewModel::class) fun bindRoomHistoryVisibilitySharedActionViewModel(viewModel: RoomHistoryVisibilitySharedActionViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(RoomJoinRuleSharedActionViewModel::class) + fun bindRoomJoinRuleSharedActionViewModel(viewModel: RoomJoinRuleSharedActionViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(RoomDirectorySharedActionViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt index f88a7cbfd5..867c605030 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsAction.kt @@ -17,13 +17,17 @@ package im.vector.app.features.roomprofile.settings import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules sealed class RoomSettingsAction : VectorViewModelAction { data class SetAvatarAction(val avatarAction: RoomSettingsViewState.AvatarAction) : RoomSettingsAction() data class SetRoomName(val newName: String) : RoomSettingsAction() data class SetRoomTopic(val newTopic: String) : RoomSettingsAction() data class SetRoomHistoryVisibility(val visibility: RoomHistoryVisibility) : RoomSettingsAction() + data class SetRoomJoinRule(val roomJoinRule: RoomJoinRules?, val roomGuestAccess: GuestAccess?) : RoomSettingsAction() + object Save : RoomSettingsAction() object Cancel : RoomSettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt index 96ae07e0e9..bf3c1f87f8 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsController.kt @@ -26,6 +26,8 @@ import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableAvatarItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject @@ -44,6 +46,7 @@ class RoomSettingsController @Inject constructor( fun onTopicChanged(topic: String) fun onHistoryVisibilityClicked() fun onRoomAliasesClicked() + fun onJoinRuleClicked() } private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) @@ -117,9 +120,33 @@ class RoomSettingsController @Inject constructor( title = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_title), subtitle = roomHistoryVisibilityFormatter.getSetting(data.newHistoryVisibility ?: data.currentHistoryVisibility), dividerColor = dividerColor, - divider = false, + divider = true, editable = data.actionPermissions.canChangeHistoryVisibility, action = { if (data.actionPermissions.canChangeHistoryVisibility) callback?.onHistoryVisibilityClicked() } ) + + buildProfileAction( + id = "joinRule", + title = stringProvider.getString(R.string.room_settings_room_access_title), + subtitle = data.getJoinRuleWording(), + dividerColor = dividerColor, + divider = false, + editable = data.actionPermissions.canChangeJoinRule, + action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } + ) + } + + private fun RoomSettingsViewState.getJoinRuleWording(): String { + val joinRule = newRoomJoinRules.newJoinRules ?: currentRoomJoinRules + val guestAccess = newRoomJoinRules.newGuestAccess ?: currentGuestAccess + return stringProvider.getString(if (joinRule == RoomJoinRules.INVITE) { + R.string.room_settings_room_access_entry_only_invited + } else { + if (guestAccess == GuestAccess.CanJoin) { + R.string.room_settings_room_access_entry_anyone_with_link_including_guest + } else { + R.string.room_settings_room_access_entry_anyone_with_link_apart_guest + } + }) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt index 5dbf8470df..d8c8c41936 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsFragment.kt @@ -42,6 +42,8 @@ import im.vector.app.features.roomprofile.RoomProfileSharedAction import im.vector.app.features.roomprofile.RoomProfileSharedActionViewModel import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilityBottomSheet +import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet +import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel import kotlinx.android.synthetic.main.fragment_room_setting_generic.* import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import org.matrix.android.sdk.api.util.toMatrixItem @@ -62,6 +64,8 @@ class RoomSettingsFragment @Inject constructor( private val viewModel: RoomSettingsViewModel by fragmentViewModel() private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private lateinit var roomHistoryVisibilitySharedActionViewModel: RoomHistoryVisibilitySharedActionViewModel + private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel + private val roomProfileArgs: RoomProfileArgs by args() private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) @@ -73,6 +77,7 @@ class RoomSettingsFragment @Inject constructor( super.onViewCreated(view, savedInstanceState) roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java) setupRoomHistoryVisibilitySharedActionViewModel() + setupRoomJoinRuleSharedActionViewModel() controller.callback = this setupToolbar(roomSettingsToolbar) roomSettingsRecyclerView.configureWith(controller, hasFixedSize = true) @@ -91,6 +96,16 @@ class RoomSettingsFragment @Inject constructor( } } + private fun setupRoomJoinRuleSharedActionViewModel() { + roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java) + roomJoinRuleSharedActionViewModel + .observe() + .subscribe { action -> + viewModel.handle(RoomSettingsAction.SetRoomJoinRule(action.roomJoinRule, action.roomGuestAccess)) + } + .disposeOnDestroyView() + } + private fun setupRoomHistoryVisibilitySharedActionViewModel() { roomHistoryVisibilitySharedActionViewModel = activityViewModelProvider.get(RoomHistoryVisibilitySharedActionViewModel::class.java) roomHistoryVisibilitySharedActionViewModel @@ -159,6 +174,13 @@ class RoomSettingsFragment @Inject constructor( roomProfileSharedActionViewModel.post(RoomProfileSharedAction.OpenRoomAliasesSettings) } + override fun onJoinRuleClicked() = withState(viewModel) { state -> + val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules + val currentGuestAccess = state.newRoomJoinRules.newGuestAccess ?: state.currentGuestAccess + RoomJoinRuleBottomSheet.newInstance(currentJoinRule, currentGuestAccess) + .show(childFragmentManager, "RoomJoinRuleBottomSheet") + } + override fun onImageReady(uri: Uri?) { uri ?: return viewModel.handle( diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 699e8ef6e2..48ff38f92e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -33,7 +33,9 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomAvatarContent +import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent +import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.rx.mapOptional import org.matrix.android.sdk.rx.rx @@ -62,6 +64,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: init { observeRoomSummary() observeRoomHistoryVisibility() + observeJoinRule() + observeGuestAccess() observeRoomAvatar() observeState() } @@ -72,10 +76,12 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState::newName, RoomSettingsViewState::newTopic, RoomSettingsViewState::newHistoryVisibility, + RoomSettingsViewState::newRoomJoinRules, RoomSettingsViewState::roomSummary) { avatarAction, newName, newTopic, newHistoryVisibility, + newJoinRule, asyncSummary -> val summary = asyncSummary() setState { @@ -84,6 +90,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: || summary?.name != newName || summary?.topic != newTopic || (newHistoryVisibility != null && newHistoryVisibility != currentHistoryVisibility) + || newJoinRule.hasChanged() ) } } @@ -111,7 +118,11 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME), canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC), canChangeHistoryVisibility = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, - EventType.STATE_ROOM_HISTORY_VISIBILITY) + EventType.STATE_ROOM_HISTORY_VISIBILITY), + canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, + EventType.STATE_ROOM_JOIN_RULES) + && powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, + EventType.STATE_ROOM_GUEST_ACCESS) ) setState { copy(actionPermissions = permissions) } } @@ -131,6 +142,32 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: .disposeOnClear() } + private fun observeGuestAccess() { + room.rx() + .liveStateEvent(EventType.STATE_ROOM_JOIN_RULES, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + .subscribe { + it.joinRules?.let { + setState { copy(currentRoomJoinRules = it) } + } + } + .disposeOnClear() + } + + private fun observeJoinRule() { + room.rx() + .liveStateEvent(EventType.STATE_ROOM_GUEST_ACCESS, QueryStringValue.NoCondition) + .mapOptional { it.content.toModel() } + .unwrap() + .subscribe { + it.guestAccess?.let { + setState { copy(currentGuestAccess = it) } + } + } + .disposeOnClear() + } + /** * We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar. */ @@ -151,11 +188,21 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: is RoomSettingsAction.SetRoomName -> setState { copy(newName = action.newName) } is RoomSettingsAction.SetRoomTopic -> setState { copy(newTopic = action.newTopic) } is RoomSettingsAction.SetRoomHistoryVisibility -> setState { copy(newHistoryVisibility = action.visibility) } + is RoomSettingsAction.SetRoomJoinRule -> handleSetRoomJoinRule(action) is RoomSettingsAction.Save -> saveSettings() is RoomSettingsAction.Cancel -> cancel() }.exhaustive } + private fun handleSetRoomJoinRule(action: RoomSettingsAction.SetRoomJoinRule) = withState { state -> + setState { + copy(newRoomJoinRules = RoomSettingsViewState.NewJoinRule( + action.roomJoinRule.takeIf { it != state.currentRoomJoinRules }, + action.roomGuestAccess.takeIf { it != state.currentGuestAccess } + )) + } + } + private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) { setState { deletePendingAvatar(this) @@ -202,6 +249,10 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: operationList.add(room.rx().updateHistoryReadability(state.newHistoryVisibility)) } + if (state.newRoomJoinRules.hasChanged()) { + operationList.add(room.rx().updateJoinRule(state.newRoomJoinRules.newJoinRules, state.newRoomJoinRules.newGuestAccess)) + } + Observable .fromIterable(operationList) .concatMapCompletable { it } @@ -212,7 +263,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: deletePendingAvatar(this) copy( avatarAction = RoomSettingsViewState.AvatarAction.None, - newHistoryVisibility = null + newHistoryVisibility = null, + newRoomJoinRules = RoomSettingsViewState.NewJoinRule() ) } _viewEvents.post(RoomSettingsViewEvents.Success) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt index fc9393d141..7403917d48 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewState.kt @@ -21,13 +21,17 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.roomprofile.RoomProfileArgs +import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomSummary data class RoomSettingsViewState( val roomId: String, // Default value: https://matrix.org/docs/spec/client_server/r0.6.1#id88 val currentHistoryVisibility: RoomHistoryVisibility = RoomHistoryVisibility.SHARED, + val currentRoomJoinRules: RoomJoinRules = RoomJoinRules.INVITE, + val currentGuestAccess: GuestAccess? = null, val roomSummary: Async = Uninitialized, val isLoading: Boolean = false, val currentRoomAvatarUrl: String? = null, @@ -35,6 +39,7 @@ data class RoomSettingsViewState( val newName: String? = null, val newTopic: String? = null, val newHistoryVisibility: RoomHistoryVisibility? = null, + val newRoomJoinRules: NewJoinRule = NewJoinRule(), val showSaveAction: Boolean = false, val actionPermissions: ActionPermissions = ActionPermissions() ) : MvRxState { @@ -45,7 +50,8 @@ data class RoomSettingsViewState( val canChangeAvatar: Boolean = false, val canChangeName: Boolean = false, val canChangeTopic: Boolean = false, - val canChangeHistoryVisibility: Boolean = false + val canChangeHistoryVisibility: Boolean = false, + val canChangeJoinRule: Boolean = false ) sealed class AvatarAction { @@ -54,4 +60,11 @@ data class RoomSettingsViewState( data class UpdateAvatar(val newAvatarUri: Uri, val newAvatarFileName: String) : AvatarAction() } + + data class NewJoinRule( + val newJoinRules: RoomJoinRules? = null, + val newGuestAccess: GuestAccess? = null + ) { + fun hasChanged() = newJoinRules != null || newGuestAccess != null + } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt new file mode 100644 index 0000000000..1c9018d9d1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt @@ -0,0 +1,36 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +import androidx.annotation.DrawableRes +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericAction +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules + +class RoomJoinRuleAction( + val roomJoinRule: RoomJoinRules, + val roomGuestAccess: GuestAccess?, + title: String, + @DrawableRes iconResId: Int, + isSelected: Boolean, + destructive: Boolean +) : BottomSheetGenericAction( + title = title, + iconResId = iconResId, + isSelected = isSelected, + destructive = destructive +) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt new file mode 100644 index 0000000000..4996187029 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt @@ -0,0 +1,73 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.ui.bottomsheet.BottomSheetGeneric +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController +import kotlinx.android.parcel.Parcelize +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules +import javax.inject.Inject + +@Parcelize +data class RoomJoinRuleBottomSheetArgs( + val currentRoomJoinRule: RoomJoinRules, + val currentGuestAccess: GuestAccess? +) : Parcelable + +class RoomJoinRuleBottomSheet : BottomSheetGeneric() { + + private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel + @Inject lateinit var controller: RoomJoinRuleController + @Inject lateinit var roomJoinRuleViewModelFactory: RoomJoinRuleViewModel.Factory + private val viewModel: RoomJoinRuleViewModel by fragmentViewModel(RoomJoinRuleViewModel::class) + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override fun getController(): BottomSheetGenericController = controller + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java) + } + + override fun didSelectAction(action: RoomJoinRuleAction) { + roomJoinRuleSharedActionViewModel.post(action) + dismiss() + } + + override fun invalidate() = withState(viewModel) { + controller.setData(it) + super.invalidate() + } + + companion object { + fun newInstance(currentRoomJoinRule: RoomJoinRules, currentGuestAccess: GuestAccess?): RoomJoinRuleBottomSheet { + return RoomJoinRuleBottomSheet().apply { + setArguments(RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, currentGuestAccess)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt new file mode 100644 index 0000000000..6fd481c91f --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt @@ -0,0 +1,58 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules +import javax.inject.Inject + +class RoomJoinRuleController @Inject constructor( + private val stringProvider: StringProvider +) : BottomSheetGenericController() { + + override fun getActions(state: RoomJoinRuleState): List { + return listOf( + RoomJoinRuleAction( + roomJoinRule = RoomJoinRules.INVITE, + roomGuestAccess = null, + title = stringProvider.getString(R.string.room_settings_room_access_entry_only_invited), + iconResId = 0, + isSelected = state.currentRoomJoinRule == RoomJoinRules.INVITE, + destructive = false + ), + RoomJoinRuleAction( + roomJoinRule = RoomJoinRules.PUBLIC, + roomGuestAccess = GuestAccess.Forbidden, + title = stringProvider.getString(R.string.room_settings_room_access_entry_anyone_with_link_apart_guest), + iconResId = 0, + isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.Forbidden, + destructive = false + ), + RoomJoinRuleAction( + roomJoinRule = RoomJoinRules.PUBLIC, + roomGuestAccess = GuestAccess.CanJoin, + title = stringProvider.getString(R.string.room_settings_room_access_entry_anyone_with_link_including_guest), + iconResId = 0, + isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.CanJoin, + destructive = false + ) + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt new file mode 100644 index 0000000000..934b0dfc76 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt @@ -0,0 +1,23 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +import im.vector.app.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +class RoomJoinRuleSharedActionViewModel @Inject constructor() + : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt new file mode 100644 index 0000000000..ec16b02d60 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt @@ -0,0 +1,32 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericState +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules + +data class RoomJoinRuleState( + val currentRoomJoinRule: RoomJoinRules = RoomJoinRules.INVITE, + val currentGuestAccess: GuestAccess? = null +) : BottomSheetGenericState() { + + constructor(args: RoomJoinRuleBottomSheetArgs) : this( + currentRoomJoinRule = args.currentRoomJoinRule, + currentGuestAccess = args.currentGuestAccess + ) +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt new file mode 100644 index 0000000000..08d1f8a231 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt @@ -0,0 +1,47 @@ +/* + * 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.app.features.roomprofile.settings.joinrule + +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.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel + +class RoomJoinRuleViewModel @AssistedInject constructor(@Assisted initialState: RoomJoinRuleState) + : VectorViewModel(initialState) { + + @AssistedInject.Factory + interface Factory { + fun create(initialState: RoomJoinRuleState): RoomJoinRuleViewModel + } + + companion object : MvRxViewModelFactory { + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleState): RoomJoinRuleViewModel? { + val fragment: RoomJoinRuleBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.roomJoinRuleViewModelFactory.create(state) + } + } + + override fun handle(action: EmptyAction) { + // No op + } +} diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index c653f58be9..e74b25057b 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1023,6 +1023,7 @@ Room History Readability Who can read history? Who can access this room? + Room access Room addresses From 476f721f5eaac9f9ecb10108a6486189fc114e96 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 22:43:51 +0100 Subject: [PATCH 55/73] Cleanup --- .../historyvisibility/RoomHistoryVisibilityAction.kt | 5 ++--- .../historyvisibility/RoomHistoryVisibilityController.kt | 3 +-- .../roomprofile/settings/joinrule/RoomJoinRuleAction.kt | 5 ++--- .../settings/joinrule/RoomJoinRuleController.kt | 9 +++------ 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt index f37964ae23..3c989a7dbe 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityAction.kt @@ -24,11 +24,10 @@ class RoomHistoryVisibilityAction( val roomHistoryVisibility: RoomHistoryVisibility, title: String, @DrawableRes iconResId: Int, - isSelected: Boolean, - destructive: Boolean + isSelected: Boolean ) : BottomSheetGenericAction( title = title, iconResId = iconResId, isSelected = isSelected, - destructive = destructive + destructive = false ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt index 9c9abc03a1..6727b0580c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -37,8 +37,7 @@ class RoomHistoryVisibilityController @Inject constructor( roomHistoryVisibility = roomHistoryVisibility, title = historyVisibilityFormatter.getSetting(roomHistoryVisibility), iconResId = 0, - isSelected = roomHistoryVisibility == state.currentRoomHistoryVisibility, - destructive = false + isSelected = roomHistoryVisibility == state.currentRoomHistoryVisibility ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt index 1c9018d9d1..6f71669002 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleAction.kt @@ -26,11 +26,10 @@ class RoomJoinRuleAction( val roomGuestAccess: GuestAccess?, title: String, @DrawableRes iconResId: Int, - isSelected: Boolean, - destructive: Boolean + isSelected: Boolean ) : BottomSheetGenericAction( title = title, iconResId = iconResId, isSelected = isSelected, - destructive = destructive + destructive = false ) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt index 6fd481c91f..1829707dae 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt @@ -34,24 +34,21 @@ class RoomJoinRuleController @Inject constructor( roomGuestAccess = null, title = stringProvider.getString(R.string.room_settings_room_access_entry_only_invited), iconResId = 0, - isSelected = state.currentRoomJoinRule == RoomJoinRules.INVITE, - destructive = false + isSelected = state.currentRoomJoinRule == RoomJoinRules.INVITE ), RoomJoinRuleAction( roomJoinRule = RoomJoinRules.PUBLIC, roomGuestAccess = GuestAccess.Forbidden, title = stringProvider.getString(R.string.room_settings_room_access_entry_anyone_with_link_apart_guest), iconResId = 0, - isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.Forbidden, - destructive = false + isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.Forbidden ), RoomJoinRuleAction( roomJoinRule = RoomJoinRules.PUBLIC, roomGuestAccess = GuestAccess.CanJoin, title = stringProvider.getString(R.string.room_settings_room_access_entry_anyone_with_link_including_guest), iconResId = 0, - isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.CanJoin, - destructive = false + isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.CanJoin ) ) } From 589c301606419377f999b207376653884eca52a5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 26 Nov 2020 22:56:37 +0100 Subject: [PATCH 56/73] Fix bad copyright --- .../im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt | 2 +- .../app/core/ui/bottomsheet/BottomSheetGenericController.kt | 2 +- .../ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt | 2 +- .../vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt index d2e1495f1b..da136fb072 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt index 2dfa4fc93a..903c568400 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt index ba8bd4abba..49147b954a 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericSharedActionViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt index b01216afad..38c81a7ef6 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericState.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 New Vector Ltd + * 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. From 056b9df65e6046ad45b6244025950b79ccdafac3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 27 Nov 2020 08:47:44 +0100 Subject: [PATCH 57/73] Add title to the new two bottom sheets --- .../BottomSheetGenericController.kt | 20 +++++++- .../ui/bottomsheet/BottomSheetTitleItem.kt | 49 +++++++++++++++++++ .../RoomHistoryVisibilityController.kt | 9 +++- .../joinrule/RoomJoinRuleController.kt | 2 + .../res/layout/item_bottom_sheet_title.xml | 40 +++++++++++++++ vector/src/main/res/values/strings.xml | 1 + 6 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetTitleItem.kt create mode 100644 vector/src/main/res/layout/item_bottom_sheet_title.xml diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt index 903c568400..a41a389dda 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt @@ -17,19 +17,37 @@ package im.vector.app.core.ui.bottomsheet import android.view.View import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.core.epoxy.dividerItem /** * Epoxy controller for generic bottom sheet actions */ -abstract class BottomSheetGenericController +abstract class BottomSheetGenericController : TypedEpoxyController() { var listener: Listener? = null + abstract fun getTitle(): String? + + open fun getSubTitle(): String? = null + abstract fun getActions(state: State): List override fun buildModels(state: State?) { state ?: return + // Title + getTitle()?.let { title -> + bottomSheetTitleItem { + id("title") + title(title) + subTitle(getSubTitle()) + } + + dividerItem { + id("title_separator") + } + } + // Actions getActions(state).forEach { action -> action.toBottomSheetItem() .listener(View.OnClickListener { listener?.didSelectAction(action) }) diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetTitleItem.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetTitleItem.kt new file mode 100644 index 0000000000..27fb634480 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetTitleItem.kt @@ -0,0 +1,49 @@ +/* + * 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.app.core.ui.bottomsheet + +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextOrHide + +/** + * A title for bottom sheet, with an optional subtitle. It does not include the bottom separator. + */ +@EpoxyModelClass(layout = R.layout.item_bottom_sheet_title) +abstract class BottomSheetTitleItem : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var title: String + + @EpoxyAttribute + var subTitle: String? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.title.text = title + holder.subtitle.setTextOrHide(subTitle) + } + + class Holder : VectorEpoxyHolder() { + val title by bind(R.id.itemBottomSheetTitleTitle) + val subtitle by bind(R.id.itemBottomSheetTitleSubtitle) + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt index 6727b0580c..a4899711f7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityController.kt @@ -16,15 +16,22 @@ package im.vector.app.features.roomprofile.settings.historyvisibility +import im.vector.app.R +import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController import im.vector.app.features.home.room.detail.timeline.format.RoomHistoryVisibilityFormatter import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import javax.inject.Inject class RoomHistoryVisibilityController @Inject constructor( - private val historyVisibilityFormatter: RoomHistoryVisibilityFormatter + private val historyVisibilityFormatter: RoomHistoryVisibilityFormatter, + private val stringProvider: StringProvider ) : BottomSheetGenericController() { + override fun getTitle() = stringProvider.getString(R.string.room_settings_room_read_history_rules_pref_dialog_title) + + override fun getSubTitle() = stringProvider.getString(R.string.room_settings_room_read_history_dialog_subtitle) + override fun getActions(state: RoomHistoryVisibilityState): List { return listOf( RoomHistoryVisibility.WORLD_READABLE, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt index 1829707dae..ab00396dbe 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt @@ -27,6 +27,8 @@ class RoomJoinRuleController @Inject constructor( private val stringProvider: StringProvider ) : BottomSheetGenericController() { + override fun getTitle() = stringProvider.getString(R.string.room_settings_room_access_rules_pref_dialog_title) + override fun getActions(state: RoomJoinRuleState): List { return listOf( RoomJoinRuleAction( diff --git a/vector/src/main/res/layout/item_bottom_sheet_title.xml b/vector/src/main/res/layout/item_bottom_sheet_title.xml new file mode 100644 index 0000000000..5113c43f39 --- /dev/null +++ b/vector/src/main/res/layout/item_bottom_sheet_title.xml @@ -0,0 +1,40 @@ + + + + + + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index e74b25057b..cff35b962e 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1022,6 +1022,7 @@ Room Access Room History Readability Who can read history? + Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged. Who can access this room? Room access From b78f1dbb93003c5ada02d11c0aa25a2d5fb98240 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 27 Nov 2020 08:59:38 +0100 Subject: [PATCH 58/73] Hide the icon area if there is no icon to display --- .../bottomsheet/BottomSheetActionItem.kt | 4 ++ .../BottomSheetGenericController.kt | 5 +- .../res/layout/item_bottom_sheet_action.xml | 53 +++++++++++-------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt index 3666cabce3..80792648f6 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetActionItem.kt @@ -44,6 +44,9 @@ abstract class BottomSheetActionItem : VectorEpoxyModel + val actions = getActions(state) + val showIcons = actions.any { it.iconResId > 0 } + actions.forEach { action -> action.toBottomSheetItem() + .showIcon(showIcons) .listener(View.OnClickListener { listener?.didSelectAction(action) }) .addTo(this) } diff --git a/vector/src/main/res/layout/item_bottom_sheet_action.xml b/vector/src/main/res/layout/item_bottom_sheet_action.xml index 8b5716cd8e..7456f50670 100644 --- a/vector/src/main/res/layout/item_bottom_sheet_action.xml +++ b/vector/src/main/res/layout/item_bottom_sheet_action.xml @@ -13,6 +13,7 @@ android:paddingEnd="@dimen/layout_horizontal_margin" android:paddingBottom="8dp"> + + tools:ignore="MissingPrefix" + tools:src="@drawable/ic_room_actions_notifications_all" /> - + app:layout_constraintStart_toEndOf="@id/actionIcon" + app:layout_constraintTop_toTopOf="parent"> + + + - + tools:ignore="MissingPrefix" + tools:visibility="visible" /> From c785ea63e76f64872a9a61c1a97e6e76b5f0d901 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 17:53:31 +0100 Subject: [PATCH 59/73] Update Change after release --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 86258ecc57..6afa782a07 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,9 +3,11 @@ Changes in Element 1.0.12 (2020-XX-XX) Features ✨: - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428) + - Room setting: update join rules and guest access (#2442) Improvements 🙌: - Add Setting Item to Change PIN (#2462) + - Improve room history visibility setting UX (#1579) Bugfix 🐛: - Double bottomsheet effect after verify with passphrase @@ -32,7 +34,6 @@ Features ✨: - Create DMs with users by scanning their QR code (#2025) - Add Invite friends quick invite actions (#2348) - Add friend by scanning QR code, show your code to friends (#2025) - - Room setting: update join rules and guest access (#2442) Improvements 🙌: - New room creation tile with quick action (#2346) @@ -44,7 +45,6 @@ Improvements 🙌: - Move "Enable Encryption" from room setting screen to room profile screen (#2394) - Home empty screens quick design update (#2347) - Improve Invite user screen (seamless search for matrix ID) - - Improve room history visibility setting UX (#1579) Bugfix 🐛: - Fix crash on AttachmentViewer (#2365) From c6ba2960287d51344b469be9529dc39e165b7a78 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 1 Dec 2020 14:48:10 +0100 Subject: [PATCH 60/73] Create an extension to apply the fix at several places --- .../java/im/vector/app/core/extensions/EditText.kt | 12 ++++++++++++ .../im/vector/app/features/form/FormEditTextItem.kt | 7 ++----- .../app/features/form/FormEditTextWithButtonItem.kt | 5 ++--- .../roomdirectory/createroom/RoomAliasEditItem.kt | 5 ++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt index 05b70def3d..33e7199334 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt @@ -57,3 +57,15 @@ fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_searc return@OnTouchListener false }) } + +/** + * Update the edit text value, only if necessary and move the cursor to the end of the text + */ +fun EditText.setTextSafe(value: String?) { + if (value != null && text.toString() != value) { + setText(value) + // To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426 + // Note: there is still a known bug if deleting char in the middle of the text, by long pressing on the backspace button. + setSelection(value.length) + } +} diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index eb8945d29c..68e2e6b371 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -26,6 +26,7 @@ import com.google.android.material.textfield.TextInputLayout import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextSafe import im.vector.app.core.platform.SimpleTextWatcher @EpoxyModelClass(layout = R.layout.item_form_text_input) @@ -65,11 +66,7 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputLayout.error = errorMessage // Update only if text is different and value is not null - if (value != null && holder.textInputEditText.text.toString() != value) { - holder.textInputEditText.setText(value) - // To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426 - holder.textInputEditText.setSelection(value?.length ?: 0) - } + holder.textInputEditText.setTextSafe(value) holder.textInputEditText.isEnabled = enabled inputType?.let { holder.textInputEditText.inputType = it } diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextWithButtonItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextWithButtonItem.kt index eadae3ba0c..08fc435e11 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextWithButtonItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextWithButtonItem.kt @@ -26,6 +26,7 @@ import com.google.android.material.textfield.TextInputLayout import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.core.extensions.setTextSafe import im.vector.app.core.platform.SimpleTextWatcher @EpoxyModelClass(layout = R.layout.item_form_text_input_with_button) @@ -61,9 +62,7 @@ abstract class FormEditTextWithButtonItem : VectorEpoxyModel() holder.textInputLayout.error = errorMessage // Update only if text is different and value is not null - if (value != null && holder.textInputEditText.text.toString() != value) { - holder.textInputEditText.setText(value) - } + holder.textInputEditText.setTextSafe(value) holder.textInputEditText.isEnabled = enabled holder.textInputEditText.addTextChangedListener(onTextChangeListener) holder.homeServerText.text = homeServer From aa6c7afbbdcc74716ca47e203a2e5e8f2372e5d2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 1 Dec 2020 16:19:50 +0100 Subject: [PATCH 61/73] Remove Status theme and handle migration or current status theme to light theme (#2424) --- CHANGES.md | 2 +- .../res/layout/activity_test_linkify.xml | 2 +- vector/src/debug/res/layout/demo_themes.xml | 10 -- .../features/themes/ActivityOtherThemes.kt | 8 +- .../vector/app/features/themes/ThemeUtils.kt | 18 +-- .../src/main/res/values-v23/theme_status.xml | 11 -- .../src/main/res/values-v27/theme_status.xml | 11 -- vector/src/main/res/values/array.xml | 2 - vector/src/main/res/values/colors_riot.xml | 15 --- vector/src/main/res/values/colors_riotx.xml | 3 - vector/src/main/res/values/styles_riot.xml | 8 -- vector/src/main/res/values/theme_status.xml | 118 ------------------ 12 files changed, 14 insertions(+), 194 deletions(-) delete mode 100644 vector/src/main/res/values-v23/theme_status.xml delete mode 100644 vector/src/main/res/values-v27/theme_status.xml delete mode 100644 vector/src/main/res/values/theme_status.xml diff --git a/CHANGES.md b/CHANGES.md index 6afa782a07..4d3f1c5b9b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,7 +25,7 @@ Test: - Other changes: - - + - Remove "Status.im" theme #2424 Changes in Element 1.0.11 (2020-11-27) =================================================== diff --git a/vector/src/debug/res/layout/activity_test_linkify.xml b/vector/src/debug/res/layout/activity_test_linkify.xml index bbaadb20a2..7e625ad08c 100644 --- a/vector/src/debug/res/layout/activity_test_linkify.xml +++ b/vector/src/debug/res/layout/activity_test_linkify.xml @@ -4,7 +4,7 @@ android:id="@+id/test_linkify_coordinator" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/riot_secondary_text_color_status" + android:background="#7F70808D" tools:context=".features.debug.TestLinkifyActivity"> - - - - - - \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/themes/ActivityOtherThemes.kt b/vector/src/main/java/im/vector/app/features/themes/ActivityOtherThemes.kt index 3aba6a4dad..847caeab4c 100644 --- a/vector/src/main/java/im/vector/app/features/themes/ActivityOtherThemes.kt +++ b/vector/src/main/java/im/vector/app/features/themes/ActivityOtherThemes.kt @@ -24,23 +24,19 @@ import im.vector.app.R * Note that style for light theme is default and is declared in the Android Manifest */ sealed class ActivityOtherThemes(@StyleRes val dark: Int, - @StyleRes val black: Int, - @StyleRes val status: Int) { + @StyleRes val black: Int) { object Default : ActivityOtherThemes( R.style.AppTheme_Dark, - R.style.AppTheme_Black, - R.style.AppTheme_Status + R.style.AppTheme_Black ) object AttachmentsPreview : ActivityOtherThemes( - R.style.AppTheme_AttachmentsPreview, R.style.AppTheme_AttachmentsPreview, R.style.AppTheme_AttachmentsPreview ) object VectorAttachmentsPreview : ActivityOtherThemes( - R.style.AppTheme_Transparent, R.style.AppTheme_Transparent, R.style.AppTheme_Transparent ) diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt index 18faa07954..bba6b9c253 100644 --- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt +++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt @@ -24,6 +24,7 @@ import android.view.Menu import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.core.content.ContextCompat +import androidx.core.content.edit import androidx.core.graphics.drawable.DrawableCompat import im.vector.app.R import im.vector.app.core.di.DefaultSharedPreferences @@ -41,7 +42,6 @@ object ThemeUtils { private const val THEME_DARK_VALUE = "dark" private const val THEME_LIGHT_VALUE = "light" private const val THEME_BLACK_VALUE = "black" - private const val THEME_STATUS_VALUE = "status" private var currentTheme = AtomicReference(null) @@ -58,9 +58,8 @@ object ThemeUtils { */ fun isLightTheme(context: Context): Boolean { return when (getApplicationTheme(context)) { - THEME_LIGHT_VALUE, - THEME_STATUS_VALUE -> true - else -> false + THEME_LIGHT_VALUE -> true + else -> false } } @@ -73,8 +72,13 @@ object ThemeUtils { fun getApplicationTheme(context: Context): String { val currentTheme = this.currentTheme.get() return if (currentTheme == null) { - val themeFromPref = DefaultSharedPreferences.getInstance(context) - .getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE + val prefs = DefaultSharedPreferences.getInstance(context) + var themeFromPref = prefs.getString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) ?: THEME_LIGHT_VALUE + if (themeFromPref == "status") { + // Migrate to light theme, which is the closest theme + themeFromPref = THEME_LIGHT_VALUE + prefs.edit { putString(APPLICATION_THEME_KEY, THEME_LIGHT_VALUE) } + } this.currentTheme.set(themeFromPref) themeFromPref } else { @@ -92,7 +96,6 @@ object ThemeUtils { when (aTheme) { THEME_DARK_VALUE -> context.setTheme(R.style.AppTheme_Dark) THEME_BLACK_VALUE -> context.setTheme(R.style.AppTheme_Black) - THEME_STATUS_VALUE -> context.setTheme(R.style.AppTheme_Status) else -> context.setTheme(R.style.AppTheme_Light) } @@ -109,7 +112,6 @@ object ThemeUtils { when (getApplicationTheme(activity)) { THEME_DARK_VALUE -> activity.setTheme(otherThemes.dark) THEME_BLACK_VALUE -> activity.setTheme(otherThemes.black) - THEME_STATUS_VALUE -> activity.setTheme(otherThemes.status) } mColorByAttr.clear() diff --git a/vector/src/main/res/values-v23/theme_status.xml b/vector/src/main/res/values-v23/theme_status.xml deleted file mode 100644 index 236864d4b8..0000000000 --- a/vector/src/main/res/values-v23/theme_status.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - -